1 /*
2  * Copyright (C) 2009 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.app;
18 
19 import android.annotation.Nullable;
20 import android.annotation.SystemApi;
21 import android.app.slice.Slice;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageManager;
26 import android.content.pm.PackageManager.NameNotFoundException;
27 import android.content.pm.ResolveInfo;
28 import android.content.pm.ServiceInfo;
29 import android.content.res.Resources;
30 import android.content.res.Resources.NotFoundException;
31 import android.content.res.TypedArray;
32 import android.content.res.XmlResourceParser;
33 import android.graphics.drawable.Drawable;
34 import android.net.Uri;
35 import android.os.Parcel;
36 import android.os.Parcelable;
37 import android.service.wallpaper.WallpaperService;
38 import android.util.AttributeSet;
39 import android.util.Printer;
40 import android.util.Xml;
41 import android.view.SurfaceHolder;
42 
43 import org.xmlpull.v1.XmlPullParser;
44 import org.xmlpull.v1.XmlPullParserException;
45 
46 import java.io.IOException;
47 
48 /**
49  * This class is used to specify meta information of a wallpaper service.
50  */
51 public final class WallpaperInfo implements Parcelable {
52     static final String TAG = "WallpaperInfo";
53 
54     /**
55      * The Service that implements this wallpaper component.
56      */
57     final ResolveInfo mService;
58 
59     /**
60      * The wallpaper setting activity's name, to
61      * launch the setting activity of this wallpaper.
62      */
63     final String mSettingsActivityName;
64 
65     /**
66      * Resource identifier for this wallpaper's thumbnail image.
67      */
68     final int mThumbnailResource;
69 
70     /**
71      * Resource identifier for a string indicating the author of the wallpaper.
72      */
73     final int mAuthorResource;
74 
75     /**
76      * Resource identifier for a string containing a short description of the wallpaper.
77      */
78     final int mDescriptionResource;
79 
80     final int mContextUriResource;
81     final int mContextDescriptionResource;
82     final boolean mShowMetadataInPreview;
83     final boolean mSupportsAmbientMode;
84     final String mSettingsSliceUri;
85     final boolean mSupportMultipleDisplays;
86 
87     /**
88      * Constructor.
89      *
90      * @param context The Context in which we are parsing the wallpaper.
91      * @param service The ResolveInfo returned from the package manager about
92      * this wallpaper's component.
93      */
WallpaperInfo(Context context, ResolveInfo service)94     public WallpaperInfo(Context context, ResolveInfo service)
95             throws XmlPullParserException, IOException {
96         mService = service;
97         ServiceInfo si = service.serviceInfo;
98 
99         final PackageManager pm = context.getPackageManager();
100         XmlResourceParser parser = null;
101         try {
102             parser = si.loadXmlMetaData(pm, WallpaperService.SERVICE_META_DATA);
103             if (parser == null) {
104                 throw new XmlPullParserException("No "
105                         + WallpaperService.SERVICE_META_DATA + " meta-data");
106             }
107 
108             Resources res = pm.getResourcesForApplication(si.applicationInfo);
109 
110             AttributeSet attrs = Xml.asAttributeSet(parser);
111 
112             int type;
113             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
114                     && type != XmlPullParser.START_TAG) {
115             }
116 
117             String nodeName = parser.getName();
118             if (!"wallpaper".equals(nodeName)) {
119                 throw new XmlPullParserException(
120                         "Meta-data does not start with wallpaper tag");
121             }
122 
123             TypedArray sa = res.obtainAttributes(attrs,
124                     com.android.internal.R.styleable.Wallpaper);
125             mSettingsActivityName = sa.getString(
126                     com.android.internal.R.styleable.Wallpaper_settingsActivity);
127             mThumbnailResource = sa.getResourceId(
128                     com.android.internal.R.styleable.Wallpaper_thumbnail,
129                     -1);
130             mAuthorResource = sa.getResourceId(
131                     com.android.internal.R.styleable.Wallpaper_author,
132                     -1);
133             mDescriptionResource = sa.getResourceId(
134                     com.android.internal.R.styleable.Wallpaper_description,
135                     -1);
136             mContextUriResource = sa.getResourceId(
137                     com.android.internal.R.styleable.Wallpaper_contextUri,
138                     -1);
139             mContextDescriptionResource = sa.getResourceId(
140                     com.android.internal.R.styleable.Wallpaper_contextDescription,
141                     -1);
142             mShowMetadataInPreview = sa.getBoolean(
143                     com.android.internal.R.styleable.Wallpaper_showMetadataInPreview,
144                     false);
145             mSupportsAmbientMode = sa.getBoolean(
146                     com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
147                     false);
148             mSettingsSliceUri = sa.getString(
149                     com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
150             mSupportMultipleDisplays = sa.getBoolean(
151                     com.android.internal.R.styleable.Wallpaper_supportsMultipleDisplays,
152                     false);
153 
154             sa.recycle();
155         } catch (NameNotFoundException e) {
156             throw new XmlPullParserException(
157                     "Unable to create context for: " + si.packageName);
158         } finally {
159             if (parser != null) parser.close();
160         }
161     }
162 
WallpaperInfo(Parcel source)163     WallpaperInfo(Parcel source) {
164         mSettingsActivityName = source.readString();
165         mThumbnailResource = source.readInt();
166         mAuthorResource = source.readInt();
167         mDescriptionResource = source.readInt();
168         mContextUriResource = source.readInt();
169         mContextDescriptionResource = source.readInt();
170         mShowMetadataInPreview = source.readInt() != 0;
171         mSupportsAmbientMode = source.readInt() != 0;
172         mSettingsSliceUri = source.readString();
173         mSupportMultipleDisplays = source.readInt() != 0;
174         mService = ResolveInfo.CREATOR.createFromParcel(source);
175     }
176 
177     /**
178      * Return the .apk package that implements this wallpaper.
179      */
getPackageName()180     public String getPackageName() {
181         return mService.serviceInfo.packageName;
182     }
183 
184     /**
185      * Return the class name of the service component that implements
186      * this wallpaper.
187      */
getServiceName()188     public String getServiceName() {
189         return mService.serviceInfo.name;
190     }
191 
192     /**
193      * Return the raw information about the Service implementing this
194      * wallpaper.  Do not modify the returned object.
195      */
getServiceInfo()196     public ServiceInfo getServiceInfo() {
197         return mService.serviceInfo;
198     }
199 
200     /**
201      * Return the component of the service that implements this wallpaper.
202      */
getComponent()203     public ComponentName getComponent() {
204         return new ComponentName(mService.serviceInfo.packageName,
205                 mService.serviceInfo.name);
206     }
207 
208     /**
209      * Load the user-displayed label for this wallpaper.
210      *
211      * @param pm Supply a PackageManager used to load the wallpaper's
212      * resources.
213      */
loadLabel(PackageManager pm)214     public CharSequence loadLabel(PackageManager pm) {
215         return mService.loadLabel(pm);
216     }
217 
218     /**
219      * Load the user-displayed icon for this wallpaper.
220      *
221      * @param pm Supply a PackageManager used to load the wallpaper's
222      * resources.
223      */
loadIcon(PackageManager pm)224     public Drawable loadIcon(PackageManager pm) {
225         return mService.loadIcon(pm);
226     }
227 
228     /**
229      * Load the thumbnail image for this wallpaper.
230      *
231      * @param pm Supply a PackageManager used to load the wallpaper's
232      * resources.
233      */
loadThumbnail(PackageManager pm)234     public Drawable loadThumbnail(PackageManager pm) {
235         if (mThumbnailResource < 0) return null;
236 
237         return pm.getDrawable(mService.serviceInfo.packageName,
238                               mThumbnailResource,
239                               mService.serviceInfo.applicationInfo);
240     }
241 
242     /**
243      * Return a string indicating the author(s) of this wallpaper.
244      */
loadAuthor(PackageManager pm)245     public CharSequence loadAuthor(PackageManager pm) throws NotFoundException {
246         if (mAuthorResource <= 0) throw new NotFoundException();
247         String packageName = mService.resolvePackageName;
248         ApplicationInfo applicationInfo = null;
249         if (packageName == null) {
250             packageName = mService.serviceInfo.packageName;
251             applicationInfo = mService.serviceInfo.applicationInfo;
252         }
253         return pm.getText(packageName, mAuthorResource, applicationInfo);
254     }
255 
256     /**
257      * Return a brief summary of this wallpaper's behavior.
258      */
loadDescription(PackageManager pm)259     public CharSequence loadDescription(PackageManager pm) throws NotFoundException {
260         String packageName = mService.resolvePackageName;
261         ApplicationInfo applicationInfo = null;
262         if (packageName == null) {
263             packageName = mService.serviceInfo.packageName;
264             applicationInfo = mService.serviceInfo.applicationInfo;
265         }
266         if (mService.serviceInfo.descriptionRes != 0) {
267             return pm.getText(packageName, mService.serviceInfo.descriptionRes,
268                     applicationInfo);
269 
270         }
271         if (mDescriptionResource <= 0) throw new NotFoundException();
272         return pm.getText(packageName, mDescriptionResource,
273                 mService.serviceInfo.applicationInfo);
274     }
275 
276     /**
277      * Returns an URI that specifies a link for further context about this wallpaper.
278      *
279      * @param pm An instance of {@link PackageManager} to retrieve the URI.
280      * @return The URI.
281      */
loadContextUri(PackageManager pm)282     public Uri loadContextUri(PackageManager pm) throws NotFoundException {
283         if (mContextUriResource <= 0) throw new NotFoundException();
284         String packageName = mService.resolvePackageName;
285         ApplicationInfo applicationInfo = null;
286         if (packageName == null) {
287             packageName = mService.serviceInfo.packageName;
288             applicationInfo = mService.serviceInfo.applicationInfo;
289         }
290         String contextUriString = pm.getText(
291                 packageName, mContextUriResource, applicationInfo).toString();
292         if (contextUriString == null) {
293             return null;
294         }
295         return Uri.parse(contextUriString);
296     }
297 
298     /**
299      * Retrieves a title of the URI that specifies a link for further context about this wallpaper.
300      *
301      * @param pm An instance of {@link PackageManager} to retrieve the title.
302      * @return The title.
303      */
loadContextDescription(PackageManager pm)304     public CharSequence loadContextDescription(PackageManager pm) throws NotFoundException {
305         if (mContextDescriptionResource <= 0) throw new NotFoundException();
306         String packageName = mService.resolvePackageName;
307         ApplicationInfo applicationInfo = null;
308         if (packageName == null) {
309             packageName = mService.serviceInfo.packageName;
310             applicationInfo = mService.serviceInfo.applicationInfo;
311         }
312         return pm.getText(packageName, mContextDescriptionResource, applicationInfo).toString();
313     }
314 
315     /**
316      * Queries whether any metadata should be shown when previewing the wallpaper. If this value is
317      * set to true, any component that shows a preview of this live wallpaper should also show
318      * accompanying information like {@link #loadLabel},
319      * {@link #loadDescription}, {@link #loadAuthor} and
320      * {@link #loadContextDescription(PackageManager)}, so the user gets to know further information
321      * about this wallpaper.
322      *
323      * @return Whether any metadata should be shown when previewing the wallpaper.
324      */
getShowMetadataInPreview()325     public boolean getShowMetadataInPreview() {
326         return mShowMetadataInPreview;
327     }
328 
329     /**
330      * Returns whether a wallpaper was optimized or not for ambient mode and can be drawn in there.
331      *
332      * @see WallpaperService.Engine#onAmbientModeChanged(boolean, boolean)
333      * @see WallpaperService.Engine#isInAmbientMode()
334      * @return {@code true} if wallpaper can draw when in ambient mode.
335      * @hide
336      */
337     @SystemApi
supportsAmbientMode()338     public boolean supportsAmbientMode() {
339         return mSupportsAmbientMode;
340     }
341 
342     /**
343      * Return the class name of an activity that provides a settings UI for
344      * the wallpaper.  You can launch this activity be starting it with
345      * an {@link android.content.Intent} whose action is MAIN and with an
346      * explicit {@link android.content.ComponentName}
347      * composed of {@link #getPackageName} and the class name returned here.
348      *
349      * <p>{@code null} will be returned if there is no settings activity associated
350      * with the wallpaper.
351      */
getSettingsActivity()352     public String getSettingsActivity() {
353         return mSettingsActivityName;
354     }
355 
356     /**
357      * Returns an URI that provides a settings {@link Slice} for this wallpaper.
358      * The wallpaper should implement a SliceProvider associated with this URI.
359      * The system will display the Slice in the customization section while previewing the live
360      * wallpaper. Because this URI is accessible to other apps, it is recommended to protect it
361      * with the android.permission.BIND_WALLPAPER permission.
362      *
363      * <p>{@code null} will be returned if there is no settings Slice URI associated
364      * with the wallpaper.
365      *
366      * @return The URI.
367      */
368     @Nullable
getSettingsSliceUri()369     public Uri getSettingsSliceUri() {
370         if (mSettingsSliceUri == null) {
371             return null;
372         }
373         return Uri.parse(mSettingsSliceUri);
374     }
375 
376     /**
377      * Returns whether this wallpaper service can support multiple engines to render on each surface
378      * independently. An example use case is a multi-display set-up where the wallpaper service can
379      * render surfaces to each of the connected displays.
380      * <p>
381      * This corresponds to the value {@link android.R.styleable#Wallpaper_supportsMultipleDisplays}
382      * in the XML description of the wallpaper.
383      * <p>
384      * The default value is {@code false}.
385      *
386      * @see WallpaperService#onCreateEngine()
387      * @see WallpaperService.Engine#onCreate(SurfaceHolder)
388      * @return {@code true} if multiple engines can render independently on each surface.
389      *
390      * @attr ref android.R.styleable#Wallpaper_supportsMultipleDisplays
391      */
supportsMultipleDisplays()392     public boolean supportsMultipleDisplays() {
393         return mSupportMultipleDisplays;
394     }
395 
dump(Printer pw, String prefix)396     public void dump(Printer pw, String prefix) {
397         pw.println(prefix + "Service:");
398         mService.dump(pw, prefix + "  ");
399         pw.println(prefix + "mSettingsActivityName=" + mSettingsActivityName);
400     }
401 
402     @Override
toString()403     public String toString() {
404         return "WallpaperInfo{" + mService.serviceInfo.name
405                 + ", settings: "
406                 + mSettingsActivityName + "}";
407     }
408 
409     /**
410      * Used to package this object into a {@link Parcel}.
411      *
412      * @param dest The {@link Parcel} to be written.
413      * @param flags The flags used for parceling.
414      */
writeToParcel(Parcel dest, int flags)415     public void writeToParcel(Parcel dest, int flags) {
416         dest.writeString(mSettingsActivityName);
417         dest.writeInt(mThumbnailResource);
418         dest.writeInt(mAuthorResource);
419         dest.writeInt(mDescriptionResource);
420         dest.writeInt(mContextUriResource);
421         dest.writeInt(mContextDescriptionResource);
422         dest.writeInt(mShowMetadataInPreview ? 1 : 0);
423         dest.writeInt(mSupportsAmbientMode ? 1 : 0);
424         dest.writeString(mSettingsSliceUri);
425         dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
426         mService.writeToParcel(dest, flags);
427     }
428 
429     /**
430      * Used to make this class parcelable.
431      */
432     public static final @android.annotation.NonNull Parcelable.Creator<WallpaperInfo> CREATOR = new Parcelable.Creator<WallpaperInfo>() {
433         public WallpaperInfo createFromParcel(Parcel source) {
434             return new WallpaperInfo(source);
435         }
436 
437         public WallpaperInfo[] newArray(int size) {
438             return new WallpaperInfo[size];
439         }
440     };
441 
describeContents()442     public int describeContents() {
443         return 0;
444     }
445 }
446