• 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 
17 package com.android.systemui.statusbar;
18 
19 import android.content.Context;
20 import android.content.res.Configuration;
21 import android.content.res.Resources;
22 import android.graphics.Point;
23 import android.graphics.Rect;
24 import android.os.Bundle;
25 import android.os.Parcelable;
26 import android.util.AttributeSet;
27 import android.view.DisplayCutout;
28 import android.view.View;
29 import android.widget.TextView;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.keyguard.AlphaOptimizedLinearLayout;
33 import com.android.systemui.R;
34 import com.android.systemui.plugins.DarkIconDispatcher;
35 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
36 
37 import java.util.List;
38 
39 /**
40  * The view in the statusBar that contains part of the heads-up information
41  */
42 public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
43     private static final String HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE =
44             "heads_up_status_bar_view_super_parcelable";
45     private static final String FIRST_LAYOUT = "first_layout";
46     private static final String PUBLIC_MODE = "public_mode";
47     private static final String VISIBILITY = "visibility";
48     private static final String ALPHA = "alpha";
49     private int mAbsoluteStartPadding;
50     private int mEndMargin;
51     private View mIconPlaceholder;
52     private TextView mTextView;
53     private NotificationEntry mShowingEntry;
54     private Rect mLayoutedIconRect = new Rect();
55     private int[] mTmpPosition = new int[2];
56     private boolean mFirstLayout = true;
57     private boolean mPublicMode;
58     private int mMaxWidth;
59     private View mRootView;
60     private int mSysWinInset;
61     private int mCutOutInset;
62     private List<Rect> mCutOutBounds;
63     private Rect mIconDrawingRect = new Rect();
64     private Point mDisplaySize;
65     private Runnable mOnDrawingRectChangedListener;
66 
HeadsUpStatusBarView(Context context)67     public HeadsUpStatusBarView(Context context) {
68         this(context, null);
69     }
70 
HeadsUpStatusBarView(Context context, AttributeSet attrs)71     public HeadsUpStatusBarView(Context context, AttributeSet attrs) {
72         this(context, attrs, 0);
73     }
74 
HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr)75     public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
76         this(context, attrs, defStyleAttr, 0);
77     }
78 
HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)79     public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr,
80             int defStyleRes) {
81         super(context, attrs, defStyleAttr, defStyleRes);
82         Resources res = getResources();
83         mAbsoluteStartPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings)
84             + res.getDimensionPixelSize(
85                     com.android.internal.R.dimen.notification_content_margin_start);
86         mEndMargin = res.getDimensionPixelSize(
87                 com.android.internal.R.dimen.notification_content_margin_end);
88         setPaddingRelative(mAbsoluteStartPadding, 0, mEndMargin, 0);
89         updateMaxWidth();
90     }
91 
updateMaxWidth()92     private void updateMaxWidth() {
93         int maxWidth = getResources().getDimensionPixelSize(R.dimen.qs_panel_width);
94         if (maxWidth != mMaxWidth) {
95             // maxWidth doesn't work with fill_parent, let's manually make it at most as big as the
96             // notification panel
97             mMaxWidth = maxWidth;
98             requestLayout();
99         }
100     }
101 
102     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)103     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
104         if (mMaxWidth > 0) {
105             int newSize = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth);
106             widthMeasureSpec = MeasureSpec.makeMeasureSpec(newSize,
107                     MeasureSpec.getMode(widthMeasureSpec));
108         }
109         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
110     }
111 
112     @Override
onConfigurationChanged(Configuration newConfig)113     protected void onConfigurationChanged(Configuration newConfig) {
114         super.onConfigurationChanged(newConfig);
115         updateMaxWidth();
116     }
117 
118     @Override
onSaveInstanceState()119     public Bundle onSaveInstanceState() {
120         Bundle bundle = new Bundle();
121         bundle.putParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE,
122                 super.onSaveInstanceState());
123         bundle.putBoolean(FIRST_LAYOUT, mFirstLayout);
124         bundle.putBoolean(PUBLIC_MODE, mPublicMode);
125         bundle.putInt(VISIBILITY, getVisibility());
126         bundle.putFloat(ALPHA, getAlpha());
127 
128         return bundle;
129     }
130 
131     @Override
onRestoreInstanceState(Parcelable state)132     public void onRestoreInstanceState(Parcelable state) {
133         if (state == null || !(state instanceof Bundle)) {
134             super.onRestoreInstanceState(state);
135             return;
136         }
137 
138         Bundle bundle = (Bundle) state;
139         Parcelable superState = bundle.getParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE);
140         super.onRestoreInstanceState(superState);
141         mFirstLayout = bundle.getBoolean(FIRST_LAYOUT, true);
142         mPublicMode = bundle.getBoolean(PUBLIC_MODE, false);
143         if (bundle.containsKey(VISIBILITY)) {
144             setVisibility(bundle.getInt(VISIBILITY));
145         }
146         if (bundle.containsKey(ALPHA)) {
147             setAlpha(bundle.getFloat(ALPHA));
148         }
149     }
150 
151     @VisibleForTesting
HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView)152     public HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView) {
153         this(context);
154         mIconPlaceholder = iconPlaceholder;
155         mTextView = textView;
156     }
157 
158     @Override
onFinishInflate()159     protected void onFinishInflate() {
160         super.onFinishInflate();
161         mIconPlaceholder = findViewById(R.id.icon_placeholder);
162         mTextView = findViewById(R.id.text);
163     }
164 
setEntry(NotificationEntry entry)165     public void setEntry(NotificationEntry entry) {
166         if (entry != null) {
167             mShowingEntry = entry;
168             CharSequence text = entry.headsUpStatusBarText;
169             if (mPublicMode) {
170                 text = entry.headsUpStatusBarTextPublic;
171             }
172             mTextView.setText(text);
173         } else {
174             mShowingEntry = null;
175         }
176     }
177 
178     @Override
onLayout(boolean changed, int l, int t, int r, int b)179     protected void onLayout(boolean changed, int l, int t, int r, int b) {
180         super.onLayout(changed, l, t, r, b);
181         mIconPlaceholder.getLocationOnScreen(mTmpPosition);
182         int left = (int) (mTmpPosition[0] - getTranslationX());
183         int top = mTmpPosition[1];
184         int right = left + mIconPlaceholder.getWidth();
185         int bottom = top + mIconPlaceholder.getHeight();
186         mLayoutedIconRect.set(left, top, right, bottom);
187         updateDrawingRect();
188         int targetPadding = mAbsoluteStartPadding + mSysWinInset + mCutOutInset;
189         boolean isRtl = isLayoutRtl();
190         int start = isRtl ? (mDisplaySize.x - right) : left;
191 
192         if (start != targetPadding) {
193             if (mCutOutBounds != null) {
194                 for (Rect cutOutRect : mCutOutBounds) {
195                     int cutOutStart = (isRtl)
196                             ? (mDisplaySize.x - cutOutRect.right) : cutOutRect.left;
197                     if (start > cutOutStart) {
198                         start -= cutOutRect.width();
199                         break;
200                     }
201                 }
202             }
203 
204             int newPadding = targetPadding - start + getPaddingStart();
205             setPaddingRelative(newPadding, 0, mEndMargin, 0);
206         }
207         if (mFirstLayout) {
208             // we need to do the padding calculation in the first frame, so the layout specified
209             // our visibility to be INVISIBLE in the beginning. let's correct that and set it
210             // to GONE.
211             setVisibility(GONE);
212             mFirstLayout = false;
213         }
214     }
215 
216     /** In order to do UI alignment, this view will be notified by
217      * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout}.
218      * After scroller laid out, the scroller will tell this view about scroller's getX()
219      * @param translationX how to translate the horizontal position
220      */
setPanelTranslation(float translationX)221     public void setPanelTranslation(float translationX) {
222         setTranslationX(translationX);
223         updateDrawingRect();
224     }
225 
updateDrawingRect()226     private void updateDrawingRect() {
227         float oldLeft = mIconDrawingRect.left;
228         mIconDrawingRect.set(mLayoutedIconRect);
229         mIconDrawingRect.offset((int) getTranslationX(), 0);
230         if (oldLeft != mIconDrawingRect.left && mOnDrawingRectChangedListener != null) {
231             mOnDrawingRectChangedListener.run();
232         }
233     }
234 
235     @Override
fitSystemWindows(Rect insets)236     protected boolean fitSystemWindows(Rect insets) {
237         boolean isRtl = isLayoutRtl();
238         mSysWinInset = isRtl ? insets.right : insets.left;
239         DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
240         mCutOutInset = (displayCutout != null)
241                 ? (isRtl ? displayCutout.getSafeInsetRight() : displayCutout.getSafeInsetLeft())
242                 : 0;
243 
244         getDisplaySize();
245 
246         mCutOutBounds = null;
247         if (displayCutout != null && displayCutout.getSafeInsetRight() == 0
248                 && displayCutout.getSafeInsetLeft() == 0) {
249             mCutOutBounds = displayCutout.getBoundingRects();
250         }
251 
252         // For Double Cut Out mode, the System window navigation bar is at the right
253         // side of the left cut out. In this condition, mSysWinInset include the left cut
254         // out width so we set mCutOutInset to be 0. For RTL, the condition is the same.
255         // The navigation bar is at the left side of the right cut out and include the
256         // right cut out width.
257         if (mSysWinInset != 0) {
258             mCutOutInset = 0;
259         }
260 
261         return super.fitSystemWindows(insets);
262     }
263 
getShowingEntry()264     public NotificationEntry getShowingEntry() {
265         return mShowingEntry;
266     }
267 
getIconDrawingRect()268     public Rect getIconDrawingRect() {
269         return mIconDrawingRect;
270     }
271 
onDarkChanged(Rect area, float darkIntensity, int tint)272     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
273         mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
274     }
275 
setPublicMode(boolean publicMode)276     public void setPublicMode(boolean publicMode) {
277         mPublicMode = publicMode;
278     }
279 
setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener)280     public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
281         mOnDrawingRectChangedListener = onDrawingRectChangedListener;
282     }
283 
getDisplaySize()284     private void getDisplaySize() {
285         if (mDisplaySize == null) {
286             mDisplaySize = new Point();
287         }
288         getDisplay().getRealSize(mDisplaySize);
289     }
290 
291     @Override
onAttachedToWindow()292     protected void onAttachedToWindow() {
293         super.onAttachedToWindow();
294         getDisplaySize();
295     }
296 }
297