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