Files
Lawnchair/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
T
Tony Wickham 81feb2c547 Don't draw bottom scrim in fully gestural mode
The scrim exists primarily to protect nav bar buttons which
don't exist in this mode.

Change-Id: Ic6de7d54f27f135eecb116ac09714c91c05b88da
2020-03-20 18:37:26 -07:00

298 lines
11 KiB
Java

/*
* 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.Region;
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 androidx.core.graphics.ColorUtils;
import com.android.launcher3.CellLayout;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.Themes;
/**
* View scrim which draws behind hotseat and workspace
*/
public class WorkspaceAndHotseatScrim extends Scrim {
public static final FloatProperty<WorkspaceAndHotseatScrim> SYSUI_PROGRESS =
new FloatProperty<WorkspaceAndHotseatScrim>("sysUiProgress") {
@Override
public Float get(WorkspaceAndHotseatScrim scrim) {
return scrim.mSysUiProgress;
}
@Override
public void setValue(WorkspaceAndHotseatScrim scrim, float value) {
scrim.setSysUiProgress(value);
}
};
private static final FloatProperty<WorkspaceAndHotseatScrim> SYSUI_ANIM_MULTIPLIER =
new FloatProperty<WorkspaceAndHotseatScrim>("sysUiAnimMultiplier") {
@Override
public Float get(WorkspaceAndHotseatScrim scrim) {
return scrim.mSysUiAnimMultiplier;
}
@Override
public void setValue(WorkspaceAndHotseatScrim scrim, float value) {
scrim.mSysUiAnimMultiplier = value;
scrim.reapplySysUiAlpha();
}
};
/**
* Receiver used to get a signal that the user unlocked their device.
* @see KEYGUARD_ANIMATION For proper signal.
*/
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 DARK_SCRIM_COLOR = 0x55000000;
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 final Rect mHighlightRect = new Rect();
private Workspace mWorkspace;
private boolean mDrawTopScrim, mDrawBottomScrim;
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 Drawable mTopScrim;
private float mSysUiProgress = 1;
private boolean mHideSysUiScrim;
private boolean mAnimateScrimOnNextDraw = false;
private float mSysUiAnimMultiplier = 1;
public WorkspaceAndHotseatScrim(View view) {
super(view);
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;
onExtractedColorsChanged(mWallpaperColorInfo);
}
public void setWorkspace(Workspace workspace) {
mWorkspace = workspace;
}
public void draw(Canvas canvas) {
// Draw the background below children.
if (mScrimAlpha > 0) {
// Update the scroll position first to ensure scrim cutout is in the right place.
mWorkspace.computeScrollWithoutInvalidation();
CellLayout currCellLayout = mWorkspace.getCurrentDragOverlappingLayout();
canvas.save();
if (currCellLayout != null && currCellLayout != mLauncher.getHotseat()) {
// Cut a hole in the darkening scrim on the page that should be highlighted, if any.
mLauncher.getDragLayer()
.getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
}
super.draw(canvas);
canvas.restore();
}
if (!mHideSysUiScrim) {
if (mSysUiProgress <= 0) {
mAnimateScrimOnNextDraw = false;
return;
}
if (mAnimateScrimOnNextDraw) {
mSysUiAnimMultiplier = 0;
reapplySysUiAlphaNoInvalidate();
ObjectAnimator oa = createSysuiMultiplierAnim(1);
oa.setDuration(600);
oa.setStartDelay(mLauncher.getWindow().getTransitionBackgroundFadeDuration());
oa.start();
mAnimateScrimOnNextDraw = false;
}
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, boolean allowSysuiScrims) {
mDrawTopScrim = allowSysuiScrims
&& mTopScrim != null
&& insets.top > 0;
mDrawBottomScrim = allowSysuiScrims
&& mBottomMask != null
&& !mLauncher.getDeviceProfile().isVerticalBarLayout()
&& hasBottomNavButtons();
}
private boolean hasBottomNavButtons() {
if (Utilities.ATLEAST_Q && mLauncher.getRootView() != null
&& mLauncher.getRootView().getRootWindowInsets() != null) {
WindowInsets windowInsets = mLauncher.getRootView().getRootWindowInsets();
return windowInsets.getTappableElementInsets().bottom > 0;
}
return true;
}
@Override
public void onViewAttachedToWindow(View view) {
super.onViewAttachedToWindow(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) {
super.onViewDetachedFromWindow(view);
if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
mRoot.getContext().unregisterReceiver(mReceiver);
}
}
@Override
public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
// for super light wallpaper it needs to be darken for contrast to workspace
// for dark wallpapers the text is white so darkening works as well
mBottomMaskPaint.setColor(ColorUtils.compositeColors(DARK_SCRIM_COLOR,
wallpaperColorInfo.getMainColor()));
reapplySysUiAlpha();
super.onExtractedColorsChanged(wallpaperColorInfo);
}
public void setSize(int w, int h) {
if (mTopScrim != null) {
mTopScrim.setBounds(0, 0, w, h);
mFinalMaskRect.set(0, h - mMaskHeight, w, h);
}
}
private void setSysUiProgress(float progress) {
if (progress != mSysUiProgress) {
mSysUiProgress = progress;
reapplySysUiAlpha();
}
}
private void reapplySysUiAlpha() {
reapplySysUiAlphaNoInvalidate();
if (!mHideSysUiScrim) {
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));
}
}
private Bitmap createDitheredAlphaMask() {
DisplayMetrics dm = mLauncher.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;
}
}