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