blob: 59863ecb11917bf172a72f151de41b7682f2e7f6 [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;
Selim Cinek80c2abe2015-06-17 15:37:30 -070062 private Runnable mHideExpandedRunnable = new Runnable() {
63 @Override
64 public void run() {
Selim Cinek529c5322016-04-06 20:03:45 -070065 if (mPanelFraction == 0.0f) {
66 mBar.makeExpandedInvisible();
67 }
Selim Cinek80c2abe2015-06-17 15:37:30 -070068 }
69 };
Jason Monkaa573e92017-01-27 17:00:29 -050070 private DarkReceiver mBattery;
Evan Laird058c8ae2018-01-12 14:26:10 -050071 private int mLastOrientation;
Evan Lairdcda685d2018-01-22 18:25:17 -050072 @Nullable
Evan Laird058c8ae2018-01-12 14:26:10 -050073 private View mCutoutSpace;
74 @Nullable
75 private DisplayCutout mDisplayCutout;
Evan Laird17a96ba2018-05-23 18:21:56 -040076 /**
77 * Draw this many pixels into the left/right side of the cutout to optimally use the space
78 */
79 private int mCutoutSideNudge = 0;
Joe Onorato119a4012010-06-30 14:49:51 -040080
Joe Onoratofd52b182010-11-10 18:00:52 -080081 public PhoneStatusBarView(Context context, AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 super(context, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083
John Spurlock7edfbca2013-09-14 11:58:55 -040084 mBarTransitions = new PhoneStatusBarTransitions(this);
John Spurlocke932e302013-08-12 10:16:29 -040085 }
86
87 public BarTransitions getBarTransitions() {
88 return mBarTransitions;
Daniel Sandler1c1edaa2012-08-14 11:14:45 -040089 }
90
Jason Monk2a6ea9c2017-01-26 11:14:51 -050091 public void setBar(StatusBar bar) {
Daniel Sandlerefb0faf2012-10-10 14:15:34 -070092 mBar = bar;
93 }
94
Jorim Jaggiecc798e2014-05-26 18:14:37 +020095 public void setScrimController(ScrimController scrimController) {
96 mScrimController = scrimController;
97 }
98
Daniel Sandlerefb0faf2012-10-10 14:15:34 -070099 @Override
Jorim Jaggiacea1002014-05-10 01:30:10 +0200100 public void onFinishInflate() {
John Spurlocke6f0a712013-09-03 16:23:49 -0400101 mBarTransitions.init();
Alan Viverette51efddb2017-04-05 10:00:01 -0400102 mBattery = findViewById(R.id.battery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500103 mCutoutSpace = findViewById(R.id.cutout_space_view);
Evan Laird17a96ba2018-05-23 18:21:56 -0400104
105 updateResources();
Jason Monkaa573e92017-01-27 17:00:29 -0500106 }
107
108 @Override
109 protected void onAttachedToWindow() {
110 super.onAttachedToWindow();
111 // Always have Battery meters in the status bar observe the dark/light modes.
112 Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500113 if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
Adrian Roosedfab3b2018-03-08 18:39:20 +0100114 updateLayoutForCutout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500115 }
Jason Monkaa573e92017-01-27 17:00:29 -0500116 }
117
118 @Override
119 protected void onDetachedFromWindow() {
120 super.onDetachedFromWindow();
121 Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500122 mDisplayCutout = null;
123 }
124
125 @Override
126 protected void onConfigurationChanged(Configuration newConfig) {
127 super.onConfigurationChanged(newConfig);
128
129 // May trigger cutout space layout-ing
130 if (updateOrientationAndCutout(newConfig.orientation)) {
Adrian Roosedfab3b2018-03-08 18:39:20 +0100131 updateLayoutForCutout();
132 requestLayout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500133 }
134 }
135
Adrian Roosedfab3b2018-03-08 18:39:20 +0100136 @Override
137 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
138 if (updateOrientationAndCutout(mLastOrientation)) {
139 updateLayoutForCutout();
140 requestLayout();
141 }
142 return super.onApplyWindowInsets(insets);
143 }
144
Evan Laird058c8ae2018-01-12 14:26:10 -0500145 /**
146 *
147 * @param newOrientation may pass NO_VALUE for no change
148 * @return boolean indicating if we need to update the cutout location / margins
149 */
150 private boolean updateOrientationAndCutout(int newOrientation) {
151 boolean changed = false;
152 if (newOrientation != NO_VALUE) {
153 if (mLastOrientation != newOrientation) {
154 changed = true;
155 mLastOrientation = newOrientation;
156 }
157 }
158
Adrian Roosedfab3b2018-03-08 18:39:20 +0100159 if (!Objects.equals(getRootWindowInsets().getDisplayCutout(), mDisplayCutout)) {
160 changed = true;
161 mDisplayCutout = getRootWindowInsets().getDisplayCutout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500162 }
163
164 return changed;
Daniel Sandlerefb0faf2012-10-10 14:15:34 -0700165 }
166
Daniel Sandler1c1edaa2012-08-14 11:14:45 -0400167 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800168 public boolean panelEnabled() {
John Spurlock97642182013-07-29 17:58:39 -0400169 return mBar.panelsEnabled();
Daniel Sandler1e8feef2012-08-16 11:37:41 -0400170 }
171
172 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800173 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
Alan Viverette5b046752015-01-08 11:13:06 -0800174 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700175 // The status bar is very small so augment the view that the user is touching
176 // with the content of the status bar a whole. This way an accessibility service
177 // may announce the current item as well as the entire content if appropriate.
178 AccessibilityEvent record = AccessibilityEvent.obtain();
179 onInitializeAccessibilityEvent(record);
180 dispatchPopulateAccessibilityEvent(record);
181 event.appendRecord(record);
182 return true;
183 }
184 return false;
185 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400186
187 @Override
188 public void onPanelPeeked() {
189 super.onPanelPeeked();
Jorim Jaggifa505a72014-04-28 20:04:11 +0200190 mBar.makeExpandedVisible(false);
Daniel Sandler67eab792012-10-02 17:08:23 -0400191 }
192
193 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800194 public void onPanelCollapsed() {
195 super.onPanelCollapsed();
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200196 // Close the status bar in the next frame so we can show the end of the animation.
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200197 post(mHideExpandedRunnable);
Xiaohui Chen9f967112016-01-07 14:14:06 -0800198 mIsFullyOpenedPanel = false;
Daniel Sandler8e72c9e2012-08-15 00:09:26 -0400199 }
200
Selim Cinek80c2abe2015-06-17 15:37:30 -0700201 public void removePendingHideExpandedRunnables() {
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200202 removeCallbacks(mHideExpandedRunnable);
Selim Cinek80c2abe2015-06-17 15:37:30 -0700203 }
204
Daniel Sandler8e72c9e2012-08-15 00:09:26 -0400205 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800206 public void onPanelFullyOpened() {
207 super.onPanelFullyOpened();
208 if (!mIsFullyOpenedPanel) {
209 mPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700210 }
Xiaohui Chen9f967112016-01-07 14:14:06 -0800211 mIsFullyOpenedPanel = true;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400212 }
213
214 @Override
215 public boolean onTouchEvent(MotionEvent event) {
Chris Wren64161cc2012-12-17 16:49:30 -0500216 boolean barConsumedEvent = mBar.interceptTouchEvent(event);
217
218 if (DEBUG_GESTURES) {
219 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
220 EventLog.writeEvent(EventLogTags.SYSUI_PANELBAR_TOUCH,
221 event.getActionMasked(), (int) event.getX(), (int) event.getY(),
222 barConsumedEvent ? 1 : 0);
223 }
224 }
225
226 return barConsumedEvent || super.onTouchEvent(event);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400227 }
228
229 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800230 public void onTrackingStarted() {
231 super.onTrackingStarted();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200232 mBar.onTrackingStarted();
Jorim Jaggiecc798e2014-05-26 18:14:37 +0200233 mScrimController.onTrackingStarted();
Selim Cinek529c5322016-04-06 20:03:45 -0700234 removePendingHideExpandedRunnables();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200235 }
236
237 @Override
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200238 public void onClosingFinished() {
239 super.onClosingFinished();
240 mBar.onClosingFinished();
241 }
242
243 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800244 public void onTrackingStopped(boolean expand) {
245 super.onTrackingStopped(expand);
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200246 mBar.onTrackingStopped(expand);
247 }
248
249 @Override
250 public void onExpandingFinished() {
251 super.onExpandingFinished();
252 mScrimController.onExpandingFinished();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200253 }
254
255 @Override
Daniel Sandler08d05e32012-08-08 16:39:54 -0400256 public boolean onInterceptTouchEvent(MotionEvent event) {
257 return mBar.interceptTouchEvent(event) || super.onInterceptTouchEvent(event);
258 }
259
260 @Override
Selim Cinek3d395c62015-06-16 19:37:37 -0700261 public void panelScrimMinFractionChanged(float minFraction) {
262 if (mMinFraction != minFraction) {
263 mMinFraction = minFraction;
264 updateScrimFraction();
265 }
266 }
267
268 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800269 public void panelExpansionChanged(float frac, boolean expanded) {
270 super.panelExpansionChanged(frac, expanded);
Selim Cinek3d395c62015-06-16 19:37:37 -0700271 updateScrimFraction();
Matthew Ng78f88d12018-01-23 12:39:55 -0800272 if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
273 mBar.getNavigationBarView().onPanelExpandedChange(expanded);
274 }
Selim Cinek3d395c62015-06-16 19:37:37 -0700275 }
276
277 private void updateScrimFraction() {
Selim Cinekae76b4c2017-03-07 16:49:43 -0800278 float scrimFraction = mPanelFraction;
279 if (mMinFraction < 1.0f) {
280 scrimFraction = Math.max((mPanelFraction - mMinFraction) / (1.0f - mMinFraction),
281 0);
282 }
Selim Cinek3d395c62015-06-16 19:37:37 -0700283 mScrimController.setPanelExpansion(scrimFraction);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400284 }
Selim Cinek3e7592d2016-04-11 09:35:54 +0800285
Adrian Roos22af6502018-02-22 16:57:08 +0100286 public void updateResources() {
Evan Laird17a96ba2018-05-23 18:21:56 -0400287 mCutoutSideNudge = getResources().getDimensionPixelSize(
288 R.dimen.display_cutout_margin_consumption);
289
Selim Cinek3e7592d2016-04-11 09:35:54 +0800290 ViewGroup.LayoutParams layoutParams = getLayoutParams();
291 layoutParams.height = getResources().getDimensionPixelSize(
292 R.dimen.status_bar_height);
293 setLayoutParams(layoutParams);
294 }
Evan Laird058c8ae2018-01-12 14:26:10 -0500295
296 private void updateLayoutForCutout() {
Adrian Roos13836052018-03-15 21:06:37 +0100297 Pair<Integer, Integer> cornerCutoutMargins = cornerCutoutMargins(mDisplayCutout,
298 getDisplay());
299 updateCutoutLocation(cornerCutoutMargins);
300 updateSafeInsets(cornerCutoutMargins);
Evan Laird058c8ae2018-01-12 14:26:10 -0500301 }
302
Adrian Roos13836052018-03-15 21:06:37 +0100303 private void updateCutoutLocation(Pair<Integer, Integer> cornerCutoutMargins) {
Evan Lairdcda685d2018-01-22 18:25:17 -0500304 // Not all layouts have a cutout (e.g., Car)
305 if (mCutoutSpace == null) {
306 return;
307 }
308
Evan Laird058c8ae2018-01-12 14:26:10 -0500309 if (mDisplayCutout == null || mDisplayCutout.isEmpty()
Adrian Roos13836052018-03-15 21:06:37 +0100310 || mLastOrientation != ORIENTATION_PORTRAIT || cornerCutoutMargins != null) {
Evan Laird058c8ae2018-01-12 14:26:10 -0500311 mCutoutSpace.setVisibility(View.GONE);
312 return;
313 }
314
315 mCutoutSpace.setVisibility(View.VISIBLE);
316 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams();
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100317
318 Rect bounds = new Rect();
319 boundsFromDirection(mDisplayCutout, Gravity.TOP, bounds);
320
Evan Laird17a96ba2018-05-23 18:21:56 -0400321 bounds.left = bounds.left + mCutoutSideNudge;
322 bounds.right = bounds.right - mCutoutSideNudge;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100323 lp.width = bounds.width();
324 lp.height = bounds.height();
Evan Laird058c8ae2018-01-12 14:26:10 -0500325 }
326
Adrian Roos13836052018-03-15 21:06:37 +0100327 private void updateSafeInsets(Pair<Integer, Integer> cornerCutoutMargins) {
Evan Laird058c8ae2018-01-12 14:26:10 -0500328 // Depending on our rotation, we may have to work around a cutout in the middle of the view,
329 // or letterboxing from the right or left sides.
330
331 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
felkachang7749c9a2018-06-11 15:56:15 +0800332 if (mDisplayCutout == null || mDisplayCutout.isEmpty()
333 || mLastOrientation != ORIENTATION_PORTRAIT || cornerCutoutMargins == null) {
Evan Laird058c8ae2018-01-12 14:26:10 -0500334 lp.leftMargin = 0;
335 lp.rightMargin = 0;
336 return;
337 }
338
felkachang7749c9a2018-06-11 15:56:15 +0800339 lp.leftMargin = Math.max(lp.leftMargin, cornerCutoutMargins.first);
340 lp.rightMargin = Math.max(lp.rightMargin, cornerCutoutMargins.second);
Adrian Roos13836052018-03-15 21:06:37 +0100341
felkachang7749c9a2018-06-11 15:56:15 +0800342 // If we're already inset enough (e.g. on the status bar side), we can have 0 margin
343 WindowInsets insets = getRootWindowInsets();
344 int leftInset = insets.getSystemWindowInsetLeft();
345 int rightInset = insets.getSystemWindowInsetRight();
346 if (lp.leftMargin <= leftInset) {
347 lp.leftMargin = 0;
348 }
349 if (lp.rightMargin <= rightInset) {
350 lp.rightMargin = 0;
Adrian Roos13836052018-03-15 21:06:37 +0100351 }
352 }
353
354 public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
355 Display display) {
356 if (cutout == null) {
357 return null;
358 }
359 Point size = new Point();
360 display.getRealSize(size);
361
362 Rect bounds = new Rect();
363 boundsFromDirection(cutout, Gravity.TOP, bounds);
364
365 if (bounds.left <= 0) {
366 return new Pair<>(bounds.right, 0);
367 }
368 if (bounds.right >= size.x) {
369 return new Pair<>(0, size.x - bounds.left);
370 }
371 return null;
Evan Laird058c8ae2018-01-12 14:26:10 -0500372 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700373}