/*
|
* Copyright (C) 2016 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License
|
*/
|
|
package com.android.systemui.statusbar.phone;
|
|
import android.annotation.Nullable;
|
import android.app.ActivityManager;
|
import android.app.IWallpaperManager;
|
import android.app.IWallpaperManagerCallback;
|
import android.app.WallpaperColors;
|
import android.app.WallpaperManager;
|
import android.content.Context;
|
import android.content.res.Resources;
|
import android.graphics.Bitmap;
|
import android.graphics.BitmapFactory;
|
import android.graphics.Rect;
|
import android.graphics.Xfermode;
|
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.DrawableWrapper;
|
import android.os.AsyncTask;
|
import android.os.Handler;
|
import android.os.ParcelFileDescriptor;
|
import android.os.RemoteException;
|
import android.os.ServiceManager;
|
import android.os.UserHandle;
|
import android.util.Log;
|
|
import com.android.keyguard.KeyguardUpdateMonitor;
|
import com.android.systemui.Dependency;
|
import com.android.systemui.statusbar.NotificationMediaManager;
|
|
import libcore.io.IoUtils;
|
|
import java.util.Objects;
|
|
/**
|
* Manages the lockscreen wallpaper.
|
*/
|
public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable {
|
|
private static final String TAG = "LockscreenWallpaper";
|
|
private final NotificationMediaManager mMediaManager =
|
Dependency.get(NotificationMediaManager.class);
|
|
private final StatusBar mBar;
|
private final WallpaperManager mWallpaperManager;
|
private final Handler mH;
|
private final KeyguardUpdateMonitor mUpdateMonitor;
|
|
private boolean mCached;
|
private Bitmap mCache;
|
private int mCurrentUserId;
|
// The user selected in the UI, or null if no user is selected or UI doesn't support selecting
|
// users.
|
private UserHandle mSelectedUser;
|
private AsyncTask<Void, Void, LoaderResult> mLoader;
|
|
public LockscreenWallpaper(Context ctx, StatusBar bar, Handler h) {
|
mBar = bar;
|
mH = h;
|
mWallpaperManager = (WallpaperManager) ctx.getSystemService(Context.WALLPAPER_SERVICE);
|
mCurrentUserId = ActivityManager.getCurrentUser();
|
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(ctx);
|
|
IWallpaperManager service = IWallpaperManager.Stub.asInterface(
|
ServiceManager.getService(Context.WALLPAPER_SERVICE));
|
try {
|
service.setLockWallpaperCallback(this);
|
} catch (RemoteException e) {
|
Log.e(TAG, "System dead?" + e);
|
}
|
}
|
|
public Bitmap getBitmap() {
|
if (mCached) {
|
return mCache;
|
}
|
if (!mWallpaperManager.isWallpaperSupported()) {
|
mCached = true;
|
mCache = null;
|
return null;
|
}
|
|
LoaderResult result = loadBitmap(mCurrentUserId, mSelectedUser);
|
if (result.success) {
|
mCached = true;
|
mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null);
|
mCache = result.bitmap;
|
}
|
return mCache;
|
}
|
|
public LoaderResult loadBitmap(int currentUserId, UserHandle selectedUser) {
|
// May be called on any thread - only use thread safe operations.
|
|
// Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK
|
// wallpaper.
|
final int lockWallpaperUserId =
|
selectedUser != null ? selectedUser.getIdentifier() : currentUserId;
|
ParcelFileDescriptor fd = mWallpaperManager.getWallpaperFile(
|
WallpaperManager.FLAG_LOCK, lockWallpaperUserId);
|
|
if (fd != null) {
|
try {
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
return LoaderResult.success(BitmapFactory.decodeFileDescriptor(
|
fd.getFileDescriptor(), null, options));
|
} catch (OutOfMemoryError e) {
|
Log.w(TAG, "Can't decode file", e);
|
return LoaderResult.fail();
|
} finally {
|
IoUtils.closeQuietly(fd);
|
}
|
} else {
|
if (selectedUser != null) {
|
// Show the selected user's static wallpaper.
|
return LoaderResult.success(mWallpaperManager.getBitmapAsUser(
|
selectedUser.getIdentifier(), true /* hardware */));
|
|
} else {
|
// When there is no selected user, show the system wallpaper
|
return LoaderResult.success(null);
|
}
|
}
|
}
|
|
public void setCurrentUser(int user) {
|
if (user != mCurrentUserId) {
|
if (mSelectedUser == null || user != mSelectedUser.getIdentifier()) {
|
mCached = false;
|
}
|
mCurrentUserId = user;
|
}
|
}
|
|
public void setSelectedUser(UserHandle selectedUser) {
|
if (Objects.equals(selectedUser, mSelectedUser)) {
|
return;
|
}
|
mSelectedUser = selectedUser;
|
postUpdateWallpaper();
|
}
|
|
@Override
|
public void onWallpaperChanged() {
|
// Called on Binder thread.
|
postUpdateWallpaper();
|
}
|
|
@Override
|
public void onWallpaperColorsChanged(WallpaperColors colors, int which, int userId) {
|
|
}
|
|
private void postUpdateWallpaper() {
|
mH.removeCallbacks(this);
|
mH.post(this);
|
}
|
|
@Override
|
public void run() {
|
// Called in response to onWallpaperChanged on the main thread.
|
|
if (mLoader != null) {
|
mLoader.cancel(false /* interrupt */);
|
}
|
|
final int currentUser = mCurrentUserId;
|
final UserHandle selectedUser = mSelectedUser;
|
mLoader = new AsyncTask<Void, Void, LoaderResult>() {
|
@Override
|
protected LoaderResult doInBackground(Void... params) {
|
return loadBitmap(currentUser, selectedUser);
|
}
|
|
@Override
|
protected void onPostExecute(LoaderResult result) {
|
super.onPostExecute(result);
|
if (isCancelled()) {
|
return;
|
}
|
if (result.success) {
|
mCached = true;
|
mCache = result.bitmap;
|
mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null);
|
mMediaManager.updateMediaMetaData(
|
true /* metaDataChanged */, true /* allowEnterAnimation */);
|
}
|
mLoader = null;
|
}
|
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
}
|
|
private static class LoaderResult {
|
public final boolean success;
|
public final Bitmap bitmap;
|
|
LoaderResult(boolean success, Bitmap bitmap) {
|
this.success = success;
|
this.bitmap = bitmap;
|
}
|
|
static LoaderResult success(Bitmap b) {
|
return new LoaderResult(true, b);
|
}
|
|
static LoaderResult fail() {
|
return new LoaderResult(false, null);
|
}
|
}
|
|
/**
|
* Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
|
*/
|
public static class WallpaperDrawable extends DrawableWrapper {
|
|
private final ConstantState mState;
|
private final Rect mTmpRect = new Rect();
|
|
public WallpaperDrawable(Resources r, Bitmap b) {
|
this(r, new ConstantState(b));
|
}
|
|
private WallpaperDrawable(Resources r, ConstantState state) {
|
super(new BitmapDrawable(r, state.mBackground));
|
mState = state;
|
}
|
|
@Override
|
public void setXfermode(@Nullable Xfermode mode) {
|
// DrawableWrapper does not call this for us.
|
getDrawable().setXfermode(mode);
|
}
|
|
@Override
|
public int getIntrinsicWidth() {
|
return -1;
|
}
|
|
@Override
|
public int getIntrinsicHeight() {
|
return -1;
|
}
|
|
@Override
|
protected void onBoundsChange(Rect bounds) {
|
int vwidth = getBounds().width();
|
int vheight = getBounds().height();
|
int dwidth = mState.mBackground.getWidth();
|
int dheight = mState.mBackground.getHeight();
|
float scale;
|
float dx = 0, dy = 0;
|
|
if (dwidth * vheight > vwidth * dheight) {
|
scale = (float) vheight / (float) dheight;
|
} else {
|
scale = (float) vwidth / (float) dwidth;
|
}
|
|
if (scale <= 1f) {
|
scale = 1f;
|
}
|
dy = (vheight - dheight * scale) * 0.5f;
|
|
mTmpRect.set(
|
bounds.left,
|
bounds.top + Math.round(dy),
|
bounds.left + Math.round(dwidth * scale),
|
bounds.top + Math.round(dheight * scale + dy));
|
|
super.onBoundsChange(mTmpRect);
|
}
|
|
@Override
|
public ConstantState getConstantState() {
|
return mState;
|
}
|
|
static class ConstantState extends Drawable.ConstantState {
|
|
private final Bitmap mBackground;
|
|
ConstantState(Bitmap background) {
|
mBackground = background;
|
}
|
|
@Override
|
public Drawable newDrawable() {
|
return newDrawable(null);
|
}
|
|
@Override
|
public Drawable newDrawable(@Nullable Resources res) {
|
return new WallpaperDrawable(res, this);
|
}
|
|
@Override
|
public int getChangingConfigurations() {
|
// DrawableWrapper already handles this for us.
|
return 0;
|
}
|
}
|
}
|
}
|