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