RemoteViews.java revision c431c156bbb5c59f921c031e56744e5d6f12a77a
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.ActivityOptions; 20import android.app.PendingIntent; 21import android.appwidget.AppWidgetHostView; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentSender; 25import android.content.pm.PackageManager.NameNotFoundException; 26import android.content.res.Configuration; 27import android.graphics.Bitmap; 28import android.graphics.PorterDuff; 29import android.graphics.Rect; 30import android.graphics.drawable.Drawable; 31import android.net.Uri; 32import android.os.Bundle; 33import android.os.Parcel; 34import android.os.Parcelable; 35import android.text.TextUtils; 36import android.util.Log; 37import android.view.LayoutInflater; 38import android.view.LayoutInflater.Filter; 39import android.view.RemotableViewMethod; 40import android.view.View; 41import android.view.View.OnClickListener; 42import android.view.ViewGroup; 43import android.widget.AdapterView.OnItemClickListener; 44 45import java.lang.annotation.ElementType; 46import java.lang.annotation.Retention; 47import java.lang.annotation.RetentionPolicy; 48import java.lang.annotation.Target; 49import java.lang.reflect.Method; 50import java.util.ArrayList; 51 52 53/** 54 * A class that describes a view hierarchy that can be displayed in 55 * another process. The hierarchy is inflated from a layout resource 56 * file, and this class provides some basic operations for modifying 57 * the content of the inflated hierarchy. 58 */ 59public class RemoteViews implements Parcelable, Filter { 60 61 private static final String LOG_TAG = "RemoteViews"; 62 63 /** 64 * The intent extra that contains the appWidgetId. 65 * @hide 66 */ 67 static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId"; 68 69 /** 70 * The package name of the package containing the layout 71 * resource. (Added to the parcel) 72 */ 73 private final String mPackage; 74 75 /** 76 * The resource ID of the layout file. (Added to the parcel) 77 */ 78 private final int mLayoutId; 79 80 /** 81 * An array of actions to perform on the view tree once it has been 82 * inflated 83 */ 84 private ArrayList<Action> mActions; 85 86 /** 87 * A class to keep track of memory usage by this RemoteViews 88 */ 89 private MemoryUsageCounter mMemoryUsageCounter; 90 91 /** 92 * Maps bitmaps to unique indicies to avoid Bitmap duplication. 93 */ 94 private BitmapCache mBitmapCache; 95 96 /** 97 * Indicates whether or not this RemoteViews object is contained as a child of any other 98 * RemoteViews. 99 */ 100 private boolean mIsRoot = true; 101 102 /** 103 * Constants to whether or not this RemoteViews is composed of a landscape and portrait 104 * RemoteViews. 105 */ 106 private static final int MODE_NORMAL = 0; 107 private static final int MODE_HAS_LANDSCAPE_AND_PORTRAIT = 1; 108 109 /** 110 * Used in conjunction with the special constructor 111 * {@link #RemoteViews(RemoteViews, RemoteViews)} to keep track of the landscape and portrait 112 * RemoteViews. 113 */ 114 private RemoteViews mLandscape = null; 115 private RemoteViews mPortrait = null; 116 117 /** 118 * This flag indicates whether this RemoteViews object is being created from a 119 * RemoteViewsService for use as a child of a widget collection. This flag is used 120 * to determine whether or not certain features are available, in particular, 121 * setting on click extras and setting on click pending intents. The former is enabled, 122 * and the latter disabled when this flag is true. 123 */ 124 private boolean mIsWidgetCollectionChild = false; 125 126 /** 127 * This annotation indicates that a subclass of View is alllowed to be used 128 * with the {@link RemoteViews} mechanism. 129 */ 130 @Target({ ElementType.TYPE }) 131 @Retention(RetentionPolicy.RUNTIME) 132 public @interface RemoteView { 133 } 134 135 /** 136 * Exception to send when something goes wrong executing an action 137 * 138 */ 139 public static class ActionException extends RuntimeException { 140 public ActionException(Exception ex) { 141 super(ex); 142 } 143 public ActionException(String message) { 144 super(message); 145 } 146 } 147 148 /** 149 * Base class for all actions that can be performed on an 150 * inflated view. 151 * 152 * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! 153 */ 154 private abstract static class Action implements Parcelable { 155 public abstract void apply(View root, ViewGroup rootParent) throws ActionException; 156 157 public int describeContents() { 158 return 0; 159 } 160 161 /** 162 * Overridden by each class to report on it's own memory usage 163 */ 164 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) { 165 // We currently only calculate Bitmap memory usage, so by default, don't do anything 166 // here 167 return; 168 } 169 170 protected boolean startIntentSafely(View view, PendingIntent pendingIntent, 171 Intent fillInIntent) { 172 try { 173 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? 174 Context context = view.getContext(); 175 ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view, 176 0, 0, 177 view.getMeasuredWidth(), view.getMeasuredHeight()); 178 context.startIntentSender( 179 pendingIntent.getIntentSender(), fillInIntent, 180 Intent.FLAG_ACTIVITY_NEW_TASK, 181 Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle()); 182 } catch (IntentSender.SendIntentException e) { 183 android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e); 184 return false; 185 } catch (Exception e) { 186 android.util.Log.e(LOG_TAG, "Cannot send pending intent due to " + 187 "unknown exception: ", e); 188 return false; 189 } 190 return true; 191 } 192 193 public void setBitmapCache(BitmapCache bitmapCache) { 194 // Do nothing 195 } 196 } 197 198 private class SetEmptyView extends Action { 199 int viewId; 200 int emptyViewId; 201 202 public final static int TAG = 6; 203 204 SetEmptyView(int viewId, int emptyViewId) { 205 this.viewId = viewId; 206 this.emptyViewId = emptyViewId; 207 } 208 209 SetEmptyView(Parcel in) { 210 this.viewId = in.readInt(); 211 this.emptyViewId = in.readInt(); 212 } 213 214 public void writeToParcel(Parcel out, int flags) { 215 out.writeInt(TAG); 216 out.writeInt(this.viewId); 217 out.writeInt(this.emptyViewId); 218 } 219 220 @Override 221 public void apply(View root, ViewGroup rootParent) { 222 final View view = root.findViewById(viewId); 223 if (!(view instanceof AdapterView<?>)) return; 224 225 AdapterView<?> adapterView = (AdapterView<?>) view; 226 227 final View emptyView = root.findViewById(emptyViewId); 228 if (emptyView == null) return; 229 230 adapterView.setEmptyView(emptyView); 231 } 232 } 233 234 private class SetOnClickFillInIntent extends Action { 235 public SetOnClickFillInIntent(int id, Intent fillInIntent) { 236 this.viewId = id; 237 this.fillInIntent = fillInIntent; 238 } 239 240 public SetOnClickFillInIntent(Parcel parcel) { 241 viewId = parcel.readInt(); 242 fillInIntent = Intent.CREATOR.createFromParcel(parcel); 243 } 244 245 public void writeToParcel(Parcel dest, int flags) { 246 dest.writeInt(TAG); 247 dest.writeInt(viewId); 248 fillInIntent.writeToParcel(dest, 0 /* no flags */); 249 } 250 251 @Override 252 public void apply(View root, ViewGroup rootParent) { 253 final View target = root.findViewById(viewId); 254 if (target == null) return; 255 256 if (!mIsWidgetCollectionChild) { 257 Log.e("RemoteViews", "The method setOnClickFillInIntent is available " + 258 "only from RemoteViewsFactory (ie. on collection items)."); 259 return; 260 } 261 if (target == root) { 262 target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent); 263 } else if (target != null && fillInIntent != null) { 264 OnClickListener listener = new OnClickListener() { 265 public void onClick(View v) { 266 // Insure that this view is a child of an AdapterView 267 View parent = (View) v.getParent(); 268 while (!(parent instanceof AdapterView<?>) 269 && !(parent instanceof AppWidgetHostView)) { 270 parent = (View) parent.getParent(); 271 } 272 273 if (parent instanceof AppWidgetHostView) { 274 // Somehow they've managed to get this far without having 275 // and AdapterView as a parent. 276 Log.e("RemoteViews", "Collection item doesn't have AdapterView parent"); 277 return; 278 } 279 280 // Insure that a template pending intent has been set on an ancestor 281 if (!(parent.getTag() instanceof PendingIntent)) { 282 Log.e("RemoteViews", "Attempting setOnClickFillInIntent without" + 283 " calling setPendingIntentTemplate on parent."); 284 return; 285 } 286 287 PendingIntent pendingIntent = (PendingIntent) parent.getTag(); 288 289 final float appScale = v.getContext().getResources() 290 .getCompatibilityInfo().applicationScale; 291 final int[] pos = new int[2]; 292 v.getLocationOnScreen(pos); 293 294 final Rect rect = new Rect(); 295 rect.left = (int) (pos[0] * appScale + 0.5f); 296 rect.top = (int) (pos[1] * appScale + 0.5f); 297 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f); 298 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f); 299 300 fillInIntent.setSourceBounds(rect); 301 startIntentSafely(v, pendingIntent, fillInIntent); 302 } 303 304 }; 305 target.setOnClickListener(listener); 306 } 307 } 308 309 int viewId; 310 Intent fillInIntent; 311 312 public final static int TAG = 9; 313 } 314 315 private class SetPendingIntentTemplate extends Action { 316 public SetPendingIntentTemplate(int id, PendingIntent pendingIntentTemplate) { 317 this.viewId = id; 318 this.pendingIntentTemplate = pendingIntentTemplate; 319 } 320 321 public SetPendingIntentTemplate(Parcel parcel) { 322 viewId = parcel.readInt(); 323 pendingIntentTemplate = PendingIntent.readPendingIntentOrNullFromParcel(parcel); 324 } 325 326 public void writeToParcel(Parcel dest, int flags) { 327 dest.writeInt(TAG); 328 dest.writeInt(viewId); 329 pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */); 330 } 331 332 @Override 333 public void apply(View root, ViewGroup rootParent) { 334 final View target = root.findViewById(viewId); 335 if (target == null) return; 336 337 // If the view isn't an AdapterView, setting a PendingIntent template doesn't make sense 338 if (target instanceof AdapterView<?>) { 339 AdapterView<?> av = (AdapterView<?>) target; 340 // The PendingIntent template is stored in the view's tag. 341 OnItemClickListener listener = new OnItemClickListener() { 342 public void onItemClick(AdapterView<?> parent, View view, 343 int position, long id) { 344 // The view should be a frame layout 345 if (view instanceof ViewGroup) { 346 ViewGroup vg = (ViewGroup) view; 347 348 // AdapterViews contain their children in a frame 349 // so we need to go one layer deeper here. 350 if (parent instanceof AdapterViewAnimator) { 351 vg = (ViewGroup) vg.getChildAt(0); 352 } 353 if (vg == null) return; 354 355 Intent fillInIntent = null; 356 int childCount = vg.getChildCount(); 357 for (int i = 0; i < childCount; i++) { 358 Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent); 359 if (tag instanceof Intent) { 360 fillInIntent = (Intent) tag; 361 break; 362 } 363 } 364 if (fillInIntent == null) return; 365 366 final float appScale = view.getContext().getResources() 367 .getCompatibilityInfo().applicationScale; 368 final int[] pos = new int[2]; 369 view.getLocationOnScreen(pos); 370 371 final Rect rect = new Rect(); 372 rect.left = (int) (pos[0] * appScale + 0.5f); 373 rect.top = (int) (pos[1] * appScale + 0.5f); 374 rect.right = (int) ((pos[0] + view.getWidth()) * appScale + 0.5f); 375 rect.bottom = (int) ((pos[1] + view.getHeight()) * appScale + 0.5f); 376 377 final Intent intent = new Intent(); 378 intent.setSourceBounds(rect); 379 startIntentSafely(view, pendingIntentTemplate, fillInIntent); 380 } 381 } 382 }; 383 av.setOnItemClickListener(listener); 384 av.setTag(pendingIntentTemplate); 385 } else { 386 Log.e("RemoteViews", "Cannot setPendingIntentTemplate on a view which is not" + 387 "an AdapterView (id: " + viewId + ")"); 388 return; 389 } 390 } 391 392 int viewId; 393 PendingIntent pendingIntentTemplate; 394 395 public final static int TAG = 8; 396 } 397 398 private class SetRemoteViewsAdapterIntent extends Action { 399 public SetRemoteViewsAdapterIntent(int id, Intent intent) { 400 this.viewId = id; 401 this.intent = intent; 402 } 403 404 public SetRemoteViewsAdapterIntent(Parcel parcel) { 405 viewId = parcel.readInt(); 406 intent = Intent.CREATOR.createFromParcel(parcel); 407 } 408 409 public void writeToParcel(Parcel dest, int flags) { 410 dest.writeInt(TAG); 411 dest.writeInt(viewId); 412 intent.writeToParcel(dest, flags); 413 } 414 415 @Override 416 public void apply(View root, ViewGroup rootParent) { 417 final View target = root.findViewById(viewId); 418 if (target == null) return; 419 420 // Ensure that we are applying to an AppWidget root 421 if (!(rootParent instanceof AppWidgetHostView)) { 422 Log.e("RemoteViews", "SetRemoteViewsAdapterIntent action can only be used for " + 423 "AppWidgets (root id: " + viewId + ")"); 424 return; 425 } 426 // Ensure that we are calling setRemoteAdapter on an AdapterView that supports it 427 if (!(target instanceof AbsListView) && !(target instanceof AdapterViewAnimator)) { 428 Log.e("RemoteViews", "Cannot setRemoteViewsAdapter on a view which is not " + 429 "an AbsListView or AdapterViewAnimator (id: " + viewId + ")"); 430 return; 431 } 432 433 // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent 434 // RemoteViewsService 435 AppWidgetHostView host = (AppWidgetHostView) rootParent; 436 intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId()); 437 if (target instanceof AbsListView) { 438 AbsListView v = (AbsListView) target; 439 v.setRemoteViewsAdapter(intent); 440 } else if (target instanceof AdapterViewAnimator) { 441 AdapterViewAnimator v = (AdapterViewAnimator) target; 442 v.setRemoteViewsAdapter(intent); 443 } 444 } 445 446 int viewId; 447 Intent intent; 448 449 public final static int TAG = 10; 450 } 451 452 /** 453 * Equivalent to calling 454 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)} 455 * to launch the provided {@link PendingIntent}. 456 */ 457 private class SetOnClickPendingIntent extends Action { 458 public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) { 459 this.viewId = id; 460 this.pendingIntent = pendingIntent; 461 } 462 463 public SetOnClickPendingIntent(Parcel parcel) { 464 viewId = parcel.readInt(); 465 466 // We check a flag to determine if the parcel contains a PendingIntent. 467 if (parcel.readInt() != 0) { 468 pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel); 469 } 470 } 471 472 public void writeToParcel(Parcel dest, int flags) { 473 dest.writeInt(TAG); 474 dest.writeInt(viewId); 475 476 // We use a flag to indicate whether the parcel contains a valid object. 477 dest.writeInt(pendingIntent != null ? 1 : 0); 478 if (pendingIntent != null) { 479 pendingIntent.writeToParcel(dest, 0 /* no flags */); 480 } 481 } 482 483 @Override 484 public void apply(View root, ViewGroup rootParent) { 485 final View target = root.findViewById(viewId); 486 if (target == null) return; 487 488 // If the view is an AdapterView, setting a PendingIntent on click doesn't make much 489 // sense, do they mean to set a PendingIntent template for the AdapterView's children? 490 if (mIsWidgetCollectionChild) { 491 Log.e("RemoteViews", "Cannot setOnClickPendingIntent for collection item " + 492 "(id: " + viewId + ")"); 493 // TODO: return; We'll let this slide until apps are up to date. 494 } 495 496 if (target != null) { 497 // If the pendingIntent is null, we clear the onClickListener 498 OnClickListener listener = null; 499 if (pendingIntent != null) { 500 listener = new OnClickListener() { 501 public void onClick(View v) { 502 // Find target view location in screen coordinates and 503 // fill into PendingIntent before sending. 504 final float appScale = v.getContext().getResources() 505 .getCompatibilityInfo().applicationScale; 506 final int[] pos = new int[2]; 507 v.getLocationOnScreen(pos); 508 509 final Rect rect = new Rect(); 510 rect.left = (int) (pos[0] * appScale + 0.5f); 511 rect.top = (int) (pos[1] * appScale + 0.5f); 512 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f); 513 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f); 514 515 final Intent intent = new Intent(); 516 intent.setSourceBounds(rect); 517 startIntentSafely(v, pendingIntent, intent); 518 } 519 }; 520 } 521 target.setOnClickListener(listener); 522 } 523 } 524 525 int viewId; 526 PendingIntent pendingIntent; 527 528 public final static int TAG = 1; 529 } 530 531 /** 532 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, 533 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, 534 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view. 535 * <p> 536 * These operations will be performed on the {@link Drawable} returned by the 537 * target {@link View#getBackground()} by default. If targetBackground is false, 538 * we assume the target is an {@link ImageView} and try applying the operations 539 * to {@link ImageView#getDrawable()}. 540 * <p> 541 * You can omit specific calls by marking their values with null or -1. 542 */ 543 private class SetDrawableParameters extends Action { 544 public SetDrawableParameters(int id, boolean targetBackground, int alpha, 545 int colorFilter, PorterDuff.Mode mode, int level) { 546 this.viewId = id; 547 this.targetBackground = targetBackground; 548 this.alpha = alpha; 549 this.colorFilter = colorFilter; 550 this.filterMode = mode; 551 this.level = level; 552 } 553 554 public SetDrawableParameters(Parcel parcel) { 555 viewId = parcel.readInt(); 556 targetBackground = parcel.readInt() != 0; 557 alpha = parcel.readInt(); 558 colorFilter = parcel.readInt(); 559 boolean hasMode = parcel.readInt() != 0; 560 if (hasMode) { 561 filterMode = PorterDuff.Mode.valueOf(parcel.readString()); 562 } else { 563 filterMode = null; 564 } 565 level = parcel.readInt(); 566 } 567 568 public void writeToParcel(Parcel dest, int flags) { 569 dest.writeInt(TAG); 570 dest.writeInt(viewId); 571 dest.writeInt(targetBackground ? 1 : 0); 572 dest.writeInt(alpha); 573 dest.writeInt(colorFilter); 574 if (filterMode != null) { 575 dest.writeInt(1); 576 dest.writeString(filterMode.toString()); 577 } else { 578 dest.writeInt(0); 579 } 580 dest.writeInt(level); 581 } 582 583 @Override 584 public void apply(View root, ViewGroup rootParent) { 585 final View target = root.findViewById(viewId); 586 if (target == null) return; 587 588 // Pick the correct drawable to modify for this view 589 Drawable targetDrawable = null; 590 if (targetBackground) { 591 targetDrawable = target.getBackground(); 592 } else if (target instanceof ImageView) { 593 ImageView imageView = (ImageView) target; 594 targetDrawable = imageView.getDrawable(); 595 } 596 597 if (targetDrawable != null) { 598 // Perform modifications only if values are set correctly 599 if (alpha != -1) { 600 targetDrawable.setAlpha(alpha); 601 } 602 if (colorFilter != -1 && filterMode != null) { 603 targetDrawable.setColorFilter(colorFilter, filterMode); 604 } 605 if (level != -1) { 606 targetDrawable.setLevel(level); 607 } 608 } 609 } 610 611 int viewId; 612 boolean targetBackground; 613 int alpha; 614 int colorFilter; 615 PorterDuff.Mode filterMode; 616 int level; 617 618 public final static int TAG = 3; 619 } 620 621 private class ReflectionActionWithoutParams extends Action { 622 int viewId; 623 String methodName; 624 625 public final static int TAG = 5; 626 627 ReflectionActionWithoutParams(int viewId, String methodName) { 628 this.viewId = viewId; 629 this.methodName = methodName; 630 } 631 632 ReflectionActionWithoutParams(Parcel in) { 633 this.viewId = in.readInt(); 634 this.methodName = in.readString(); 635 } 636 637 public void writeToParcel(Parcel out, int flags) { 638 out.writeInt(TAG); 639 out.writeInt(this.viewId); 640 out.writeString(this.methodName); 641 } 642 643 @Override 644 public void apply(View root, ViewGroup rootParent) { 645 final View view = root.findViewById(viewId); 646 if (view == null) return; 647 648 Class klass = view.getClass(); 649 Method method; 650 try { 651 method = klass.getMethod(this.methodName); 652 } catch (NoSuchMethodException ex) { 653 throw new ActionException("view: " + klass.getName() + " doesn't have method: " 654 + this.methodName + "()"); 655 } 656 657 if (!method.isAnnotationPresent(RemotableViewMethod.class)) { 658 throw new ActionException("view: " + klass.getName() 659 + " can't use method with RemoteViews: " 660 + this.methodName + "()"); 661 } 662 663 try { 664 //noinspection ConstantIfStatement 665 if (false) { 666 Log.d("RemoteViews", "view: " + klass.getName() + " calling method: " 667 + this.methodName + "()"); 668 } 669 method.invoke(view); 670 } catch (Exception ex) { 671 throw new ActionException(ex); 672 } 673 } 674 } 675 676 private static class BitmapCache { 677 ArrayList<Bitmap> mBitmaps; 678 679 public BitmapCache() { 680 mBitmaps = new ArrayList<Bitmap>(); 681 } 682 683 public BitmapCache(Parcel source) { 684 int count = source.readInt(); 685 mBitmaps = new ArrayList<Bitmap>(); 686 for (int i = 0; i < count; i++) { 687 Bitmap b = Bitmap.CREATOR.createFromParcel(source); 688 mBitmaps.add(b); 689 } 690 } 691 692 public int getBitmapId(Bitmap b) { 693 if (b == null) { 694 return -1; 695 } else { 696 if (mBitmaps.contains(b)) { 697 return mBitmaps.indexOf(b); 698 } else { 699 mBitmaps.add(b); 700 return (mBitmaps.size() - 1); 701 } 702 } 703 } 704 705 public Bitmap getBitmapForId(int id) { 706 if (id == -1 || id >= mBitmaps.size()) { 707 return null; 708 } else { 709 return mBitmaps.get(id); 710 } 711 } 712 713 public void writeBitmapsToParcel(Parcel dest, int flags) { 714 int count = mBitmaps.size(); 715 dest.writeInt(count); 716 for (int i = 0; i < count; i++) { 717 mBitmaps.get(i).writeToParcel(dest, flags); 718 } 719 } 720 721 public void assimilate(BitmapCache bitmapCache) { 722 ArrayList<Bitmap> bitmapsToBeAdded = bitmapCache.mBitmaps; 723 int count = bitmapsToBeAdded.size(); 724 for (int i = 0; i < count; i++) { 725 Bitmap b = bitmapsToBeAdded.get(i); 726 if (!mBitmaps.contains(b)) { 727 mBitmaps.add(b); 728 } 729 } 730 } 731 732 public void addBitmapMemory(MemoryUsageCounter memoryCounter) { 733 for (int i = 0; i < mBitmaps.size(); i++) { 734 memoryCounter.addBitmapMemory(mBitmaps.get(i)); 735 } 736 } 737 } 738 739 private class BitmapReflectionAction extends Action { 740 int bitmapId; 741 int viewId; 742 Bitmap bitmap; 743 String methodName; 744 745 BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) { 746 this.bitmap = bitmap; 747 this.viewId = viewId; 748 this.methodName = methodName; 749 bitmapId = mBitmapCache.getBitmapId(bitmap); 750 } 751 752 BitmapReflectionAction(Parcel in) { 753 viewId = in.readInt(); 754 methodName = in.readString(); 755 bitmapId = in.readInt(); 756 bitmap = mBitmapCache.getBitmapForId(bitmapId); 757 } 758 759 @Override 760 public void writeToParcel(Parcel dest, int flags) { 761 dest.writeInt(TAG); 762 dest.writeInt(viewId); 763 dest.writeString(methodName); 764 dest.writeInt(bitmapId); 765 } 766 767 @Override 768 public void apply(View root, ViewGroup rootParent) throws ActionException { 769 ReflectionAction ra = new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP, 770 bitmap); 771 ra.apply(root, rootParent); 772 } 773 774 @Override 775 public void setBitmapCache(BitmapCache bitmapCache) { 776 bitmapId = bitmapCache.getBitmapId(bitmap); 777 } 778 779 public final static int TAG = 12; 780 } 781 782 /** 783 * Base class for the reflection actions. 784 */ 785 private class ReflectionAction extends Action { 786 static final int TAG = 2; 787 788 static final int BOOLEAN = 1; 789 static final int BYTE = 2; 790 static final int SHORT = 3; 791 static final int INT = 4; 792 static final int LONG = 5; 793 static final int FLOAT = 6; 794 static final int DOUBLE = 7; 795 static final int CHAR = 8; 796 static final int STRING = 9; 797 static final int CHAR_SEQUENCE = 10; 798 static final int URI = 11; 799 static final int BITMAP = 12; 800 static final int BUNDLE = 13; 801 static final int INTENT = 14; 802 803 int viewId; 804 String methodName; 805 int type; 806 Object value; 807 808 ReflectionAction(int viewId, String methodName, int type, Object value) { 809 this.viewId = viewId; 810 this.methodName = methodName; 811 this.type = type; 812 this.value = value; 813 } 814 815 ReflectionAction(Parcel in) { 816 this.viewId = in.readInt(); 817 this.methodName = in.readString(); 818 this.type = in.readInt(); 819 //noinspection ConstantIfStatement 820 if (false) { 821 Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId) 822 + " methodName=" + this.methodName + " type=" + this.type); 823 } 824 825 // For some values that may have been null, we first check a flag to see if they were 826 // written to the parcel. 827 switch (this.type) { 828 case BOOLEAN: 829 this.value = in.readInt() != 0; 830 break; 831 case BYTE: 832 this.value = in.readByte(); 833 break; 834 case SHORT: 835 this.value = (short)in.readInt(); 836 break; 837 case INT: 838 this.value = in.readInt(); 839 break; 840 case LONG: 841 this.value = in.readLong(); 842 break; 843 case FLOAT: 844 this.value = in.readFloat(); 845 break; 846 case DOUBLE: 847 this.value = in.readDouble(); 848 break; 849 case CHAR: 850 this.value = (char)in.readInt(); 851 break; 852 case STRING: 853 this.value = in.readString(); 854 break; 855 case CHAR_SEQUENCE: 856 this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 857 break; 858 case URI: 859 if (in.readInt() != 0) { 860 this.value = Uri.CREATOR.createFromParcel(in); 861 } 862 break; 863 case BITMAP: 864 if (in.readInt() != 0) { 865 this.value = Bitmap.CREATOR.createFromParcel(in); 866 } 867 break; 868 case BUNDLE: 869 this.value = in.readBundle(); 870 break; 871 case INTENT: 872 if (in.readInt() != 0) { 873 this.value = Intent.CREATOR.createFromParcel(in); 874 } 875 break; 876 default: 877 break; 878 } 879 } 880 881 public void writeToParcel(Parcel out, int flags) { 882 out.writeInt(TAG); 883 out.writeInt(this.viewId); 884 out.writeString(this.methodName); 885 out.writeInt(this.type); 886 //noinspection ConstantIfStatement 887 if (false) { 888 Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId) 889 + " methodName=" + this.methodName + " type=" + this.type); 890 } 891 892 // For some values which are null, we record an integer flag to indicate whether 893 // we have written a valid value to the parcel. 894 switch (this.type) { 895 case BOOLEAN: 896 out.writeInt((Boolean) this.value ? 1 : 0); 897 break; 898 case BYTE: 899 out.writeByte((Byte) this.value); 900 break; 901 case SHORT: 902 out.writeInt((Short) this.value); 903 break; 904 case INT: 905 out.writeInt((Integer) this.value); 906 break; 907 case LONG: 908 out.writeLong((Long) this.value); 909 break; 910 case FLOAT: 911 out.writeFloat((Float) this.value); 912 break; 913 case DOUBLE: 914 out.writeDouble((Double) this.value); 915 break; 916 case CHAR: 917 out.writeInt((int)((Character)this.value).charValue()); 918 break; 919 case STRING: 920 out.writeString((String)this.value); 921 break; 922 case CHAR_SEQUENCE: 923 TextUtils.writeToParcel((CharSequence)this.value, out, flags); 924 break; 925 case URI: 926 out.writeInt(this.value != null ? 1 : 0); 927 if (this.value != null) { 928 ((Uri)this.value).writeToParcel(out, flags); 929 } 930 break; 931 case BITMAP: 932 out.writeInt(this.value != null ? 1 : 0); 933 if (this.value != null) { 934 ((Bitmap)this.value).writeToParcel(out, flags); 935 } 936 break; 937 case BUNDLE: 938 out.writeBundle((Bundle) this.value); 939 break; 940 case INTENT: 941 out.writeInt(this.value != null ? 1 : 0); 942 if (this.value != null) { 943 ((Intent)this.value).writeToParcel(out, flags); 944 } 945 break; 946 default: 947 break; 948 } 949 } 950 951 private Class getParameterType() { 952 switch (this.type) { 953 case BOOLEAN: 954 return boolean.class; 955 case BYTE: 956 return byte.class; 957 case SHORT: 958 return short.class; 959 case INT: 960 return int.class; 961 case LONG: 962 return long.class; 963 case FLOAT: 964 return float.class; 965 case DOUBLE: 966 return double.class; 967 case CHAR: 968 return char.class; 969 case STRING: 970 return String.class; 971 case CHAR_SEQUENCE: 972 return CharSequence.class; 973 case URI: 974 return Uri.class; 975 case BITMAP: 976 return Bitmap.class; 977 case BUNDLE: 978 return Bundle.class; 979 case INTENT: 980 return Intent.class; 981 default: 982 return null; 983 } 984 } 985 986 @Override 987 public void apply(View root, ViewGroup rootParent) { 988 final View view = root.findViewById(viewId); 989 if (view == null) return; 990 991 Class param = getParameterType(); 992 if (param == null) { 993 throw new ActionException("bad type: " + this.type); 994 } 995 996 Class klass = view.getClass(); 997 Method method; 998 try { 999 method = klass.getMethod(this.methodName, getParameterType()); 1000 } 1001 catch (NoSuchMethodException ex) { 1002 throw new ActionException("view: " + klass.getName() + " doesn't have method: " 1003 + this.methodName + "(" + param.getName() + ")"); 1004 } 1005 1006 if (!method.isAnnotationPresent(RemotableViewMethod.class)) { 1007 throw new ActionException("view: " + klass.getName() 1008 + " can't use method with RemoteViews: " 1009 + this.methodName + "(" + param.getName() + ")"); 1010 } 1011 1012 try { 1013 //noinspection ConstantIfStatement 1014 if (false) { 1015 Log.d("RemoteViews", "view: " + klass.getName() + " calling method: " 1016 + this.methodName + "(" + param.getName() + ") with " 1017 + (this.value == null ? "null" : this.value.getClass().getName())); 1018 } 1019 method.invoke(view, this.value); 1020 } 1021 catch (Exception ex) { 1022 throw new ActionException(ex); 1023 } 1024 } 1025 1026 @Override 1027 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) { 1028 // We currently only calculate Bitmap memory usage 1029 switch (this.type) { 1030 case BITMAP: 1031 if (this.value != null) { 1032 final Bitmap b = (Bitmap) this.value; 1033 counter.addBitmapMemory(b); 1034 } 1035 break; 1036 default: 1037 break; 1038 } 1039 } 1040 } 1041 1042 private void configureRemoteViewsAsChild(RemoteViews rv) { 1043 mBitmapCache.assimilate(rv.mBitmapCache); 1044 rv.setBitmapCache(mBitmapCache); 1045 rv.setNotRoot(); 1046 } 1047 1048 void setNotRoot() { 1049 mIsRoot = false; 1050 } 1051 1052 /** 1053 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the 1054 * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()} 1055 * when null. This allows users to build "nested" {@link RemoteViews}. 1056 */ 1057 private class ViewGroupAction extends Action { 1058 public ViewGroupAction(int viewId, RemoteViews nestedViews) { 1059 this.viewId = viewId; 1060 this.nestedViews = nestedViews; 1061 if (nestedViews != null) { 1062 configureRemoteViewsAsChild(nestedViews); 1063 } 1064 } 1065 1066 public ViewGroupAction(Parcel parcel, BitmapCache bitmapCache) { 1067 viewId = parcel.readInt(); 1068 boolean nestedViewsNull = parcel.readInt() == 0; 1069 if (!nestedViewsNull) { 1070 nestedViews = new RemoteViews(parcel, bitmapCache); 1071 } else { 1072 nestedViews = null; 1073 } 1074 } 1075 1076 public void writeToParcel(Parcel dest, int flags) { 1077 dest.writeInt(TAG); 1078 dest.writeInt(viewId); 1079 if (nestedViews != null) { 1080 dest.writeInt(1); 1081 nestedViews.writeToParcel(dest, flags); 1082 } else { 1083 // signifies null 1084 dest.writeInt(0); 1085 } 1086 } 1087 1088 @Override 1089 public void apply(View root, ViewGroup rootParent) { 1090 final Context context = root.getContext(); 1091 final ViewGroup target = (ViewGroup) root.findViewById(viewId); 1092 if (target == null) return; 1093 if (nestedViews != null) { 1094 // Inflate nested views and add as children 1095 target.addView(nestedViews.apply(context, target)); 1096 } else { 1097 // Clear all children when nested views omitted 1098 target.removeAllViews(); 1099 } 1100 } 1101 1102 @Override 1103 public void updateMemoryUsageEstimate(MemoryUsageCounter counter) { 1104 if (nestedViews != null) { 1105 counter.increment(nestedViews.estimateMemoryUsage()); 1106 } 1107 } 1108 1109 @Override 1110 public void setBitmapCache(BitmapCache bitmapCache) { 1111 if (nestedViews != null) { 1112 nestedViews.setBitmapCache(bitmapCache); 1113 } 1114 } 1115 1116 int viewId; 1117 RemoteViews nestedViews; 1118 1119 public final static int TAG = 4; 1120 } 1121 1122 /** 1123 * Helper action to set compound drawables on a TextView. Supports relative 1124 * (s/t/e/b) or cardinal (l/t/r/b) arrangement. 1125 */ 1126 private class TextViewDrawableAction extends Action { 1127 public TextViewDrawableAction(int viewId, boolean isRelative, int d1, int d2, int d3, int d4) { 1128 this.viewId = viewId; 1129 this.isRelative = isRelative; 1130 this.d1 = d1; 1131 this.d2 = d2; 1132 this.d3 = d3; 1133 this.d4 = d4; 1134 } 1135 1136 public TextViewDrawableAction(Parcel parcel) { 1137 viewId = parcel.readInt(); 1138 isRelative = (parcel.readInt() != 0); 1139 d1 = parcel.readInt(); 1140 d2 = parcel.readInt(); 1141 d3 = parcel.readInt(); 1142 d4 = parcel.readInt(); 1143 } 1144 1145 public void writeToParcel(Parcel dest, int flags) { 1146 dest.writeInt(TAG); 1147 dest.writeInt(viewId); 1148 dest.writeInt(isRelative ? 1 : 0); 1149 dest.writeInt(d1); 1150 dest.writeInt(d2); 1151 dest.writeInt(d3); 1152 dest.writeInt(d4); 1153 } 1154 1155 @Override 1156 public void apply(View root, ViewGroup rootParent) { 1157 final Context context = root.getContext(); 1158 final TextView target = (TextView) root.findViewById(viewId); 1159 if (target == null) return; 1160 if (isRelative) { 1161 target.setCompoundDrawablesRelativeWithIntrinsicBounds(d1, d2, d3, d4); 1162 } else { 1163 target.setCompoundDrawablesWithIntrinsicBounds(d1, d2, d3, d4); 1164 } 1165 } 1166 1167 int viewId; 1168 boolean isRelative = false; 1169 int d1, d2, d3, d4; 1170 1171 public final static int TAG = 11; 1172 } 1173 1174 /** 1175 * Simple class used to keep track of memory usage in a RemoteViews. 1176 * 1177 */ 1178 private class MemoryUsageCounter { 1179 public void clear() { 1180 mMemoryUsage = 0; 1181 } 1182 1183 public void increment(int numBytes) { 1184 mMemoryUsage += numBytes; 1185 } 1186 1187 public int getMemoryUsage() { 1188 return mMemoryUsage; 1189 } 1190 1191 public void addBitmapMemory(Bitmap b) { 1192 final Bitmap.Config c = b.getConfig(); 1193 // If we don't know, be pessimistic and assume 4 1194 int bpp = 4; 1195 if (c != null) { 1196 switch (c) { 1197 case ALPHA_8: 1198 bpp = 1; 1199 break; 1200 case RGB_565: 1201 case ARGB_4444: 1202 bpp = 2; 1203 break; 1204 case ARGB_8888: 1205 bpp = 4; 1206 break; 1207 } 1208 } 1209 increment(b.getWidth() * b.getHeight() * bpp); 1210 } 1211 1212 int mMemoryUsage; 1213 } 1214 1215 /** 1216 * Create a new RemoteViews object that will display the views contained 1217 * in the specified layout file. 1218 * 1219 * @param packageName Name of the package that contains the layout resource 1220 * @param layoutId The id of the layout resource 1221 */ 1222 public RemoteViews(String packageName, int layoutId) { 1223 mPackage = packageName; 1224 mLayoutId = layoutId; 1225 mBitmapCache = new BitmapCache(); 1226 1227 // setup the memory usage statistics 1228 mMemoryUsageCounter = new MemoryUsageCounter(); 1229 recalculateMemoryUsage(); 1230 } 1231 1232 private boolean hasLandscapeAndPortraitLayouts() { 1233 return (mLandscape != null) && (mPortrait != null); 1234 } 1235 1236 /** 1237 * Create a new RemoteViews object that will inflate as the specified 1238 * landspace or portrait RemoteViews, depending on the current configuration. 1239 * 1240 * @param landscape The RemoteViews to inflate in landscape configuration 1241 * @param portrait The RemoteViews to inflate in portrait configuration 1242 */ 1243 public RemoteViews(RemoteViews landscape, RemoteViews portrait) { 1244 if (landscape == null || portrait == null) { 1245 throw new RuntimeException("Both RemoteViews must be non-null"); 1246 } 1247 if (landscape.getPackage().compareTo(portrait.getPackage()) != 0) { 1248 throw new RuntimeException("Both RemoteViews must share the same package"); 1249 } 1250 mPackage = portrait.getPackage(); 1251 mLayoutId = portrait.getLayoutId(); 1252 1253 mLandscape = landscape; 1254 mPortrait = portrait; 1255 1256 // setup the memory usage statistics 1257 mMemoryUsageCounter = new MemoryUsageCounter(); 1258 1259 mBitmapCache = new BitmapCache(); 1260 configureRemoteViewsAsChild(landscape); 1261 configureRemoteViewsAsChild(portrait); 1262 1263 recalculateMemoryUsage(); 1264 } 1265 1266 /** 1267 * Reads a RemoteViews object from a parcel. 1268 * 1269 * @param parcel 1270 */ 1271 public RemoteViews(Parcel parcel) { 1272 this(parcel, null); 1273 } 1274 1275 private RemoteViews(Parcel parcel, BitmapCache bitmapCache) { 1276 int mode = parcel.readInt(); 1277 1278 // We only store a bitmap cache in the root of the RemoteViews. 1279 if (bitmapCache == null) { 1280 mBitmapCache = new BitmapCache(parcel); 1281 } else { 1282 setBitmapCache(bitmapCache); 1283 setNotRoot(); 1284 } 1285 1286 if (mode == MODE_NORMAL) { 1287 mPackage = parcel.readString(); 1288 mLayoutId = parcel.readInt(); 1289 mIsWidgetCollectionChild = parcel.readInt() == 1 ? true : false; 1290 1291 int count = parcel.readInt(); 1292 if (count > 0) { 1293 mActions = new ArrayList<Action>(count); 1294 for (int i=0; i<count; i++) { 1295 int tag = parcel.readInt(); 1296 switch (tag) { 1297 case SetOnClickPendingIntent.TAG: 1298 mActions.add(new SetOnClickPendingIntent(parcel)); 1299 break; 1300 case SetDrawableParameters.TAG: 1301 mActions.add(new SetDrawableParameters(parcel)); 1302 break; 1303 case ReflectionAction.TAG: 1304 mActions.add(new ReflectionAction(parcel)); 1305 break; 1306 case ViewGroupAction.TAG: 1307 mActions.add(new ViewGroupAction(parcel, mBitmapCache)); 1308 break; 1309 case ReflectionActionWithoutParams.TAG: 1310 mActions.add(new ReflectionActionWithoutParams(parcel)); 1311 break; 1312 case SetEmptyView.TAG: 1313 mActions.add(new SetEmptyView(parcel)); 1314 break; 1315 case SetPendingIntentTemplate.TAG: 1316 mActions.add(new SetPendingIntentTemplate(parcel)); 1317 break; 1318 case SetOnClickFillInIntent.TAG: 1319 mActions.add(new SetOnClickFillInIntent(parcel)); 1320 break; 1321 case SetRemoteViewsAdapterIntent.TAG: 1322 mActions.add(new SetRemoteViewsAdapterIntent(parcel)); 1323 break; 1324 case TextViewDrawableAction.TAG: 1325 mActions.add(new TextViewDrawableAction(parcel)); 1326 break; 1327 case BitmapReflectionAction.TAG: 1328 mActions.add(new BitmapReflectionAction(parcel)); 1329 break; 1330 default: 1331 throw new ActionException("Tag " + tag + " not found"); 1332 } 1333 } 1334 } 1335 } else { 1336 // MODE_HAS_LANDSCAPE_AND_PORTRAIT 1337 mLandscape = new RemoteViews(parcel, mBitmapCache); 1338 mPortrait = new RemoteViews(parcel, mBitmapCache); 1339 mPackage = mPortrait.getPackage(); 1340 mLayoutId = mPortrait.getLayoutId(); 1341 } 1342 1343 // setup the memory usage statistics 1344 mMemoryUsageCounter = new MemoryUsageCounter(); 1345 recalculateMemoryUsage(); 1346 } 1347 1348 @Override 1349 public RemoteViews clone() { 1350 RemoteViews that; 1351 if (!hasLandscapeAndPortraitLayouts()) { 1352 that = new RemoteViews(mPackage, mLayoutId); 1353 1354 if (mActions != null) { 1355 that.mActions = (ArrayList<Action>)mActions.clone(); 1356 } 1357 } else { 1358 RemoteViews land = mLandscape.clone(); 1359 RemoteViews port = mPortrait.clone(); 1360 that = new RemoteViews(land, port); 1361 } 1362 // update the memory usage stats of the cloned RemoteViews 1363 that.recalculateMemoryUsage(); 1364 return that; 1365 } 1366 1367 public String getPackage() { 1368 return mPackage; 1369 } 1370 1371 /** 1372 * Reutrns the layout id of the root layout associated with this RemoteViews. In the case 1373 * that the RemoteViews has both a landscape and portrait root, this will return the layout 1374 * id associated with the portrait layout. 1375 * 1376 * @return the layout id. 1377 */ 1378 public int getLayoutId() { 1379 return mLayoutId; 1380 } 1381 1382 /* 1383 * This flag indicates whether this RemoteViews object is being created from a 1384 * RemoteViewsService for use as a child of a widget collection. This flag is used 1385 * to determine whether or not certain features are available, in particular, 1386 * setting on click extras and setting on click pending intents. The former is enabled, 1387 * and the latter disabled when this flag is true. 1388 */ 1389 void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) { 1390 mIsWidgetCollectionChild = isWidgetCollectionChild; 1391 } 1392 1393 /** 1394 * Updates the memory usage statistics. 1395 */ 1396 private void recalculateMemoryUsage() { 1397 mMemoryUsageCounter.clear(); 1398 1399 if (!hasLandscapeAndPortraitLayouts()) { 1400 // Accumulate the memory usage for each action 1401 if (mActions != null) { 1402 final int count = mActions.size(); 1403 for (int i= 0; i < count; ++i) { 1404 mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter); 1405 } 1406 } 1407 if (mIsRoot) { 1408 mBitmapCache.addBitmapMemory(mMemoryUsageCounter); 1409 } 1410 } else { 1411 mMemoryUsageCounter.increment(mLandscape.estimateMemoryUsage()); 1412 mMemoryUsageCounter.increment(mPortrait.estimateMemoryUsage()); 1413 mBitmapCache.addBitmapMemory(mMemoryUsageCounter); 1414 } 1415 } 1416 1417 /** 1418 * Recursively sets BitmapCache in the hierarchy and update the bitmap ids. 1419 */ 1420 private void setBitmapCache(BitmapCache bitmapCache) { 1421 mBitmapCache = bitmapCache; 1422 if (!hasLandscapeAndPortraitLayouts()) { 1423 if (mActions != null) { 1424 final int count = mActions.size(); 1425 for (int i= 0; i < count; ++i) { 1426 mActions.get(i).setBitmapCache(bitmapCache); 1427 } 1428 } 1429 } else { 1430 mLandscape.setBitmapCache(bitmapCache); 1431 mPortrait.setBitmapCache(bitmapCache); 1432 } 1433 } 1434 1435 /** 1436 * Returns an estimate of the bitmap heap memory usage for this RemoteViews. 1437 */ 1438 int estimateMemoryUsage() { 1439 return mMemoryUsageCounter.getMemoryUsage(); 1440 } 1441 1442 /** 1443 * Add an action to be executed on the remote side when apply is called. 1444 * 1445 * @param a The action to add 1446 */ 1447 private void addAction(Action a) { 1448 if (hasLandscapeAndPortraitLayouts()) { 1449 throw new RuntimeException("RemoteViews specifying separate landscape and portrait" + 1450 " layouts cannot be modified. Instead, fully configure the landscape and" + 1451 " portrait layouts individually before constructing the combined layout."); 1452 } 1453 if (mActions == null) { 1454 mActions = new ArrayList<Action>(); 1455 } 1456 mActions.add(a); 1457 1458 // update the memory usage stats 1459 a.updateMemoryUsageEstimate(mMemoryUsageCounter); 1460 } 1461 1462 /** 1463 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the 1464 * given {@link RemoteViews}. This allows users to build "nested" 1465 * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may 1466 * recycle layouts, use {@link #removeAllViews(int)} to clear any existing 1467 * children. 1468 * 1469 * @param viewId The id of the parent {@link ViewGroup} to add child into. 1470 * @param nestedView {@link RemoteViews} that describes the child. 1471 */ 1472 public void addView(int viewId, RemoteViews nestedView) { 1473 addAction(new ViewGroupAction(viewId, nestedView)); 1474 } 1475 1476 /** 1477 * Equivalent to calling {@link ViewGroup#removeAllViews()}. 1478 * 1479 * @param viewId The id of the parent {@link ViewGroup} to remove all 1480 * children from. 1481 */ 1482 public void removeAllViews(int viewId) { 1483 addAction(new ViewGroupAction(viewId, null)); 1484 } 1485 1486 /** 1487 * Equivalent to calling {@link AdapterViewAnimator#showNext()} 1488 * 1489 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()} 1490 */ 1491 public void showNext(int viewId) { 1492 addAction(new ReflectionActionWithoutParams(viewId, "showNext")); 1493 } 1494 1495 /** 1496 * Equivalent to calling {@link AdapterViewAnimator#showPrevious()} 1497 * 1498 * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()} 1499 */ 1500 public void showPrevious(int viewId) { 1501 addAction(new ReflectionActionWithoutParams(viewId, "showPrevious")); 1502 } 1503 1504 /** 1505 * Equivalent to calling {@link AdapterViewAnimator#setDisplayedChild(int)} 1506 * 1507 * @param viewId The id of the view on which to call 1508 * {@link AdapterViewAnimator#setDisplayedChild(int)} 1509 */ 1510 public void setDisplayedChild(int viewId, int childIndex) { 1511 setInt(viewId, "setDisplayedChild", childIndex); 1512 } 1513 1514 /** 1515 * Equivalent to calling View.setVisibility 1516 * 1517 * @param viewId The id of the view whose visibility should change 1518 * @param visibility The new visibility for the view 1519 */ 1520 public void setViewVisibility(int viewId, int visibility) { 1521 setInt(viewId, "setVisibility", visibility); 1522 } 1523 1524 /** 1525 * Equivalent to calling TextView.setText 1526 * 1527 * @param viewId The id of the view whose text should change 1528 * @param text The new text for the view 1529 */ 1530 public void setTextViewText(int viewId, CharSequence text) { 1531 setCharSequence(viewId, "setText", text); 1532 } 1533 1534 /** 1535 * Equivalent to calling 1536 * {@link TextView#setCompoundDrawablesWithIntrinsicBounds(int, int, int, int)}. 1537 * 1538 * @param viewId The id of the view whose text should change 1539 * @param left The id of a drawable to place to the left of the text, or 0 1540 * @param top The id of a drawable to place above the text, or 0 1541 * @param right The id of a drawable to place to the right of the text, or 0 1542 * @param bottom The id of a drawable to place below the text, or 0 1543 */ 1544 public void setTextViewCompoundDrawables(int viewId, int left, int top, int right, int bottom) { 1545 addAction(new TextViewDrawableAction(viewId, false, left, top, right, bottom)); 1546 } 1547 1548 /** 1549 * Equivalent to calling {@link 1550 * TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)}. 1551 * 1552 * @param viewId The id of the view whose text should change 1553 * @param start The id of a drawable to place before the text (relative to the 1554 * layout direction), or 0 1555 * @param top The id of a drawable to place above the text, or 0 1556 * @param end The id of a drawable to place after the text, or 0 1557 * @param bottom The id of a drawable to place below the text, or 0 1558 */ 1559 public void setTextViewCompoundDrawablesRelative(int viewId, int start, int top, int end, int bottom) { 1560 addAction(new TextViewDrawableAction(viewId, true, start, top, end, bottom)); 1561 } 1562 1563 /** 1564 * Equivalent to calling ImageView.setImageResource 1565 * 1566 * @param viewId The id of the view whose drawable should change 1567 * @param srcId The new resource id for the drawable 1568 */ 1569 public void setImageViewResource(int viewId, int srcId) { 1570 setInt(viewId, "setImageResource", srcId); 1571 } 1572 1573 /** 1574 * Equivalent to calling ImageView.setImageURI 1575 * 1576 * @param viewId The id of the view whose drawable should change 1577 * @param uri The Uri for the image 1578 */ 1579 public void setImageViewUri(int viewId, Uri uri) { 1580 setUri(viewId, "setImageURI", uri); 1581 } 1582 1583 /** 1584 * Equivalent to calling ImageView.setImageBitmap 1585 * 1586 * @param viewId The id of the view whose bitmap should change 1587 * @param bitmap The new Bitmap for the drawable 1588 */ 1589 public void setImageViewBitmap(int viewId, Bitmap bitmap) { 1590 setBitmap(viewId, "setImageBitmap", bitmap); 1591 } 1592 1593 /** 1594 * Equivalent to calling AdapterView.setEmptyView 1595 * 1596 * @param viewId The id of the view on which to set the empty view 1597 * @param emptyViewId The view id of the empty view 1598 */ 1599 public void setEmptyView(int viewId, int emptyViewId) { 1600 addAction(new SetEmptyView(viewId, emptyViewId)); 1601 } 1602 1603 /** 1604 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase}, 1605 * {@link Chronometer#setFormat Chronometer.setFormat}, 1606 * and {@link Chronometer#start Chronometer.start()} or 1607 * {@link Chronometer#stop Chronometer.stop()}. 1608 * 1609 * @param viewId The id of the {@link Chronometer} to change 1610 * @param base The time at which the timer would have read 0:00. This 1611 * time should be based off of 1612 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}. 1613 * @param format The Chronometer format string, or null to 1614 * simply display the timer value. 1615 * @param started True if you want the clock to be started, false if not. 1616 */ 1617 public void setChronometer(int viewId, long base, String format, boolean started) { 1618 setLong(viewId, "setBase", base); 1619 setString(viewId, "setFormat", format); 1620 setBoolean(viewId, "setStarted", started); 1621 } 1622 1623 /** 1624 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax}, 1625 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and 1626 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate} 1627 * 1628 * If indeterminate is true, then the values for max and progress are ignored. 1629 * 1630 * @param viewId The id of the {@link ProgressBar} to change 1631 * @param max The 100% value for the progress bar 1632 * @param progress The current value of the progress bar. 1633 * @param indeterminate True if the progress bar is indeterminate, 1634 * false if not. 1635 */ 1636 public void setProgressBar(int viewId, int max, int progress, 1637 boolean indeterminate) { 1638 setBoolean(viewId, "setIndeterminate", indeterminate); 1639 if (!indeterminate) { 1640 setInt(viewId, "setMax", max); 1641 setInt(viewId, "setProgress", progress); 1642 } 1643 } 1644 1645 /** 1646 * Equivalent to calling 1647 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)} 1648 * to launch the provided {@link PendingIntent}. 1649 * 1650 * When setting the on-click action of items within collections (eg. {@link ListView}, 1651 * {@link StackView} etc.), this method will not work. Instead, use {@link 1652 * RemoteViews#setPendingIntentTemplate(int, PendingIntent) in conjunction with 1653 * RemoteViews#setOnClickFillInIntent(int, Intent). 1654 * 1655 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked 1656 * @param pendingIntent The {@link PendingIntent} to send when user clicks 1657 */ 1658 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) { 1659 addAction(new SetOnClickPendingIntent(viewId, pendingIntent)); 1660 } 1661 1662 /** 1663 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very 1664 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead 1665 * this method should be used to set a single PendingIntent template on the collection, and 1666 * individual items can differentiate their on-click behavior using 1667 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}. 1668 * 1669 * @param viewId The id of the collection who's children will use this PendingIntent template 1670 * when clicked 1671 * @param pendingIntentTemplate The {@link PendingIntent} to be combined with extras specified 1672 * by a child of viewId and executed when that child is clicked 1673 */ 1674 public void setPendingIntentTemplate(int viewId, PendingIntent pendingIntentTemplate) { 1675 addAction(new SetPendingIntentTemplate(viewId, pendingIntentTemplate)); 1676 } 1677 1678 /** 1679 * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very 1680 * costly to set PendingIntents on the individual items, and is hence not permitted. Instead 1681 * a single PendingIntent template can be set on the collection, see {@link 1682 * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click 1683 * action of a given item can be distinguished by setting a fillInIntent on that item. The 1684 * fillInIntent is then combined with the PendingIntent template in order to determine the final 1685 * intent which will be executed when the item is clicked. This works as follows: any fields 1686 * which are left blank in the PendingIntent template, but are provided by the fillInIntent 1687 * will be overwritten, and the resulting PendingIntent will be used. 1688 * 1689 * 1690 * of the PendingIntent template will then be filled in with the associated fields that are 1691 * set in fillInIntent. See {@link Intent#fillIn(Intent, int)} for more details. 1692 * 1693 * @param viewId The id of the view on which to set the fillInIntent 1694 * @param fillInIntent The intent which will be combined with the parent's PendingIntent 1695 * in order to determine the on-click behavior of the view specified by viewId 1696 */ 1697 public void setOnClickFillInIntent(int viewId, Intent fillInIntent) { 1698 addAction(new SetOnClickFillInIntent(viewId, fillInIntent)); 1699 } 1700 1701 /** 1702 * @hide 1703 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, 1704 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, 1705 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given 1706 * view. 1707 * <p> 1708 * You can omit specific calls by marking their values with null or -1. 1709 * 1710 * @param viewId The id of the view that contains the target 1711 * {@link Drawable} 1712 * @param targetBackground If true, apply these parameters to the 1713 * {@link Drawable} returned by 1714 * {@link android.view.View#getBackground()}. Otherwise, assume 1715 * the target view is an {@link ImageView} and apply them to 1716 * {@link ImageView#getDrawable()}. 1717 * @param alpha Specify an alpha value for the drawable, or -1 to leave 1718 * unchanged. 1719 * @param colorFilter Specify a color for a 1720 * {@link android.graphics.ColorFilter} for this drawable, or -1 1721 * to leave unchanged. 1722 * @param mode Specify a PorterDuff mode for this drawable, or null to leave 1723 * unchanged. 1724 * @param level Specify the level for the drawable, or -1 to leave 1725 * unchanged. 1726 */ 1727 public void setDrawableParameters(int viewId, boolean targetBackground, int alpha, 1728 int colorFilter, PorterDuff.Mode mode, int level) { 1729 addAction(new SetDrawableParameters(viewId, targetBackground, alpha, 1730 colorFilter, mode, level)); 1731 } 1732 1733 /** 1734 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}. 1735 * 1736 * @param viewId The id of the view whose text color should change 1737 * @param color Sets the text color for all the states (normal, selected, 1738 * focused) to be this color. 1739 */ 1740 public void setTextColor(int viewId, int color) { 1741 setInt(viewId, "setTextColor", color); 1742 } 1743 1744 /** 1745 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}. 1746 * 1747 * @param appWidgetId The id of the app widget which contains the specified view. (This 1748 * parameter is ignored in this deprecated method) 1749 * @param viewId The id of the {@link AbsListView} 1750 * @param intent The intent of the service which will be 1751 * providing data to the RemoteViewsAdapter 1752 * @deprecated This method has been deprecated. See 1753 * {@link android.widget.RemoteViews#setRemoteAdapter(int, Intent)} 1754 */ 1755 @Deprecated 1756 public void setRemoteAdapter(int appWidgetId, int viewId, Intent intent) { 1757 setRemoteAdapter(viewId, intent); 1758 } 1759 1760 /** 1761 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}. 1762 * Can only be used for App Widgets. 1763 * 1764 * @param viewId The id of the {@link AbsListView} 1765 * @param intent The intent of the service which will be 1766 * providing data to the RemoteViewsAdapter 1767 */ 1768 public void setRemoteAdapter(int viewId, Intent intent) { 1769 addAction(new SetRemoteViewsAdapterIntent(viewId, intent)); 1770 } 1771 1772 /** 1773 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}. 1774 * 1775 * @param viewId The id of the view to change 1776 * @param position Scroll to this adapter position 1777 */ 1778 public void setScrollPosition(int viewId, int position) { 1779 setInt(viewId, "smoothScrollToPosition", position); 1780 } 1781 1782 /** 1783 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}. 1784 * 1785 * @param viewId The id of the view to change 1786 * @param offset Scroll by this adapter position offset 1787 */ 1788 public void setRelativeScrollPosition(int viewId, int offset) { 1789 setInt(viewId, "smoothScrollByOffset", offset); 1790 } 1791 1792 /** 1793 * Call a method taking one boolean on a view in the layout for this RemoteViews. 1794 * 1795 * @param viewId The id of the view on which to call the method. 1796 * @param methodName The name of the method to call. 1797 * @param value The value to pass to the method. 1798 */ 1799 public void setBoolean(int viewId, String methodName, boolean value) { 1800 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value)); 1801 } 1802 1803 /** 1804 * Call a method taking one byte on a view in the layout for this RemoteViews. 1805 * 1806 * @param viewId The id of the view on which to call the method. 1807 * @param methodName The name of the method to call. 1808 * @param value The value to pass to the method. 1809 */ 1810 public void setByte(int viewId, String methodName, byte value) { 1811 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value)); 1812 } 1813 1814 /** 1815 * Call a method taking one short on a view in the layout for this RemoteViews. 1816 * 1817 * @param viewId The id of the view on which to call the method. 1818 * @param methodName The name of the method to call. 1819 * @param value The value to pass to the method. 1820 */ 1821 public void setShort(int viewId, String methodName, short value) { 1822 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value)); 1823 } 1824 1825 /** 1826 * Call a method taking one int on a view in the layout for this RemoteViews. 1827 * 1828 * @param viewId The id of the view on which to call the method. 1829 * @param methodName The name of the method to call. 1830 * @param value The value to pass to the method. 1831 */ 1832 public void setInt(int viewId, String methodName, int value) { 1833 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value)); 1834 } 1835 1836 /** 1837 * Call a method taking one long on a view in the layout for this RemoteViews. 1838 * 1839 * @param viewId The id of the view on which to call the method. 1840 * @param methodName The name of the method to call. 1841 * @param value The value to pass to the method. 1842 */ 1843 public void setLong(int viewId, String methodName, long value) { 1844 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value)); 1845 } 1846 1847 /** 1848 * Call a method taking one float on a view in the layout for this RemoteViews. 1849 * 1850 * @param viewId The id of the view on which to call the method. 1851 * @param methodName The name of the method to call. 1852 * @param value The value to pass to the method. 1853 */ 1854 public void setFloat(int viewId, String methodName, float value) { 1855 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value)); 1856 } 1857 1858 /** 1859 * Call a method taking one double on a view in the layout for this RemoteViews. 1860 * 1861 * @param viewId The id of the view on which to call the method. 1862 * @param methodName The name of the method to call. 1863 * @param value The value to pass to the method. 1864 */ 1865 public void setDouble(int viewId, String methodName, double value) { 1866 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value)); 1867 } 1868 1869 /** 1870 * Call a method taking one char on a view in the layout for this RemoteViews. 1871 * 1872 * @param viewId The id of the view on which to call the method. 1873 * @param methodName The name of the method to call. 1874 * @param value The value to pass to the method. 1875 */ 1876 public void setChar(int viewId, String methodName, char value) { 1877 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value)); 1878 } 1879 1880 /** 1881 * Call a method taking one String on a view in the layout for this RemoteViews. 1882 * 1883 * @param viewId The id of the view on which to call the method. 1884 * @param methodName The name of the method to call. 1885 * @param value The value to pass to the method. 1886 */ 1887 public void setString(int viewId, String methodName, String value) { 1888 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value)); 1889 } 1890 1891 /** 1892 * Call a method taking one CharSequence on a view in the layout for this RemoteViews. 1893 * 1894 * @param viewId The id of the view on which to call the method. 1895 * @param methodName The name of the method to call. 1896 * @param value The value to pass to the method. 1897 */ 1898 public void setCharSequence(int viewId, String methodName, CharSequence value) { 1899 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value)); 1900 } 1901 1902 /** 1903 * Call a method taking one Uri on a view in the layout for this RemoteViews. 1904 * 1905 * @param viewId The id of the view on which to call the method. 1906 * @param methodName The name of the method to call. 1907 * @param value The value to pass to the method. 1908 */ 1909 public void setUri(int viewId, String methodName, Uri value) { 1910 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value)); 1911 } 1912 1913 /** 1914 * Call a method taking one Bitmap on a view in the layout for this RemoteViews. 1915 * @more 1916 * <p class="note">The bitmap will be flattened into the parcel if this object is 1917 * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p> 1918 * 1919 * @param viewId The id of the view on which to call the method. 1920 * @param methodName The name of the method to call. 1921 * @param value The value to pass to the method. 1922 */ 1923 public void setBitmap(int viewId, String methodName, Bitmap value) { 1924 addAction(new BitmapReflectionAction(viewId, methodName, value)); 1925 } 1926 1927 /** 1928 * Call a method taking one Bundle on a view in the layout for this RemoteViews. 1929 * 1930 * @param viewId The id of the view on which to call the method. 1931 * @param methodName The name of the method to call. 1932 * @param value The value to pass to the method. 1933 */ 1934 public void setBundle(int viewId, String methodName, Bundle value) { 1935 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value)); 1936 } 1937 1938 /** 1939 * Call a method taking one Intent on a view in the layout for this RemoteViews. 1940 * 1941 * @param viewId The id of the view on which to call the method. 1942 * @param methodName The name of the method to call. 1943 * @param value The {@link android.content.Intent} to pass the method. 1944 */ 1945 public void setIntent(int viewId, String methodName, Intent value) { 1946 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value)); 1947 } 1948 1949 /** 1950 * Equivalent to calling View.setContentDescription 1951 * 1952 * @param viewId The id of the view whose content description should change 1953 * @param contentDescription The new content description for the view 1954 */ 1955 public void setContentDescription(int viewId, CharSequence contentDescription) { 1956 setCharSequence(viewId, "setContentDescription", contentDescription); 1957 } 1958 1959 private RemoteViews getRemoteViewsToApply(Context context) { 1960 if (hasLandscapeAndPortraitLayouts()) { 1961 int orientation = context.getResources().getConfiguration().orientation; 1962 if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 1963 return mLandscape; 1964 } else { 1965 return mPortrait; 1966 } 1967 } 1968 return this; 1969 } 1970 1971 /** 1972 * Inflates the view hierarchy represented by this object and applies 1973 * all of the actions. 1974 * 1975 * <p><strong>Caller beware: this may throw</strong> 1976 * 1977 * @param context Default context to use 1978 * @param parent Parent that the resulting view hierarchy will be attached to. This method 1979 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate. 1980 * @return The inflated view hierarchy 1981 */ 1982 public View apply(Context context, ViewGroup parent) { 1983 RemoteViews rvToApply = getRemoteViewsToApply(context); 1984 1985 View result; 1986 1987 Context c = prepareContext(context); 1988 1989 LayoutInflater inflater = (LayoutInflater) 1990 c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 1991 1992 inflater = inflater.cloneInContext(c); 1993 inflater.setFilter(this); 1994 1995 result = inflater.inflate(rvToApply.getLayoutId(), parent, false); 1996 1997 rvToApply.performApply(result, parent); 1998 1999 return result; 2000 } 2001 2002 /** 2003 * Applies all of the actions to the provided view. 2004 * 2005 * <p><strong>Caller beware: this may throw</strong> 2006 * 2007 * @param v The view to apply the actions to. This should be the result of 2008 * the {@link #apply(Context,ViewGroup)} call. 2009 */ 2010 public void reapply(Context context, View v) { 2011 RemoteViews rvToApply = getRemoteViewsToApply(context); 2012 2013 // In the case that a view has this RemoteViews applied in one orientation, is persisted 2014 // across orientation change, and has the RemoteViews re-applied in the new orientation, 2015 // we throw an exception, since the layouts may be completely unrelated. 2016 if (hasLandscapeAndPortraitLayouts()) { 2017 if (v.getId() != rvToApply.getLayoutId()) { 2018 throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" + 2019 " that does not share the same root layout id."); 2020 } 2021 } 2022 2023 prepareContext(context); 2024 rvToApply.performApply(v, (ViewGroup) v.getParent()); 2025 } 2026 2027 private void performApply(View v, ViewGroup parent) { 2028 if (mActions != null) { 2029 final int count = mActions.size(); 2030 for (int i = 0; i < count; i++) { 2031 Action a = mActions.get(i); 2032 a.apply(v, parent); 2033 } 2034 } 2035 } 2036 2037 private Context prepareContext(Context context) { 2038 Context c; 2039 String packageName = mPackage; 2040 2041 if (packageName != null) { 2042 try { 2043 c = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED); 2044 } catch (NameNotFoundException e) { 2045 Log.e(LOG_TAG, "Package name " + packageName + " not found"); 2046 c = context; 2047 } 2048 } else { 2049 c = context; 2050 } 2051 2052 return c; 2053 } 2054 2055 /* (non-Javadoc) 2056 * Used to restrict the views which can be inflated 2057 * 2058 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class) 2059 */ 2060 public boolean onLoadClass(Class clazz) { 2061 return clazz.isAnnotationPresent(RemoteView.class); 2062 } 2063 2064 public int describeContents() { 2065 return 0; 2066 } 2067 2068 public void writeToParcel(Parcel dest, int flags) { 2069 if (!hasLandscapeAndPortraitLayouts()) { 2070 dest.writeInt(MODE_NORMAL); 2071 // We only write the bitmap cache if we are the root RemoteViews, as this cache 2072 // is shared by all children. 2073 if (mIsRoot) { 2074 mBitmapCache.writeBitmapsToParcel(dest, flags); 2075 } 2076 dest.writeString(mPackage); 2077 dest.writeInt(mLayoutId); 2078 dest.writeInt(mIsWidgetCollectionChild ? 1 : 0); 2079 int count; 2080 if (mActions != null) { 2081 count = mActions.size(); 2082 } else { 2083 count = 0; 2084 } 2085 dest.writeInt(count); 2086 for (int i=0; i<count; i++) { 2087 Action a = mActions.get(i); 2088 a.writeToParcel(dest, 0); 2089 } 2090 } else { 2091 dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT); 2092 // We only write the bitmap cache if we are the root RemoteViews, as this cache 2093 // is shared by all children. 2094 if (mIsRoot) { 2095 mBitmapCache.writeBitmapsToParcel(dest, flags); 2096 } 2097 mLandscape.writeToParcel(dest, flags); 2098 mPortrait.writeToParcel(dest, flags); 2099 } 2100 } 2101 2102 /** 2103 * Parcelable.Creator that instantiates RemoteViews objects 2104 */ 2105 public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() { 2106 public RemoteViews createFromParcel(Parcel parcel) { 2107 return new RemoteViews(parcel); 2108 } 2109 2110 public RemoteViews[] newArray(int size) { 2111 return new RemoteViews[size]; 2112 } 2113 }; 2114} 2115