RemoteViews.java revision da996f390e17e16f2dfa60e972e7ebc4f868f37e
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.widget; 18 19import android.app.PendingIntent; 20import android.app.PendingIntent.CanceledException; 21import android.content.Context; 22import android.content.pm.PackageManager.NameNotFoundException; 23import android.content.res.Resources; 24import android.graphics.Bitmap; 25import android.graphics.PorterDuff; 26import android.graphics.drawable.BitmapDrawable; 27import android.graphics.drawable.Drawable; 28import android.net.Uri; 29import android.os.Parcel; 30import android.os.Parcelable; 31import android.text.TextUtils; 32import android.util.Log; 33import android.view.LayoutInflater; 34import android.view.View; 35import android.view.ViewGroup; 36import android.view.LayoutInflater.Filter; 37import android.view.View.OnClickListener; 38import android.view.animation.Animation; 39import android.view.animation.AnimationUtils; 40 41import java.lang.annotation.ElementType; 42import java.lang.annotation.Retention; 43import java.lang.annotation.RetentionPolicy; 44import java.lang.annotation.Target; 45import java.util.ArrayList; 46 47 48/** 49 * A class that describes a view hierarchy that can be displayed in 50 * another process. The hierarchy is inflated from a layout resource 51 * file, and this class provides some basic operations for modifying 52 * the content of the inflated hierarchy. 53 */ 54public class RemoteViews implements Parcelable, Filter { 55 56 private static final String LOG_TAG = "RemoteViews"; 57 58 /** 59 * The package name of the package containing the layout 60 * resource. (Added to the parcel) 61 */ 62 private String mPackage; 63 64 /** 65 * The resource ID of the layout file. (Added to the parcel) 66 */ 67 private int mLayoutId; 68 69 /** 70 * The Context object used to inflate the layout file. Also may 71 * be used by actions if they need access to the senders resources. 72 */ 73 private Context mContext; 74 75 /** 76 * An array of actions to perform on the view tree once it has been 77 * inflated 78 */ 79 private ArrayList<Action> mActions; 80 81 82 /** 83 * This annotation indicates that a subclass of View is alllowed to be used with the 84 * {@link android.widget.RemoteViews} mechanism. 85 */ 86 @Target({ ElementType.TYPE }) 87 @Retention(RetentionPolicy.RUNTIME) 88 public @interface RemoteView { 89 } 90 91 /** 92 * Exception to send when something goes wrong executing an action 93 * 94 */ 95 public static class ActionException extends RuntimeException { 96 public ActionException(String message) { 97 super(message); 98 } 99 } 100 101 /** 102 * Base class for all actions that can be performed on an 103 * inflated view. 104 * 105 */ 106 private abstract static class Action implements Parcelable { 107 public abstract void apply(View root) throws ActionException; 108 109 public int describeContents() { 110 return 0; 111 } 112 }; 113 114 /** 115 * Equivalent to calling View.setVisibility 116 */ 117 private class SetViewVisibility extends Action { 118 public SetViewVisibility(int id, int vis) { 119 viewId = id; 120 visibility = vis; 121 } 122 123 public SetViewVisibility(Parcel parcel) { 124 viewId = parcel.readInt(); 125 visibility = parcel.readInt(); 126 } 127 128 public void writeToParcel(Parcel dest, int flags) { 129 dest.writeInt(TAG); 130 dest.writeInt(viewId); 131 dest.writeInt(visibility); 132 } 133 134 @Override 135 public void apply(View root) { 136 View target = root.findViewById(viewId); 137 if (target != null) { 138 target.setVisibility(visibility); 139 } 140 } 141 142 private int viewId; 143 private int visibility; 144 public final static int TAG = 0; 145 } 146 147 /** 148 * Equivalent to calling TextView.setText 149 */ 150 private class SetTextViewText extends Action { 151 public SetTextViewText(int id, CharSequence t) { 152 viewId = id; 153 text = t; 154 } 155 156 public SetTextViewText(Parcel parcel) { 157 viewId = parcel.readInt(); 158 text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 159 } 160 161 public void writeToParcel(Parcel dest, int flags) { 162 dest.writeInt(TAG); 163 dest.writeInt(viewId); 164 TextUtils.writeToParcel(text, dest, flags); 165 } 166 167 @Override 168 public void apply(View root) { 169 TextView target = (TextView) root.findViewById(viewId); 170 if (target != null) { 171 target.setText(text); 172 } 173 } 174 175 int viewId; 176 CharSequence text; 177 public final static int TAG = 1; 178 } 179 180 /** 181 * Equivalent to calling ImageView.setResource 182 */ 183 private class SetImageViewResource extends Action { 184 public SetImageViewResource(int id, int src) { 185 viewId = id; 186 srcId = src; 187 } 188 189 public SetImageViewResource(Parcel parcel) { 190 viewId = parcel.readInt(); 191 srcId = parcel.readInt(); 192 } 193 194 public void writeToParcel(Parcel dest, int flags) { 195 dest.writeInt(TAG); 196 dest.writeInt(viewId); 197 dest.writeInt(srcId); 198 } 199 200 @Override 201 public void apply(View root) { 202 ImageView target = (ImageView) root.findViewById(viewId); 203 Drawable d = mContext.getResources().getDrawable(srcId); 204 if (target != null) { 205 target.setImageDrawable(d); 206 } 207 } 208 209 int viewId; 210 int srcId; 211 public final static int TAG = 2; 212 } 213 214 /** 215 * Equivalent to calling ImageView.setImageURI 216 */ 217 private class SetImageViewUri extends Action { 218 public SetImageViewUri(int id, Uri u) { 219 viewId = id; 220 uri = u; 221 } 222 223 public SetImageViewUri(Parcel parcel) { 224 viewId = parcel.readInt(); 225 uri = Uri.CREATOR.createFromParcel(parcel); 226 } 227 228 public void writeToParcel(Parcel dest, int flags) { 229 dest.writeInt(TAG); 230 dest.writeInt(viewId); 231 Uri.writeToParcel(dest, uri); 232 } 233 234 @Override 235 public void apply(View root) { 236 ImageView target = (ImageView) root.findViewById(viewId); 237 if (target != null) { 238 target.setImageURI(uri); 239 } 240 } 241 242 int viewId; 243 Uri uri; 244 public final static int TAG = 3; 245 } 246 247 /** 248 * Equivalent to calling ImageView.setImageBitmap 249 */ 250 private class SetImageViewBitmap extends Action { 251 public SetImageViewBitmap(int id, Bitmap src) { 252 viewId = id; 253 bitmap = src; 254 } 255 256 public SetImageViewBitmap(Parcel parcel) { 257 viewId = parcel.readInt(); 258 bitmap = Bitmap.CREATOR.createFromParcel(parcel); 259 } 260 261 public void writeToParcel(Parcel dest, int flags) { 262 dest.writeInt(TAG); 263 dest.writeInt(viewId); 264 if (bitmap != null) { 265 bitmap.writeToParcel(dest, flags); 266 } 267 } 268 269 @Override 270 public void apply(View root) { 271 if (bitmap != null) { 272 ImageView target = (ImageView) root.findViewById(viewId); 273 Drawable d = new BitmapDrawable(bitmap); 274 if (target != null) { 275 target.setImageDrawable(d); 276 } 277 } 278 } 279 280 int viewId; 281 Bitmap bitmap; 282 public final static int TAG = 4; 283 } 284 285 /** 286 * Equivalent to calling Chronometer.setBase, Chronometer.setFormat, 287 * and Chronometer.start/stop. 288 */ 289 private class SetChronometer extends Action { 290 public SetChronometer(int id, long base, String format, boolean running) { 291 this.viewId = id; 292 this.base = base; 293 this.format = format; 294 this.running = running; 295 } 296 297 public SetChronometer(Parcel parcel) { 298 viewId = parcel.readInt(); 299 base = parcel.readLong(); 300 format = parcel.readString(); 301 running = parcel.readInt() != 0; 302 } 303 304 public void writeToParcel(Parcel dest, int flags) { 305 dest.writeInt(TAG); 306 dest.writeInt(viewId); 307 dest.writeLong(base); 308 dest.writeString(format); 309 dest.writeInt(running ? 1 : 0); 310 } 311 312 @Override 313 public void apply(View root) { 314 Chronometer target = (Chronometer) root.findViewById(viewId); 315 if (target != null) { 316 target.setBase(base); 317 target.setFormat(format); 318 if (running) { 319 target.start(); 320 } else { 321 target.stop(); 322 } 323 } 324 } 325 326 int viewId; 327 boolean running; 328 long base; 329 String format; 330 331 public final static int TAG = 5; 332 } 333 334 /** 335 * Equivalent to calling ProgressBar.setMax, ProgressBar.setProgress and 336 * ProgressBar.setIndeterminate 337 */ 338 private class SetProgressBar extends Action { 339 public SetProgressBar(int id, int max, int progress, boolean indeterminate) { 340 this.viewId = id; 341 this.progress = progress; 342 this.max = max; 343 this.indeterminate = indeterminate; 344 } 345 346 public SetProgressBar(Parcel parcel) { 347 viewId = parcel.readInt(); 348 progress = parcel.readInt(); 349 max = parcel.readInt(); 350 indeterminate = parcel.readInt() != 0; 351 } 352 353 public void writeToParcel(Parcel dest, int flags) { 354 dest.writeInt(TAG); 355 dest.writeInt(viewId); 356 dest.writeInt(progress); 357 dest.writeInt(max); 358 dest.writeInt(indeterminate ? 1 : 0); 359 } 360 361 @Override 362 public void apply(View root) { 363 ProgressBar target = (ProgressBar) root.findViewById(viewId); 364 if (target != null) { 365 target.setIndeterminate(indeterminate); 366 if (!indeterminate) { 367 target.setMax(max); 368 target.setProgress(progress); 369 } 370 } 371 } 372 373 int viewId; 374 boolean indeterminate; 375 int progress; 376 int max; 377 378 public final static int TAG = 6; 379 } 380 381 /** 382 * Equivalent to calling 383 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)} 384 * to launch the provided {@link PendingIntent}. 385 */ 386 private class SetOnClickPendingIntent extends Action { 387 public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) { 388 this.viewId = id; 389 this.pendingIntent = pendingIntent; 390 } 391 392 public SetOnClickPendingIntent(Parcel parcel) { 393 viewId = parcel.readInt(); 394 pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel); 395 } 396 397 public void writeToParcel(Parcel dest, int flags) { 398 dest.writeInt(TAG); 399 dest.writeInt(viewId); 400 pendingIntent.writeToParcel(dest, 0 /* no flags */); 401 } 402 403 @Override 404 public void apply(View root) { 405 final View target = root.findViewById(viewId); 406 if (target != null && pendingIntent != null) { 407 OnClickListener listener = new OnClickListener() { 408 public void onClick(View v) { 409 try { 410 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? 411 pendingIntent.send(); 412 } catch (CanceledException e) { 413 throw new ActionException(e.toString()); 414 } 415 } 416 }; 417 target.setOnClickListener(listener); 418 } 419 } 420 421 int viewId; 422 PendingIntent pendingIntent; 423 424 public final static int TAG = 7; 425 } 426 427 /** 428 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, 429 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, 430 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view. 431 * <p> 432 * These operations will be performed on the {@link Drawable} returned by the 433 * target {@link View#getBackground()} by default. If targetBackground is false, 434 * we assume the target is an {@link ImageView} and try applying the operations 435 * to {@link ImageView#getDrawable()}. 436 * <p> 437 * You can omit specific calls by marking their values with null or -1. 438 */ 439 private class SetDrawableParameters extends Action { 440 public SetDrawableParameters(int id, boolean targetBackground, int alpha, 441 int colorFilter, PorterDuff.Mode mode, int level) { 442 this.viewId = id; 443 this.targetBackground = targetBackground; 444 this.alpha = alpha; 445 this.colorFilter = colorFilter; 446 this.filterMode = mode; 447 this.level = level; 448 } 449 450 public SetDrawableParameters(Parcel parcel) { 451 viewId = parcel.readInt(); 452 targetBackground = parcel.readInt() != 0; 453 alpha = parcel.readInt(); 454 colorFilter = parcel.readInt(); 455 boolean hasMode = parcel.readInt() != 0; 456 if (hasMode) { 457 filterMode = PorterDuff.Mode.valueOf(parcel.readString()); 458 } else { 459 filterMode = null; 460 } 461 level = parcel.readInt(); 462 } 463 464 public void writeToParcel(Parcel dest, int flags) { 465 dest.writeInt(TAG); 466 dest.writeInt(viewId); 467 dest.writeInt(targetBackground ? 1 : 0); 468 dest.writeInt(alpha); 469 dest.writeInt(colorFilter); 470 if (filterMode != null) { 471 dest.writeInt(1); 472 dest.writeString(filterMode.toString()); 473 } else { 474 dest.writeInt(0); 475 } 476 dest.writeInt(level); 477 } 478 479 @Override 480 public void apply(View root) { 481 final View target = root.findViewById(viewId); 482 if (target == null) { 483 return; 484 } 485 486 // Pick the correct drawable to modify for this view 487 Drawable targetDrawable = null; 488 if (targetBackground) { 489 targetDrawable = target.getBackground(); 490 } else if (target instanceof ImageView) { 491 ImageView imageView = (ImageView) target; 492 targetDrawable = imageView.getDrawable(); 493 } 494 495 // Perform modifications only if values are set correctly 496 if (alpha != -1) { 497 targetDrawable.setAlpha(alpha); 498 } 499 if (colorFilter != -1 && filterMode != null) { 500 targetDrawable.setColorFilter(colorFilter, filterMode); 501 } 502 if (level != -1) { 503 targetDrawable.setLevel(level); 504 } 505 } 506 507 int viewId; 508 boolean targetBackground; 509 int alpha; 510 int colorFilter; 511 PorterDuff.Mode filterMode; 512 int level; 513 514 public final static int TAG = 8; 515 } 516 517 /** 518 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}. 519 */ 520 private class SetTextColor extends Action { 521 public SetTextColor(int id, int color) { 522 this.viewId = id; 523 this.color = color; 524 } 525 526 public SetTextColor(Parcel parcel) { 527 viewId = parcel.readInt(); 528 color = parcel.readInt(); 529 } 530 531 public void writeToParcel(Parcel dest, int flags) { 532 dest.writeInt(TAG); 533 dest.writeInt(viewId); 534 dest.writeInt(color); 535 } 536 537 @Override 538 public void apply(View root) { 539 final View target = root.findViewById(viewId); 540 if (target instanceof TextView) { 541 final TextView textView = (TextView) target; 542 textView.setTextColor(color); 543 } 544 } 545 546 int viewId; 547 int color; 548 549 public final static int TAG = 9; 550 } 551 552 /** 553 * Equivalent to calling {@link android.widget.ViewFlipper#startFlipping()} 554 * or {@link android.widget.ViewFlipper#stopFlipping()} along with 555 * {@link android.widget.ViewFlipper#setFlipInterval(int)}. 556 */ 557 private class SetFlipping extends Action { 558 public SetFlipping(int id, boolean flipping, int milliseconds) { 559 this.viewId = id; 560 this.flipping = flipping; 561 this.milliseconds = milliseconds; 562 } 563 564 public SetFlipping(Parcel parcel) { 565 viewId = parcel.readInt(); 566 flipping = parcel.readInt() != 0; 567 milliseconds = parcel.readInt(); 568 } 569 570 public void writeToParcel(Parcel dest, int flags) { 571 dest.writeInt(TAG); 572 dest.writeInt(viewId); 573 dest.writeInt(flipping ? 1 : 0); 574 dest.writeInt(milliseconds); 575 } 576 577 @Override 578 public void apply(View root) { 579 final View target = root.findViewById(viewId); 580 if (target instanceof ViewFlipper) { 581 final ViewFlipper flipper = (ViewFlipper) target; 582 if (milliseconds != -1) { 583 flipper.setFlipInterval(milliseconds); 584 } 585 if (flipping) { 586 flipper.startFlipping(); 587 } else { 588 flipper.stopFlipping(); 589 } 590 } 591 } 592 593 int viewId; 594 boolean flipping; 595 int milliseconds; 596 597 public final static int TAG = 10; 598 } 599 600 /** 601 * Create a new RemoteViews object that will display the views contained 602 * in the specified layout file. 603 * 604 * @param packageName Name of the package that contains the layout resource 605 * @param layoutId The id of the layout resource 606 */ 607 public RemoteViews(String packageName, int layoutId) { 608 mPackage = packageName; 609 mLayoutId = layoutId; 610 } 611 612 /** 613 * Reads a RemoteViews object from a parcel. 614 * 615 * @param parcel 616 */ 617 public RemoteViews(Parcel parcel) { 618 mPackage = parcel.readString(); 619 mLayoutId = parcel.readInt(); 620 int count = parcel.readInt(); 621 if (count > 0) { 622 mActions = new ArrayList<Action>(count); 623 for (int i=0; i<count; i++) { 624 int tag = parcel.readInt(); 625 switch (tag) { 626 case SetViewVisibility.TAG: 627 mActions.add(new SetViewVisibility(parcel)); 628 break; 629 case SetTextViewText.TAG: 630 mActions.add(new SetTextViewText(parcel)); 631 break; 632 case SetImageViewResource.TAG: 633 mActions.add(new SetImageViewResource(parcel)); 634 break; 635 case SetImageViewUri.TAG: 636 mActions.add(new SetImageViewUri(parcel)); 637 break; 638 case SetImageViewBitmap.TAG: 639 mActions.add(new SetImageViewBitmap(parcel)); 640 break; 641 case SetChronometer.TAG: 642 mActions.add(new SetChronometer(parcel)); 643 break; 644 case SetProgressBar.TAG: 645 mActions.add(new SetProgressBar(parcel)); 646 break; 647 case SetOnClickPendingIntent.TAG: 648 mActions.add(new SetOnClickPendingIntent(parcel)); 649 break; 650 case SetDrawableParameters.TAG: 651 mActions.add(new SetDrawableParameters(parcel)); 652 break; 653 case SetTextColor.TAG: 654 mActions.add(new SetTextColor(parcel)); 655 break; 656 case SetFlipping.TAG: 657 mActions.add(new SetFlipping(parcel)); 658 break; 659 default: 660 throw new ActionException("Tag " + tag + "not found"); 661 } 662 } 663 } 664 } 665 666 public String getPackage() { 667 return mPackage; 668 } 669 670 public int getLayoutId() { 671 return mLayoutId; 672 } 673 674 /** 675 * Add an action to be executed on the remote side when apply is called. 676 * 677 * @param a The action to add 678 */ 679 private void addAction(Action a) { 680 if (mActions == null) { 681 mActions = new ArrayList<Action>(); 682 } 683 mActions.add(a); 684 } 685 686 /** 687 * Equivalent to calling View.setVisibility 688 * 689 * @param viewId The id of the view whose visibility should change 690 * @param visibility The new visibility for the view 691 */ 692 public void setViewVisibility(int viewId, int visibility) { 693 addAction(new SetViewVisibility(viewId, visibility)); 694 } 695 696 /** 697 * Equivalent to calling TextView.setText 698 * 699 * @param viewId The id of the view whose text should change 700 * @param text The new text for the view 701 */ 702 public void setTextViewText(int viewId, CharSequence text) { 703 addAction(new SetTextViewText(viewId, text)); 704 } 705 706 /** 707 * Equivalent to calling ImageView.setImageResource 708 * 709 * @param viewId The id of the view whose drawable should change 710 * @param srcId The new resource id for the drawable 711 */ 712 public void setImageViewResource(int viewId, int srcId) { 713 addAction(new SetImageViewResource(viewId, srcId)); 714 } 715 716 /** 717 * Equivalent to calling ImageView.setImageURI 718 * 719 * @param viewId The id of the view whose drawable should change 720 * @param uri The Uri for the image 721 */ 722 public void setImageViewUri(int viewId, Uri uri) { 723 addAction(new SetImageViewUri(viewId, uri)); 724 } 725 726 /** 727 * Equivalent to calling ImageView.setImageBitmap 728 * 729 * @param viewId The id of the view whose drawable should change 730 * @param bitmap The new Bitmap for the drawable 731 */ 732 public void setImageViewBitmap(int viewId, Bitmap bitmap) { 733 addAction(new SetImageViewBitmap(viewId, bitmap)); 734 } 735 736 /** 737 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase}, 738 * {@link Chronometer#setFormat Chronometer.setFormat}, 739 * and {@link Chronometer#start Chronometer.start()} or 740 * {@link Chronometer#stop Chronometer.stop()}. 741 * 742 * @param viewId The id of the view whose text should change 743 * @param base The time at which the timer would have read 0:00. This 744 * time should be based off of 745 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}. 746 * @param format The Chronometer format string, or null to 747 * simply display the timer value. 748 * @param running True if you want the clock to be running, false if not. 749 */ 750 public void setChronometer(int viewId, long base, String format, boolean running) { 751 addAction(new SetChronometer(viewId, base, format, running)); 752 } 753 754 /** 755 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax}, 756 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and 757 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate} 758 * 759 * @param viewId The id of the view whose text should change 760 * @param max The 100% value for the progress bar 761 * @param progress The current value of the progress bar. 762 * @param indeterminate True if the progress bar is indeterminate, 763 * false if not. 764 */ 765 public void setProgressBar(int viewId, int max, int progress, 766 boolean indeterminate) { 767 addAction(new SetProgressBar(viewId, max, progress, indeterminate)); 768 } 769 770 /** 771 * Equivalent to calling 772 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)} 773 * to launch the provided {@link PendingIntent}. 774 * 775 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked 776 * @param pendingIntent The {@link PendingIntent} to send when user clicks 777 */ 778 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) { 779 addAction(new SetOnClickPendingIntent(viewId, pendingIntent)); 780 } 781 782 /** 783 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, 784 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, 785 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given 786 * view. 787 * <p> 788 * You can omit specific calls by marking their values with null or -1. 789 * 790 * @param viewId The id of the view that contains the target 791 * {@link Drawable} 792 * @param targetBackground If true, apply these parameters to the 793 * {@link Drawable} returned by 794 * {@link android.view.View#getBackground()}. Otherwise, assume 795 * the target view is an {@link ImageView} and apply them to 796 * {@link ImageView#getDrawable()}. 797 * @param alpha Specify an alpha value for the drawable, or -1 to leave 798 * unchanged. 799 * @param colorFilter Specify a color for a 800 * {@link android.graphics.ColorFilter} for this drawable, or -1 801 * to leave unchanged. 802 * @param mode Specify a PorterDuff mode for this drawable, or null to leave 803 * unchanged. 804 * @param level Specify the level for the drawable, or -1 to leave 805 * unchanged. 806 */ 807 public void setDrawableParameters(int viewId, boolean targetBackground, int alpha, 808 int colorFilter, PorterDuff.Mode mode, int level) { 809 addAction(new SetDrawableParameters(viewId, targetBackground, alpha, 810 colorFilter, mode, level)); 811 } 812 813 /** 814 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}. 815 * 816 * @param viewId The id of the view whose text should change 817 * @param color Sets the text color for all the states (normal, selected, 818 * focused) to be this color. 819 */ 820 public void setTextColor(int viewId, int color) { 821 addAction(new SetTextColor(viewId, color)); 822 } 823 824 /** 825 * Equivalent to calling {@link android.widget.ViewFlipper#startFlipping()} 826 * or {@link android.widget.ViewFlipper#stopFlipping()} along with 827 * {@link android.widget.ViewFlipper#setFlipInterval(int)}. 828 * 829 * @param viewId The id of the view to apply changes to 830 * @param flipping True means we should 831 * {@link android.widget.ViewFlipper#startFlipping()}, otherwise 832 * {@link android.widget.ViewFlipper#stopFlipping()}. 833 * @param milliseconds How long to wait before flipping to the next view, or 834 * -1 to leave unchanged. 835 */ 836 public void setFlipping(int viewId, boolean flipping, int milliseconds) { 837 addAction(new SetFlipping(viewId, flipping, milliseconds)); 838 } 839 840 /** 841 * Inflates the view hierarchy represented by this object and applies 842 * all of the actions. 843 * 844 * <p><strong>Caller beware: this may throw</strong> 845 * 846 * @param context Default context to use 847 * @param parent Parent that the resulting view hierarchy will be attached to. This method 848 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate. 849 * @return The inflated view hierarchy 850 */ 851 public View apply(Context context, ViewGroup parent) { 852 View result = null; 853 854 Context c = prepareContext(context); 855 856 Resources r = c.getResources(); 857 LayoutInflater inflater = (LayoutInflater) c 858 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 859 860 inflater = inflater.cloneInContext(c); 861 inflater.setFilter(this); 862 863 result = inflater.inflate(mLayoutId, parent, false); 864 865 performApply(result); 866 867 return result; 868 } 869 870 /** 871 * Applies all of the actions to the provided view. 872 * 873 * <p><strong>Caller beware: this may throw</strong> 874 * 875 * @param v The view to apply the actions to. This should be the result of 876 * the {@link #apply(Context,ViewGroup)} call. 877 */ 878 public void reapply(Context context, View v) { 879 prepareContext(context); 880 performApply(v); 881 } 882 883 private void performApply(View v) { 884 if (mActions != null) { 885 final int count = mActions.size(); 886 for (int i = 0; i < count; i++) { 887 Action a = mActions.get(i); 888 a.apply(v); 889 } 890 } 891 } 892 893 private Context prepareContext(Context context) { 894 Context c = null; 895 String packageName = mPackage; 896 897 if (packageName != null) { 898 try { 899 c = context.createPackageContext(packageName, 0); 900 } catch (NameNotFoundException e) { 901 Log.e(LOG_TAG, "Package name " + packageName + " not found"); 902 c = context; 903 } 904 } else { 905 c = context; 906 } 907 908 mContext = c; 909 910 return c; 911 } 912 913 /* (non-Javadoc) 914 * Used to restrict the views which can be inflated 915 * 916 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class) 917 */ 918 public boolean onLoadClass(Class clazz) { 919 return clazz.isAnnotationPresent(RemoteView.class); 920 } 921 922 public int describeContents() { 923 return 0; 924 } 925 926 public void writeToParcel(Parcel dest, int flags) { 927 dest.writeString(mPackage); 928 dest.writeInt(mLayoutId); 929 int count; 930 if (mActions != null) { 931 count = mActions.size(); 932 } else { 933 count = 0; 934 } 935 dest.writeInt(count); 936 for (int i=0; i<count; i++) { 937 Action a = mActions.get(i); 938 a.writeToParcel(dest, 0); 939 } 940 } 941 942 /** 943 * Parcelable.Creator that instantiates RemoteViews objects 944 */ 945 public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() { 946 public RemoteViews createFromParcel(Parcel parcel) { 947 return new RemoteViews(parcel); 948 } 949 950 public RemoteViews[] newArray(int size) { 951 return new RemoteViews[size]; 952 } 953 }; 954} 955