blob: 5477f882e5f9032193b66635452a37065438b7d5 [file] [log] [blame]
Kenny Root15a4d2f2010-03-11 18:20:12 -08001/*
2 * Copyright (C) 2008 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
Joe Onoratofd52b182010-11-10 18:00:52 -080017package com.android.systemui.statusbar.phone;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Evan Laird058c8ae2018-01-12 14:26:10 -050019import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
20
Adrian Roos6a4fa0e2018-03-05 19:50:16 +010021import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
22
Evan Laird058c8ae2018-01-12 14:26:10 -050023import android.annotation.Nullable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
Evan Laird058c8ae2018-01-12 14:26:10 -050025import android.content.res.Configuration;
Adrian Roos13836052018-03-15 21:06:37 +010026import android.graphics.Point;
Evan Laird058c8ae2018-01-12 14:26:10 -050027import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.util.AttributeSet;
Chris Wren64161cc2012-12-17 16:49:30 -050029import android.util.EventLog;
Adrian Roos13836052018-03-15 21:06:37 +010030import android.util.Pair;
31import android.view.Display;
Evan Laird058c8ae2018-01-12 14:26:10 -050032import android.view.DisplayCutout;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +010033import android.view.Gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.view.MotionEvent;
35import android.view.View;
Selim Cinek3e7592d2016-04-11 09:35:54 +080036import android.view.ViewGroup;
Adrian Roosedfab3b2018-03-08 18:39:20 +010037import android.view.WindowInsets;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070038import android.view.accessibility.AccessibilityEvent;
Evan Laird058c8ae2018-01-12 14:26:10 -050039import android.widget.FrameLayout;
40import android.widget.LinearLayout;
Adrian Roos13836052018-03-15 21:06:37 +010041
Jason Monkaa573e92017-01-27 17:00:29 -050042import com.android.systemui.Dependency;
Chris Wren64161cc2012-12-17 16:49:30 -050043import com.android.systemui.EventLogTags;
Selim Cinek3e7592d2016-04-11 09:35:54 +080044import com.android.systemui.R;
Jason Monkaa573e92017-01-27 17:00:29 -050045import com.android.systemui.statusbar.policy.DarkIconDispatcher;
46import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
Adrian Roosedfab3b2018-03-08 18:39:20 +010047
48import java.util.Objects;
Joe Onorato79de0c52010-05-26 17:03:26 -040049
Daniel Sandler08d05e32012-08-08 16:39:54 -040050public class PhoneStatusBarView extends PanelBar {
Joe Onoratofd52b182010-11-10 18:00:52 -080051 private static final String TAG = "PhoneStatusBarView";
Jason Monk2a6ea9c2017-01-26 11:14:51 -050052 private static final boolean DEBUG = StatusBar.DEBUG;
Christoph Studer934025e2014-11-25 12:53:55 +010053 private static final boolean DEBUG_GESTURES = false;
Evan Laird058c8ae2018-01-12 14:26:10 -050054 private static final int NO_VALUE = Integer.MIN_VALUE;
Daniel Sandler198a0302012-08-17 16:04:31 -040055
Jason Monk2a6ea9c2017-01-26 11:14:51 -050056 StatusBar mBar;
Daniel Sandler5a8aefa2012-09-25 01:21:12 -040057
Xiaohui Chen9f967112016-01-07 14:14:06 -080058 boolean mIsFullyOpenedPanel = false;
John Spurlock7edfbca2013-09-14 11:58:55 -040059 private final PhoneStatusBarTransitions mBarTransitions;
Jorim Jaggiecc798e2014-05-26 18:14:37 +020060 private ScrimController mScrimController;
Selim Cinek3d395c62015-06-16 19:37:37 -070061 private float mMinFraction;
62 private float mPanelFraction;
Selim Cinek80c2abe2015-06-17 15:37:30 -070063 private Runnable mHideExpandedRunnable = new Runnable() {
64 @Override
65 public void run() {
Selim Cinek529c5322016-04-06 20:03:45 -070066 if (mPanelFraction == 0.0f) {
67 mBar.makeExpandedInvisible();
68 }
Selim Cinek80c2abe2015-06-17 15:37:30 -070069 }
70 };
Jason Monkaa573e92017-01-27 17:00:29 -050071 private DarkReceiver mBattery;
Evan Laird058c8ae2018-01-12 14:26:10 -050072 private int mLastOrientation;
Evan Lairdcda685d2018-01-22 18:25:17 -050073 @Nullable
Evan Laird058c8ae2018-01-12 14:26:10 -050074 private View mCutoutSpace;
75 @Nullable
76 private DisplayCutout mDisplayCutout;
Evan Laird17a96ba2018-05-23 18:21:56 -040077 /**
78 * Draw this many pixels into the left/right side of the cutout to optimally use the space
79 */
80 private int mCutoutSideNudge = 0;
Joe Onorato119a4012010-06-30 14:49:51 -040081
Joe Onoratofd52b182010-11-10 18:00:52 -080082 public PhoneStatusBarView(Context context, AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 super(context, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
John Spurlock7edfbca2013-09-14 11:58:55 -040085 mBarTransitions = new PhoneStatusBarTransitions(this);
John Spurlocke932e302013-08-12 10:16:29 -040086 }
87
88 public BarTransitions getBarTransitions() {
89 return mBarTransitions;
Daniel Sandler1c1edaa2012-08-14 11:14:45 -040090 }
91
Jason Monk2a6ea9c2017-01-26 11:14:51 -050092 public void setBar(StatusBar bar) {
Daniel Sandlerefb0faf2012-10-10 14:15:34 -070093 mBar = bar;
94 }
95
Jorim Jaggiecc798e2014-05-26 18:14:37 +020096 public void setScrimController(ScrimController scrimController) {
97 mScrimController = scrimController;
98 }
99
Daniel Sandlerefb0faf2012-10-10 14:15:34 -0700100 @Override
Jorim Jaggiacea1002014-05-10 01:30:10 +0200101 public void onFinishInflate() {
John Spurlocke6f0a712013-09-03 16:23:49 -0400102 mBarTransitions.init();
Alan Viverette51efddb2017-04-05 10:00:01 -0400103 mBattery = findViewById(R.id.battery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500104 mCutoutSpace = findViewById(R.id.cutout_space_view);
Evan Laird17a96ba2018-05-23 18:21:56 -0400105
106 updateResources();
Jason Monkaa573e92017-01-27 17:00:29 -0500107 }
108
109 @Override
110 protected void onAttachedToWindow() {
111 super.onAttachedToWindow();
112 // Always have Battery meters in the status bar observe the dark/light modes.
113 Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500114 if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
Adrian Roosedfab3b2018-03-08 18:39:20 +0100115 updateLayoutForCutout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500116 }
Jason Monkaa573e92017-01-27 17:00:29 -0500117 }
118
119 @Override
120 protected void onDetachedFromWindow() {
121 super.onDetachedFromWindow();
122 Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500123 mDisplayCutout = null;
124 }
125
126 @Override
127 protected void onConfigurationChanged(Configuration newConfig) {
128 super.onConfigurationChanged(newConfig);
129
130 // May trigger cutout space layout-ing
131 if (updateOrientationAndCutout(newConfig.orientation)) {
Adrian Roosedfab3b2018-03-08 18:39:20 +0100132 updateLayoutForCutout();
133 requestLayout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500134 }
135 }
136
Adrian Roosedfab3b2018-03-08 18:39:20 +0100137 @Override
138 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
139 if (updateOrientationAndCutout(mLastOrientation)) {
140 updateLayoutForCutout();
141 requestLayout();
142 }
143 return super.onApplyWindowInsets(insets);
144 }
145
Evan Laird058c8ae2018-01-12 14:26:10 -0500146 /**
147 *
148 * @param newOrientation may pass NO_VALUE for no change
149 * @return boolean indicating if we need to update the cutout location / margins
150 */
151 private boolean updateOrientationAndCutout(int newOrientation) {
152 boolean changed = false;
153 if (newOrientation != NO_VALUE) {
154 if (mLastOrientation != newOrientation) {
155 changed = true;
156 mLastOrientation = newOrientation;
157 }
158 }
159
Adrian Roosedfab3b2018-03-08 18:39:20 +0100160 if (!Objects.equals(getRootWindowInsets().getDisplayCutout(), mDisplayCutout)) {
161 changed = true;
162 mDisplayCutout = getRootWindowInsets().getDisplayCutout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500163 }
164
165 return changed;
Daniel Sandlerefb0faf2012-10-10 14:15:34 -0700166 }
167
Daniel Sandler1c1edaa2012-08-14 11:14:45 -0400168 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800169 public boolean panelEnabled() {
John Spurlock97642182013-07-29 17:58:39 -0400170 return mBar.panelsEnabled();
Daniel Sandler1e8feef2012-08-16 11:37:41 -0400171 }
172
173 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800174 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
Alan Viverette5b046752015-01-08 11:13:06 -0800175 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700176 // The status bar is very small so augment the view that the user is touching
177 // with the content of the status bar a whole. This way an accessibility service
178 // may announce the current item as well as the entire content if appropriate.
179 AccessibilityEvent record = AccessibilityEvent.obtain();
180 onInitializeAccessibilityEvent(record);
181 dispatchPopulateAccessibilityEvent(record);
182 event.appendRecord(record);
183 return true;
184 }
185 return false;
186 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400187
188 @Override
189 public void onPanelPeeked() {
190 super.onPanelPeeked();
Jorim Jaggifa505a72014-04-28 20:04:11 +0200191 mBar.makeExpandedVisible(false);
Daniel Sandler67eab792012-10-02 17:08:23 -0400192 }
193
194 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800195 public void onPanelCollapsed() {
196 super.onPanelCollapsed();
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200197 // Close the status bar in the next frame so we can show the end of the animation.
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200198 post(mHideExpandedRunnable);
Xiaohui Chen9f967112016-01-07 14:14:06 -0800199 mIsFullyOpenedPanel = false;
Daniel Sandler8e72c9e2012-08-15 00:09:26 -0400200 }
201
Selim Cinek80c2abe2015-06-17 15:37:30 -0700202 public void removePendingHideExpandedRunnables() {
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200203 removeCallbacks(mHideExpandedRunnable);
Selim Cinek80c2abe2015-06-17 15:37:30 -0700204 }
205
Daniel Sandler8e72c9e2012-08-15 00:09:26 -0400206 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800207 public void onPanelFullyOpened() {
208 super.onPanelFullyOpened();
209 if (!mIsFullyOpenedPanel) {
210 mPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700211 }
Xiaohui Chen9f967112016-01-07 14:14:06 -0800212 mIsFullyOpenedPanel = true;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400213 }
214
215 @Override
216 public boolean onTouchEvent(MotionEvent event) {
Chris Wren64161cc2012-12-17 16:49:30 -0500217 boolean barConsumedEvent = mBar.interceptTouchEvent(event);
218
219 if (DEBUG_GESTURES) {
220 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
221 EventLog.writeEvent(EventLogTags.SYSUI_PANELBAR_TOUCH,
222 event.getActionMasked(), (int) event.getX(), (int) event.getY(),
223 barConsumedEvent ? 1 : 0);
224 }
225 }
226
227 return barConsumedEvent || super.onTouchEvent(event);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400228 }
229
230 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800231 public void onTrackingStarted() {
232 super.onTrackingStarted();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200233 mBar.onTrackingStarted();
Jorim Jaggiecc798e2014-05-26 18:14:37 +0200234 mScrimController.onTrackingStarted();
Selim Cinek529c5322016-04-06 20:03:45 -0700235 removePendingHideExpandedRunnables();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200236 }
237
238 @Override
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200239 public void onClosingFinished() {
240 super.onClosingFinished();
241 mBar.onClosingFinished();
242 }
243
244 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800245 public void onTrackingStopped(boolean expand) {
246 super.onTrackingStopped(expand);
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200247 mBar.onTrackingStopped(expand);
248 }
249
250 @Override
251 public void onExpandingFinished() {
252 super.onExpandingFinished();
253 mScrimController.onExpandingFinished();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200254 }
255
256 @Override
Daniel Sandler08d05e32012-08-08 16:39:54 -0400257 public boolean onInterceptTouchEvent(MotionEvent event) {
258 return mBar.interceptTouchEvent(event) || super.onInterceptTouchEvent(event);
259 }
260
261 @Override
Selim Cinek3d395c62015-06-16 19:37:37 -0700262 public void panelScrimMinFractionChanged(float minFraction) {
263 if (mMinFraction != minFraction) {
264 mMinFraction = minFraction;
265 updateScrimFraction();
266 }
267 }
268
269 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800270 public void panelExpansionChanged(float frac, boolean expanded) {
271 super.panelExpansionChanged(frac, expanded);
Selim Cinek3d395c62015-06-16 19:37:37 -0700272 mPanelFraction = frac;
273 updateScrimFraction();
Matthew Ng78f88d12018-01-23 12:39:55 -0800274 if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
275 mBar.getNavigationBarView().onPanelExpandedChange(expanded);
276 }
Selim Cinek3d395c62015-06-16 19:37:37 -0700277 }
278
279 private void updateScrimFraction() {
Selim Cinekae76b4c2017-03-07 16:49:43 -0800280 float scrimFraction = mPanelFraction;
281 if (mMinFraction < 1.0f) {
282 scrimFraction = Math.max((mPanelFraction - mMinFraction) / (1.0f - mMinFraction),
283 0);
284 }
Selim Cinek3d395c62015-06-16 19:37:37 -0700285 mScrimController.setPanelExpansion(scrimFraction);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400286 }
Selim Cinek3e7592d2016-04-11 09:35:54 +0800287
Adrian Roos22af6502018-02-22 16:57:08 +0100288 public void updateResources() {
Evan Laird17a96ba2018-05-23 18:21:56 -0400289 mCutoutSideNudge = getResources().getDimensionPixelSize(
290 R.dimen.display_cutout_margin_consumption);
291
Selim Cinek3e7592d2016-04-11 09:35:54 +0800292 ViewGroup.LayoutParams layoutParams = getLayoutParams();
293 layoutParams.height = getResources().getDimensionPixelSize(
294 R.dimen.status_bar_height);
295 setLayoutParams(layoutParams);
296 }
Evan Laird058c8ae2018-01-12 14:26:10 -0500297
298 private void updateLayoutForCutout() {
Adrian Roos13836052018-03-15 21:06:37 +0100299 Pair<Integer, Integer> cornerCutoutMargins = cornerCutoutMargins(mDisplayCutout,
300 getDisplay());
301 updateCutoutLocation(cornerCutoutMargins);
302 updateSafeInsets(cornerCutoutMargins);
Evan Laird058c8ae2018-01-12 14:26:10 -0500303 }
304
Adrian Roos13836052018-03-15 21:06:37 +0100305 private void updateCutoutLocation(Pair<Integer, Integer> cornerCutoutMargins) {
Evan Lairdcda685d2018-01-22 18:25:17 -0500306 // Not all layouts have a cutout (e.g., Car)
307 if (mCutoutSpace == null) {
308 return;
309 }
310
Evan Laird058c8ae2018-01-12 14:26:10 -0500311 if (mDisplayCutout == null || mDisplayCutout.isEmpty()
Adrian Roos13836052018-03-15 21:06:37 +0100312 || mLastOrientation != ORIENTATION_PORTRAIT || cornerCutoutMargins != null) {
Evan Laird058c8ae2018-01-12 14:26:10 -0500313 mCutoutSpace.setVisibility(View.GONE);
314 return;
315 }
316
317 mCutoutSpace.setVisibility(View.VISIBLE);
318 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams();
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100319
320 Rect bounds = new Rect();
321 boundsFromDirection(mDisplayCutout, Gravity.TOP, bounds);
322
Evan Laird17a96ba2018-05-23 18:21:56 -0400323 bounds.left = bounds.left + mCutoutSideNudge;
324 bounds.right = bounds.right - mCutoutSideNudge;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100325 lp.width = bounds.width();
326 lp.height = bounds.height();
Evan Laird058c8ae2018-01-12 14:26:10 -0500327 }
328
Adrian Roos13836052018-03-15 21:06:37 +0100329 private void updateSafeInsets(Pair<Integer, Integer> cornerCutoutMargins) {
Evan Laird058c8ae2018-01-12 14:26:10 -0500330 // Depending on our rotation, we may have to work around a cutout in the middle of the view,
331 // or letterboxing from the right or left sides.
332
333 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
Adrian Roosedfab3b2018-03-08 18:39:20 +0100334 if (mDisplayCutout == null) {
Evan Laird058c8ae2018-01-12 14:26:10 -0500335 lp.leftMargin = 0;
336 lp.rightMargin = 0;
337 return;
338 }
339
Adrian Roosedfab3b2018-03-08 18:39:20 +0100340 lp.leftMargin = mDisplayCutout.getSafeInsetLeft();
341 lp.rightMargin = mDisplayCutout.getSafeInsetRight();
Adrian Roos13836052018-03-15 21:06:37 +0100342
343 if (cornerCutoutMargins != null) {
344 lp.leftMargin = Math.max(lp.leftMargin, cornerCutoutMargins.first);
345 lp.rightMargin = Math.max(lp.rightMargin, cornerCutoutMargins.second);
Evan Laird8bd70cb2018-04-17 17:42:36 -0400346
347 // If we're already inset enough (e.g. on the status bar side), we can have 0 margin
348 WindowInsets insets = getRootWindowInsets();
349 int leftInset = insets.getSystemWindowInsetLeft();
350 int rightInset = insets.getSystemWindowInsetRight();
351 if (lp.leftMargin <= leftInset) {
352 lp.leftMargin = 0;
353 }
354 if (lp.rightMargin <= rightInset) {
355 lp.rightMargin = 0;
356 }
357
Adrian Roos13836052018-03-15 21:06:37 +0100358 }
359 }
360
361 public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
362 Display display) {
363 if (cutout == null) {
364 return null;
365 }
366 Point size = new Point();
367 display.getRealSize(size);
368
369 Rect bounds = new Rect();
370 boundsFromDirection(cutout, Gravity.TOP, bounds);
371
372 if (bounds.left <= 0) {
373 return new Pair<>(bounds.right, 0);
374 }
375 if (bounds.right >= size.x) {
376 return new Pair<>(0, size.x - bounds.left);
377 }
378 return null;
Evan Laird058c8ae2018-01-12 14:26:10 -0500379 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700380}