1、预览壁纸设置之后,略微被放大
Android 设置壁纸之后,实际效果和预览界面有区别
//frameworks/base/core/res/res/values/config.xml
<!-- The max scale for the wallpaper when it's zoomed in -->
<item name="config_wallpaperMaxScale" format="float" type="dimen">1.10</item>
这是Android Q的新特性,主界面或者锁屏界面下拉通知栏,壁纸会有一个缩小的效果。
默认设置壁纸,主界面和锁屏会被放大1.10f倍显示,下拉通知栏,缩小到1.0倍,有一个壁纸的动画效果。
2、锁屏壁纸被拉伸
横竖屏显示不同的壁纸
我们知道,锁屏壁纸是system UI单独显示的一个ImageView
1、锁屏壁纸的具体设置
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
private ImageView mBackdropFront
//
private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation,
@Nullable Bitmap bmp) {
Drawable artworkDrawable = null;
if (bmp != null) {
artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
}
boolean hasMediaArtwork = artworkDrawable != null;
boolean allowWhenShade = false;
if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
//获取锁屏壁纸
Bitmap[] lockWallpaper =
mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
Log.d(TAG, "finishUpdateMediaMetaData lockWallpaper: "+lockWallpaper);
if (lockWallpaper != null && lockWallpaper[0]!= null) {
//new Drawable
artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
mBackdropBack.getResources(), lockWallpaper);
// We're in the SHADE mode on the SIM screen - yet we still need to show
// the lockscreen wallpaper in that mode.
allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
}
}
...
...
if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
&& (mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade)
&& mBiometricUnlockController != null && mBiometricUnlockController.getMode()
!= BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
&& !hideBecauseOccluded) {
...
//设置壁纸
if (metaDataChanged) {
mBackdropBack.setImageDrawable(artworkDrawable);
}
} else {
...
...
}
}
2、锁屏壁纸数据
由上面可以看出,锁屏壁纸的Bitmap来自于mLockscreenWallpaper.getBitmap()
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
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 && result.bitmap[0] != null);
mCache = result.bitmap;
Log.d(TAG, "getmCacheL: " + mCache);
}
return mCache;
}
public LoaderResult loadBitmap(int currentUserId, UserHandle selectedUser) {
// May be called on any thread - only use thread safe operations.
if (!mWallpaperManager.isWallpaperSupported()) {
// When wallpaper is not supported, show the system wallpaper
return LoaderResult.success(null);
}
// Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK
// wallpaper.
final int lockWallpaperUserId =
selectedUser != null ? selectedUser.getIdentifier() : currentUserId;
//这里原先是mWallpaperManager.getWallpaperFile
//后来因为横竖屏锁屏适配不同的壁纸,被我扩展了framework接口,一次性返回2张壁纸
ParcelFileDescriptor[] fdLV = mWallpaperManager.getWallpaperWithFeatureHorizontalAndVertical(
WallpaperManager.FLAG_LOCK, lockWallpaperUserId);
Log.d(TAG, "loadBitmap fdLV: " + fdLV);
Log.d(TAG, "loadBitmap selectedUser: " + selectedUser);
if (fdLV != null && fdLV[0] != null) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.HARDWARE;
Log.d(TAG, "loadBitmap 1111111 ");
Bitmap[] bitmaps = new Bitmap[2];
bitmaps[0] = BitmapFactory.decodeFileDescriptor(fdLV[0].getFileDescriptor(), null, options);
bitmaps[1] = null;
if (fdLV[1] !=null){
Log.d(TAG, "loadBitmap 1111111 222 ");
bitmaps[1] = BitmapFactory.decodeFileDescriptor(fdLV[1].getFileDescriptor(), null, options);
}
return LoaderResult.success(bitmaps);
} catch (OutOfMemoryError e) {
Log.w(TAG, "Can't decode file", e);
return LoaderResult.fail();
} finally {
IoUtils.closeQuietly(fdLV[0]);
IoUtils.closeQuietly(fdLV[1]);
}
} else {
if (selectedUser != null) {
Log.d(TAG, "loadBitmap 222222 ");
// Show the selected user's static wallpaper.
Bitmap[] bitmaps = new Bitmap[2];
bitmaps[0] = mWallpaperManager.getBitmapAsUser(selectedUser.getIdentifier(), true /* hardware */);
bitmaps[1] = null;
return LoaderResult.success(bitmaps);
} else {
Log.d(TAG, "loadBitmap 333333 ");
// When there is no selected user, show the system wallpaper
return LoaderResult.success(null);
}
}
}
3、壁纸横竖屏显示不同壁纸
/**
* Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
*/
public static class WallpaperDrawable extends DrawableWrapper {
...
@Override
protected void onBoundsChange(Rect bounds) {
if (mState.hasLVBitmap()){
//这里是自定义逻辑,当获取到2张壁纸时,判断横竖屏设置不同的壁纸
boolean isVerticalWallpaper = (r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) ? true : false;
Log.i(TAG, "onBoundsChange isVerticalWallpaper: " + isVerticalWallpaper);
if (isVerticalWallpaper){
setDrawable(new BitmapDrawable(r, mState.mBackground[0]));
}else{
setDrawable(new BitmapDrawable(r, mState.mBackground[1]));
}
super.onBoundsChange(bounds);
}else{
//这里是原先的逻辑,走这里会拉伸锁屏壁纸
int vwidth = getBounds().width();
int vheight = getBounds().height();
int dwidth = mState.mBackground[0].getWidth();
int dheight = mState.mBackground[0].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));
Log.i(TAG,"bounds: "+bounds);
Log.d(TAG,"mTmpRect: "+mTmpRect);
super.onBoundsChange(mTmpRect);
}
}
...
}
tips:横竖屏设置不同的壁纸,也可以在onConfigChanged中调用finishUpdateMediaMetaData,设置不同的壁纸。
3、锁屏和主界面设置不同的壁纸,锁屏,旋转,点击锁屏,壁纸发生位移
SystemUI显示壁纸其实也对壁纸进行了放大处理,但是旋转屏幕时,放大没有生效,需要点击一下才会重新走到回调。
tips:如何验证修改成功?
设置2张一样线条,但是颜色不同的壁纸作为主屏幕和锁屏壁纸,看看旋转之后效果是否完全一样,是否由拉伸、位移等情况
锁屏壁纸旋转横竖屏之后,没有被放大,点击锁屏之后界面刷新,才放大1.1倍,显示正确的壁纸位置。
1、为什么会放大?
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java中的private ImageView mBackdropFront其实是
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java传入的值
StatusBar中:
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
...
backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop);
mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
//这里读取config_wallpaperMaxScale配置,并在回调中设置缩放
float maxWallpaperZoom = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_wallpaperMaxScale);
mNotificationShadeDepthControllerLazy.get().addListener(depth -> {
float scale = MathUtils.lerp(maxWallpaperZoom, 1f, depth);
backdrop.setPivotX(backdrop.getWidth() / 2f);
backdrop.setPivotY(backdrop.getHeight() / 2f);
backdrop.setScaleX(scale);
backdrop.setScaleY(scale);
});
...
}
这里涉及到kotlin代码
public static abstract interface DepthListener {
/**
* Current wallpaper zoom out, where 0 is the closest, and 1 the farthest
*/
public abstract void onWallpaperZoomOutChanged(float zoomOut);
}
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt中调用了回调方法。
2、当我打断点的时候,也没找到是哪里调用的kotlin,然后NotificationShadeDepthController.kt又调用到上面的回调,走到Java中。
3、尝试在onConfigChanged时,手动调用上面的backdrop.setScaleY(scale);代码,但是没有效果。
我很困惑,我不理解。view刷新和请求布局都调用过了,也不好使~~~
临时方案:非正解~!
4、尝试模拟点击事件,因为锁屏旋转的时候壁纸位置不对、但是点击之后恢复正常。
跟踪点击事件,尝试在onConfigChanged之后调用点击事件的方法,让壁纸走到正常。
5、点击锁屏,锁屏状态会发生变化,文字也会有变。通过搜索锁屏上的文字,打断点看看哪里调用了修改文字的方法,最终找到锁屏点击事件
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
protected boolean onEmptySpaceClick(float x) {
if (mHintAnimationRunning) {
return true;
}
return onMiddleClicked();
}
所以我在vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java的onConfigChanged调用了它
@Override
public void onConfigChanged(Configuration newConfig) {
updateResources();
updateDisplaySize(); // populates mDisplayMetrics
if (DEBUG) {
Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
}
mViewHierarchyManager.updateRowStates();
mScreenPinningRequest.onConfigurationChanged();
//这里添加代码调用onMiddleClicked方法,因为它是protected,所以自己添加了一个public方法
mNotificationPanelViewController.onMiddleClickedForWallpaper();
}
6、动画效果优化,取消动画事件,只想修复壁纸
//把onMiddleClicked的逻辑扣出来
public void onMiddleClickedForWallpaper(){
// onMiddleClicked();
if (mBarState == StatusBarState.KEYGUARD) {
if (!mDozingOnDown) {
if (mKeyguardBypassController.getBypassEnabled()) {
mUpdateMonitor.requestFaceAuth();
} else {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
mLockscreenGestureLogger
.log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT);
// startUnlockHintAnimation();
//直接启动动画会导致旋转屏幕时,锁屏状态发生变化,通知栏弹一下动画,相当于手动点击的效果,我并不想要这个效果,这里我只想刷新一下壁纸
//startUnlockHintAnimation代码抠出来,把setDuration写入了0
//
if (mHeightAnimator != null || mTracking) {
return;
}
cancelPeek();
notifyExpandingStarted();
//动画方法,因为要把setDuration写入了0,所以自己写了一遍
//startUnlockHintAnimationPhase1
//startUnlockHintAnimationPhase2 2个动画阶段都要改
startUnlockHintAnimationPhase1111(() -> {
notifyExpandingFinished();
onUnlockHintFinished();
mHintAnimationRunning = false;
});
onUnlockHintStarted();
mHintAnimationRunning = true;
}
}
}
}
这样就修复了壁纸位移的问题。
tips:最后,这一段代码加上判断
1、是否有锁屏壁纸
2、判断旋转屏幕
3、是否在锁屏界面
防止其他情况下不必要的执行。