1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.appwidget; 18 19 import static android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS; 20 import static android.appwidget.flags.Flags.generatedPreviews; 21 22 import android.annotation.FlaggedApi; 23 import android.annotation.IdRes; 24 import android.annotation.IntDef; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.SuppressLint; 28 import android.app.PendingIntent; 29 import android.compat.annotation.UnsupportedAppUsage; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.pm.ActivityInfo; 33 import android.content.pm.PackageManager; 34 import android.content.res.ResourceId; 35 import android.content.res.Resources; 36 import android.graphics.drawable.Drawable; 37 import android.os.Bundle; 38 import android.os.Parcel; 39 import android.os.Parcelable; 40 import android.os.UserHandle; 41 import android.util.DisplayMetrics; 42 import android.util.TypedValue; 43 44 import java.lang.annotation.Retention; 45 import java.lang.annotation.RetentionPolicy; 46 47 /** 48 * Describes the meta data for an installed AppWidget provider. The fields in this class 49 * correspond to the fields in the <code><appwidget-provider></code> xml tag. 50 */ 51 public class AppWidgetProviderInfo implements Parcelable { 52 53 /** 54 * Widget is not resizable. 55 */ 56 public static final int RESIZE_NONE = 0; 57 /** 58 * Widget is resizable in the horizontal axis only. 59 */ 60 public static final int RESIZE_HORIZONTAL = 1; 61 /** 62 * Widget is resizable in the vertical axis only. 63 */ 64 public static final int RESIZE_VERTICAL = 2; 65 /** 66 * Widget is resizable in both the horizontal and vertical axes. 67 */ 68 public static final int RESIZE_BOTH = RESIZE_HORIZONTAL | RESIZE_VERTICAL; 69 70 /** @hide */ 71 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 72 RESIZE_HORIZONTAL, 73 RESIZE_VERTICAL, 74 }) 75 @Retention(RetentionPolicy.SOURCE) 76 public @interface ResizeModeFlags {} 77 78 /** {@hide} */ 79 public static final int WIDGET_CATEGORY_UNKNOWN = -1; 80 81 /** 82 * Indicates that the widget can be displayed on the home screen. This is the default value. 83 */ 84 public static final int WIDGET_CATEGORY_HOME_SCREEN = 1; 85 86 /** 87 * Indicates that the widget can be displayed on the keyguard. 88 */ 89 public static final int WIDGET_CATEGORY_KEYGUARD = 2; 90 91 /** 92 * Indicates that the widget can be displayed within a space reserved for the search box. 93 */ 94 public static final int WIDGET_CATEGORY_SEARCHBOX = 4; 95 96 /** @hide */ 97 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 98 WIDGET_CATEGORY_HOME_SCREEN, 99 WIDGET_CATEGORY_KEYGUARD, 100 WIDGET_CATEGORY_SEARCHBOX, 101 }) 102 @Retention(RetentionPolicy.SOURCE) 103 public @interface CategoryFlags {} 104 105 /** 106 * The widget can be reconfigured anytime after it is bound by starting the 107 * {@link #configure} activity. 108 * 109 * @see #widgetFeatures 110 */ 111 public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; 112 113 /** 114 * The widget is added directly by the app, and the host may hide this widget when providing 115 * the user with the list of available widgets to choose from. 116 * 117 * @see AppWidgetManager#requestPinAppWidget(ComponentName, Bundle, PendingIntent) 118 * @see #widgetFeatures 119 */ 120 public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2; 121 122 /** 123 * The widget provides a default configuration. The host may choose not to launch the provided 124 * configuration activity. 125 * 126 * @see #widgetFeatures 127 */ 128 public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4; 129 130 /** @hide */ 131 @IntDef(flag = true, prefix = { "FLAG_" }, value = { 132 WIDGET_FEATURE_RECONFIGURABLE, 133 WIDGET_FEATURE_HIDE_FROM_PICKER, 134 WIDGET_FEATURE_CONFIGURATION_OPTIONAL 135 }) 136 @Retention(RetentionPolicy.SOURCE) 137 public @interface FeatureFlags {} 138 139 /** 140 * Identity of this AppWidget component. This component should be a {@link 141 * android.content.BroadcastReceiver}, and it will be sent the AppWidget intents 142 * {@link android.appwidget as described in the AppWidget package documentation}. 143 * 144 * <p>This field corresponds to the <code>android:name</code> attribute in 145 * the <code><receiver></code> element in the AndroidManifest.xml file. 146 */ 147 public ComponentName provider; 148 149 /** 150 * The default width of the widget when added to a host, in px. The widget will get 151 * at least this width, and will often be given more, depending on the host. 152 * 153 * <p>This field corresponds to the <code>android:minWidth</code> attribute in 154 * the AppWidget meta-data file. 155 */ 156 public int minWidth; 157 158 /** 159 * The default height of the widget when added to a host, in px. The widget will get 160 * at least this height, and will often be given more, depending on the host. 161 * 162 * <p>This field corresponds to the <code>android:minHeight</code> attribute in 163 * the AppWidget meta-data file. 164 */ 165 public int minHeight; 166 167 /** 168 * Minimum width (in px) which the widget can be resized to. This field has no effect if it 169 * is greater than minWidth or if horizontal resizing isn't enabled (see {@link #resizeMode}). 170 * 171 * <p>This field corresponds to the <code>android:minResizeWidth</code> attribute in 172 * the AppWidget meta-data file. 173 */ 174 public int minResizeWidth; 175 176 /** 177 * Minimum height (in px) which the widget can be resized to. This field has no effect if it 178 * is greater than minHeight or if vertical resizing isn't enabled (see {@link #resizeMode}). 179 * 180 * <p>This field corresponds to the <code>android:minResizeHeight</code> attribute in 181 * the AppWidget meta-data file. 182 */ 183 public int minResizeHeight; 184 185 /** 186 * Maximum width (in px) which the widget can be resized to. This field has no effect if it is 187 * smaller than minWidth or if horizontal resizing isn't enabled (see {@link #resizeMode}). 188 * 189 * <p>This field corresponds to the <code>android:maxResizeWidth</code> attribute in the 190 * AppWidget meta-data file. 191 */ 192 @SuppressLint("MutableBareField") 193 public int maxResizeWidth; 194 195 /** 196 * Maximum height (in px) which the widget can be resized to. This field has no effect if it is 197 * smaller than minHeight or if vertical resizing isn't enabled (see {@link #resizeMode}). 198 * 199 * <p>This field corresponds to the <code>android:maxResizeHeight</code> attribute in the 200 * AppWidget meta-data file. 201 */ 202 @SuppressLint("MutableBareField") 203 public int maxResizeHeight; 204 205 /** 206 * The default width of a widget when added to a host, in units of launcher grid cells. 207 * 208 * <p>This field corresponds to the <code>android:targetCellWidth</code> attribute in the 209 * AppWidget meta-data file. 210 */ 211 @SuppressLint("MutableBareField") 212 public int targetCellWidth; 213 214 /** 215 * The default height of a widget when added to a host, in units of launcher grid cells. 216 * 217 * <p>This field corresponds to the <code>android:targetCellHeight</code> attribute in the 218 * AppWidget meta-data file. 219 */ 220 @SuppressLint("MutableBareField") 221 public int targetCellHeight; 222 223 /** 224 * How often, in milliseconds, that this AppWidget wants to be updated. 225 * The AppWidget manager may place a limit on how often a AppWidget is updated. 226 * 227 * <p>This field corresponds to the <code>android:updatePeriodMillis</code> attribute in 228 * the AppWidget meta-data file. 229 * 230 * <p class="note"><b>Note:</b> Updates requested with <code>updatePeriodMillis</code> 231 * will not be delivered more than once every 30 minutes.</p> 232 */ 233 public int updatePeriodMillis; 234 235 /** 236 * The resource id of the initial layout for this AppWidget. This should be 237 * displayed until the RemoteViews for the AppWidget is available. 238 * 239 * <p>This field corresponds to the <code>android:initialLayout</code> attribute in 240 * the AppWidget meta-data file. 241 */ 242 public int initialLayout; 243 244 /** 245 * The resource id of the initial layout for this AppWidget when it is displayed on keyguard. 246 * This parameter only needs to be provided if the widget can be displayed on the keyguard, 247 * see {@link #widgetCategory}. 248 * 249 * <p>This field corresponds to the <code>android:initialKeyguardLayout</code> attribute in 250 * the AppWidget meta-data file. 251 */ 252 public int initialKeyguardLayout; 253 254 /** 255 * The activity to launch that will configure the AppWidget. 256 * 257 * <p>This class name of field corresponds to the <code>android:configure</code> attribute in 258 * the AppWidget meta-data file. The package name always corresponds to the package containing 259 * the AppWidget provider. 260 */ 261 public ComponentName configure; 262 263 /** 264 * The label to display to the user in the AppWidget picker. 265 * 266 * @deprecated Use {@link #loadLabel(android.content.pm.PackageManager)}. 267 */ 268 @Deprecated 269 public String label; 270 271 /** 272 * The icon to display for this AppWidget in the AppWidget picker. If not supplied in the 273 * xml, the application icon will be used. 274 * 275 * <p>This field corresponds to the <code>android:icon</code> attribute in 276 * the <code><receiver></code> element in the AndroidManifest.xml file. 277 */ 278 public int icon; 279 280 /** 281 * The view id of the AppWidget subview which should be auto-advanced by the widget's host. 282 * 283 * <p>This field corresponds to the <code>android:autoAdvanceViewId</code> attribute in 284 * the AppWidget meta-data file. 285 */ 286 public int autoAdvanceViewId; 287 288 /** 289 * A preview of what the AppWidget will look like after it's configured. 290 * If not supplied, the AppWidget's icon will be used. 291 * 292 * <p>This field corresponds to the <code>android:previewImage</code> attribute in the AppWidget 293 * meta-data file. 294 */ 295 public int previewImage; 296 297 /** 298 * The layout resource id of a preview of what the AppWidget will look like after it's 299 * configured. 300 * 301 * <p>Unlike previewImage, previewLayout can better showcase AppWidget in different locales, 302 * system themes, display sizes & density etc. 303 * 304 * <p>If supplied, this will take precedence over the previewImage on supported widget hosts. 305 * Otherwise, previewImage will be used. 306 * 307 * <p>This field corresponds to the <code>android:previewLayout</code> attribute in the 308 * AppWidget meta-data file. 309 */ 310 @SuppressLint("MutableBareField") 311 @IdRes 312 public int previewLayout; 313 314 /** 315 * The rules by which a widget can be resized. See {@link #RESIZE_NONE}, 316 * {@link #RESIZE_NONE}, {@link #RESIZE_HORIZONTAL}, 317 * {@link #RESIZE_VERTICAL}, {@link #RESIZE_BOTH}. 318 * 319 * <p>This field corresponds to the <code>android:resizeMode</code> attribute in 320 * the AppWidget meta-data file. 321 */ 322 @ResizeModeFlags 323 public int resizeMode; 324 325 /** 326 * Determines whether this widget can be displayed on the home screen, the keyguard, or both. 327 * A widget which is displayed on both needs to ensure that it follows the design guidelines 328 * for both widget classes. This can be achieved by querying the AppWidget options in its 329 * widget provider's update method. 330 * 331 * <p>This field corresponds to the <code>widgetCategory</code> attribute in 332 * the AppWidget meta-data file. 333 */ 334 @CategoryFlags 335 public int widgetCategory; 336 337 /** 338 * Resource id for the description of the AppWidget. 339 * 340 * <p>This field corresponds to the <code>android:description</code> attribute in the AppWidget 341 * meta-data file. 342 */ 343 @SuppressLint("MutableBareField") 344 @IdRes 345 public int descriptionRes; 346 347 /** 348 * Flags indicating various features supported by the widget. These are hints to the widget 349 * host, and do not actually change the behavior of the widget. 350 * 351 * @see #WIDGET_FEATURE_RECONFIGURABLE 352 * @see #WIDGET_FEATURE_HIDE_FROM_PICKER 353 * @see #WIDGET_FEATURE_CONFIGURATION_OPTIONAL 354 */ 355 @FeatureFlags 356 public int widgetFeatures; 357 358 /** @hide */ 359 @UnsupportedAppUsage 360 public ActivityInfo providerInfo; 361 362 /** @hide */ 363 public boolean isExtendedFromAppWidgetProvider; 364 365 /** 366 * Flags indicating the widget categories for which generated previews are available. 367 * These correspond to the previews set by this provider with 368 * {@link AppWidgetManager#setWidgetPreview}. 369 * 370 * @see #WIDGET_CATEGORY_HOME_SCREEN 371 * @see #WIDGET_CATEGORY_KEYGUARD 372 * @see #WIDGET_CATEGORY_SEARCHBOX 373 * @see AppWidgetManager#getWidgetPreview 374 */ 375 @FlaggedApi(FLAG_GENERATED_PREVIEWS) 376 @SuppressLint("MutableBareField") 377 public int generatedPreviewCategories; 378 AppWidgetProviderInfo()379 public AppWidgetProviderInfo() { 380 381 } 382 383 /** 384 * Unflatten the AppWidgetProviderInfo from a parcel. 385 */ 386 @SuppressWarnings("deprecation") AppWidgetProviderInfo(Parcel in)387 public AppWidgetProviderInfo(Parcel in) { 388 this.provider = in.readTypedObject(ComponentName.CREATOR); 389 this.minWidth = in.readInt(); 390 this.minHeight = in.readInt(); 391 this.minResizeWidth = in.readInt(); 392 this.minResizeHeight = in.readInt(); 393 this.maxResizeWidth = in.readInt(); 394 this.maxResizeHeight = in.readInt(); 395 this.targetCellWidth = in.readInt(); 396 this.targetCellHeight = in.readInt(); 397 this.updatePeriodMillis = in.readInt(); 398 this.initialLayout = in.readInt(); 399 this.initialKeyguardLayout = in.readInt(); 400 this.configure = in.readTypedObject(ComponentName.CREATOR); 401 this.label = in.readString(); 402 this.icon = in.readInt(); 403 this.previewImage = in.readInt(); 404 this.previewLayout = in.readInt(); 405 this.autoAdvanceViewId = in.readInt(); 406 this.resizeMode = in.readInt(); 407 this.widgetCategory = in.readInt(); 408 this.providerInfo = in.readTypedObject(ActivityInfo.CREATOR); 409 this.widgetFeatures = in.readInt(); 410 this.descriptionRes = in.readInt(); 411 this.isExtendedFromAppWidgetProvider = in.readBoolean(); 412 if (generatedPreviews()) { 413 generatedPreviewCategories = in.readInt(); 414 } 415 } 416 417 /** 418 * Loads the localized label to display to the user in the AppWidget picker. 419 * 420 * @param packageManager Package manager instance for loading resources. 421 * @return The label for the current locale. 422 */ loadLabel(PackageManager packageManager)423 public final String loadLabel(PackageManager packageManager) { 424 CharSequence label = providerInfo.loadLabel(packageManager); 425 if (label != null) { 426 return label.toString().trim(); 427 } 428 return null; 429 } 430 431 /** 432 * Loads the icon to display for this AppWidget in the AppWidget picker. If not 433 * supplied in the xml, the application icon will be used. A client can optionally 434 * provide a desired density such as {@link android.util.DisplayMetrics#DENSITY_LOW} 435 * {@link android.util.DisplayMetrics#DENSITY_MEDIUM}, etc. If no density is 436 * provided, the density of the current display will be used. 437 * <p> 438 * The loaded icon corresponds to the <code>android:icon</code> attribute in 439 * the <code><receiver></code> element in the AndroidManifest.xml file. 440 * </p> 441 * 442 * @param context Context for accessing resources. 443 * @param density The optional desired density as per 444 * {@link android.util.DisplayMetrics#densityDpi}. 445 * @return The provider icon. 446 */ loadIcon(@onNull Context context, int density)447 public final Drawable loadIcon(@NonNull Context context, int density) { 448 return loadDrawable(context, density, providerInfo.getIconResource(), true); 449 } 450 451 /** 452 * Loads a preview of what the AppWidget will look like after it's configured. 453 * A client can optionally provide a desired density such as 454 * {@link android.util.DisplayMetrics#DENSITY_LOW} 455 * {@link android.util.DisplayMetrics#DENSITY_MEDIUM}, etc. If no density is 456 * provided, the density of the current display will be used. 457 * <p> 458 * The loaded image corresponds to the <code>android:previewImage</code> attribute 459 * in the <code><receiver></code> element in the AndroidManifest.xml file. 460 * </p> 461 * 462 * @param context Context for accessing resources. 463 * @param density The optional desired density as per 464 * {@link android.util.DisplayMetrics#densityDpi}. 465 * @return The widget preview image or null if preview image is not available. 466 */ loadPreviewImage(@onNull Context context, int density)467 public final Drawable loadPreviewImage(@NonNull Context context, int density) { 468 return loadDrawable(context, density, previewImage, false); 469 } 470 471 /** 472 * Loads localized description for the app widget. 473 * 474 * <p>Description is intended to be displayed in the UI of the widget picker. 475 * 476 * @param context Context for accessing resources. 477 * 478 * @return CharSequence for app widget description for the current locale. 479 */ 480 @Nullable loadDescription(@onNull Context context)481 public final CharSequence loadDescription(@NonNull Context context) { 482 if (ResourceId.isValid(descriptionRes)) { 483 CharSequence description = 484 context.getPackageManager().getText( 485 providerInfo.packageName, 486 descriptionRes, 487 providerInfo.applicationInfo); 488 if (description != null) { 489 return description.toString().trim(); 490 } 491 } 492 return null; 493 } 494 495 /** 496 * Gets the user profile in which the provider resides. 497 * 498 * @return The hosting user profile. 499 */ getProfile()500 public final UserHandle getProfile() { 501 return new UserHandle(UserHandle.getUserId(providerInfo.applicationInfo.uid)); 502 } 503 504 /** 505 * Returns the broadcast receiver that is providing this widget. 506 */ 507 @NonNull getActivityInfo()508 public ActivityInfo getActivityInfo() { 509 return providerInfo; 510 } 511 512 @Override 513 @SuppressWarnings("deprecation") writeToParcel(Parcel out, int flags)514 public void writeToParcel(Parcel out, int flags) { 515 out.writeTypedObject(this.provider, flags); 516 out.writeInt(this.minWidth); 517 out.writeInt(this.minHeight); 518 out.writeInt(this.minResizeWidth); 519 out.writeInt(this.minResizeHeight); 520 out.writeInt(this.maxResizeWidth); 521 out.writeInt(this.maxResizeHeight); 522 out.writeInt(this.targetCellWidth); 523 out.writeInt(this.targetCellHeight); 524 out.writeInt(this.updatePeriodMillis); 525 out.writeInt(this.initialLayout); 526 out.writeInt(this.initialKeyguardLayout); 527 out.writeTypedObject(this.configure, flags); 528 out.writeString(this.label); 529 out.writeInt(this.icon); 530 out.writeInt(this.previewImage); 531 out.writeInt(this.previewLayout); 532 out.writeInt(this.autoAdvanceViewId); 533 out.writeInt(this.resizeMode); 534 out.writeInt(this.widgetCategory); 535 out.writeTypedObject(this.providerInfo, flags); 536 out.writeInt(this.widgetFeatures); 537 out.writeInt(this.descriptionRes); 538 out.writeBoolean(this.isExtendedFromAppWidgetProvider); 539 if (generatedPreviews()) { 540 out.writeInt(this.generatedPreviewCategories); 541 } 542 } 543 544 @Override 545 @SuppressWarnings("deprecation") clone()546 public AppWidgetProviderInfo clone() { 547 AppWidgetProviderInfo that = new AppWidgetProviderInfo(); 548 that.provider = this.provider == null ? null : this.provider.clone(); 549 that.minWidth = this.minWidth; 550 that.minHeight = this.minHeight; 551 that.minResizeWidth = this.minResizeWidth; 552 that.minResizeHeight = this.minResizeHeight; 553 that.maxResizeWidth = this.maxResizeWidth; 554 that.maxResizeHeight = this.maxResizeHeight; 555 that.targetCellWidth = this.targetCellWidth; 556 that.targetCellHeight = this.targetCellHeight; 557 that.updatePeriodMillis = this.updatePeriodMillis; 558 that.initialLayout = this.initialLayout; 559 that.initialKeyguardLayout = this.initialKeyguardLayout; 560 that.configure = this.configure == null ? null : this.configure.clone(); 561 that.label = this.label; 562 that.icon = this.icon; 563 that.previewImage = this.previewImage; 564 that.previewLayout = this.previewLayout; 565 that.autoAdvanceViewId = this.autoAdvanceViewId; 566 that.resizeMode = this.resizeMode; 567 that.widgetCategory = this.widgetCategory; 568 that.providerInfo = this.providerInfo; 569 that.widgetFeatures = this.widgetFeatures; 570 that.descriptionRes = this.descriptionRes; 571 that.isExtendedFromAppWidgetProvider = this.isExtendedFromAppWidgetProvider; 572 if (generatedPreviews()) { 573 that.generatedPreviewCategories = this.generatedPreviewCategories; 574 } 575 return that; 576 } 577 describeContents()578 public int describeContents() { 579 return 0; 580 } 581 loadDrawable(Context context, int density, int resourceId, boolean loadDefaultIcon)582 private Drawable loadDrawable(Context context, int density, int resourceId, 583 boolean loadDefaultIcon) { 584 try { 585 Resources resources = context.getPackageManager().getResourcesForApplication( 586 providerInfo.applicationInfo); 587 if (ResourceId.isValid(resourceId)) { 588 if (density < 0) { 589 density = 0; 590 } 591 return resources.getDrawableForDensity(resourceId, density, null); 592 } 593 } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { 594 /* ignore */ 595 } 596 return loadDefaultIcon ? providerInfo.loadIcon(context.getPackageManager()) : null; 597 } 598 599 /** 600 * @hide 601 */ updateDimensions(DisplayMetrics displayMetrics)602 public void updateDimensions(DisplayMetrics displayMetrics) { 603 // Converting complex to dp. 604 minWidth = TypedValue.complexToDimensionPixelSize(minWidth, displayMetrics); 605 minHeight = TypedValue.complexToDimensionPixelSize(minHeight, displayMetrics); 606 minResizeWidth = TypedValue.complexToDimensionPixelSize(minResizeWidth, displayMetrics); 607 minResizeHeight = TypedValue.complexToDimensionPixelSize(minResizeHeight, displayMetrics); 608 maxResizeWidth = TypedValue.complexToDimensionPixelSize(maxResizeWidth, displayMetrics); 609 maxResizeHeight = TypedValue.complexToDimensionPixelSize(maxResizeHeight, displayMetrics); 610 } 611 612 /** 613 * Parcelable.Creator that instantiates AppWidgetProviderInfo objects 614 */ 615 public static final @android.annotation.NonNull Parcelable.Creator<AppWidgetProviderInfo> CREATOR 616 = new Parcelable.Creator<AppWidgetProviderInfo>() 617 { 618 public AppWidgetProviderInfo createFromParcel(Parcel parcel) 619 { 620 return new AppWidgetProviderInfo(parcel); 621 } 622 623 public AppWidgetProviderInfo[] newArray(int size) 624 { 625 return new AppWidgetProviderInfo[size]; 626 } 627 }; 628 toString()629 public String toString() { 630 return "AppWidgetProviderInfo(" + getProfile() + '/' + provider + ')'; 631 } 632 } 633