• 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.core.graphics.ColorUtils;
30 
31 import com.android.launcher3.BaseActivity;
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 
60     @Override
hasOverlappingRendering()61     public boolean hasOverlappingRendering() {
62         return false;
63     }
64 
65     @Override
onSetAlpha(int alpha)66     protected boolean onSetAlpha(int alpha) {
67         updateSysUiColors();
68         dispatchVisibilityListenersIfNeeded();
69         return super.onSetAlpha(alpha);
70     }
71 
72     @Override
setBackgroundColor(int color)73     public void setBackgroundColor(int color) {
74         mBackgroundColor = color;
75         updateSysUiColors();
76         dispatchVisibilityListenersIfNeeded();
77         super.setBackgroundColor(color);
78     }
79 
getBackgroundColor()80     public int getBackgroundColor() {
81         return mBackgroundColor;
82     }
83 
84     @Override
onVisibilityAggregated(boolean isVisible)85     public void onVisibilityAggregated(boolean isVisible) {
86         super.onVisibilityAggregated(isVisible);
87         mIsVisible = isVisible;
88         dispatchVisibilityListenersIfNeeded();
89     }
90 
isFullyOpaque()91     public boolean isFullyOpaque() {
92         return mIsVisible && getAlpha() == 1 && Color.alpha(mBackgroundColor) == 255;
93     }
94 
95     @Override
onDraw(Canvas canvas)96     protected void onDraw(Canvas canvas) {
97         super.onDraw(canvas);
98         if (mDrawingController != null) {
99             mDrawingController.drawOnScrimWithScale(canvas, mHeaderScale);
100         }
101     }
102 
103     /** Set scrim header's scale and bottom offset. */
setScrimHeaderScale(float scale)104     public void setScrimHeaderScale(float scale) {
105         boolean hasChanged = mHeaderScale != scale;
106         mHeaderScale = scale;
107         if (hasChanged) {
108             invalidate();
109         }
110     }
111 
112     @Override
onVisibilityChanged(View changedView, int visibility)113     protected void onVisibilityChanged(View changedView, int visibility) {
114         super.onVisibilityChanged(changedView, visibility);
115         updateSysUiColors();
116     }
117 
updateSysUiColors()118     private void updateSysUiColors() {
119         // Use a light system UI (dark icons) if all apps is behind at least half of the
120         // status bar.
121         final float threshold = STATUS_BAR_COLOR_FORCE_UPDATE_THRESHOLD;
122         boolean forceChange = getVisibility() == VISIBLE
123                 && getAlpha() > threshold
124                 && (Color.alpha(mBackgroundColor) / 255f) > threshold;
125         if (forceChange) {
126             getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !isScrimDark());
127         } else {
128             getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
129         }
130     }
131 
dispatchVisibilityListenersIfNeeded()132     private void dispatchVisibilityListenersIfNeeded() {
133         boolean fullyOpaque = isFullyOpaque();
134         if (mLastDispatchedOpaqueness == fullyOpaque) {
135             return;
136         }
137         mLastDispatchedOpaqueness = fullyOpaque;
138         for (int i = 0; i < mOpaquenessListeners.size(); i++) {
139             mOpaquenessListeners.get(i).run();
140         }
141     }
142 
getSystemUiController()143     private SystemUiController getSystemUiController() {
144         if (mSystemUiController == null) {
145             mSystemUiController = BaseActivity.fromContext(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          * Called inside ScrimView#OnDraw
192          */
drawOnScrimWithScale(Canvas canvas, float scale)193         void drawOnScrimWithScale(Canvas canvas, float scale);
194     }
195 }
196