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