blob: 4597b16568849cc234bb0b1c671fe599e6d0a50a [file] [log] [blame]
Selim Cinekaa9db1f2018-02-27 17:35:47 -08001/*
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
17package com.android.systemui.statusbar;
18
19import android.content.Context;
Selim Cinek332c23f2018-03-16 17:37:50 -070020import android.content.res.Configuration;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080021import android.content.res.Resources;
felkachang3d00f352018-05-22 12:53:50 +080022import android.graphics.Point;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080023import android.graphics.Rect;
felkachange6c03a02018-05-24 15:38:04 +080024import android.os.Bundle;
25import android.os.Parcelable;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080026import android.util.AttributeSet;
felkachang3d00f352018-05-22 12:53:50 +080027import android.view.DisplayCutout;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080028import android.view.View;
29import android.widget.TextView;
30
31import com.android.internal.annotations.VisibleForTesting;
32import com.android.keyguard.AlphaOptimizedLinearLayout;
33import com.android.systemui.R;
Beverly1be62f42018-12-19 17:17:48 -050034import com.android.systemui.plugins.DarkIconDispatcher;
Ned Burnsf81c4c42019-01-07 14:10:43 -050035import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080036
felkachange2cf7c82018-05-25 10:41:17 +080037import java.util.List;
38
Selim Cinekaa9db1f2018-02-27 17:35:47 -080039/**
40 * The view in the statusBar that contains part of the heads-up information
41 */
42public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
felkachange6c03a02018-05-24 15:38:04 +080043 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";
felkachange6c03a02018-05-24 15:38:04 +080046 private static final String VISIBILITY = "visibility";
47 private static final String ALPHA = "alpha";
Selim Cinekaa9db1f2018-02-27 17:35:47 -080048 private int mAbsoluteStartPadding;
49 private int mEndMargin;
50 private View mIconPlaceholder;
51 private TextView mTextView;
Ned Burnsf81c4c42019-01-07 14:10:43 -050052 private NotificationEntry mShowingEntry;
Selim Cinek332c23f2018-03-16 17:37:50 -070053 private Rect mLayoutedIconRect = new Rect();
Selim Cinekaa9db1f2018-02-27 17:35:47 -080054 private int[] mTmpPosition = new int[2];
55 private boolean mFirstLayout = true;
Selim Cinek332c23f2018-03-16 17:37:50 -070056 private int mMaxWidth;
57 private View mRootView;
felkachang3d00f352018-05-22 12:53:50 +080058 private int mSysWinInset;
59 private int mCutOutInset;
felkachange2cf7c82018-05-25 10:41:17 +080060 private List<Rect> mCutOutBounds;
Selim Cinek332c23f2018-03-16 17:37:50 -070061 private Rect mIconDrawingRect = new Rect();
felkachange2cf7c82018-05-25 10:41:17 +080062 private Point mDisplaySize;
Selim Cinek332c23f2018-03-16 17:37:50 -070063 private Runnable mOnDrawingRectChangedListener;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080064
65 public HeadsUpStatusBarView(Context context) {
66 this(context, null);
67 }
68
69 public HeadsUpStatusBarView(Context context, AttributeSet attrs) {
70 this(context, attrs, 0);
71 }
72
73 public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
74 this(context, attrs, defStyleAttr, 0);
75 }
76
77 public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr,
78 int defStyleRes) {
79 super(context, attrs, defStyleAttr, defStyleRes);
80 Resources res = getResources();
81 mAbsoluteStartPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings)
82 + res.getDimensionPixelSize(
83 com.android.internal.R.dimen.notification_content_margin_start);
84 mEndMargin = res.getDimensionPixelSize(
85 com.android.internal.R.dimen.notification_content_margin_end);
86 setPaddingRelative(mAbsoluteStartPadding, 0, mEndMargin, 0);
Selim Cinek332c23f2018-03-16 17:37:50 -070087 updateMaxWidth();
88 }
89
90 private void updateMaxWidth() {
91 int maxWidth = getResources().getDimensionPixelSize(R.dimen.qs_panel_width);
92 if (maxWidth != mMaxWidth) {
93 // maxWidth doesn't work with fill_parent, let's manually make it at most as big as the
94 // notification panel
95 mMaxWidth = maxWidth;
96 requestLayout();
97 }
98 }
99
100 @Override
101 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
102 if (mMaxWidth > 0) {
103 int newSize = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth);
104 widthMeasureSpec = MeasureSpec.makeMeasureSpec(newSize,
105 MeasureSpec.getMode(widthMeasureSpec));
106 }
107 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
108 }
109
110 @Override
111 protected void onConfigurationChanged(Configuration newConfig) {
112 super.onConfigurationChanged(newConfig);
113 updateMaxWidth();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800114 }
115
felkachange6c03a02018-05-24 15:38:04 +0800116 @Override
117 public Bundle onSaveInstanceState() {
118 Bundle bundle = new Bundle();
119 bundle.putParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE,
120 super.onSaveInstanceState());
121 bundle.putBoolean(FIRST_LAYOUT, mFirstLayout);
felkachange6c03a02018-05-24 15:38:04 +0800122 bundle.putInt(VISIBILITY, getVisibility());
123 bundle.putFloat(ALPHA, getAlpha());
124
125 return bundle;
126 }
127
128 @Override
129 public void onRestoreInstanceState(Parcelable state) {
130 if (state == null || !(state instanceof Bundle)) {
131 super.onRestoreInstanceState(state);
132 return;
133 }
134
135 Bundle bundle = (Bundle) state;
136 Parcelable superState = bundle.getParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE);
137 super.onRestoreInstanceState(superState);
138 mFirstLayout = bundle.getBoolean(FIRST_LAYOUT, true);
felkachange6c03a02018-05-24 15:38:04 +0800139 if (bundle.containsKey(VISIBILITY)) {
140 setVisibility(bundle.getInt(VISIBILITY));
141 }
142 if (bundle.containsKey(ALPHA)) {
143 setAlpha(bundle.getFloat(ALPHA));
144 }
145 }
146
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800147 @VisibleForTesting
148 public HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView) {
149 this(context);
150 mIconPlaceholder = iconPlaceholder;
151 mTextView = textView;
152 }
153
154 @Override
155 protected void onFinishInflate() {
156 super.onFinishInflate();
157 mIconPlaceholder = findViewById(R.id.icon_placeholder);
158 mTextView = findViewById(R.id.text);
159 }
160
Ned Burnsf81c4c42019-01-07 14:10:43 -0500161 public void setEntry(NotificationEntry entry) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800162 if (entry != null) {
163 mShowingEntry = entry;
164 CharSequence text = entry.headsUpStatusBarText;
Selim Cinekb2c5dc52019-06-24 15:46:52 -0700165 if (entry.isSensitive()) {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800166 text = entry.headsUpStatusBarTextPublic;
167 }
168 mTextView.setText(text);
Selim Cinekb2c5dc52019-06-24 15:46:52 -0700169 mShowingEntry.setOnSensitiveChangedListener(() -> setEntry(entry));
170 } else if (mShowingEntry != null){
171 mShowingEntry.setOnSensitiveChangedListener(null);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800172 mShowingEntry = null;
173 }
174 }
175
176 @Override
177 protected void onLayout(boolean changed, int l, int t, int r, int b) {
178 super.onLayout(changed, l, t, r, b);
179 mIconPlaceholder.getLocationOnScreen(mTmpPosition);
Selim Cinek332c23f2018-03-16 17:37:50 -0700180 int left = (int) (mTmpPosition[0] - getTranslationX());
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800181 int top = mTmpPosition[1];
182 int right = left + mIconPlaceholder.getWidth();
183 int bottom = top + mIconPlaceholder.getHeight();
Selim Cinek332c23f2018-03-16 17:37:50 -0700184 mLayoutedIconRect.set(left, top, right, bottom);
185 updateDrawingRect();
felkachang3d00f352018-05-22 12:53:50 +0800186 int targetPadding = mAbsoluteStartPadding + mSysWinInset + mCutOutInset;
felkachange2cf7c82018-05-25 10:41:17 +0800187 boolean isRtl = isLayoutRtl();
188 int start = isRtl ? (mDisplaySize.x - right) : left;
189
190 if (start != targetPadding) {
191 if (mCutOutBounds != null) {
192 for (Rect cutOutRect : mCutOutBounds) {
193 int cutOutStart = (isRtl)
194 ? (mDisplaySize.x - cutOutRect.right) : cutOutRect.left;
195 if (start > cutOutStart) {
196 start -= cutOutRect.width();
197 break;
198 }
felkachang3d00f352018-05-22 12:53:50 +0800199 }
felkachang3d00f352018-05-22 12:53:50 +0800200 }
201
202 int newPadding = targetPadding - start + getPaddingStart();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800203 setPaddingRelative(newPadding, 0, mEndMargin, 0);
204 }
205 if (mFirstLayout) {
206 // we need to do the padding calculation in the first frame, so the layout specified
207 // our visibility to be INVISIBLE in the beginning. let's correct that and set it
208 // to GONE.
209 setVisibility(GONE);
210 mFirstLayout = false;
211 }
212 }
213
felkachange2cf7c82018-05-25 10:41:17 +0800214 /** In order to do UI alignment, this view will be notified by
Rohan Shah20790b82018-07-02 17:21:04 -0700215 * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout}.
felkachange2cf7c82018-05-25 10:41:17 +0800216 * After scroller laid out, the scroller will tell this view about scroller's getX()
217 * @param translationX how to translate the horizontal position
218 */
felkachange8a35362018-05-18 20:11:38 +0800219 public void setPanelTranslation(float translationX) {
felkachang7749c9a2018-06-11 15:56:15 +0800220 setTranslationX(translationX);
Selim Cinek332c23f2018-03-16 17:37:50 -0700221 updateDrawingRect();
222 }
223
224 private void updateDrawingRect() {
225 float oldLeft = mIconDrawingRect.left;
226 mIconDrawingRect.set(mLayoutedIconRect);
227 mIconDrawingRect.offset((int) getTranslationX(), 0);
228 if (oldLeft != mIconDrawingRect.left && mOnDrawingRectChangedListener != null) {
229 mOnDrawingRectChangedListener.run();
230 }
231 }
232
233 @Override
234 protected boolean fitSystemWindows(Rect insets) {
felkachang3d00f352018-05-22 12:53:50 +0800235 boolean isRtl = isLayoutRtl();
236 mSysWinInset = isRtl ? insets.right : insets.left;
237 DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
238 mCutOutInset = (displayCutout != null)
239 ? (isRtl ? displayCutout.getSafeInsetRight() : displayCutout.getSafeInsetLeft())
240 : 0;
felkachange2cf7c82018-05-25 10:41:17 +0800241
242 getDisplaySize();
243
244 mCutOutBounds = null;
245 if (displayCutout != null && displayCutout.getSafeInsetRight() == 0
246 && displayCutout.getSafeInsetLeft() == 0) {
247 mCutOutBounds = displayCutout.getBoundingRects();
248 }
249
felkachange8a35362018-05-18 20:11:38 +0800250 // For Double Cut Out mode, the System window navigation bar is at the right
felkachang3d00f352018-05-22 12:53:50 +0800251 // side of the left cut out. In this condition, mSysWinInset include the left cut
252 // out width so we set mCutOutInset to be 0. For RTL, the condition is the same.
253 // The navigation bar is at the left side of the right cut out and include the
254 // right cut out width.
255 if (mSysWinInset != 0) {
256 mCutOutInset = 0;
felkachange8a35362018-05-18 20:11:38 +0800257 }
258
Selim Cinek332c23f2018-03-16 17:37:50 -0700259 return super.fitSystemWindows(insets);
260 }
261
Ned Burnsf81c4c42019-01-07 14:10:43 -0500262 public NotificationEntry getShowingEntry() {
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800263 return mShowingEntry;
264 }
265
266 public Rect getIconDrawingRect() {
Selim Cinek332c23f2018-03-16 17:37:50 -0700267 return mIconDrawingRect;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800268 }
269
270 public void onDarkChanged(Rect area, float darkIntensity, int tint) {
271 mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
272 }
273
Selim Cinek332c23f2018-03-16 17:37:50 -0700274 public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
275 mOnDrawingRectChangedListener = onDrawingRectChangedListener;
276 }
felkachange2cf7c82018-05-25 10:41:17 +0800277
278 private void getDisplaySize() {
279 if (mDisplaySize == null) {
280 mDisplaySize = new Point();
281 }
282 getDisplay().getRealSize(mDisplaySize);
283 }
284
285 @Override
286 protected void onAttachedToWindow() {
287 super.onAttachedToWindow();
288 getDisplaySize();
289 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800290}