《学习笔记》android6.0 锁屏壁纸功能
来源:互联网 发布:散粉哪个比较好知乎 编辑:程序博客网 时间:2024/06/02 01:20
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 锁屏壁纸功能
- android6.0实现锁屏壁纸
- [RK3288][Android6.0] 调试笔记 --- 移除锁屏功能
- android6.0 壁纸设置流程讲解
- Android6.0 锁屏权限申请和功能实现
- android6.0权限申请学习笔记
- 【学习笔记】android动态壁纸
- [RK3288][Android6.0] 调试笔记 --- Su添加密码登陆功能
- android6.0 固定屏幕功能
- 锁屏壁纸开关
- Android学习笔记-Android6.0运行时权限
- Android学习笔记-Android6.0运行时权限(续)
- Android6.0 Bluetooth学习
- Android6.0 无法锁屏
- Android开发学习之设置Android壁纸的功能实现
- [RK3288][Android6.0] 调试笔记 --- 关闭音频播放停止后进入Standby功能
- [RK3288][Android6.0] 调试笔记 --- 开机动画支持播放mp4视频功能
- 【分享笔记】android6.0特性
- iOS tableview cell下拉展开显示详情选择城市
- 虚拟机安装jzmq 方法
- 链表的原理
- angularjs 几个模块学习
- Java命令学习系列(七)——javap
- 《学习笔记》android6.0 锁屏壁纸功能
- Tomcat指定jdk版本启动
- SPOJ 7001. Visible Lattice Points 莫比乌斯反演
- 附件(blob)下载输出实例代码参考
- 使用js Math.random()函数生成n到m间的随机数字
- wampserver环境配置--Apache2.4+PHP5.6+Mysql5.7.11
- String & StringBuilder & StringBuffer 区别与性能比较
- 定时器的使用方法
- 最简单的jquery 异步上传图片