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