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、是否在锁屏界面
防止其他情况下不必要的执行。