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