RemoteViews.java revision 2dd2197805edb4d9547b143deef2226413218f4c
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.Rect; 27import android.graphics.drawable.Drawable; 28import android.net.Uri; 29import android.os.Bundle; 30import android.os.Parcel; 31import android.os.Parcelable; 32import android.text.TextUtils; 33import android.util.Log; 34import android.view.LayoutInflater; 35import android.view.RemotableViewMethod; 36import android.view.View; 37import android.view.ViewGroup; 38import android.view.LayoutInflater.Filter; 39import android.view.View.OnClickListener; 40 41import java.lang.annotation.ElementType; 42import java.lang.annotation.Retention; 43import java.lang.annotation.RetentionPolicy; 44import java.lang.annotation.Target; 45import java.lang.reflect.Method; 46import java.util.ArrayList; 47 48 49/** 50 * A class that describes a view hierarchy that can be displayed in 51 * another process. The hierarchy is inflated from a layout resource 52 * file, and this class provides some basic operations for modifying 53 * the content of the inflated hierarchy. 54 */ 55public class RemoteViews implements Parcelable, Filter { 56 57 private static final String LOG_TAG = "RemoteViews"; 58 59 /** 60 * The package name of the package containing the layout 61 * resource. (Added to the parcel) 62 */ 63 private final String mPackage; 64 65 /** 66 * The resource ID of the layout file. (Added to the parcel) 67 */ 68 private final int mLayoutId; 69 70 /** 71 * An array of actions to perform on the view tree once it has been 72 * inflated 73 */ 74 private ArrayList<Action> mActions; 75 76 77 /** 78 * This annotation indicates that a subclass of View is alllowed to be used 79 * with the {@link RemoteViews} mechanism. 80 */ 81 @Target({ ElementType.TYPE }) 82 @Retention(RetentionPolicy.RUNTIME) 83 public @interface RemoteView { 84 } 85 86 /** 87 * Exception to send when something goes wrong executing an action 88 * 89 */ 90 public static class ActionException extends RuntimeException { 91 public ActionException(Exception ex) { 92 super(ex); 93 } 94 public ActionException(String message) { 95 super(message); 96 } 97 } 98 99 /** 100 * Base class for all actions that can be performed on an 101 * inflated view. 102 * 103 * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! 104 */ 105 private abstract static class Action implements Parcelable { 106 public abstract void apply(View root) throws ActionException; 107 108 public int describeContents() { 109 return 0; 110 } 111 } 112 113 /** 114 * Equivalent to calling 115 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)} 116 * to launch the provided {@link PendingIntent}. 117 */ 118 private class SetOnClickPendingIntent extends Action { 119 public SetOnClickPendingIntent(int id, PendingIntent pendingIntent) { 120 this.viewId = id; 121 this.pendingIntent = pendingIntent; 122 } 123 124 public SetOnClickPendingIntent(Parcel parcel) { 125 viewId = parcel.readInt(); 126 pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel); 127 } 128 129 public void writeToParcel(Parcel dest, int flags) { 130 dest.writeInt(TAG); 131 dest.writeInt(viewId); 132 pendingIntent.writeToParcel(dest, 0 /* no flags */); 133 } 134 135 @Override 136 public void apply(View root) { 137 final View target = root.findViewById(viewId); 138 if (target != null && pendingIntent != null) { 139 OnClickListener listener = new OnClickListener() { 140 public void onClick(View v) { 141 // Find target view location in screen coordinates and 142 // fill into PendingIntent before sending. 143 final float appScale = v.getContext().getResources() 144 .getCompatibilityInfo().applicationScale; 145 final int[] pos = new int[2]; 146 v.getLocationOnScreen(pos); 147 148 final Rect rect = new Rect(); 149 rect.left = (int) (pos[0] * appScale + 0.5f); 150 rect.top = (int) (pos[1] * appScale + 0.5f); 151 rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f); 152 rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f); 153 154 final Intent intent = new Intent(); 155 intent.setSourceBounds(rect); 156 try { 157 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? 158 v.getContext().startIntentSender( 159 pendingIntent.getIntentSender(), intent, 160 Intent.FLAG_ACTIVITY_NEW_TASK, 161 Intent.FLAG_ACTIVITY_NEW_TASK, 0); 162 } catch (IntentSender.SendIntentException e) { 163 android.util.Log.e(LOG_TAG, "Cannot send pending intent: ", e); 164 } 165 } 166 }; 167 target.setOnClickListener(listener); 168 } 169 } 170 171 int viewId; 172 PendingIntent pendingIntent; 173 174 public final static int TAG = 1; 175 } 176 177 /** 178 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, 179 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, 180 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view. 181 * <p> 182 * These operations will be performed on the {@link Drawable} returned by the 183 * target {@link View#getBackground()} by default. If targetBackground is false, 184 * we assume the target is an {@link ImageView} and try applying the operations 185 * to {@link ImageView#getDrawable()}. 186 * <p> 187 * You can omit specific calls by marking their values with null or -1. 188 */ 189 private class SetDrawableParameters extends Action { 190 public SetDrawableParameters(int id, boolean targetBackground, int alpha, 191 int colorFilter, PorterDuff.Mode mode, int level) { 192 this.viewId = id; 193 this.targetBackground = targetBackground; 194 this.alpha = alpha; 195 this.colorFilter = colorFilter; 196 this.filterMode = mode; 197 this.level = level; 198 } 199 200 public SetDrawableParameters(Parcel parcel) { 201 viewId = parcel.readInt(); 202 targetBackground = parcel.readInt() != 0; 203 alpha = parcel.readInt(); 204 colorFilter = parcel.readInt(); 205 boolean hasMode = parcel.readInt() != 0; 206 if (hasMode) { 207 filterMode = PorterDuff.Mode.valueOf(parcel.readString()); 208 } else { 209 filterMode = null; 210 } 211 level = parcel.readInt(); 212 } 213 214 public void writeToParcel(Parcel dest, int flags) { 215 dest.writeInt(TAG); 216 dest.writeInt(viewId); 217 dest.writeInt(targetBackground ? 1 : 0); 218 dest.writeInt(alpha); 219 dest.writeInt(colorFilter); 220 if (filterMode != null) { 221 dest.writeInt(1); 222 dest.writeString(filterMode.toString()); 223 } else { 224 dest.writeInt(0); 225 } 226 dest.writeInt(level); 227 } 228 229 @Override 230 public void apply(View root) { 231 final View target = root.findViewById(viewId); 232 if (target == null) { 233 return; 234 } 235 236 // Pick the correct drawable to modify for this view 237 Drawable targetDrawable = null; 238 if (targetBackground) { 239 targetDrawable = target.getBackground(); 240 } else if (target instanceof ImageView) { 241 ImageView imageView = (ImageView) target; 242 targetDrawable = imageView.getDrawable(); 243 } 244 245 if (targetDrawable != null) { 246 // Perform modifications only if values are set correctly 247 if (alpha != -1) { 248 targetDrawable.setAlpha(alpha); 249 } 250 if (colorFilter != -1 && filterMode != null) { 251 targetDrawable.setColorFilter(colorFilter, filterMode); 252 } 253 if (level != -1) { 254 targetDrawable.setLevel(level); 255 } 256 } 257 } 258 259 int viewId; 260 boolean targetBackground; 261 int alpha; 262 int colorFilter; 263 PorterDuff.Mode filterMode; 264 int level; 265 266 public final static int TAG = 3; 267 } 268 269 private class ReflectionActionWithoutParams extends Action { 270 int viewId; 271 String methodName; 272 273 public final static int TAG = 5; 274 275 ReflectionActionWithoutParams(int viewId, String methodName) { 276 this.viewId = viewId; 277 this.methodName = methodName; 278 } 279 280 ReflectionActionWithoutParams(Parcel in) { 281 this.viewId = in.readInt(); 282 this.methodName = in.readString(); 283 } 284 285 public void writeToParcel(Parcel out, int flags) { 286 out.writeInt(TAG); 287 out.writeInt(this.viewId); 288 out.writeString(this.methodName); 289 } 290 291 @Override 292 public void apply(View root) { 293 final View view = root.findViewById(viewId); 294 if (view == null) { 295 throw new ActionException("can't find view: 0x" + Integer.toHexString(viewId)); 296 } 297 298 Class klass = view.getClass(); 299 Method method; 300 try { 301 method = klass.getMethod(this.methodName); 302 } catch (NoSuchMethodException ex) { 303 throw new ActionException("view: " + klass.getName() + " doesn't have method: " 304 + this.methodName + "()"); 305 } 306 307 if (!method.isAnnotationPresent(RemotableViewMethod.class)) { 308 throw new ActionException("view: " + klass.getName() 309 + " can't use method with RemoteViews: " 310 + this.methodName + "()"); 311 } 312 313 try { 314 //noinspection ConstantIfStatement 315 if (false) { 316 Log.d("RemoteViews", "view: " + klass.getName() + " calling method: " 317 + this.methodName + "()"); 318 } 319 method.invoke(view); 320 } catch (Exception ex) { 321 throw new ActionException(ex); 322 } 323 } 324 } 325 326 /** 327 * Base class for the reflection actions. 328 */ 329 private class ReflectionAction extends Action { 330 static final int TAG = 2; 331 332 static final int BOOLEAN = 1; 333 static final int BYTE = 2; 334 static final int SHORT = 3; 335 static final int INT = 4; 336 static final int LONG = 5; 337 static final int FLOAT = 6; 338 static final int DOUBLE = 7; 339 static final int CHAR = 8; 340 static final int STRING = 9; 341 static final int CHAR_SEQUENCE = 10; 342 static final int URI = 11; 343 static final int BITMAP = 12; 344 static final int BUNDLE = 13; 345 static final int INTENT = 14; 346 347 int viewId; 348 String methodName; 349 int type; 350 Object value; 351 352 ReflectionAction(int viewId, String methodName, int type, Object value) { 353 this.viewId = viewId; 354 this.methodName = methodName; 355 this.type = type; 356 this.value = value; 357 } 358 359 ReflectionAction(Parcel in) { 360 this.viewId = in.readInt(); 361 this.methodName = in.readString(); 362 this.type = in.readInt(); 363 //noinspection ConstantIfStatement 364 if (false) { 365 Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId) 366 + " methodName=" + this.methodName + " type=" + this.type); 367 } 368 switch (this.type) { 369 case BOOLEAN: 370 this.value = in.readInt() != 0; 371 break; 372 case BYTE: 373 this.value = in.readByte(); 374 break; 375 case SHORT: 376 this.value = (short)in.readInt(); 377 break; 378 case INT: 379 this.value = in.readInt(); 380 break; 381 case LONG: 382 this.value = in.readLong(); 383 break; 384 case FLOAT: 385 this.value = in.readFloat(); 386 break; 387 case DOUBLE: 388 this.value = in.readDouble(); 389 break; 390 case CHAR: 391 this.value = (char)in.readInt(); 392 break; 393 case STRING: 394 this.value = in.readString(); 395 break; 396 case CHAR_SEQUENCE: 397 this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 398 break; 399 case URI: 400 this.value = Uri.CREATOR.createFromParcel(in); 401 break; 402 case BITMAP: 403 this.value = Bitmap.CREATOR.createFromParcel(in); 404 break; 405 case BUNDLE: 406 this.value = in.readBundle(); 407 break; 408 case INTENT: 409 this.value = Intent.CREATOR.createFromParcel(in); 410 break; 411 default: 412 break; 413 } 414 } 415 416 public void writeToParcel(Parcel out, int flags) { 417 out.writeInt(TAG); 418 out.writeInt(this.viewId); 419 out.writeString(this.methodName); 420 out.writeInt(this.type); 421 //noinspection ConstantIfStatement 422 if (false) { 423 Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId) 424 + " methodName=" + this.methodName + " type=" + this.type); 425 } 426 switch (this.type) { 427 case BOOLEAN: 428 out.writeInt((Boolean) this.value ? 1 : 0); 429 break; 430 case BYTE: 431 out.writeByte((Byte) this.value); 432 break; 433 case SHORT: 434 out.writeInt((Short) this.value); 435 break; 436 case INT: 437 out.writeInt((Integer) this.value); 438 break; 439 case LONG: 440 out.writeLong((Long) this.value); 441 break; 442 case FLOAT: 443 out.writeFloat((Float) this.value); 444 break; 445 case DOUBLE: 446 out.writeDouble((Double) this.value); 447 break; 448 case CHAR: 449 out.writeInt((int)((Character)this.value).charValue()); 450 break; 451 case STRING: 452 out.writeString((String)this.value); 453 break; 454 case CHAR_SEQUENCE: 455 TextUtils.writeToParcel((CharSequence)this.value, out, flags); 456 break; 457 case URI: 458 ((Uri)this.value).writeToParcel(out, flags); 459 break; 460 case BITMAP: 461 ((Bitmap)this.value).writeToParcel(out, flags); 462 break; 463 case BUNDLE: 464 out.writeBundle((Bundle) this.value); 465 break; 466 case INTENT: 467 ((Intent)this.value).writeToParcel(out, flags); 468 break; 469 default: 470 break; 471 } 472 } 473 474 private Class getParameterType() { 475 switch (this.type) { 476 case BOOLEAN: 477 return boolean.class; 478 case BYTE: 479 return byte.class; 480 case SHORT: 481 return short.class; 482 case INT: 483 return int.class; 484 case LONG: 485 return long.class; 486 case FLOAT: 487 return float.class; 488 case DOUBLE: 489 return double.class; 490 case CHAR: 491 return char.class; 492 case STRING: 493 return String.class; 494 case CHAR_SEQUENCE: 495 return CharSequence.class; 496 case URI: 497 return Uri.class; 498 case BITMAP: 499 return Bitmap.class; 500 case BUNDLE: 501 return Bundle.class; 502 case INTENT: 503 return Intent.class; 504 default: 505 return null; 506 } 507 } 508 509 @Override 510 public void apply(View root) { 511 final View view = root.findViewById(viewId); 512 if (view == null) { 513 throw new ActionException("can't find view: 0x" + Integer.toHexString(viewId)); 514 } 515 516 Class param = getParameterType(); 517 if (param == null) { 518 throw new ActionException("bad type: " + this.type); 519 } 520 521 Class klass = view.getClass(); 522 Method method; 523 try { 524 method = klass.getMethod(this.methodName, getParameterType()); 525 } 526 catch (NoSuchMethodException ex) { 527 throw new ActionException("view: " + klass.getName() + " doesn't have method: " 528 + this.methodName + "(" + param.getName() + ")"); 529 } 530 531 if (!method.isAnnotationPresent(RemotableViewMethod.class)) { 532 throw new ActionException("view: " + klass.getName() 533 + " can't use method with RemoteViews: " 534 + this.methodName + "(" + param.getName() + ")"); 535 } 536 537 try { 538 //noinspection ConstantIfStatement 539 if (false) { 540 Log.d("RemoteViews", "view: " + klass.getName() + " calling method: " 541 + this.methodName + "(" + param.getName() + ") with " 542 + (this.value == null ? "null" : this.value.getClass().getName())); 543 } 544 method.invoke(view, this.value); 545 } 546 catch (Exception ex) { 547 throw new ActionException(ex); 548 } 549 } 550 } 551 552 /** 553 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the 554 * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()} 555 * when null. This allows users to build "nested" {@link RemoteViews}. 556 */ 557 private class ViewGroupAction extends Action { 558 public ViewGroupAction(int viewId, RemoteViews nestedViews) { 559 this.viewId = viewId; 560 this.nestedViews = nestedViews; 561 } 562 563 public ViewGroupAction(Parcel parcel) { 564 viewId = parcel.readInt(); 565 nestedViews = parcel.readParcelable(null); 566 } 567 568 public void writeToParcel(Parcel dest, int flags) { 569 dest.writeInt(TAG); 570 dest.writeInt(viewId); 571 dest.writeParcelable(nestedViews, 0 /* no flags */); 572 } 573 574 @Override 575 public void apply(View root) { 576 final Context context = root.getContext(); 577 final ViewGroup target = (ViewGroup) root.findViewById(viewId); 578 if (nestedViews != null) { 579 // Inflate nested views and add as children 580 target.addView(nestedViews.apply(context, target)); 581 } else if (target != null) { 582 // Clear all children when nested views omitted 583 target.removeAllViews(); 584 } 585 } 586 587 int viewId; 588 RemoteViews nestedViews; 589 590 public final static int TAG = 4; 591 } 592 593 /** 594 * Create a new RemoteViews object that will display the views contained 595 * in the specified layout file. 596 * 597 * @param packageName Name of the package that contains the layout resource 598 * @param layoutId The id of the layout resource 599 */ 600 public RemoteViews(String packageName, int layoutId) { 601 mPackage = packageName; 602 mLayoutId = layoutId; 603 } 604 605 /** 606 * Reads a RemoteViews object from a parcel. 607 * 608 * @param parcel 609 */ 610 public RemoteViews(Parcel parcel) { 611 mPackage = parcel.readString(); 612 mLayoutId = parcel.readInt(); 613 int count = parcel.readInt(); 614 if (count > 0) { 615 mActions = new ArrayList<Action>(count); 616 for (int i=0; i<count; i++) { 617 int tag = parcel.readInt(); 618 switch (tag) { 619 case SetOnClickPendingIntent.TAG: 620 mActions.add(new SetOnClickPendingIntent(parcel)); 621 break; 622 case SetDrawableParameters.TAG: 623 mActions.add(new SetDrawableParameters(parcel)); 624 break; 625 case ReflectionAction.TAG: 626 mActions.add(new ReflectionAction(parcel)); 627 break; 628 case ViewGroupAction.TAG: 629 mActions.add(new ViewGroupAction(parcel)); 630 break; 631 case ReflectionActionWithoutParams.TAG: 632 mActions.add(new ReflectionActionWithoutParams(parcel)); 633 break; 634 default: 635 throw new ActionException("Tag " + tag + " not found"); 636 } 637 } 638 } 639 } 640 641 @Override 642 public RemoteViews clone() { 643 final RemoteViews that = new RemoteViews(mPackage, mLayoutId); 644 if (mActions != null) { 645 that.mActions = (ArrayList<Action>)mActions.clone(); 646 } 647 return that; 648 } 649 650 public String getPackage() { 651 return mPackage; 652 } 653 654 public int getLayoutId() { 655 return mLayoutId; 656 } 657 658 /** 659 * Add an action to be executed on the remote side when apply is called. 660 * 661 * @param a The action to add 662 */ 663 private void addAction(Action a) { 664 if (mActions == null) { 665 mActions = new ArrayList<Action>(); 666 } 667 mActions.add(a); 668 } 669 670 /** 671 * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the 672 * given {@link RemoteViews}. This allows users to build "nested" 673 * {@link RemoteViews}. In cases where consumers of {@link RemoteViews} may 674 * recycle layouts, use {@link #removeAllViews(int)} to clear any existing 675 * children. 676 * 677 * @param viewId The id of the parent {@link ViewGroup} to add child into. 678 * @param nestedView {@link RemoteViews} that describes the child. 679 */ 680 public void addView(int viewId, RemoteViews nestedView) { 681 addAction(new ViewGroupAction(viewId, nestedView)); 682 } 683 684 /** 685 * Equivalent to calling {@link ViewGroup#removeAllViews()}. 686 * 687 * @param viewId The id of the parent {@link ViewGroup} to remove all 688 * children from. 689 */ 690 public void removeAllViews(int viewId) { 691 addAction(new ViewGroupAction(viewId, null)); 692 } 693 694 /** 695 * Equivalent to calling {@link AdapterViewFlipper#showNext()} 696 * 697 * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showNext()} 698 */ 699 public void showNext(int viewId) { 700 addAction(new ReflectionActionWithoutParams(viewId, "showNext")); 701 } 702 703 /** 704 * Equivalent to calling {@link AdapterViewFlipper#showPrevious()} 705 * 706 * @param viewId The id of the view on which to call {@link AdapterViewFlipper#showPrevious()} 707 */ 708 public void showPrevious(int viewId) { 709 addAction(new ReflectionActionWithoutParams(viewId, "showPrevious")); 710 } 711 712 /** 713 * Equivalent to calling View.setVisibility 714 * 715 * @param viewId The id of the view whose visibility should change 716 * @param visibility The new visibility for the view 717 */ 718 public void setViewVisibility(int viewId, int visibility) { 719 setInt(viewId, "setVisibility", visibility); 720 } 721 722 /** 723 * Equivalent to calling TextView.setText 724 * 725 * @param viewId The id of the view whose text should change 726 * @param text The new text for the view 727 */ 728 public void setTextViewText(int viewId, CharSequence text) { 729 setCharSequence(viewId, "setText", text); 730 } 731 732 /** 733 * Equivalent to calling ImageView.setImageResource 734 * 735 * @param viewId The id of the view whose drawable should change 736 * @param srcId The new resource id for the drawable 737 */ 738 public void setImageViewResource(int viewId, int srcId) { 739 setInt(viewId, "setImageResource", srcId); 740 } 741 742 /** 743 * Equivalent to calling ImageView.setImageURI 744 * 745 * @param viewId The id of the view whose drawable should change 746 * @param uri The Uri for the image 747 */ 748 public void setImageViewUri(int viewId, Uri uri) { 749 setUri(viewId, "setImageURI", uri); 750 } 751 752 /** 753 * Equivalent to calling ImageView.setImageBitmap 754 * 755 * @param viewId The id of the view whose drawable should change 756 * @param bitmap The new Bitmap for the drawable 757 */ 758 public void setImageViewBitmap(int viewId, Bitmap bitmap) { 759 setBitmap(viewId, "setImageBitmap", bitmap); 760 } 761 762 /** 763 * Equivalent to calling {@link Chronometer#setBase Chronometer.setBase}, 764 * {@link Chronometer#setFormat Chronometer.setFormat}, 765 * and {@link Chronometer#start Chronometer.start()} or 766 * {@link Chronometer#stop Chronometer.stop()}. 767 * 768 * @param viewId The id of the view whose text should change 769 * @param base The time at which the timer would have read 0:00. This 770 * time should be based off of 771 * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}. 772 * @param format The Chronometer format string, or null to 773 * simply display the timer value. 774 * @param started True if you want the clock to be started, false if not. 775 */ 776 public void setChronometer(int viewId, long base, String format, boolean started) { 777 setLong(viewId, "setBase", base); 778 setString(viewId, "setFormat", format); 779 setBoolean(viewId, "setStarted", started); 780 } 781 782 /** 783 * Equivalent to calling {@link ProgressBar#setMax ProgressBar.setMax}, 784 * {@link ProgressBar#setProgress ProgressBar.setProgress}, and 785 * {@link ProgressBar#setIndeterminate ProgressBar.setIndeterminate} 786 * 787 * If indeterminate is true, then the values for max and progress are ignored. 788 * 789 * @param viewId The id of the view whose text should change 790 * @param max The 100% value for the progress bar 791 * @param progress The current value of the progress bar. 792 * @param indeterminate True if the progress bar is indeterminate, 793 * false if not. 794 */ 795 public void setProgressBar(int viewId, int max, int progress, 796 boolean indeterminate) { 797 setBoolean(viewId, "setIndeterminate", indeterminate); 798 if (!indeterminate) { 799 setInt(viewId, "setMax", max); 800 setInt(viewId, "setProgress", progress); 801 } 802 } 803 804 /** 805 * Equivalent to calling 806 * {@link android.view.View#setOnClickListener(android.view.View.OnClickListener)} 807 * to launch the provided {@link PendingIntent}. 808 * 809 * @param viewId The id of the view that will trigger the {@link PendingIntent} when clicked 810 * @param pendingIntent The {@link PendingIntent} to send when user clicks 811 */ 812 public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) { 813 addAction(new SetOnClickPendingIntent(viewId, pendingIntent)); 814 } 815 816 /** 817 * @hide 818 * Equivalent to calling a combination of {@link Drawable#setAlpha(int)}, 819 * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, 820 * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given 821 * view. 822 * <p> 823 * You can omit specific calls by marking their values with null or -1. 824 * 825 * @param viewId The id of the view that contains the target 826 * {@link Drawable} 827 * @param targetBackground If true, apply these parameters to the 828 * {@link Drawable} returned by 829 * {@link android.view.View#getBackground()}. Otherwise, assume 830 * the target view is an {@link ImageView} and apply them to 831 * {@link ImageView#getDrawable()}. 832 * @param alpha Specify an alpha value for the drawable, or -1 to leave 833 * unchanged. 834 * @param colorFilter Specify a color for a 835 * {@link android.graphics.ColorFilter} for this drawable, or -1 836 * to leave unchanged. 837 * @param mode Specify a PorterDuff mode for this drawable, or null to leave 838 * unchanged. 839 * @param level Specify the level for the drawable, or -1 to leave 840 * unchanged. 841 */ 842 public void setDrawableParameters(int viewId, boolean targetBackground, int alpha, 843 int colorFilter, PorterDuff.Mode mode, int level) { 844 addAction(new SetDrawableParameters(viewId, targetBackground, alpha, 845 colorFilter, mode, level)); 846 } 847 848 /** 849 * Equivalent to calling {@link android.widget.TextView#setTextColor(int)}. 850 * 851 * @param viewId The id of the view whose text should change 852 * @param color Sets the text color for all the states (normal, selected, 853 * focused) to be this color. 854 */ 855 public void setTextColor(int viewId, int color) { 856 setInt(viewId, "setTextColor", color); 857 } 858 859 /** 860 * Equivalent to calling {@link android.widget.AbsListView#setRemoteViewsAdapter(Intent)}. 861 * 862 * @param viewId The id of the view whose text should change 863 * @param intent The intent of the service which will be 864 * providing data to the RemoteViewsAdapter 865 */ 866 public void setRemoteAdapter(int viewId, Intent intent) { 867 setIntent(viewId, "setRemoteViewsAdapter", intent); 868 } 869 870 /** 871 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}. 872 * 873 * @param viewId The id of the view whose text should change 874 * @param position Scroll to this adapter position 875 */ 876 public void setScrollPosition(int viewId, int position) { 877 setInt(viewId, "smoothScrollToPosition", position); 878 } 879 880 /** 881 * Equivalent to calling {@link android.widget.AbsListView#smoothScrollToPosition(int, int)}. 882 * 883 * @param viewId The id of the view whose text should change 884 * @param offset Scroll by this adapter position offset 885 */ 886 public void setRelativeScrollPosition(int viewId, int offset) { 887 setInt(viewId, "smoothScrollByOffset", offset); 888 } 889 890 /** 891 * Call a method taking one boolean on a view in the layout for this RemoteViews. 892 * 893 * @param viewId The id of the view whose text should change 894 * @param methodName The name of the method to call. 895 * @param value The value to pass to the method. 896 */ 897 public void setBoolean(int viewId, String methodName, boolean value) { 898 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BOOLEAN, value)); 899 } 900 901 /** 902 * Call a method taking one byte on a view in the layout for this RemoteViews. 903 * 904 * @param viewId The id of the view whose text should change 905 * @param methodName The name of the method to call. 906 * @param value The value to pass to the method. 907 */ 908 public void setByte(int viewId, String methodName, byte value) { 909 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BYTE, value)); 910 } 911 912 /** 913 * Call a method taking one short on a view in the layout for this RemoteViews. 914 * 915 * @param viewId The id of the view whose text should change 916 * @param methodName The name of the method to call. 917 * @param value The value to pass to the method. 918 */ 919 public void setShort(int viewId, String methodName, short value) { 920 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.SHORT, value)); 921 } 922 923 /** 924 * Call a method taking one int on a view in the layout for this RemoteViews. 925 * 926 * @param viewId The id of the view whose text should change 927 * @param methodName The name of the method to call. 928 * @param value The value to pass to the method. 929 */ 930 public void setInt(int viewId, String methodName, int value) { 931 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INT, value)); 932 } 933 934 /** 935 * Call a method taking one long on a view in the layout for this RemoteViews. 936 * 937 * @param viewId The id of the view whose text should change 938 * @param methodName The name of the method to call. 939 * @param value The value to pass to the method. 940 */ 941 public void setLong(int viewId, String methodName, long value) { 942 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.LONG, value)); 943 } 944 945 /** 946 * Call a method taking one float on a view in the layout for this RemoteViews. 947 * 948 * @param viewId The id of the view whose text should change 949 * @param methodName The name of the method to call. 950 * @param value The value to pass to the method. 951 */ 952 public void setFloat(int viewId, String methodName, float value) { 953 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.FLOAT, value)); 954 } 955 956 /** 957 * Call a method taking one double on a view in the layout for this RemoteViews. 958 * 959 * @param viewId The id of the view whose text should change 960 * @param methodName The name of the method to call. 961 * @param value The value to pass to the method. 962 */ 963 public void setDouble(int viewId, String methodName, double value) { 964 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.DOUBLE, value)); 965 } 966 967 /** 968 * Call a method taking one char on a view in the layout for this RemoteViews. 969 * 970 * @param viewId The id of the view whose text should change 971 * @param methodName The name of the method to call. 972 * @param value The value to pass to the method. 973 */ 974 public void setChar(int viewId, String methodName, char value) { 975 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR, value)); 976 } 977 978 /** 979 * Call a method taking one String on a view in the layout for this RemoteViews. 980 * 981 * @param viewId The id of the view whose text should change 982 * @param methodName The name of the method to call. 983 * @param value The value to pass to the method. 984 */ 985 public void setString(int viewId, String methodName, String value) { 986 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.STRING, value)); 987 } 988 989 /** 990 * Call a method taking one CharSequence on a view in the layout for this RemoteViews. 991 * 992 * @param viewId The id of the view whose text should change 993 * @param methodName The name of the method to call. 994 * @param value The value to pass to the method. 995 */ 996 public void setCharSequence(int viewId, String methodName, CharSequence value) { 997 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value)); 998 } 999 1000 /** 1001 * Call a method taking one Uri on a view in the layout for this RemoteViews. 1002 * 1003 * @param viewId The id of the view whose text should change 1004 * @param methodName The name of the method to call. 1005 * @param value The value to pass to the method. 1006 */ 1007 public void setUri(int viewId, String methodName, Uri value) { 1008 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value)); 1009 } 1010 1011 /** 1012 * Call a method taking one Bitmap on a view in the layout for this RemoteViews. 1013 * @more 1014 * <p class="note">The bitmap will be flattened into the parcel if this object is 1015 * sent across processes, so it may end up using a lot of memory, and may be fairly slow.</p> 1016 * 1017 * @param viewId The id of the view whose text should change 1018 * @param methodName The name of the method to call. 1019 * @param value The value to pass to the method. 1020 */ 1021 public void setBitmap(int viewId, String methodName, Bitmap value) { 1022 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BITMAP, value)); 1023 } 1024 1025 /** 1026 * Call a method taking one Bundle on a view in the layout for this RemoteViews. 1027 * 1028 * @param viewId The id of the view whose text should change 1029 * @param methodName The name of the method to call. 1030 * @param value The value to pass to the method. 1031 */ 1032 public void setBundle(int viewId, String methodName, Bundle value) { 1033 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value)); 1034 } 1035 1036 /** 1037 * 1038 * @param viewId 1039 * @param methodName 1040 * @param value 1041 */ 1042 public void setIntent(int viewId, String methodName, Intent value) { 1043 addAction(new ReflectionAction(viewId, methodName, ReflectionAction.INTENT, value)); 1044 } 1045 1046 /** 1047 * Inflates the view hierarchy represented by this object and applies 1048 * all of the actions. 1049 * 1050 * <p><strong>Caller beware: this may throw</strong> 1051 * 1052 * @param context Default context to use 1053 * @param parent Parent that the resulting view hierarchy will be attached to. This method 1054 * does <strong>not</strong> attach the hierarchy. The caller should do so when appropriate. 1055 * @return The inflated view hierarchy 1056 */ 1057 public View apply(Context context, ViewGroup parent) { 1058 View result; 1059 1060 Context c = prepareContext(context); 1061 1062 LayoutInflater inflater = (LayoutInflater) 1063 c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 1064 1065 inflater = inflater.cloneInContext(c); 1066 inflater.setFilter(this); 1067 1068 result = inflater.inflate(mLayoutId, parent, false); 1069 1070 performApply(result); 1071 1072 return result; 1073 } 1074 1075 /** 1076 * Applies all of the actions to the provided view. 1077 * 1078 * <p><strong>Caller beware: this may throw</strong> 1079 * 1080 * @param v The view to apply the actions to. This should be the result of 1081 * the {@link #apply(Context,ViewGroup)} call. 1082 */ 1083 public void reapply(Context context, View v) { 1084 prepareContext(context); 1085 performApply(v); 1086 } 1087 1088 private void performApply(View v) { 1089 if (mActions != null) { 1090 final int count = mActions.size(); 1091 for (int i = 0; i < count; i++) { 1092 Action a = mActions.get(i); 1093 a.apply(v); 1094 } 1095 } 1096 } 1097 1098 private Context prepareContext(Context context) { 1099 Context c; 1100 String packageName = mPackage; 1101 1102 if (packageName != null) { 1103 try { 1104 c = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED); 1105 } catch (NameNotFoundException e) { 1106 Log.e(LOG_TAG, "Package name " + packageName + " not found"); 1107 c = context; 1108 } 1109 } else { 1110 c = context; 1111 } 1112 1113 return c; 1114 } 1115 1116 /* (non-Javadoc) 1117 * Used to restrict the views which can be inflated 1118 * 1119 * @see android.view.LayoutInflater.Filter#onLoadClass(java.lang.Class) 1120 */ 1121 public boolean onLoadClass(Class clazz) { 1122 return clazz.isAnnotationPresent(RemoteView.class); 1123 } 1124 1125 public int describeContents() { 1126 return 0; 1127 } 1128 1129 public void writeToParcel(Parcel dest, int flags) { 1130 dest.writeString(mPackage); 1131 dest.writeInt(mLayoutId); 1132 int count; 1133 if (mActions != null) { 1134 count = mActions.size(); 1135 } else { 1136 count = 0; 1137 } 1138 dest.writeInt(count); 1139 for (int i=0; i<count; i++) { 1140 Action a = mActions.get(i); 1141 a.writeToParcel(dest, 0); 1142 } 1143 } 1144 1145 /** 1146 * Parcelable.Creator that instantiates RemoteViews objects 1147 */ 1148 public static final Parcelable.Creator<RemoteViews> CREATOR = new Parcelable.Creator<RemoteViews>() { 1149 public RemoteViews createFromParcel(Parcel parcel) { 1150 return new RemoteViews(parcel); 1151 } 1152 1153 public RemoteViews[] newArray(int size) { 1154 return new RemoteViews[size]; 1155 } 1156 }; 1157} 1158