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>&lt;appwidget-provider&gt;</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>&lt;receiver&gt;</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>&lt;receiver&gt;</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>&lt;receiver&gt;</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>&lt;receiver&gt;</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