• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.launcher3.views;
17 
18 import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
19 
20 import android.content.Context;
21 import android.graphics.Canvas;
22 import android.graphics.Color;
23 import android.graphics.Rect;
24 import android.graphics.drawable.ColorDrawable;
25 import android.util.AttributeSet;
26 import android.view.View;
27 
28 import androidx.annotation.NonNull;
29 import androidx.annotation.Px;
30 import androidx.core.graphics.ColorUtils;
31 
32 import com.android.launcher3.Insettable;
33 import com.android.launcher3.util.SystemUiController;
34 
35 import java.util.ArrayList;
36 
37 /**
38  * Simple scrim which draws a flat color
39  */
40 public class ScrimView extends View implements Insettable {
41     private static final float STATUS_BAR_COLOR_FORCE_UPDATE_THRESHOLD = 0.9f;
42 
43     private final ArrayList<Runnable> mOpaquenessListeners = new ArrayList<>(1);
44     private SystemUiController mSystemUiController;
45     private ScrimDrawingController mDrawingController;
46     private int mBackgroundColor;
47     private boolean mIsVisible = true;
48     private boolean mLastDispatchedOpaqueness;
49     private float mHeaderScale = 1f;
50 
ScrimView(Context context, AttributeSet attrs)51     public ScrimView(Context context, AttributeSet attrs) {
52         super(context, attrs);
53         setFocusable(false);
54     }
55 
56     @Override
setInsets(Rect insets)57     public void setInsets(Rect insets) {}
58 
59     @Override
hasOverlappingRendering()60     public boolean hasOverlappingRendering() {
61         return false;
62     }
63 
64     @Override
onSetAlpha(int alpha)65     protected boolean onSetAlpha(int alpha) {
66         updateSysUiColors();
67         dispatchVisibilityListenersIfNeeded();
68         return super.onSetAlpha(alpha);
69     }
70 
71     @Override
setBackgroundColor(int color)72     public void setBackgroundColor(int color) {
73         mBackgroundColor = color;
74         updateSysUiColors();
75         dispatchVisibilityListenersIfNeeded();
76         super.setBackgroundColor(color);
77     }
78 
getBackgroundColor()79     public int getBackgroundColor() {
80         return mBackgroundColor;
81     }
82 
83     @Override
onVisibilityAggregated(boolean isVisible)84     public void onVisibilityAggregated(boolean isVisible) {
85         super.onVisibilityAggregated(isVisible);
86         mIsVisible = isVisible;
87         dispatchVisibilityListenersIfNeeded();
88     }
89 
isFullyOpaque()90     public boolean isFullyOpaque() {
91         return mIsVisible && getAlpha() == 1 && Color.alpha(mBackgroundColor) == 255;
92     }
93 
94     @Override
onDraw(Canvas canvas)95     protected void onDraw(Canvas canvas) {
96         super.onDraw(canvas);
97         if (mDrawingController != null) {
98             mDrawingController.drawOnScrimWithScale(canvas, mHeaderScale);
99         }
100     }
101 
102     /** Set scrim header's scale and bottom offset. */
setScrimHeaderScale(float scale)103     public void setScrimHeaderScale(float scale) {
104         boolean hasChanged = mHeaderScale != scale;
105         mHeaderScale = scale;
106         if (hasChanged) {
107             invalidate();
108         }
109     }
110 
111     @Override
onVisibilityChanged(View changedView, int visibility)112     protected void onVisibilityChanged(View changedView, int visibility) {
113         super.onVisibilityChanged(changedView, visibility);
114         updateSysUiColors();
115     }
116 
updateSysUiColors()117     private void updateSysUiColors() {
118         // Use a light system UI (dark icons) if all apps is behind at least half of the
119         // status bar.
120         final float threshold = STATUS_BAR_COLOR_FORCE_UPDATE_THRESHOLD;
121         boolean forceChange = getVisibility() == VISIBLE
122                 && getAlpha() > threshold
123                 && (Color.alpha(mBackgroundColor) / 255f) > threshold;
124         if (forceChange) {
125             getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !isScrimDark());
126         } else {
127             getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
128         }
129     }
130 
dispatchVisibilityListenersIfNeeded()131     private void dispatchVisibilityListenersIfNeeded() {
132         boolean fullyOpaque = isFullyOpaque();
133         if (mLastDispatchedOpaqueness == fullyOpaque) {
134             return;
135         }
136         mLastDispatchedOpaqueness = fullyOpaque;
137         for (int i = 0; i < mOpaquenessListeners.size(); i++) {
138             mOpaquenessListeners.get(i).run();
139         }
140     }
141 
getSystemUiController()142     private SystemUiController getSystemUiController() {
143         if (mSystemUiController == null) {
144             mSystemUiController =
145                     ActivityContext.lookupContext(getContext()).getSystemUiController();
146         }
147         return mSystemUiController;
148     }
149 
isScrimDark()150     private boolean isScrimDark() {
151         if (!(getBackground() instanceof ColorDrawable)) {
152             throw new IllegalStateException(
153                     "ScrimView must have a ColorDrawable background, this one has: "
154                             + getBackground());
155         }
156         return ColorUtils.calculateLuminance(
157                 ((ColorDrawable) getBackground()).getColor()) < 0.5f;
158     }
159 
160     /**
161      * Sets drawing controller. Invalidates ScrimView if drawerController has changed.
162      */
setDrawingController(ScrimDrawingController drawingController)163     public void setDrawingController(ScrimDrawingController drawingController) {
164         if (mDrawingController != drawingController) {
165             mDrawingController = drawingController;
166             invalidate();
167         }
168     }
169 
170     /**
171      * Registers a listener to be notified of whether the scrim is occluding other UI elements.
172      * @see #isFullyOpaque()
173      */
addOpaquenessListener(@onNull Runnable listener)174     public void addOpaquenessListener(@NonNull Runnable listener) {
175         mOpaquenessListeners.add(listener);
176     }
177 
178     /**
179      * Removes previously registered listener.
180      * @see #addOpaquenessListener(Runnable)
181      */
removeOpaquenessListener(@onNull Runnable listener)182     public void removeOpaquenessListener(@NonNull Runnable listener) {
183         mOpaquenessListeners.remove(listener);
184     }
185 
186     /**
187      * A Utility interface allowing for other surfaces to draw on ScrimView
188      */
189     public interface ScrimDrawingController {
190 
191         /** Draw scrim view on canvas with scale. */
drawOnScrimWithScale(Canvas canvas, float scale)192         default void drawOnScrimWithScale(Canvas canvas, float scale) {
193             drawOnScrimWithScaleAndBottomOffset(canvas, scale, 0);
194         }
195 
196         /** Draw scrim view on canvas with bottomOffset. */
drawOnScrimWithBottomOffset(Canvas canvas, @Px int bottomOffsetPx)197         default void drawOnScrimWithBottomOffset(Canvas canvas, @Px int bottomOffsetPx) {
198             drawOnScrimWithScaleAndBottomOffset(canvas, 1f, bottomOffsetPx);
199         }
200 
201         /** Draw scrim view on canvas with scale and bottomOffset. */
drawOnScrimWithScaleAndBottomOffset( Canvas canvas, float scale, @Px int bottomOffsetPx)202         void drawOnScrimWithScaleAndBottomOffset(
203                 Canvas canvas, float scale, @Px int bottomOffsetPx);
204     }
205 }
206