blob: 39485c3c88003bf165ee687987a24f44dfc94e34 [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;
24import android.util.AttributeSet;
felkachange2cf7c82018-05-25 10:41:17 +080025import android.view.Display;
felkachang3d00f352018-05-22 12:53:50 +080026import android.view.DisplayCutout;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080027import android.view.View;
28import android.widget.TextView;
29
30import com.android.internal.annotations.VisibleForTesting;
31import com.android.keyguard.AlphaOptimizedLinearLayout;
32import com.android.systemui.R;
33import com.android.systemui.statusbar.policy.DarkIconDispatcher;
34
felkachange2cf7c82018-05-25 10:41:17 +080035import java.util.List;
36
Selim Cinekaa9db1f2018-02-27 17:35:47 -080037/**
38 * The view in the statusBar that contains part of the heads-up information
39 */
40public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
41 private int mAbsoluteStartPadding;
42 private int mEndMargin;
43 private View mIconPlaceholder;
44 private TextView mTextView;
45 private NotificationData.Entry mShowingEntry;
Selim Cinek332c23f2018-03-16 17:37:50 -070046 private Rect mLayoutedIconRect = new Rect();
Selim Cinekaa9db1f2018-02-27 17:35:47 -080047 private int[] mTmpPosition = new int[2];
48 private boolean mFirstLayout = true;
49 private boolean mPublicMode;
Selim Cinek332c23f2018-03-16 17:37:50 -070050 private int mMaxWidth;
51 private View mRootView;
felkachang3d00f352018-05-22 12:53:50 +080052 private int mSysWinInset;
53 private int mCutOutInset;
felkachange2cf7c82018-05-25 10:41:17 +080054 private List<Rect> mCutOutBounds;
Selim Cinek332c23f2018-03-16 17:37:50 -070055 private Rect mIconDrawingRect = new Rect();
felkachange2cf7c82018-05-25 10:41:17 +080056 private Point mDisplaySize;
Selim Cinek332c23f2018-03-16 17:37:50 -070057 private Runnable mOnDrawingRectChangedListener;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080058
59 public HeadsUpStatusBarView(Context context) {
60 this(context, null);
61 }
62
63 public HeadsUpStatusBarView(Context context, AttributeSet attrs) {
64 this(context, attrs, 0);
65 }
66
67 public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
68 this(context, attrs, defStyleAttr, 0);
69 }
70
71 public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr,
72 int defStyleRes) {
73 super(context, attrs, defStyleAttr, defStyleRes);
74 Resources res = getResources();
75 mAbsoluteStartPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings)
76 + res.getDimensionPixelSize(
77 com.android.internal.R.dimen.notification_content_margin_start);
78 mEndMargin = res.getDimensionPixelSize(
79 com.android.internal.R.dimen.notification_content_margin_end);
80 setPaddingRelative(mAbsoluteStartPadding, 0, mEndMargin, 0);
Selim Cinek332c23f2018-03-16 17:37:50 -070081 updateMaxWidth();
82 }
83
84 private void updateMaxWidth() {
85 int maxWidth = getResources().getDimensionPixelSize(R.dimen.qs_panel_width);
86 if (maxWidth != mMaxWidth) {
87 // maxWidth doesn't work with fill_parent, let's manually make it at most as big as the
88 // notification panel
89 mMaxWidth = maxWidth;
90 requestLayout();
91 }
92 }
93
94 @Override
95 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
96 if (mMaxWidth > 0) {
97 int newSize = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth);
98 widthMeasureSpec = MeasureSpec.makeMeasureSpec(newSize,
99 MeasureSpec.getMode(widthMeasureSpec));
100 }
101 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
102 }
103
104 @Override
105 protected void onConfigurationChanged(Configuration newConfig) {
106 super.onConfigurationChanged(newConfig);
107 updateMaxWidth();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800108 }
109
110 @VisibleForTesting
111 public HeadsUpStatusBarView(Context context, View iconPlaceholder, TextView textView) {
112 this(context);
113 mIconPlaceholder = iconPlaceholder;
114 mTextView = textView;
115 }
116
117 @Override
118 protected void onFinishInflate() {
119 super.onFinishInflate();
120 mIconPlaceholder = findViewById(R.id.icon_placeholder);
121 mTextView = findViewById(R.id.text);
122 }
123
124 public void setEntry(NotificationData.Entry entry) {
125 if (entry != null) {
126 mShowingEntry = entry;
127 CharSequence text = entry.headsUpStatusBarText;
128 if (mPublicMode) {
129 text = entry.headsUpStatusBarTextPublic;
130 }
131 mTextView.setText(text);
132 } else {
133 mShowingEntry = null;
134 }
135 }
136
137 @Override
138 protected void onLayout(boolean changed, int l, int t, int r, int b) {
139 super.onLayout(changed, l, t, r, b);
140 mIconPlaceholder.getLocationOnScreen(mTmpPosition);
Selim Cinek332c23f2018-03-16 17:37:50 -0700141 int left = (int) (mTmpPosition[0] - getTranslationX());
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800142 int top = mTmpPosition[1];
143 int right = left + mIconPlaceholder.getWidth();
144 int bottom = top + mIconPlaceholder.getHeight();
Selim Cinek332c23f2018-03-16 17:37:50 -0700145 mLayoutedIconRect.set(left, top, right, bottom);
146 updateDrawingRect();
felkachang3d00f352018-05-22 12:53:50 +0800147 int targetPadding = mAbsoluteStartPadding + mSysWinInset + mCutOutInset;
felkachange2cf7c82018-05-25 10:41:17 +0800148 boolean isRtl = isLayoutRtl();
149 int start = isRtl ? (mDisplaySize.x - right) : left;
150
151 if (start != targetPadding) {
152 if (mCutOutBounds != null) {
153 for (Rect cutOutRect : mCutOutBounds) {
154 int cutOutStart = (isRtl)
155 ? (mDisplaySize.x - cutOutRect.right) : cutOutRect.left;
156 if (start > cutOutStart) {
157 start -= cutOutRect.width();
158 break;
159 }
felkachang3d00f352018-05-22 12:53:50 +0800160 }
felkachang3d00f352018-05-22 12:53:50 +0800161 }
162
163 int newPadding = targetPadding - start + getPaddingStart();
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800164 setPaddingRelative(newPadding, 0, mEndMargin, 0);
165 }
166 if (mFirstLayout) {
167 // we need to do the padding calculation in the first frame, so the layout specified
168 // our visibility to be INVISIBLE in the beginning. let's correct that and set it
169 // to GONE.
170 setVisibility(GONE);
171 mFirstLayout = false;
172 }
173 }
174
felkachange2cf7c82018-05-25 10:41:17 +0800175 /** In order to do UI alignment, this view will be notified by
176 * {@link com.android.systemui.statusbar.stack.NotificationStackScrollLayout}.
177 * After scroller laid out, the scroller will tell this view about scroller's getX()
178 * @param translationX how to translate the horizontal position
179 */
felkachange8a35362018-05-18 20:11:38 +0800180 public void setPanelTranslation(float translationX) {
felkachang3d00f352018-05-22 12:53:50 +0800181 if (isLayoutRtl()) {
182 setTranslationX(translationX + mCutOutInset);
183 } else {
184 setTranslationX(translationX - mCutOutInset);
185 }
Selim Cinek332c23f2018-03-16 17:37:50 -0700186 updateDrawingRect();
187 }
188
189 private void updateDrawingRect() {
190 float oldLeft = mIconDrawingRect.left;
191 mIconDrawingRect.set(mLayoutedIconRect);
192 mIconDrawingRect.offset((int) getTranslationX(), 0);
193 if (oldLeft != mIconDrawingRect.left && mOnDrawingRectChangedListener != null) {
194 mOnDrawingRectChangedListener.run();
195 }
196 }
197
198 @Override
199 protected boolean fitSystemWindows(Rect insets) {
felkachang3d00f352018-05-22 12:53:50 +0800200 boolean isRtl = isLayoutRtl();
201 mSysWinInset = isRtl ? insets.right : insets.left;
202 DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
203 mCutOutInset = (displayCutout != null)
204 ? (isRtl ? displayCutout.getSafeInsetRight() : displayCutout.getSafeInsetLeft())
205 : 0;
felkachange2cf7c82018-05-25 10:41:17 +0800206
207 getDisplaySize();
208
209 mCutOutBounds = null;
210 if (displayCutout != null && displayCutout.getSafeInsetRight() == 0
211 && displayCutout.getSafeInsetLeft() == 0) {
212 mCutOutBounds = displayCutout.getBoundingRects();
213 }
214
felkachange8a35362018-05-18 20:11:38 +0800215 // For Double Cut Out mode, the System window navigation bar is at the right
felkachang3d00f352018-05-22 12:53:50 +0800216 // side of the left cut out. In this condition, mSysWinInset include the left cut
217 // out width so we set mCutOutInset to be 0. For RTL, the condition is the same.
218 // The navigation bar is at the left side of the right cut out and include the
219 // right cut out width.
220 if (mSysWinInset != 0) {
221 mCutOutInset = 0;
felkachange8a35362018-05-18 20:11:38 +0800222 }
223
Selim Cinek332c23f2018-03-16 17:37:50 -0700224 return super.fitSystemWindows(insets);
225 }
226
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800227 public NotificationData.Entry getShowingEntry() {
228 return mShowingEntry;
229 }
230
231 public Rect getIconDrawingRect() {
Selim Cinek332c23f2018-03-16 17:37:50 -0700232 return mIconDrawingRect;
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800233 }
234
235 public void onDarkChanged(Rect area, float darkIntensity, int tint) {
236 mTextView.setTextColor(DarkIconDispatcher.getTint(area, this, tint));
237 }
238
239 public void setPublicMode(boolean publicMode) {
240 mPublicMode = publicMode;
241 }
Selim Cinek332c23f2018-03-16 17:37:50 -0700242
243 public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
244 mOnDrawingRectChangedListener = onDrawingRectChangedListener;
245 }
felkachange2cf7c82018-05-25 10:41:17 +0800246
247 private void getDisplaySize() {
248 if (mDisplaySize == null) {
249 mDisplaySize = new Point();
250 }
251 getDisplay().getRealSize(mDisplaySize);
252 }
253
254 @Override
255 protected void onAttachedToWindow() {
256 super.onAttachedToWindow();
257 getDisplaySize();
258 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800259}