/* * Copyright (C) 2018 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.launcher3.graphics; import static android.content.Intent.ACTION_SCREEN_OFF; import static android.content.Intent.ACTION_USER_PRESENT; import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; import android.animation.ObjectAnimator; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; import android.util.FloatProperty; import android.view.View; import android.view.WindowInsets; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.DynamicResource; import com.android.launcher3.util.Themes; import com.android.systemui.plugins.ResourceProvider; /** * View scrim which draws behind hotseat and workspace */ public class SysUiScrim implements View.OnAttachStateChangeListener { public static final FloatProperty SYSUI_PROGRESS = new FloatProperty("sysUiProgress") { @Override public Float get(SysUiScrim scrim) { return scrim.mSysUiProgress; } @Override public void setValue(SysUiScrim scrim, float value) { scrim.setSysUiProgress(value); } }; private static final FloatProperty SYSUI_ANIM_MULTIPLIER = new FloatProperty("sysUiAnimMultiplier") { @Override public Float get(SysUiScrim scrim) { return scrim.mSysUiAnimMultiplier; } @Override public void setValue(SysUiScrim scrim, float value) { scrim.mSysUiAnimMultiplier = value; scrim.reapplySysUiAlpha(); } }; /** * Receiver used to get a signal that the user unlocked their device. */ private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (ACTION_SCREEN_OFF.equals(action)) { mAnimateScrimOnNextDraw = true; } else if (ACTION_USER_PRESENT.equals(action)) { // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where // the user unlocked and the Launcher is not in the foreground. mAnimateScrimOnNextDraw = false; } } }; private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100; private static final int ALPHA_MASK_HEIGHT_DP = 500; private static final int ALPHA_MASK_BITMAP_DP = 200; private static final int ALPHA_MASK_WIDTH_DP = 2; private boolean mDrawTopScrim, mDrawBottomScrim, mDrawWallpaperScrim; private final RectF mWallpaperScrimRect = new RectF(); private final Paint mWallpaperScrimPaint = new Paint(); private final RectF mFinalMaskRect = new RectF(); private final Paint mBottomMaskPaint = new Paint(Paint.FILTER_BITMAP_FLAG); private final Bitmap mBottomMask; private final int mMaskHeight; private final View mRoot; private final BaseDraggingActivity mActivity; private final Drawable mTopScrim; private float mSysUiProgress = 1; private boolean mHideSysUiScrim; private boolean mAnimateScrimOnNextDraw = false; private float mSysUiAnimMultiplier = 1; private int mWallpaperScrimMaxAlpha; public SysUiScrim(View view) { mRoot = view; mActivity = BaseDraggingActivity.fromContext(view.getContext()); mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP, view.getResources().getDisplayMetrics()); mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim); mBottomMask = mTopScrim == null ? null : createDitheredAlphaMask(); mHideSysUiScrim = mTopScrim == null; mDrawWallpaperScrim = FeatureFlags.ENABLE_WALLPAPER_SCRIM.get() && !Themes.getAttrBoolean(view.getContext(), R.attr.isMainColorDark) && !Themes.getAttrBoolean(view.getContext(), R.attr.isWorkspaceDarkText); ResourceProvider rp = DynamicResource.provider(view.getContext()); int wallpaperScrimColor = rp.getColor(R.color.wallpaper_scrim_color); mWallpaperScrimMaxAlpha = Color.alpha(wallpaperScrimColor); mWallpaperScrimPaint.setColor(wallpaperScrimColor); view.addOnAttachStateChangeListener(this); } /** * Draw the top and bottom scrims */ public void draw(Canvas canvas) { if (!mHideSysUiScrim) { if (mSysUiProgress <= 0) { mAnimateScrimOnNextDraw = false; return; } if (mAnimateScrimOnNextDraw) { mSysUiAnimMultiplier = 0; reapplySysUiAlphaNoInvalidate(); ObjectAnimator oa = createSysuiMultiplierAnim(1); oa.setDuration(600); oa.setStartDelay(mActivity.getWindow().getTransitionBackgroundFadeDuration()); oa.start(); mAnimateScrimOnNextDraw = false; } if (mDrawWallpaperScrim) { canvas.drawRect(mWallpaperScrimRect, mWallpaperScrimPaint); } if (mDrawTopScrim) { mTopScrim.draw(canvas); } if (mDrawBottomScrim) { canvas.drawBitmap(mBottomMask, null, mFinalMaskRect, mBottomMaskPaint); } } } /** * @return an ObjectAnimator that controls the fade in/out of the sys ui scrim. */ public ObjectAnimator createSysuiMultiplierAnim(float... values) { ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, values); anim.setAutoCancel(true); return anim; } /** * Determines whether to draw the top and/or bottom scrim based on new insets. */ public void onInsetsChanged(Rect insets) { mDrawTopScrim = mTopScrim != null && insets.top > 0; mDrawBottomScrim = mBottomMask != null && !mActivity.getDeviceProfile().isVerticalBarLayout() && hasBottomNavButtons(); } private boolean hasBottomNavButtons() { if (Utilities.ATLEAST_Q && mActivity.getRootView() != null && mActivity.getRootView().getRootWindowInsets() != null) { WindowInsets windowInsets = mActivity.getRootView().getRootWindowInsets(); return windowInsets.getTappableElementInsets().bottom > 0; } return true; } @Override public void onViewAttachedToWindow(View view) { if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) { IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF); filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone mRoot.getContext().registerReceiver(mReceiver, filter); } } @Override public void onViewDetachedFromWindow(View view) { if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) { mRoot.getContext().unregisterReceiver(mReceiver); } } /** * Set the width and height of the view being scrimmed * @param w * @param h */ public void setSize(int w, int h) { if (mTopScrim != null) { mTopScrim.setBounds(0, 0, w, h); mFinalMaskRect.set(0, h - mMaskHeight, w, h); } mWallpaperScrimRect.set(0, 0, w, h); } private void setSysUiProgress(float progress) { if (progress != mSysUiProgress) { mSysUiProgress = progress; reapplySysUiAlpha(); } } private void reapplySysUiAlpha() { reapplySysUiAlphaNoInvalidate(); if (!mHideSysUiScrim) { mRoot.invalidate(); } } private void reapplySysUiAlphaNoInvalidate() { float factor = mSysUiProgress * mSysUiAnimMultiplier; mBottomMaskPaint.setAlpha(Math.round(MAX_HOTSEAT_SCRIM_ALPHA * factor)); if (mTopScrim != null) { mTopScrim.setAlpha(Math.round(255 * factor)); } mWallpaperScrimPaint.setAlpha(Math.round(mWallpaperScrimMaxAlpha * factor)); } private Bitmap createDitheredAlphaMask() { DisplayMetrics dm = mActivity.getResources().getDisplayMetrics(); int width = ResourceUtils.pxFromDp(ALPHA_MASK_WIDTH_DP, dm); int gradientHeight = ResourceUtils.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm); Bitmap dst = Bitmap.createBitmap(width, mMaskHeight, Bitmap.Config.ALPHA_8); Canvas c = new Canvas(dst); Paint paint = new Paint(Paint.DITHER_FLAG); LinearGradient lg = new LinearGradient(0, 0, 0, gradientHeight, new int[]{ 0x00FFFFFF, setColorAlphaBound(Color.WHITE, (int) (0xFF * 0.95)), 0xFFFFFFFF}, new float[]{0f, 0.8f, 1f}, Shader.TileMode.CLAMP); paint.setShader(lg); c.drawRect(0, 0, width, gradientHeight, paint); return dst; } }