blob: ac289d7d6262d6e8e181649aaed28097f1094a9b [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;
felkachang08579552018-05-24 15:38:04 +080024import android.os.Bundle;
25import android.os.Parcelable;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080026import android.util.AttributeSet;
felkachange2cf7c82018-05-25 10:41:17 +080027import android.view.Display;
felkachang3d00f352018-05-22 12:53:50 +080028import android.view.DisplayCutout;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080029import android.view.View;
30import android.widget.TextView;
31
32import com.android.internal.annotations.VisibleForTesting;
33import com.android.keyguard.AlphaOptimizedLinearLayout;
34import com.android.systemui.R;
35import com.android.systemui.statusbar.policy.DarkIconDispatcher;
36
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 {
felkachang08579552018-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";
46 private static final String PUBLIC_MODE = "public_mode";
47 private static final String VISIBILITY = "visibility";
48 private static final String ALPHA = "alpha";
Selim Cinekaa9db1f2018-02-27 17:35:47 -080049 private int mAbsoluteStartPadding;
50 private int mEndMargin;
51 private View mIconPlaceholder;
52 private TextView mTextView;
53 private NotificationData.Entry mShowingEntry;
Selim Cinek332c23f2018-03-16 17:37:50 -070054 private Rect mLayoutedIconRect = new Rect();
Selim Cinekaa9db1f2018-02-27 17:35:47 -080055 private int[] mTmpPosition = new int[2];
56 private boolean mFirstLayout = true;
57 private boolean mPublicMode;
Selim Cinek332c23f2018-03-16 17:37:50 -070058 private int mMaxWidth;
59 private View mRootView;
felkachang3d00f352018-05-22 12:53:50 +080060 private int mSysWinInset;
61 private int mCutOutInset;
felkachange2cf7c82018-05-25 10:41:17 +080062 private List<Rect> mCutOutBounds;
Selim Cinek332c23f2018-03-16 17:37:50 -070063 private Rect mIconDrawingRect = new Rect();
felkachange2cf7c82018-05-25 10:41:17 +080064 private Point mDisplaySize;
Selim Cinek332c23f2018-03-16 17:37:50 -070065 private Runnable mOnDrawingRectChangedListener;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080066
67 public HeadsUpStatusBarView(Context context) {
68 this(context, null);
69 }
70
71 public HeadsUpStatusBarView(Context context, AttributeSet attrs) {
72 this(context, attrs, 0);
73 }
74
75 public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
76 this(context, attrs, defStyleAttr, 0);
77 }
78
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);
Selim Cinek332c23f2018-03-16 17:37:50 -070089 updateMaxWidth();
90 }
91
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
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
113 protected void onConfigurationChanged(Configuration newConfig) {
114 super.onConfigurationChanged(newConfig);
115 updateMaxWidth();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800116 }
117
felkachang08579552018-05-24 15:38:04 +0800118 @Override
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
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
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800151 @VisibleForTesting
152 public HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView) {
153 this(context);
154 mIconPlaceholder = iconPlaceholder;
155 mTextView = textView;
156 }
157
158 @Override
159 protected void onFinishInflate() {
160 super.onFinishInflate();
161 mIconPlaceholder = findViewById(R.id.icon_placeholder);
162 mTextView = findViewById(R.id.text);
163 }
164
165 public void setEntry(NotificationData.Entry 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
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);
Selim Cinek332c23f2018-03-16 17:37:50 -0700182 int left = (int) (mTmpPosition[0] - getTranslationX());
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800183 int top = mTmpPosition[1];
184 int right = left + mIconPlaceholder.getWidth();
185 int bottom = top + mIconPlaceholder.getHeight();
Selim Cinek332c23f2018-03-16 17:37:50 -0700186 mLayoutedIconRect.set(left, top, right, bottom);
187 updateDrawingRect();
felkachang3d00f352018-05-22 12:53:50 +0800188 int targetPadding = mAbsoluteStartPadding + mSysWinInset + mCutOutInset;
felkachange2cf7c82018-05-25 10:41:17 +0800189 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 }
felkachang3d00f352018-05-22 12:53:50 +0800201 }
felkachang3d00f352018-05-22 12:53:50 +0800202 }
203
204 int newPadding = targetPadding - start + getPaddingStart();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800205 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
felkachange2cf7c82018-05-25 10:41:17 +0800216 /** In order to do UI alignment, this view will be notified by
217 * {@link com.android.systemui.statusbar.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 */
felkachange8a35362018-05-18 20:11:38 +0800221 public void setPanelTranslation(float translationX) {
felkachang7749c9a2018-06-11 15:56:15 +0800222 setTranslationX(translationX);
Selim Cinek332c23f2018-03-16 17:37:50 -0700223 updateDrawingRect();
224 }
225
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
236 protected boolean fitSystemWindows(Rect insets) {
felkachang3d00f352018-05-22 12:53:50 +0800237 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;
felkachange2cf7c82018-05-25 10:41:17 +0800243
244 getDisplaySize();
245
246 mCutOutBounds = null;
247 if (displayCutout != null && displayCutout.getSafeInsetRight() == 0
248 && displayCutout.getSafeInsetLeft() == 0) {
249 mCutOutBounds = displayCutout.getBoundingRects();
250 }
251
felkachange8a35362018-05-18 20:11:38 +0800252 // For Double Cut Out mode, the System window navigation bar is at the right
felkachang3d00f352018-05-22 12:53:50 +0800253 // 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;
felkachange8a35362018-05-18 20:11:38 +0800259 }
260
Selim Cinek332c23f2018-03-16 17:37:50 -0700261 return super.fitSystemWindows(insets);
262 }
263
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800264 public NotificationData.Entry getShowingEntry() {
265 return mShowingEntry;
266 }
267
268 public Rect getIconDrawingRect() {
Selim Cinek332c23f2018-03-16 17:37:50 -0700269 return mIconDrawingRect;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800270 }
271
272 public void onDarkChanged(Rect area, float darkIntensity, int tint) {
273 mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
274 }
275
276 public void setPublicMode(boolean publicMode) {
277 mPublicMode = publicMode;
278 }
Selim Cinek332c23f2018-03-16 17:37:50 -0700279
280 public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
281 mOnDrawingRectChangedListener = onDrawingRectChangedListener;
282 }
felkachange2cf7c82018-05-25 10:41:17 +0800283
284 private void getDisplaySize() {
285 if (mDisplaySize == null) {
286 mDisplaySize = new Point();
287 }
288 getDisplay().getRealSize(mDisplaySize);
289 }
290
291 @Override
292 protected void onAttachedToWindow() {
293 super.onAttachedToWindow();
294 getDisplaySize();
295 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800296}