Home | History | Annotate | Download | only in phone
      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.ActivityManager;
     21 import android.app.IWallpaperManager;
     22 import android.app.IWallpaperManagerCallback;
     23 import android.app.WallpaperManager;
     24 import android.content.Context;
     25 import android.content.res.Resources;
     26 import android.graphics.Bitmap;
     27 import android.graphics.BitmapFactory;
     28 import android.graphics.Rect;
     29 import android.graphics.Xfermode;
     30 import android.graphics.drawable.BitmapDrawable;
     31 import android.graphics.drawable.Drawable;
     32 import android.graphics.drawable.DrawableWrapper;
     33 import android.os.AsyncTask;
     34 import android.os.Handler;
     35 import android.os.ParcelFileDescriptor;
     36 import android.os.RemoteException;
     37 import android.os.ServiceManager;
     38 import android.os.UserHandle;
     39 import android.util.Log;
     40 
     41 import com.android.keyguard.KeyguardUpdateMonitor;
     42 
     43 import libcore.io.IoUtils;
     44 
     45 import java.util.Objects;
     46 
     47 /**
     48  * Manages the lockscreen wallpaper.
     49  */
     50 public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable {
     51 
     52     private static final String TAG = "LockscreenWallpaper";
     53 
     54     private final StatusBar mBar;
     55     private final WallpaperManager mWallpaperManager;
     56     private final Handler mH;
     57     private final KeyguardUpdateMonitor mUpdateMonitor;
     58 
     59     private boolean mCached;
     60     private Bitmap mCache;
     61     private int mCurrentUserId;
     62     // The user selected in the UI, or null if no user is selected or UI doesn't support selecting
     63     // users.
     64     private UserHandle mSelectedUser;
     65     private AsyncTask<Void, Void, LoaderResult> mLoader;
     66 
     67     public LockscreenWallpaper(Context ctx, StatusBar bar, Handler h) {
     68         mBar = bar;
     69         mH = h;
     70         mWallpaperManager = (WallpaperManager) ctx.getSystemService(Context.WALLPAPER_SERVICE);
     71         mCurrentUserId = ActivityManager.getCurrentUser();
     72         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(ctx);
     73 
     74         IWallpaperManager service = IWallpaperManager.Stub.asInterface(
     75                 ServiceManager.getService(Context.WALLPAPER_SERVICE));
     76         try {
     77             service.setLockWallpaperCallback(this);
     78         } catch (RemoteException e) {
     79             Log.e(TAG, "System dead?" + e);
     80         }
     81     }
     82 
     83     public Bitmap getBitmap() {
     84         if (mCached) {
     85             return mCache;
     86         }
     87         if (!mWallpaperManager.isWallpaperSupported()) {
     88             mCached = true;
     89             mCache = null;
     90             return null;
     91         }
     92 
     93         LoaderResult result = loadBitmap(mCurrentUserId, mSelectedUser);
     94         if (result.success) {
     95             mCached = true;
     96             mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null);
     97             mCache = result.bitmap;
     98         }
     99         return mCache;
    100     }
    101 
    102     public LoaderResult loadBitmap(int currentUserId, UserHandle selectedUser) {
    103         // May be called on any thread - only use thread safe operations.
    104 
    105         // Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK
    106         // wallpaper.
    107         final int lockWallpaperUserId =
    108                 selectedUser != null ? selectedUser.getIdentifier() : currentUserId;
    109         ParcelFileDescriptor fd = mWallpaperManager.getWallpaperFile(
    110                 WallpaperManager.FLAG_LOCK, lockWallpaperUserId);
    111 
    112         if (fd != null) {
    113             try {
    114                 BitmapFactory.Options options = new BitmapFactory.Options();
    115                 return LoaderResult.success(BitmapFactory.decodeFileDescriptor(
    116                         fd.getFileDescriptor(), null, options));
    117             } catch (OutOfMemoryError e) {
    118                 Log.w(TAG, "Can't decode file", e);
    119                 return LoaderResult.fail();
    120             } finally {
    121                 IoUtils.closeQuietly(fd);
    122             }
    123         } else {
    124             if (selectedUser != null) {
    125                 // Show the selected user's static wallpaper.
    126                 return LoaderResult.success(
    127                         mWallpaperManager.getBitmapAsUser(selectedUser.getIdentifier()));
    128 
    129             } else {
    130                 // When there is no selected user, show the system wallpaper
    131                 return LoaderResult.success(null);
    132             }
    133         }
    134     }
    135 
    136     public void setCurrentUser(int user) {
    137         if (user != mCurrentUserId) {
    138             if (mSelectedUser == null || user != mSelectedUser.getIdentifier()) {
    139                 mCached = false;
    140             }
    141             mCurrentUserId = user;
    142         }
    143     }
    144 
    145     public void setSelectedUser(UserHandle selectedUser) {
    146         if (Objects.equals(selectedUser, mSelectedUser)) {
    147             return;
    148         }
    149         mSelectedUser = selectedUser;
    150         postUpdateWallpaper();
    151     }
    152 
    153     @Override
    154     public void onWallpaperChanged() {
    155         // Called on Binder thread.
    156         postUpdateWallpaper();
    157     }
    158 
    159     private void postUpdateWallpaper() {
    160         mH.removeCallbacks(this);
    161         mH.post(this);
    162     }
    163 
    164     @Override
    165     public void run() {
    166         // Called in response to onWallpaperChanged on the main thread.
    167 
    168         if (mLoader != null) {
    169             mLoader.cancel(false /* interrupt */);
    170         }
    171 
    172         final int currentUser = mCurrentUserId;
    173         final UserHandle selectedUser = mSelectedUser;
    174         mLoader = new AsyncTask<Void, Void, LoaderResult>() {
    175             @Override
    176             protected LoaderResult doInBackground(Void... params) {
    177                 return loadBitmap(currentUser, selectedUser);
    178             }
    179 
    180             @Override
    181             protected void onPostExecute(LoaderResult result) {
    182                 super.onPostExecute(result);
    183                 if (isCancelled()) {
    184                     return;
    185                 }
    186                 if (result.success) {
    187                     mCached = true;
    188                     mCache = result.bitmap;
    189                     mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null);
    190                     mBar.updateMediaMetaData(
    191                             true /* metaDataChanged */, true /* allowEnterAnimation */);
    192                 }
    193                 mLoader = null;
    194             }
    195         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    196     }
    197 
    198     private static class LoaderResult {
    199         public final boolean success;
    200         public final Bitmap bitmap;
    201 
    202         LoaderResult(boolean success, Bitmap bitmap) {
    203             this.success = success;
    204             this.bitmap = bitmap;
    205         }
    206 
    207         static LoaderResult success(Bitmap b) {
    208             return new LoaderResult(true, b);
    209         }
    210 
    211         static LoaderResult fail() {
    212             return new LoaderResult(false, null);
    213         }
    214     }
    215 
    216     /**
    217      * Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
    218      */
    219     public static class WallpaperDrawable extends DrawableWrapper {
    220 
    221         private final ConstantState mState;
    222         private final Rect mTmpRect = new Rect();
    223 
    224         public WallpaperDrawable(Resources r, Bitmap b) {
    225             this(r, new ConstantState(b));
    226         }
    227 
    228         private WallpaperDrawable(Resources r, ConstantState state) {
    229             super(new BitmapDrawable(r, state.mBackground));
    230             mState = state;
    231         }
    232 
    233         @Override
    234         public void setXfermode(@Nullable Xfermode mode) {
    235             // DrawableWrapper does not call this for us.
    236             getDrawable().setXfermode(mode);
    237         }
    238 
    239         @Override
    240         public int getIntrinsicWidth() {
    241             return -1;
    242         }
    243 
    244         @Override
    245         public int getIntrinsicHeight() {
    246             return -1;
    247         }
    248 
    249         @Override
    250         protected void onBoundsChange(Rect bounds) {
    251             int vwidth = getBounds().width();
    252             int vheight = getBounds().height();
    253             int dwidth = mState.mBackground.getWidth();
    254             int dheight = mState.mBackground.getHeight();
    255             float scale;
    256             float dx = 0, dy = 0;
    257 
    258             if (dwidth * vheight > vwidth * dheight) {
    259                 scale = (float) vheight / (float) dheight;
    260             } else {
    261                 scale = (float) vwidth / (float) dwidth;
    262             }
    263 
    264             if (scale <= 1f) {
    265                 scale = 1f;
    266             }
    267             dy = (vheight - dheight * scale) * 0.5f;
    268 
    269             mTmpRect.set(
    270                     bounds.left,
    271                     bounds.top + Math.round(dy),
    272                     bounds.left + Math.round(dwidth * scale),
    273                     bounds.top + Math.round(dheight * scale + dy));
    274 
    275             super.onBoundsChange(mTmpRect);
    276         }
    277 
    278         @Override
    279         public ConstantState getConstantState() {
    280             return mState;
    281         }
    282 
    283         static class ConstantState extends Drawable.ConstantState {
    284 
    285             private final Bitmap mBackground;
    286 
    287             ConstantState(Bitmap background) {
    288                 mBackground = background;
    289             }
    290 
    291             @Override
    292             public Drawable newDrawable() {
    293                 return newDrawable(null);
    294             }
    295 
    296             @Override
    297             public Drawable newDrawable(@Nullable Resources res) {
    298                 return new WallpaperDrawable(res, this);
    299             }
    300 
    301             @Override
    302             public int getChangingConfigurations() {
    303                 // DrawableWrapper already handles this for us.
    304                 return 0;
    305             }
    306         }
    307     }
    308 }
    309