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 boolean mShouldUseDefaultUnfoldTransition;
85     final String mSettingsSliceUri;
86     final boolean mSupportMultipleDisplays;
87 
88     /**
89      * Constructor.
90      *
91      * @param context The Context in which we are parsing the wallpaper.
92      * @param service The ResolveInfo returned from the package manager about
93      * this wallpaper's component.
94      */
WallpaperInfo(Context context, ResolveInfo service)95     public WallpaperInfo(Context context, ResolveInfo service)
96             throws XmlPullParserException, IOException {
97         mService = service;
98         ServiceInfo si = service.serviceInfo;
99 
100         final PackageManager pm = context.getPackageManager();
101         try (XmlResourceParser parser = si.loadXmlMetaData(pm,
102                 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 
146             // Watch wallpapers support ambient mode by default.
147             final boolean defSupportsAmbientMode =
148                     pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
149             mSupportsAmbientMode = sa.getBoolean(
150                     com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
151                     defSupportsAmbientMode);
152             mShouldUseDefaultUnfoldTransition = sa.getBoolean(
153                     com.android.internal.R.styleable
154                             .Wallpaper_shouldUseDefaultUnfoldTransition, true);
155             mSettingsSliceUri = sa.getString(
156                     com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
157             mSupportMultipleDisplays = sa.getBoolean(
158                     com.android.internal.R.styleable.Wallpaper_supportsMultipleDisplays,
159                     false);
160 
161             sa.recycle();
162         } catch (NameNotFoundException e) {
163             throw new XmlPullParserException(
164                     "Unable to create context for: " + si.packageName);
165         }
166     }
167 
WallpaperInfo(Parcel source)168     WallpaperInfo(Parcel source) {
169         mSettingsActivityName = source.readString();
170         mThumbnailResource = source.readInt();
171         mAuthorResource = source.readInt();
172         mDescriptionResource = source.readInt();
173         mContextUriResource = source.readInt();
174         mContextDescriptionResource = source.readInt();
175         mShowMetadataInPreview = source.readInt() != 0;
176         mSupportsAmbientMode = source.readInt() != 0;
177         mSettingsSliceUri = source.readString();
178         mSupportMultipleDisplays = source.readInt() != 0;
179         mShouldUseDefaultUnfoldTransition = source.readInt() != 0;
180         mService = ResolveInfo.CREATOR.createFromParcel(source);
181     }
182 
183     /**
184      * Return the .apk package that implements this wallpaper.
185      */
getPackageName()186     public String getPackageName() {
187         return mService.serviceInfo.packageName;
188     }
189 
190     /**
191      * Return the class name of the service component that implements
192      * this wallpaper.
193      */
getServiceName()194     public String getServiceName() {
195         return mService.serviceInfo.name;
196     }
197 
198     /**
199      * Return the raw information about the Service implementing this
200      * wallpaper.  Do not modify the returned object.
201      */
getServiceInfo()202     public ServiceInfo getServiceInfo() {
203         return mService.serviceInfo;
204     }
205 
206     /**
207      * Return the component of the service that implements this wallpaper.
208      */
getComponent()209     public ComponentName getComponent() {
210         return new ComponentName(mService.serviceInfo.packageName,
211                 mService.serviceInfo.name);
212     }
213 
214     /**
215      * Load the user-displayed label for this wallpaper.
216      *
217      * @param pm Supply a PackageManager used to load the wallpaper's
218      * resources.
219      */
loadLabel(PackageManager pm)220     public CharSequence loadLabel(PackageManager pm) {
221         return mService.loadLabel(pm);
222     }
223 
224     /**
225      * Load the user-displayed icon for this wallpaper.
226      *
227      * @param pm Supply a PackageManager used to load the wallpaper's
228      * resources.
229      */
loadIcon(PackageManager pm)230     public Drawable loadIcon(PackageManager pm) {
231         return mService.loadIcon(pm);
232     }
233 
234     /**
235      * Load the thumbnail image for this wallpaper.
236      *
237      * @param pm Supply a PackageManager used to load the wallpaper's
238      * resources.
239      */
loadThumbnail(PackageManager pm)240     public Drawable loadThumbnail(PackageManager pm) {
241         if (mThumbnailResource < 0) return null;
242 
243         return pm.getDrawable(mService.serviceInfo.packageName,
244                               mThumbnailResource,
245                               mService.serviceInfo.applicationInfo);
246     }
247 
248     /**
249      * Return a string indicating the author(s) of this wallpaper.
250      */
loadAuthor(PackageManager pm)251     public CharSequence loadAuthor(PackageManager pm) throws NotFoundException {
252         if (mAuthorResource <= 0) throw new NotFoundException();
253         String packageName = mService.resolvePackageName;
254         ApplicationInfo applicationInfo = null;
255         if (packageName == null) {
256             packageName = mService.serviceInfo.packageName;
257             applicationInfo = mService.serviceInfo.applicationInfo;
258         }
259         return pm.getText(packageName, mAuthorResource, applicationInfo);
260     }
261 
262     /**
263      * Return a brief summary of this wallpaper's behavior.
264      */
loadDescription(PackageManager pm)265     public CharSequence loadDescription(PackageManager pm) throws NotFoundException {
266         String packageName = mService.resolvePackageName;
267         ApplicationInfo applicationInfo = null;
268         if (packageName == null) {
269             packageName = mService.serviceInfo.packageName;
270             applicationInfo = mService.serviceInfo.applicationInfo;
271         }
272         if (mService.serviceInfo.descriptionRes != 0) {
273             return pm.getText(packageName, mService.serviceInfo.descriptionRes,
274                     applicationInfo);
275 
276         }
277         if (mDescriptionResource <= 0) throw new NotFoundException();
278         return pm.getText(packageName, mDescriptionResource,
279                 mService.serviceInfo.applicationInfo);
280     }
281 
282     /**
283      * Returns an URI that specifies a link for further context about this wallpaper.
284      *
285      * @param pm An instance of {@link PackageManager} to retrieve the URI.
286      * @return The URI.
287      */
loadContextUri(PackageManager pm)288     public Uri loadContextUri(PackageManager pm) throws NotFoundException {
289         if (mContextUriResource <= 0) throw new NotFoundException();
290         String packageName = mService.resolvePackageName;
291         ApplicationInfo applicationInfo = null;
292         if (packageName == null) {
293             packageName = mService.serviceInfo.packageName;
294             applicationInfo = mService.serviceInfo.applicationInfo;
295         }
296         CharSequence contextUriCharSequence = pm.getText(
297                 packageName, mContextUriResource, applicationInfo);
298         if (contextUriCharSequence == null) {
299             return null;
300         }
301         return Uri.parse(contextUriCharSequence.toString());
302     }
303 
304     /**
305      * Retrieves a title of the URI that specifies a link for further context about this wallpaper.
306      *
307      * @param pm An instance of {@link PackageManager} to retrieve the title.
308      * @return The title.
309      */
loadContextDescription(PackageManager pm)310     public CharSequence loadContextDescription(PackageManager pm) throws NotFoundException {
311         if (mContextDescriptionResource <= 0) throw new NotFoundException();
312         String packageName = mService.resolvePackageName;
313         ApplicationInfo applicationInfo = null;
314         if (packageName == null) {
315             packageName = mService.serviceInfo.packageName;
316             applicationInfo = mService.serviceInfo.applicationInfo;
317         }
318         return pm.getText(packageName, mContextDescriptionResource, applicationInfo).toString();
319     }
320 
321     /**
322      * Queries whether any metadata should be shown when previewing the wallpaper. If this value is
323      * set to true, any component that shows a preview of this live wallpaper should also show
324      * accompanying information like {@link #loadLabel},
325      * {@link #loadDescription}, {@link #loadAuthor} and
326      * {@link #loadContextDescription(PackageManager)}, so the user gets to know further information
327      * about this wallpaper.
328      *
329      * @return Whether any metadata should be shown when previewing the wallpaper.
330      */
getShowMetadataInPreview()331     public boolean getShowMetadataInPreview() {
332         return mShowMetadataInPreview;
333     }
334 
335     /**
336      * Returns whether a wallpaper was optimized or not for ambient mode and can be drawn in there.
337      *
338      * @see WallpaperService.Engine#onAmbientModeChanged(boolean, boolean)
339      * @see WallpaperService.Engine#isInAmbientMode()
340      * @return {@code true} if wallpaper can draw when in ambient mode.
341      * @hide
342      */
343     @SystemApi
supportsAmbientMode()344     public boolean supportsAmbientMode() {
345         return mSupportsAmbientMode;
346     }
347 
348     /**
349      * Return the class name of an activity that provides a settings UI for
350      * the wallpaper.  You can launch this activity be starting it with
351      * an {@link android.content.Intent} whose action is MAIN and with an
352      * explicit {@link android.content.ComponentName}
353      * composed of {@link #getPackageName} and the class name returned here.
354      *
355      * <p>{@code null} will be returned if there is no settings activity associated
356      * with the wallpaper.
357      */
getSettingsActivity()358     public String getSettingsActivity() {
359         return mSettingsActivityName;
360     }
361 
362     /**
363      * Returns an URI that provides a settings {@link Slice} for this wallpaper.
364      * The wallpaper should implement a SliceProvider associated with this URI.
365      * The system will display the Slice in the customization section while previewing the live
366      * wallpaper. Because this URI is accessible to other apps, it is recommended to protect it
367      * with the android.permission.BIND_WALLPAPER permission.
368      *
369      * <p>{@code null} will be returned if there is no settings Slice URI associated
370      * with the wallpaper.
371      *
372      * @return The URI.
373      */
374     @Nullable
getSettingsSliceUri()375     public Uri getSettingsSliceUri() {
376         if (mSettingsSliceUri == null) {
377             return null;
378         }
379         return Uri.parse(mSettingsSliceUri);
380     }
381 
382     /**
383      * Returns whether this wallpaper service can support multiple engines to render on each surface
384      * independently. An example use case is a multi-display set-up where the wallpaper service can
385      * render surfaces to each of the connected displays.
386      * <p>
387      * This corresponds to the value {@link android.R.styleable#Wallpaper_supportsMultipleDisplays}
388      * in the XML description of the wallpaper.
389      * <p>
390      * The default value is {@code false}.
391      *
392      * @see WallpaperService#onCreateEngine()
393      * @see WallpaperService.Engine#onCreate(SurfaceHolder)
394      * @return {@code true} if multiple engines can render independently on each surface.
395      *
396      * @attr ref android.R.styleable#Wallpaper_supportsMultipleDisplays
397      */
supportsMultipleDisplays()398     public boolean supportsMultipleDisplays() {
399         return mSupportMultipleDisplays;
400     }
401 
402     /**
403      * Returns whether this wallpaper should receive default zooming updates when the device
404      * changes its state (e.g. when folding or unfolding a foldable device).
405      * If set to false the wallpaper will not receive zoom events when changing the device state,
406      * so it can implement its own transition instead.
407      * <p>
408      * This corresponds to the value {@link
409      * android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition} in the
410      * XML description of the wallpaper.
411      * <p>
412      * The default value is {@code true}.
413      *
414      * @see android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition
415      * @return {@code true} if wallpaper should receive default device state change
416      * transition updates
417      *
418      * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition
419      */
shouldUseDefaultUnfoldTransition()420     public boolean shouldUseDefaultUnfoldTransition() {
421         return mShouldUseDefaultUnfoldTransition;
422     }
423 
dump(Printer pw, String prefix)424     public void dump(Printer pw, String prefix) {
425         pw.println(prefix + "Service:");
426         mService.dump(pw, prefix + "  ");
427         pw.println(prefix + "mSettingsActivityName=" + mSettingsActivityName);
428     }
429 
430     @Override
toString()431     public String toString() {
432         return "WallpaperInfo{" + mService.serviceInfo.name
433                 + ", settings: "
434                 + mSettingsActivityName + "}";
435     }
436 
437     /**
438      * Used to package this object into a {@link Parcel}.
439      *
440      * @param dest The {@link Parcel} to be written.
441      * @param flags The flags used for parceling.
442      */
writeToParcel(Parcel dest, int flags)443     public void writeToParcel(Parcel dest, int flags) {
444         dest.writeString(mSettingsActivityName);
445         dest.writeInt(mThumbnailResource);
446         dest.writeInt(mAuthorResource);
447         dest.writeInt(mDescriptionResource);
448         dest.writeInt(mContextUriResource);
449         dest.writeInt(mContextDescriptionResource);
450         dest.writeInt(mShowMetadataInPreview ? 1 : 0);
451         dest.writeInt(mSupportsAmbientMode ? 1 : 0);
452         dest.writeString(mSettingsSliceUri);
453         dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
454         dest.writeInt(mShouldUseDefaultUnfoldTransition ? 1 : 0);
455         mService.writeToParcel(dest, flags);
456     }
457 
458     /**
459      * Used to make this class parcelable.
460      */
461     public static final @android.annotation.NonNull Parcelable.Creator<WallpaperInfo> CREATOR = new Parcelable.Creator<WallpaperInfo>() {
462         public WallpaperInfo createFromParcel(Parcel source) {
463             return new WallpaperInfo(source);
464         }
465 
466         public WallpaperInfo[] newArray(int size) {
467             return new WallpaperInfo[size];
468         }
469     };
470 
describeContents()471     public int describeContents() {
472         return 0;
473     }
474 }
475