blob: 8944c3d05f6cfa29860eb1ecb3e8730ced01095f [file] [log] [blame]
Jon Miranda16ea1b12017-12-12 14:52:48 -08001/*
2 * Copyright (C) 2017 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 */
16package com.android.wallpaper.model;
17
18import android.app.Activity;
19import android.app.WallpaperManager;
20import android.content.Context;
21import android.content.Intent;
Santiago Etchebehere28b242a2018-04-09 16:33:10 -070022import android.content.pm.ApplicationInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080023import android.content.pm.PackageManager;
24import android.content.pm.ResolveInfo;
25import android.content.res.Resources;
26import android.net.Uri;
27import android.os.Parcel;
28import android.service.wallpaper.WallpaperService;
Jon Miranda16ea1b12017-12-12 14:52:48 -080029import android.util.Log;
30
31import com.android.wallpaper.R;
32import com.android.wallpaper.asset.Asset;
33import com.android.wallpaper.asset.LiveWallpaperThumbAsset;
34import com.android.wallpaper.compat.BuildCompat;
35import com.android.wallpaper.util.ActivityUtils;
36
37import org.xmlpull.v1.XmlPullParserException;
38
39import java.io.IOException;
40import java.text.Collator;
41import java.util.ArrayList;
42import java.util.Arrays;
43import java.util.Collections;
44import java.util.Comparator;
45import java.util.Iterator;
46import java.util.List;
47
Sunny Goyal8600a3f2018-08-15 12:48:01 -070048import androidx.annotation.Nullable;
49
Jon Miranda16ea1b12017-12-12 14:52:48 -080050/**
51 * Represents a live wallpaper from the system.
52 */
53public class LiveWallpaperInfo extends WallpaperInfo {
54 public static final Creator<LiveWallpaperInfo> CREATOR =
55 new Creator<LiveWallpaperInfo>() {
56 @Override
57 public LiveWallpaperInfo createFromParcel(Parcel in) {
58 return new LiveWallpaperInfo(in);
59 }
60
61 @Override
62 public LiveWallpaperInfo[] newArray(int size) {
63 return new LiveWallpaperInfo[size];
64 }
65 };
66 private static final String TAG = "LiveWallpaperInfo";
67 private android.app.WallpaperInfo mInfo;
68 private LiveWallpaperThumbAsset mThumbAsset;
Santiago Etchebehere98a39c12018-05-11 15:06:45 -070069 private boolean mVisibleTitle;
Jon Miranda16ea1b12017-12-12 14:52:48 -080070
71 /**
72 * Constructs a LiveWallpaperInfo wrapping the given system WallpaperInfo object, representing
73 * a particular live wallpaper.
74 *
75 * @param info
76 */
77 public LiveWallpaperInfo(android.app.WallpaperInfo info) {
Santiago Etchebehere98a39c12018-05-11 15:06:45 -070078 this(info, true);
79 }
80
81 /**
82 * Constructs a LiveWallpaperInfo wrapping the given system WallpaperInfo object, representing
83 * a particular live wallpaper.
84 */
85 public LiveWallpaperInfo(android.app.WallpaperInfo info, boolean visibleTitle) {
Jon Miranda16ea1b12017-12-12 14:52:48 -080086 mInfo = info;
Santiago Etchebehere98a39c12018-05-11 15:06:45 -070087 mVisibleTitle = visibleTitle;
Jon Miranda16ea1b12017-12-12 14:52:48 -080088 }
89
90 private LiveWallpaperInfo(Parcel in) {
91 mInfo = in.readParcelable(android.app.WallpaperInfo.class.getClassLoader());
Santiago Etchebehere98a39c12018-05-11 15:06:45 -070092 mVisibleTitle = in.readInt() == 1;
Jon Miranda16ea1b12017-12-12 14:52:48 -080093 }
94
95 /**
96 * Returns all live wallpapers found on the device, excluding those residing in APKs described by
97 * the package names in excludedPackageNames.
98 */
99 public static List<WallpaperInfo> getAll(Context context,
100 @Nullable List<String> excludedPackageNames) {
101 List<ResolveInfo> resolveInfos = getAllOnDevice(context);
102 List<WallpaperInfo> wallpaperInfos = new ArrayList<>();
103
104 for (int i = 0; i < resolveInfos.size(); i++) {
105 ResolveInfo resolveInfo = resolveInfos.get(i);
106 android.app.WallpaperInfo wallpaperInfo;
107 try {
108 wallpaperInfo = new android.app.WallpaperInfo(context, resolveInfo);
109 } catch (XmlPullParserException e) {
110 Log.w(TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
111 continue;
112 } catch (IOException e) {
113 Log.w(TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
114 continue;
115 }
116
117 if (excludedPackageNames != null && excludedPackageNames.contains(
118 wallpaperInfo.getPackageName())) {
119 continue;
120 }
121
122 wallpaperInfos.add(new LiveWallpaperInfo(wallpaperInfo));
123 }
124
125 return wallpaperInfos;
126 }
127
128 /**
129 * Returns the live wallpapers having the given service names, found within the APK with the
130 * given package name.
131 */
132 public static List<WallpaperInfo> getFromSpecifiedPackage(
Santiago Etchebehere98a39c12018-05-11 15:06:45 -0700133 Context context, String packageName, @Nullable List<String> serviceNames,
134 boolean shouldShowTitle) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800135 List<ResolveInfo> resolveInfos;
136 if (serviceNames != null) {
137 resolveInfos = getAllContainingServiceNames(context, serviceNames);
138 } else {
139 resolveInfos = getAllOnDevice(context);
140 }
141 List<WallpaperInfo> wallpaperInfos = new ArrayList<>();
142
143 for (int i = 0; i < resolveInfos.size(); i++) {
144 ResolveInfo resolveInfo = resolveInfos.get(i);
145 if (resolveInfo == null) {
146 Log.e(TAG, "Found a null resolve info");
147 continue;
148 }
149
150 android.app.WallpaperInfo wallpaperInfo;
151 try {
152 wallpaperInfo = new android.app.WallpaperInfo(context, resolveInfo);
153 } catch (XmlPullParserException e) {
154 Log.w(TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
155 continue;
156 } catch (IOException e) {
157 Log.w(TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
158 continue;
159 }
160
161 if (!packageName.equals(wallpaperInfo.getPackageName())) {
162 continue;
163 }
164
Santiago Etchebehere98a39c12018-05-11 15:06:45 -0700165 wallpaperInfos.add(new LiveWallpaperInfo(wallpaperInfo, shouldShowTitle));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800166 }
167
168 return wallpaperInfos;
169 }
170
171 /**
172 * Returns ResolveInfo objects for all live wallpaper services with the specified fully qualified
173 * service names, keeping order intact.
174 */
175 private static List<ResolveInfo> getAllContainingServiceNames(Context context,
176 List<String> serviceNames) {
177 final PackageManager pm = context.getPackageManager();
178
179 List<ResolveInfo> allResolveInfos = pm.queryIntentServices(
180 new Intent(WallpaperService.SERVICE_INTERFACE),
181 PackageManager.GET_META_DATA);
182
183 // Filter ALL live wallpapers for only those in the list of specified service names.
184 // Prefer this approach so we can make only one call to PackageManager (expensive!) rather than
185 // one call per live wallpaper.
186 ResolveInfo[] specifiedResolveInfos = new ResolveInfo[serviceNames.size()];
187 for (ResolveInfo resolveInfo : allResolveInfos) {
188 int index = serviceNames.indexOf(resolveInfo.serviceInfo.name);
189 if (index != -1) {
190 specifiedResolveInfos[index] = resolveInfo;
191 }
192 }
193
194 return Arrays.asList(specifiedResolveInfos);
195 }
196
197 /**
Santiago Etchebehere28b242a2018-04-09 16:33:10 -0700198 * Returns ResolveInfo objects for all live wallpaper services installed on the device. System
199 * wallpapers are listed first, unsorted, with other installed wallpapers following sorted
Jon Miranda16ea1b12017-12-12 14:52:48 -0800200 * in alphabetical order.
201 */
202 private static List<ResolveInfo> getAllOnDevice(Context context) {
203 final PackageManager pm = context.getPackageManager();
204 final String packageName = context.getPackageName();
205
206 List<ResolveInfo> resolveInfos = pm.queryIntentServices(
207 new Intent(WallpaperService.SERVICE_INTERFACE),
208 PackageManager.GET_META_DATA);
209
Santiago Etchebehere28b242a2018-04-09 16:33:10 -0700210 List<ResolveInfo> wallpaperInfos = new ArrayList<>();
211
212 // Remove the "Rotating Image Wallpaper" live wallpaper, which is owned by this package,
213 // and separate system wallpapers to sort only non-system ones.
214 Iterator<ResolveInfo> iter = resolveInfos.iterator();
215 while (iter.hasNext()) {
216 ResolveInfo resolveInfo = iter.next();
217 if (packageName.equals(resolveInfo.serviceInfo.packageName)) {
218 iter.remove();
219 } else if (isSystemApp(resolveInfo.serviceInfo.applicationInfo)) {
220 wallpaperInfos.add(resolveInfo);
221 iter.remove();
222 }
223 }
224
225 if (resolveInfos.isEmpty()) {
226 return wallpaperInfos;
227 }
228
229 // Sort non-system wallpapers alphabetically and append them to system ones
Jon Miranda16ea1b12017-12-12 14:52:48 -0800230 Collections.sort(resolveInfos, new Comparator<ResolveInfo>() {
231 final Collator mCollator = Collator.getInstance();
232
233 @Override
234 public int compare(ResolveInfo info1, ResolveInfo info2) {
235 return mCollator.compare(info1.loadLabel(pm), info2.loadLabel(pm));
236 }
237 });
Santiago Etchebehere28b242a2018-04-09 16:33:10 -0700238 wallpaperInfos.addAll(resolveInfos);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800239
Santiago Etchebehere28b242a2018-04-09 16:33:10 -0700240 return wallpaperInfos;
241 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800242
Santiago Etchebehere28b242a2018-04-09 16:33:10 -0700243 private static boolean isSystemApp(ApplicationInfo appInfo) {
244 return (appInfo.flags & (ApplicationInfo.FLAG_SYSTEM
245 | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800246 }
247
248 @Override
249 public String getTitle(Context context) {
Santiago Etchebehere98a39c12018-05-11 15:06:45 -0700250 if (mVisibleTitle) {
251 CharSequence labelCharSeq = mInfo.loadLabel(context.getPackageManager());
252 return labelCharSeq == null ? null : labelCharSeq.toString();
253 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800254 return null;
255 }
256
257 @Override
258 public List<String> getAttributions(Context context) {
259 List<String> attributions = new ArrayList<>();
Santiago Etchebehere71c343f2018-04-06 14:03:07 -0700260 CharSequence labelCharSeq = mInfo.loadLabel(context.getPackageManager());
261 attributions.add(labelCharSeq == null ? null : labelCharSeq.toString());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800262
263 try {
264 CharSequence authorCharSeq = mInfo.loadAuthor(context.getPackageManager());
265 if (authorCharSeq != null) {
266 String author = authorCharSeq.toString();
267 attributions.add(author);
268 }
269 } catch (Resources.NotFoundException e) {
270 // No author specified, so no other attribution to add.
271 }
272
273 return attributions;
274 }
275
276 @Override
277 public String getActionUrl(Context context) {
278 if (BuildCompat.isAtLeastNMR1()) {
279 try {
280 Uri wallpaperContextUri = mInfo.loadContextUri(context.getPackageManager());
281 if (wallpaperContextUri != null) {
282 return wallpaperContextUri.toString();
283 }
284 } catch (Resources.NotFoundException e) {
285 return null;
286 }
287 }
288
289 return null;
290 }
291
292 @Override
293 public Asset getAsset(Context context) {
294 return null;
295 }
296
297 @Override
298 public Asset getThumbAsset(Context context) {
299 if (mThumbAsset == null) {
300 mThumbAsset = new LiveWallpaperThumbAsset(context, mInfo);
301 }
302 return mThumbAsset;
303 }
304
305 @Override
306 public void showPreview(Activity srcActivity, InlinePreviewIntentFactory factory,
307 int requestCode) {
308 Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
309 preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, mInfo.getComponent());
310 ActivityUtils.startActivityForResultSafely(srcActivity, preview, requestCode);
311 }
312
313 @Override
314 public void writeToParcel(Parcel parcel, int i) {
315 parcel.writeParcelable(mInfo, 0 /* flags */);
Santiago Etchebehere98a39c12018-05-11 15:06:45 -0700316 parcel.writeInt(mVisibleTitle ? 1 : 0);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800317 }
318
319 @Override
320 public android.app.WallpaperInfo getWallpaperComponent() {
321 return mInfo;
322 }
323
324 @Override
325 public String getCollectionId(Context context) {
326 return context.getString(R.string.live_wallpaper_collection_id);
327 }
328}