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