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