《学习笔记》android6.0 锁屏壁纸功能
Posted 王_健
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《学习笔记》android6.0 锁屏壁纸功能相关的知识,希望对你有一定的参考价值。
1.需求
Three entrance for this feature. one is in Home screen, the second is in Settings>Display screen, the third one is in Gallery>single view a picture>Set picture as>Wallaper screen.
2.方案
思路:
通过对WallpaperManager和SystemUI中添加对应的方法,来实现对外提供设置锁屏壁纸的接口。
步骤:
1.设置锁屏壁纸,将选好的壁纸存入SystemUI对应目录下;
2.读取锁屏壁纸,当屏幕点亮后从SystemUI对应目录下读取图片对当前锁屏界面进行背景的设置,达到效果。
代码路径:
frameworks/base/core/java/android/app/WallpaperManager.java
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
具体实现代码:
/**
* Provides access to the system wallpaper. With WallpaperManager, you can
* get the current wallpaper, get the desired dimensions for the wallpaper, set
* the wallpaper, and more. Get an instance of WallpaperManager with
* @link #getInstance(android.content.Context) getInstance().
*
* <p> An app can check whether wallpapers are supported for the current user, by calling
* @link #isWallpaperSupported().
*/
public class WallpaperManager
private static String TAG = "WallpaperManager";
private static boolean DEBUG = true;
private static final String WALLPAPERDIR = "/data/data/com.android.systemui/";
static final String MENUWALLPAPERNAME = "menuwallpaper.png";
static final String LOCKWALLPAPERNAME = "lockwallpaper.png";
/** @hide */
public static final int SCREEN_WP_MODE = 0;
/** @hide */
public static final int LOCK_WP_MODE = 1;
/** @hide */
public static final int ALL_WP_MODE = 2;
private final Context mContext;
<中间部分省略>...
//默认设置壁纸的方法
public void setStream(InputStream data) throws IOException
if (sGlobals.mService == null)
Log.w(TAG, "WallpaperService not running");
return;
try
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
mContext.getOpPackageName());
if (fd == null)
return;
FileOutputStream fos = null;
try
fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
setWallpaper(data, fos);
finally
if (fos != null)
fos.close();
catch (RemoteException e)
// Ignore
//将选好的图片流写到指定路径
private void setWallpaper(InputStream data, FileOutputStream fos)
throws IOException
byte[] buffer = new byte[32768];
int amt;
while ((amt=data.read(buffer)) > 0)
fos.write(buffer, 0, amt);
//通过指定图片文件路径,得到一个文件描述符对象
private ParcelFileDescriptor getLockWallpaperPath()
File dir = new File(WALLPAPERDIR);
if(!dir.exists())
dir.mkdirs();
FileUtils.setPermissions(WALLPAPERDIR,0777,-1,-1);
File file = null;
file = new File(dir, LOCKWALLPAPERNAME);
try
file.createNewFile();
catch (IOException io)
io.printStackTrace();
ParcelFileDescriptor fd=null;
try
fd = ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_CREATE|ParcelFileDescriptor.MODE_READ_WRITE);
catch(Exception e)
e.printStackTrace();
return fd;
//设置锁屏壁纸,将图片存入到指定路径下
public void setLockStream(InputStream data) throws IOException
Log.w(TAG, "setLockStream...");
if (sGlobals.mService == null)
return;
if(data==null)
//如果图片不存在,就以默认壁纸作为锁屏壁纸来设置
data=sGlobals.openDefaultWallpaperRes(mContext);
try
ParcelFileDescriptor fd = getLockWallpaperPath();
if (fd == null)
return;
FileOutputStream fos = null;
try
fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
setWallpaper(data, fos);
finally
if (fos != null)
fos.close();
catch (Exception e)
e.printStackTrace();
FileUtils.setPermissions(WALLPAPERDIR+"/"+LOCKWALLPAPERNAME,0777,-1,-1);
//这个方法的两个参数应该是一样的。
//应用场景:一张图片同事被设置成壁纸和锁屏壁纸。
public void setAllStream(InputStream data_home,InputStream data_lock) throws IOException
if (sGlobals.mService == null)
return;
if(data_home == null)
data_home=sGlobals.openDefaultWallpaperRes(mContext);
else if(data_lock == null)
data_lock = sGlobals.openDefaultWallpaperRes(mContext);
try
ParcelFileDescriptor fd_lock = getLockWallpaperPath();
ParcelFileDescriptor fd_home = sGlobals.mService.setWallpaper(null,
mContext.getOpPackageName());
if (fd_lock != null)
FileOutputStream fos_lock = null;
try
fos_lock = new ParcelFileDescriptor.AutoCloseOutputStream(fd_lock);
setWallpaper(data_lock, fos_lock);
finally
if (fos_lock != null)
fos_lock.close();
else if(fd_home != null)
try
fos_home = new ParcelFileDescriptor.AutoCloseOutputStream(fd_home);
setWallpaper(data_home, fos_home);
finally
if (fos_home != null)
fos_home.close();
catch (Exception e)
e.printStackTrace();
//上面是存入的过程,这个方法是得到锁屏壁纸,用于设置背景。
public Bitmap getCurrentLockWallpaper()
try
File file = new File(WALLPAPERDIR, LOCKWALLPAPERNAME);
ParcelFileDescriptor fd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
if (fd != null)
try
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bm = BitmapFactory.decodeFileDescriptor(
fd.getFileDescriptor(), null, options);
return bm;
catch (OutOfMemoryError e)
finally
try
fd.close();
catch (IOException e)
catch (Exception e)
return null;
在WallpaperManager类中,仿照默认设置壁纸方法,添加了两个方法。setLockStream()(单独对锁屏设置背景)和setAllStream()(将同一张图片分别应用于待机壁纸和锁屏壁纸),同时添加了对锁屏壁纸获取的方法 getCurrentLockWallpaper();
PhoneStatusBar.java管理着PanelHolder 以及 NotificationPanelView下的各个控件,showKeyguard 方法和 hideKeyguard方法中都调用了updateKeyguardState()来实时更新锁屏状态和调整各控件的位置大小。具体锁屏结构和流程有时间再整理。
附上代码:
public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
HeadsUpManager.OnHeadsUpChangedListener
... ...
private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked)
if (mState == StatusBarState.KEYGUARD)
mKeyguardIndicationController.setVisible(true);
mNotificationPanel.resetViews();
mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
mStatusBarView.removePendingHideExpandedRunnables();
else
mKeyguardIndicationController.setVisible(false);
mKeyguardUserSwitcher.setKeyguard(false,
goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked);
//如果当前mState 处于锁屏状态,通过wallpaperManager.getCurrentLockWallpaper取出图片资源作为PanelHolder的背景。
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
Bitmap bm = getCurrentWallpaperLocked();
if (bm != null)
Drawable dr = new BitmapDrawable(mContext.getResources(), generateBitmap(bm, 480, 1280));
dr.setDither(false);
mHolder.setBackground(dr);
else
mHolder.setBackgroundResource(0);
mScrimController.setKeyguardShowing(true);
mIconPolicy.setKeyguardShowing(true);
else
//如果mState 为其他状态,比如待机状态等,将PanelHolder背景清除。否则会出现状态栏花屏现象,因为此时PanelHolder的大小即为状态栏大小,无法呈现图片。
mHolder.setBackgroundResource(0);
mScrimController.setKeyguardShowing(false);
mIconPolicy.setKeyguardShowing(false);
mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
updateDozingState();
updatePublicMode();
updateStackScrollerState(goingToFullShade);
updateNotifications();
checkBarModes();
/// M: Support "Operator plugin - Customize Carrier Label for PLMN". @
updateCarrierLabelVisibility(false);
/// M: Support "Operator plugin - Customize Carrier Label for PLMN". @
updateMediaMetaData(false);
mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
mStatusBarKeyguardViewManager.isSecure());
private Bitmap getCurrentWallpaperLocked()
WallpaperManager wallpaperManager = WallpaperManager.getInstance(mContext);
return wallpaperManager.getCurrentLockWallpaper();
//通过给定的长宽比对Bitmap图片进行缩放,铺满屏幕
private Bitmap generateBitmap(Bitmap bm, int width, int height)
if (bm == null)
return null;
bm.setDensity(DisplayMetrics.DENSITY_HIGH);
if (width <= 0 || height <= 0
|| (bm.getWidth() == width && bm.getHeight() == height))
return bm;
// This is the final bitmap we want to return.
try
Bitmap newbm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
newbm.setDensity(DisplayMetrics.DENSITY_HIGH);
Canvas c = new Canvas(newbm);
Rect targetRect = new Rect();
targetRect.right = bm.getWidth();
targetRect.bottom = bm.getHeight();
int deltaw = width - targetRect.right;
int deltah = height - targetRect.bottom;
if (deltaw > 0 || deltah > 0)
// We need to scale up so it covers the entire area.
float scale;
if (deltaw > deltah)
scale = width / (float)targetRect.right;
else
scale = height / (float)targetRect.bottom;
targetRect.right = (int)(targetRect.right*scale);
targetRect.bottom = (int)(targetRect.bottom*scale);
deltaw = width - targetRect.right;
deltah = height - targetRect.bottom;
targetRect.offset(deltaw/2, deltah/2);
Paint paint = new Paint();
paint.setFilterBitmap(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
c.drawBitmap(bm, null, targetRect, paint);
bm.recycle();
c.setBitmap(null);
return newbm;
catch (OutOfMemoryError e)
return bm;
这个时候已经将锁屏壁纸一些基础功能完成,目前已经可以通过调用wallpaperManager.java的方法对待机壁纸和锁屏壁纸进行分别设置了。
3.例子
接下来通过对原生图库进行修改,效果如下:
用自定义pupopWindow弹框替代原生直接设置壁纸的流程,然后将通过点击选项触发相应的操作。
代码路径:
packages/apps/Launcher3/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
packages/apps/Launcher3/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
public class WallpaperCropActivity extends BaseActivity implements Handler.Callback
protected void init()
...
//set wallpaper Button
final ActionBar actionBar = getActionBar();
actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
actionBar.getCustomView().setOnClickListener(
new View.OnClickListener()
@Override
public void onClick(View v)
//会有弹窗,所以这里不需要避免多次点击。
//mSetWallpaperButton.setEnabled(false);
boolean finishActivityWhenDone = true;
BitmapCropTask.OnBitmapCroppedHandler h =
new BitmapCropTask.OnBitmapCroppedHandler()
public void onBitmapCropped(byte[] imageBytes)
Point thumbSize = getDefaultThumbnailSize(
WallpaperCropActivity.this.getResources());
Bitmap thumb = createThumbnail(
thumbSize, null, null, imageBytes, null, 0, 0, true);
mSavedWallpaper.writeImage(thumb, imageBytes);
;
//从图库进入这个界面,mWallpaperMode 初始值为-1.
if(mWallpaperMode < 0)
//执行 自定义pupopWindow。
OperationPopupWindow pw = new OperationPopupWindow(WallpaperCropActivity.this,imageUri, h, finishActivityWhenDone);
pw.show(WallpaperCropActivity.this);
else
//从主页壁纸设置入口进入的,mWallpaperMode 是指定好的;
//直接根据mWallpaperMode 来设置壁纸类型。
cropImageAndSetWallpaper(imageUri, h, finishActivityWhenDone);
///M.
);
....
//GellaryBottomPopupWindow 就不贴出来了,就是继承了PupopWindow,
public class OperationPopupWindow extends GellaryBottomPopupWindow<Void>
public Uri uri = null;
public BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler = null;
public boolean finishActivityWhenDone ;
public OperationPopupWindow(Context context,Uri uri,
BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone)
super(context, null);
this.uri = uri;
this.onBitmapCroppedHandler =onBitmapCroppedHandler;
this.finishActivityWhenDone = finishActivityWhenDone;
public void onsave()
//最终执行的方法,为了适应屏幕对图片进行剪切,然后进入设置壁纸的流程
cropImageAndSetWallpaper(uri, onBitmapCroppedHandler, finishActivityWhenDone);
@Override
protected View generateCustomView(Void data)
View root = View.inflate(context, R.layout.popup_wallpaper_opreation_gellary, null);
View homeView = root.findViewById(R.id.home);
homeView.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
dismiss();
// Ensure that a tile is slelected and loaded.
mWallpaperMode = 0;
onsave();
);
View lockView = root.findViewById(R.id.lock);
lockView.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
dismiss();
mWallpaperMode = 1;
onsave();
);
View allView = root.findViewById(R.id.all);
allView.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
mWallpaperMode = 2;
onsave();
);
View cancelView = root.findViewById(R.id.cancel);
cancelView.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
dismiss();
);
return root;
...
protected void cropImageAndSetWallpaper(Uri uri, BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone)
...
//6.0上应该首次用BitmapCropTask 这种异步任务将耗时操作分离了出来。
BitmapCropTask cropTask = new BitmapCropTask(getContext(), uri,
cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop);
cropTask.setCropSize(outWidth, outHeight);
if (onBitmapCroppedHandler != null)
cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
cropTask.setWallpaperMode(mWallpaperMode);
cropTask.execute();
cropTask.setWallpaperMode()方法是为了执行不同的壁纸设置新增加的,接下来看看 BitmapCropTask.java文件,只看关键代码:
public class BitmapCropTask extends AsyncTask<Void, Void, Boolean>
...
//添加的方法,mWallpaperMode 对应三种壁纸设置模式,默认是待机壁纸。
public void setWallpaperMode(int wpMode)
mWallpaperMode = wpMode;
@Override
protected Boolean doInBackground(Void... params)
return cropBitmap(mWallpaperMode); //上段代码中最后会执行cropTask.execute(),走到这里;
@Override
protected void onPostExecute(Boolean result)
...
public boolean cropBitmap(int flag)
boolean failure = false;
//M. ALPS01885181, sync with gallery, check the image size.
if (isOutOfSpecLimit())
Log.i(LOGTAG, "cropBitmap,image out of spec limit, mInUri:" + mInUri);
return failure;
///M.
//到这里终于看见了WallpaperManager。
WallpaperManager wallpaperManager = null;
//mSetWallpaper = true
if (mSetWallpaper)
wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
//由于壁纸设置入口很多,mNoCrop在本次流程中为false,留给其他方法调用的;
if (mSetWallpaper && mNoCrop)
try
InputStream is_home = regenerateInputStream();
InputStream is_lock = regenerateInputStream();
if (is_home != null && is_lock != null)
if(flag == WallpaperManager.LOCK_WP_MODE)
wallpaperManager.setLockStream(is_lock);
else if(flag == WallpaperManager.ALL_WP_MODE)
wallpaperManager.setAllStream(is_home,is_lock);
else
wallpaperManager.setStream(is_home);
Utils.closeSilently(is_home);
Utils.closeSilently(is_lock);
catch (IOException e)
...
return !failure;
else
...
//一大堆算法,随后剪切缩放得出适应屏幕大小的图片资源
crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
roundedTrueCrop.top, roundedTrueCrop.width(),
roundedTrueCrop.height());
if (crop == null)
Log.w(LOGTAG, "cannot decode file: " + mInUri.toString());
failure = true;
return false;
if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0)
ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
//将bitmap压缩成ByteArrayOutputStream;
if (crop.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut))
// If we need to set to the wallpaper, set it
if (mSetWallpaper && wallpaperManager != null)
try
byte[] outByteArray = tmpOut.toByteArray();
if(flag == WallpaperManager.LOCK_WP_MODE)
//仅设置锁屏壁纸
wallpaperManager.setLockStream(new ByteArrayInputStream(outByteArray));
else if(flag == WallpaperManager.ALL_WP_MODE)
//同时设置锁屏和待机壁纸
wallpaperManager.setAllStream(new ByteArrayInputStream(outByteArray),new ByteArrayInputStream(outByteArray));
else
//设置默认壁纸
wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
if (mOnBitmapCroppedHandler != null)
mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
catch (IOException e)
Log.w(LOGTAG, "cannot write stream to wallpaper", e);
failure = true;
else
Log.w(LOGTAG, "cannot compress bitmap");
failure = true;
return !failure; // True if any of the operations failed
核心流程就是这样,其他细节不做笔记了。对于Keyguard也是一知半解,下次接着写Keyguard流程,巩固知识!
以上是关于《学习笔记》android6.0 锁屏壁纸功能的主要内容,如果未能解决你的问题,请参考以下文章