1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.graphics; 18 19 import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION; 20 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; 21 22 import android.animation.ObjectAnimator; 23 import android.graphics.Bitmap; 24 import android.graphics.Canvas; 25 import android.graphics.Color; 26 import android.graphics.LinearGradient; 27 import android.graphics.Paint; 28 import android.graphics.Rect; 29 import android.graphics.RectF; 30 import android.graphics.Shader; 31 import android.graphics.drawable.Drawable; 32 import android.util.DisplayMetrics; 33 import android.util.FloatProperty; 34 import android.view.View; 35 36 import com.android.launcher3.BaseDraggingActivity; 37 import com.android.launcher3.DeviceProfile; 38 import com.android.launcher3.R; 39 import com.android.launcher3.config.FeatureFlags; 40 import com.android.launcher3.testing.shared.ResourceUtils; 41 import com.android.launcher3.util.DynamicResource; 42 import com.android.launcher3.util.ScreenOnTracker; 43 import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener; 44 import com.android.launcher3.util.Themes; 45 import com.android.systemui.plugins.ResourceProvider; 46 47 /** 48 * View scrim which draws behind hotseat and workspace 49 */ 50 public class SysUiScrim implements View.OnAttachStateChangeListener { 51 52 public static final FloatProperty<SysUiScrim> SYSUI_PROGRESS = 53 new FloatProperty<SysUiScrim>("sysUiProgress") { 54 @Override 55 public Float get(SysUiScrim scrim) { 56 return scrim.mSysUiProgress; 57 } 58 59 @Override 60 public void setValue(SysUiScrim scrim, float value) { 61 scrim.setSysUiProgress(value); 62 } 63 }; 64 65 private static final FloatProperty<SysUiScrim> SYSUI_ANIM_MULTIPLIER = 66 new FloatProperty<SysUiScrim>("sysUiAnimMultiplier") { 67 @Override 68 public Float get(SysUiScrim scrim) { 69 return scrim.mSysUiAnimMultiplier; 70 } 71 72 @Override 73 public void setValue(SysUiScrim scrim, float value) { 74 scrim.mSysUiAnimMultiplier = value; 75 scrim.reapplySysUiAlpha(); 76 } 77 }; 78 79 /** 80 * Receiver used to get a signal that the user unlocked their device. 81 */ 82 private final ScreenOnListener mScreenOnListener = new ScreenOnListener() { 83 @Override 84 public void onScreenOnChanged(boolean isOn) { 85 if (!isOn) { 86 mAnimateScrimOnNextDraw = true; 87 } 88 } 89 90 @Override 91 public void onUserPresent() { 92 // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where 93 // the user unlocked and the Launcher is not in the foreground. 94 mAnimateScrimOnNextDraw = false; 95 } 96 }; 97 98 private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100; 99 private static final int ALPHA_MASK_HEIGHT_DP = 500; 100 private static final int ALPHA_MASK_BITMAP_DP = 200; 101 private static final int ALPHA_MASK_WIDTH_DP = 2; 102 103 private boolean mDrawTopScrim, mDrawBottomScrim, mDrawWallpaperScrim; 104 105 private final RectF mWallpaperScrimRect = new RectF(); 106 private final Paint mWallpaperScrimPaint = new Paint(); 107 private final RectF mFinalMaskRect = new RectF(); 108 private final Paint mBottomMaskPaint = new Paint(Paint.FILTER_BITMAP_FLAG); 109 private final Bitmap mBottomMask; 110 private final int mMaskHeight; 111 112 private final View mRoot; 113 private final BaseDraggingActivity mActivity; 114 private final Drawable mTopScrim; 115 116 private float mSysUiProgress = 1; 117 private boolean mHideSysUiScrim; 118 119 private boolean mAnimateScrimOnNextDraw = false; 120 private float mSysUiAnimMultiplier = 1; 121 private int mWallpaperScrimMaxAlpha; 122 SysUiScrim(View view)123 public SysUiScrim(View view) { 124 mRoot = view; 125 mActivity = BaseDraggingActivity.fromContext(view.getContext()); 126 mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP, 127 view.getResources().getDisplayMetrics()); 128 mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim); 129 if (mTopScrim != null) { 130 mTopScrim.setDither(true); 131 mBottomMask = createDitheredAlphaMask(); 132 mHideSysUiScrim = false; 133 } else { 134 mBottomMask = null; 135 mHideSysUiScrim = true; 136 } 137 138 mDrawWallpaperScrim = FeatureFlags.ENABLE_WALLPAPER_SCRIM.get() 139 && !Themes.getAttrBoolean(view.getContext(), R.attr.isMainColorDark) 140 && !Themes.getAttrBoolean(view.getContext(), R.attr.isWorkspaceDarkText); 141 ResourceProvider rp = DynamicResource.provider(view.getContext()); 142 int wallpaperScrimColor = rp.getColor(R.color.wallpaper_scrim_color); 143 mWallpaperScrimMaxAlpha = Color.alpha(wallpaperScrimColor); 144 mWallpaperScrimPaint.setColor(wallpaperScrimColor); 145 146 view.addOnAttachStateChangeListener(this); 147 } 148 149 /** 150 * Draw the top and bottom scrims 151 */ draw(Canvas canvas)152 public void draw(Canvas canvas) { 153 if (!mHideSysUiScrim) { 154 if (mSysUiProgress <= 0) { 155 mAnimateScrimOnNextDraw = false; 156 return; 157 } 158 159 if (mAnimateScrimOnNextDraw) { 160 mSysUiAnimMultiplier = 0; 161 reapplySysUiAlphaNoInvalidate(); 162 163 ObjectAnimator oa = createSysuiMultiplierAnim(1); 164 oa.setDuration(600); 165 oa.setStartDelay(mActivity.getWindow().getTransitionBackgroundFadeDuration()); 166 oa.start(); 167 mAnimateScrimOnNextDraw = false; 168 } 169 170 if (mDrawWallpaperScrim) { 171 canvas.drawRect(mWallpaperScrimRect, mWallpaperScrimPaint); 172 } 173 if (mDrawTopScrim) { 174 mTopScrim.draw(canvas); 175 } 176 if (mDrawBottomScrim) { 177 canvas.drawBitmap(mBottomMask, null, mFinalMaskRect, mBottomMaskPaint); 178 } 179 } 180 } 181 182 /** 183 * @return an ObjectAnimator that controls the fade in/out of the sys ui scrim. 184 */ createSysuiMultiplierAnim(float... values)185 public ObjectAnimator createSysuiMultiplierAnim(float... values) { 186 ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, values); 187 anim.setAutoCancel(true); 188 return anim; 189 } 190 191 /** 192 * Determines whether to draw the top and/or bottom scrim based on new insets. 193 * 194 * In order for the bottom scrim to be drawn this 3 condition should be meet at the same time: 195 * the device is in 3 button navigation, the taskbar is not present and the Hotseat is 196 * horizontal 197 */ onInsetsChanged(Rect insets)198 public void onInsetsChanged(Rect insets) { 199 DeviceProfile dp = mActivity.getDeviceProfile(); 200 mDrawTopScrim = mTopScrim != null && insets.top > 0; 201 mDrawBottomScrim = mBottomMask != null 202 && !dp.isVerticalBarLayout() 203 && !dp.isGestureMode 204 && !dp.isTaskbarPresent; 205 } 206 207 @Override onViewAttachedToWindow(View view)208 public void onViewAttachedToWindow(View view) { 209 if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) { 210 ScreenOnTracker.INSTANCE.get(mActivity).addListener(mScreenOnListener); 211 } 212 } 213 214 @Override onViewDetachedFromWindow(View view)215 public void onViewDetachedFromWindow(View view) { 216 if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) { 217 ScreenOnTracker.INSTANCE.get(mActivity).removeListener(mScreenOnListener); 218 } 219 } 220 221 /** 222 * Set the width and height of the view being scrimmed 223 * @param w 224 * @param h 225 */ setSize(int w, int h)226 public void setSize(int w, int h) { 227 if (mTopScrim != null) { 228 mTopScrim.setBounds(0, 0, w, h); 229 mFinalMaskRect.set(0, h - mMaskHeight, w, h); 230 } 231 mWallpaperScrimRect.set(0, 0, w, h); 232 } 233 setSysUiProgress(float progress)234 private void setSysUiProgress(float progress) { 235 if (progress != mSysUiProgress) { 236 mSysUiProgress = progress; 237 reapplySysUiAlpha(); 238 } 239 } 240 reapplySysUiAlpha()241 private void reapplySysUiAlpha() { 242 reapplySysUiAlphaNoInvalidate(); 243 if (!mHideSysUiScrim) { 244 mRoot.invalidate(); 245 } 246 } 247 reapplySysUiAlphaNoInvalidate()248 private void reapplySysUiAlphaNoInvalidate() { 249 float factor = mSysUiProgress * mSysUiAnimMultiplier; 250 mBottomMaskPaint.setAlpha(Math.round(MAX_HOTSEAT_SCRIM_ALPHA * factor)); 251 if (mTopScrim != null) { 252 mTopScrim.setAlpha(Math.round(255 * factor)); 253 } 254 mWallpaperScrimPaint.setAlpha(Math.round(mWallpaperScrimMaxAlpha * factor)); 255 } 256 createDitheredAlphaMask()257 private Bitmap createDitheredAlphaMask() { 258 DisplayMetrics dm = mActivity.getResources().getDisplayMetrics(); 259 int width = ResourceUtils.pxFromDp(ALPHA_MASK_WIDTH_DP, dm); 260 int gradientHeight = ResourceUtils.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm); 261 Bitmap dst = Bitmap.createBitmap(width, mMaskHeight, Bitmap.Config.ALPHA_8); 262 Canvas c = new Canvas(dst); 263 Paint paint = new Paint(Paint.DITHER_FLAG); 264 LinearGradient lg = new LinearGradient(0, 0, 0, gradientHeight, 265 new int[]{ 266 0x00FFFFFF, 267 setColorAlphaBound(Color.WHITE, (int) (0xFF * 0.95)), 268 0xFFFFFFFF}, 269 new float[]{0f, 0.8f, 1f}, 270 Shader.TileMode.CLAMP); 271 paint.setShader(lg); 272 c.drawRect(0, 0, width, gradientHeight, paint); 273 return dst; 274 } 275 } 276