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