• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 com.android.systemui.statusbar.phone;
18 
19 import android.annotation.Nullable;
20 import android.app.IWallpaperManager;
21 import android.app.IWallpaperManagerCallback;
22 import android.app.WallpaperColors;
23 import android.app.WallpaperManager;
24 import android.content.res.Resources;
25 import android.graphics.Bitmap;
26 import android.graphics.BitmapFactory;
27 import android.graphics.Rect;
28 import android.graphics.Xfermode;
29 import android.graphics.drawable.BitmapDrawable;
30 import android.graphics.drawable.Drawable;
31 import android.graphics.drawable.DrawableWrapper;
32 import android.os.AsyncTask;
33 import android.os.Handler;
34 import android.os.ParcelFileDescriptor;
35 import android.os.RemoteException;
36 import android.os.UserHandle;
37 import android.util.Log;
38 
39 import androidx.annotation.NonNull;
40 
41 import com.android.internal.util.IndentingPrintWriter;
42 import com.android.keyguard.KeyguardUpdateMonitor;
43 import com.android.systemui.Dumpable;
44 import com.android.systemui.dagger.SysUISingleton;
45 import com.android.systemui.dagger.qualifiers.Main;
46 import com.android.systemui.dump.DumpManager;
47 import com.android.systemui.settings.UserTracker;
48 import com.android.systemui.statusbar.NotificationMediaManager;
49 
50 import libcore.io.IoUtils;
51 
52 import java.io.PrintWriter;
53 import java.util.Objects;
54 
55 import javax.inject.Inject;
56 
57 /**
58  * Manages the lockscreen wallpaper.
59  */
60 @SysUISingleton
61 public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable,
62         Dumpable {
63 
64     private static final String TAG = "LockscreenWallpaper";
65 
66     private final NotificationMediaManager mMediaManager;
67     private final WallpaperManager mWallpaperManager;
68     private final KeyguardUpdateMonitor mUpdateMonitor;
69     private final Handler mH;
70 
71     private boolean mCached;
72     private Bitmap mCache;
73     private int mCurrentUserId;
74     // The user selected in the UI, or null if no user is selected or UI doesn't support selecting
75     // users.
76     private UserHandle mSelectedUser;
77     private AsyncTask<Void, Void, LoaderResult> mLoader;
78 
79     @Inject
LockscreenWallpaper(WallpaperManager wallpaperManager, @Nullable IWallpaperManager iWallpaperManager, KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager, NotificationMediaManager mediaManager, @Main Handler mainHandler, UserTracker userTracker)80     public LockscreenWallpaper(WallpaperManager wallpaperManager,
81             @Nullable IWallpaperManager iWallpaperManager,
82             KeyguardUpdateMonitor keyguardUpdateMonitor,
83             DumpManager dumpManager,
84             NotificationMediaManager mediaManager,
85             @Main Handler mainHandler,
86             UserTracker userTracker) {
87         dumpManager.registerDumpable(getClass().getSimpleName(), this);
88         mWallpaperManager = wallpaperManager;
89         mCurrentUserId = userTracker.getUserId();
90         mUpdateMonitor = keyguardUpdateMonitor;
91         mMediaManager = mediaManager;
92         mH = mainHandler;
93 
94         if (iWallpaperManager != null) {
95             // Service is disabled on some devices like Automotive
96             try {
97                 iWallpaperManager.setLockWallpaperCallback(this);
98             } catch (RemoteException e) {
99                 Log.e(TAG, "System dead?" + e);
100             }
101         }
102     }
103 
getBitmap()104     public Bitmap getBitmap() {
105         if (mCached) {
106             return mCache;
107         }
108         if (!mWallpaperManager.isWallpaperSupported()) {
109             mCached = true;
110             mCache = null;
111             return null;
112         }
113 
114         LoaderResult result = loadBitmap(mCurrentUserId, mSelectedUser);
115         if (result.success) {
116             mCached = true;
117             mCache = result.bitmap;
118         }
119         return mCache;
120     }
121 
loadBitmap(int currentUserId, UserHandle selectedUser)122     public LoaderResult loadBitmap(int currentUserId, UserHandle selectedUser) {
123         // May be called on any thread - only use thread safe operations.
124 
125         if (!mWallpaperManager.isWallpaperSupported()) {
126             // When wallpaper is not supported, show the system wallpaper
127             return LoaderResult.success(null);
128         }
129 
130         // Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK
131         // wallpaper.
132         final int lockWallpaperUserId =
133                 selectedUser != null ? selectedUser.getIdentifier() : currentUserId;
134         ParcelFileDescriptor fd = mWallpaperManager.getWallpaperFile(
135                 WallpaperManager.FLAG_LOCK, lockWallpaperUserId);
136 
137         if (fd != null) {
138             try {
139                 BitmapFactory.Options options = new BitmapFactory.Options();
140                 options.inPreferredConfig = Bitmap.Config.HARDWARE;
141                 return LoaderResult.success(BitmapFactory.decodeFileDescriptor(
142                         fd.getFileDescriptor(), null, options));
143             } catch (OutOfMemoryError e) {
144                 Log.w(TAG, "Can't decode file", e);
145                 return LoaderResult.fail();
146             } finally {
147                 IoUtils.closeQuietly(fd);
148             }
149         } else {
150             if (selectedUser != null) {
151                 // Show the selected user's static wallpaper.
152                 return LoaderResult.success(mWallpaperManager.getBitmapAsUser(
153                         selectedUser.getIdentifier(), true /* hardware */));
154 
155             } else {
156                 // When there is no selected user, show the system wallpaper
157                 return LoaderResult.success(null);
158             }
159         }
160     }
161 
setCurrentUser(int user)162     public void setCurrentUser(int user) {
163         if (user != mCurrentUserId) {
164             if (mSelectedUser == null || user != mSelectedUser.getIdentifier()) {
165                 mCached = false;
166             }
167             mCurrentUserId = user;
168         }
169     }
170 
setSelectedUser(UserHandle selectedUser)171     public void setSelectedUser(UserHandle selectedUser) {
172         if (Objects.equals(selectedUser, mSelectedUser)) {
173             return;
174         }
175         mSelectedUser = selectedUser;
176         postUpdateWallpaper();
177     }
178 
179     @Override
onWallpaperChanged()180     public void onWallpaperChanged() {
181         // Called on Binder thread.
182         postUpdateWallpaper();
183     }
184 
185     @Override
onWallpaperColorsChanged(WallpaperColors colors, int which, int userId)186     public void onWallpaperColorsChanged(WallpaperColors colors, int which, int userId) {
187 
188     }
189 
postUpdateWallpaper()190     private void postUpdateWallpaper() {
191         if (mH == null) {
192             Log.wtfStack(TAG, "Trying to use LockscreenWallpaper before initialization.");
193             return;
194         }
195         mH.removeCallbacks(this);
196         mH.post(this);
197     }
198 
199     @Override
run()200     public void run() {
201         // Called in response to onWallpaperChanged on the main thread.
202 
203         if (mLoader != null) {
204             mLoader.cancel(false /* interrupt */);
205         }
206 
207         final int currentUser = mCurrentUserId;
208         final UserHandle selectedUser = mSelectedUser;
209         mLoader = new AsyncTask<Void, Void, LoaderResult>() {
210             @Override
211             protected LoaderResult doInBackground(Void... params) {
212                 return loadBitmap(currentUser, selectedUser);
213             }
214 
215             @Override
216             protected void onPostExecute(LoaderResult result) {
217                 super.onPostExecute(result);
218                 if (isCancelled()) {
219                     return;
220                 }
221                 if (result.success) {
222                     mCached = true;
223                     mCache = result.bitmap;
224                     mMediaManager.updateMediaMetaData(
225                             true /* metaDataChanged */, true /* allowEnterAnimation */);
226                 }
227                 mLoader = null;
228             }
229         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
230     }
231 
232     @Override
dump(@onNull PrintWriter pw, @NonNull String[] args)233     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
234         pw.println(getClass().getSimpleName() + ":");
235         IndentingPrintWriter iPw = new IndentingPrintWriter(pw, "  ").increaseIndent();
236         iPw.println("mCached=" + mCached);
237         iPw.println("mCache=" + mCache);
238         iPw.println("mCurrentUserId=" + mCurrentUserId);
239         iPw.println("mSelectedUser=" + mSelectedUser);
240     }
241 
242     private static class LoaderResult {
243         public final boolean success;
244         public final Bitmap bitmap;
245 
LoaderResult(boolean success, Bitmap bitmap)246         LoaderResult(boolean success, Bitmap bitmap) {
247             this.success = success;
248             this.bitmap = bitmap;
249         }
250 
success(Bitmap b)251         static LoaderResult success(Bitmap b) {
252             return new LoaderResult(true, b);
253         }
254 
fail()255         static LoaderResult fail() {
256             return new LoaderResult(false, null);
257         }
258     }
259 
260     /**
261      * Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
262      *
263      * <p>Aligns to the center when showing on the smaller internal display of a multi display
264      * device.
265      */
266     public static class WallpaperDrawable extends DrawableWrapper {
267 
268         private final ConstantState mState;
269         private final Rect mTmpRect = new Rect();
270         private boolean mIsOnSmallerInternalDisplays;
271 
WallpaperDrawable(Resources r, Bitmap b, boolean isOnSmallerInternalDisplays)272         public WallpaperDrawable(Resources r, Bitmap b, boolean isOnSmallerInternalDisplays) {
273             this(r, new ConstantState(b), isOnSmallerInternalDisplays);
274         }
275 
WallpaperDrawable(Resources r, ConstantState state, boolean isOnSmallerInternalDisplays)276         private WallpaperDrawable(Resources r, ConstantState state,
277                 boolean isOnSmallerInternalDisplays) {
278             super(new BitmapDrawable(r, state.mBackground));
279             mState = state;
280             mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
281         }
282 
283         @Override
setXfermode(@ullable Xfermode mode)284         public void setXfermode(@Nullable Xfermode mode) {
285             // DrawableWrapper does not call this for us.
286             getDrawable().setXfermode(mode);
287         }
288 
289         @Override
getIntrinsicWidth()290         public int getIntrinsicWidth() {
291             return -1;
292         }
293 
294         @Override
getIntrinsicHeight()295         public int getIntrinsicHeight() {
296             return -1;
297         }
298 
299         @Override
onBoundsChange(Rect bounds)300         protected void onBoundsChange(Rect bounds) {
301             int vwidth = getBounds().width();
302             int vheight = getBounds().height();
303             int dwidth = mState.mBackground.getWidth();
304             int dheight = mState.mBackground.getHeight();
305             float scale;
306             float dx = 0, dy = 0;
307 
308             if (dwidth * vheight > vwidth * dheight) {
309                 scale = (float) vheight / (float) dheight;
310             } else {
311                 scale = (float) vwidth / (float) dwidth;
312             }
313 
314             if (scale <= 1f) {
315                 scale = 1f;
316             }
317             dy = (vheight - dheight * scale) * 0.5f;
318 
319             int offsetX = 0;
320             // Offset to show the center area of the wallpaper on a smaller display for multi
321             // display device
322             if (mIsOnSmallerInternalDisplays) {
323                 offsetX = bounds.centerX() - (Math.round(dwidth * scale) / 2);
324             }
325 
326             mTmpRect.set(
327                     bounds.left + offsetX,
328                     bounds.top + Math.round(dy),
329                     bounds.left + Math.round(dwidth * scale) + offsetX,
330                     bounds.top + Math.round(dheight * scale + dy));
331 
332             super.onBoundsChange(mTmpRect);
333         }
334 
335         @Override
getConstantState()336         public ConstantState getConstantState() {
337             return mState;
338         }
339 
340         /**
341          * Update bounds when the hosting display or the display size has changed.
342          *
343          * @param isOnSmallerInternalDisplays tru if the drawable is on one of the internal displays
344          *                                    with the smaller area.
345          */
onDisplayUpdated(boolean isOnSmallerInternalDisplays)346         public void onDisplayUpdated(boolean isOnSmallerInternalDisplays) {
347             mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
348             onBoundsChange(getBounds());
349         }
350 
351         static class ConstantState extends Drawable.ConstantState {
352 
353             private final Bitmap mBackground;
354 
ConstantState(Bitmap background)355             ConstantState(Bitmap background) {
356                 mBackground = background;
357             }
358 
359             @Override
newDrawable()360             public Drawable newDrawable() {
361                 return newDrawable(null);
362             }
363 
364             @Override
newDrawable(@ullable Resources res)365             public Drawable newDrawable(@Nullable Resources res) {
366                 return new WallpaperDrawable(res, this, /* isOnSmallerInternalDisplays= */ false);
367             }
368 
369             @Override
getChangingConfigurations()370             public int getChangingConfigurations() {
371                 // DrawableWrapper already handles this for us.
372                 return 0;
373             }
374         }
375     }
376 }
377