blob: 68eba50984d66dac216d6537a94427fc0fb197cf [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;
Jason Monk297c04e2018-08-23 17:16:59 -040022import static com.android.systemui.SysUiServiceProvider.getComponent;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +010023
Evan Laird058c8ae2018-01-12 14:26:10 -050024import android.annotation.Nullable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
Evan Laird058c8ae2018-01-12 14:26:10 -050026import android.content.res.Configuration;
Adrian Roos13836052018-03-15 21:06:37 +010027import android.graphics.Point;
Evan Laird058c8ae2018-01-12 14:26:10 -050028import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.util.AttributeSet;
Chris Wren64161cc2012-12-17 16:49:30 -050030import android.util.EventLog;
Adrian Roos13836052018-03-15 21:06:37 +010031import android.util.Pair;
32import android.view.Display;
Evan Laird058c8ae2018-01-12 14:26:10 -050033import android.view.DisplayCutout;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +010034import android.view.Gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.view.MotionEvent;
36import android.view.View;
Selim Cinek3e7592d2016-04-11 09:35:54 +080037import android.view.ViewGroup;
Adrian Roosedfab3b2018-03-08 18:39:20 +010038import android.view.WindowInsets;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070039import android.view.accessibility.AccessibilityEvent;
Evan Laird058c8ae2018-01-12 14:26:10 -050040import android.widget.FrameLayout;
41import android.widget.LinearLayout;
Adrian Roos13836052018-03-15 21:06:37 +010042
Jason Monkaa573e92017-01-27 17:00:29 -050043import com.android.systemui.Dependency;
Chris Wren64161cc2012-12-17 16:49:30 -050044import com.android.systemui.EventLogTags;
Selim Cinek3e7592d2016-04-11 09:35:54 +080045import com.android.systemui.R;
Beverly1be62f42018-12-19 17:17:48 -050046import com.android.systemui.plugins.DarkIconDispatcher;
47import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
Jason Monk297c04e2018-08-23 17:16:59 -040048import com.android.systemui.statusbar.CommandQueue;
Adrian Roosedfab3b2018-03-08 18:39:20 +010049
50import java.util.Objects;
Joe Onorato79de0c52010-05-26 17:03:26 -040051
Daniel Sandler08d05e32012-08-08 16:39:54 -040052public class PhoneStatusBarView extends PanelBar {
Joe Onoratofd52b182010-11-10 18:00:52 -080053 private static final String TAG = "PhoneStatusBarView";
Jason Monk2a6ea9c2017-01-26 11:14:51 -050054 private static final boolean DEBUG = StatusBar.DEBUG;
Christoph Studer934025e2014-11-25 12:53:55 +010055 private static final boolean DEBUG_GESTURES = false;
Evan Laird058c8ae2018-01-12 14:26:10 -050056 private static final int NO_VALUE = Integer.MIN_VALUE;
Jason Monk297c04e2018-08-23 17:16:59 -040057 private final CommandQueue mCommandQueue;
Daniel Sandler198a0302012-08-17 16:04:31 -040058
Jason Monk2a6ea9c2017-01-26 11:14:51 -050059 StatusBar mBar;
Daniel Sandler5a8aefa2012-09-25 01:21:12 -040060
Xiaohui Chen9f967112016-01-07 14:14:06 -080061 boolean mIsFullyOpenedPanel = false;
John Spurlock7edfbca2013-09-14 11:58:55 -040062 private final PhoneStatusBarTransitions mBarTransitions;
Jorim Jaggiecc798e2014-05-26 18:14:37 +020063 private ScrimController mScrimController;
Selim Cinek3d395c62015-06-16 19:37:37 -070064 private float mMinFraction;
Selim Cinek80c2abe2015-06-17 15:37:30 -070065 private Runnable mHideExpandedRunnable = new Runnable() {
66 @Override
67 public void run() {
Selim Cinek529c5322016-04-06 20:03:45 -070068 if (mPanelFraction == 0.0f) {
69 mBar.makeExpandedInvisible();
70 }
Selim Cinek80c2abe2015-06-17 15:37:30 -070071 }
72 };
Jason Monkaa573e92017-01-27 17:00:29 -050073 private DarkReceiver mBattery;
Evan Laird058c8ae2018-01-12 14:26:10 -050074 private int mLastOrientation;
Evan Lairdcda685d2018-01-22 18:25:17 -050075 @Nullable
Beverly40770652019-02-15 15:49:49 -050076 private View mCenterIconSpace;
77 @Nullable
Evan Laird058c8ae2018-01-12 14:26:10 -050078 private View mCutoutSpace;
79 @Nullable
80 private DisplayCutout mDisplayCutout;
Evan Laird17a96ba2018-05-23 18:21:56 -040081 /**
82 * Draw this many pixels into the left/right side of the cutout to optimally use the space
83 */
84 private int mCutoutSideNudge = 0;
Joe Onorato119a4012010-06-30 14:49:51 -040085
Joe Onoratofd52b182010-11-10 18:00:52 -080086 public PhoneStatusBarView(Context context, AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 super(context, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088
John Spurlock7edfbca2013-09-14 11:58:55 -040089 mBarTransitions = new PhoneStatusBarTransitions(this);
Jason Monk297c04e2018-08-23 17:16:59 -040090 mCommandQueue = getComponent(context, CommandQueue.class);
John Spurlocke932e302013-08-12 10:16:29 -040091 }
92
93 public BarTransitions getBarTransitions() {
94 return mBarTransitions;
Daniel Sandler1c1edaa2012-08-14 11:14:45 -040095 }
96
Jason Monk2a6ea9c2017-01-26 11:14:51 -050097 public void setBar(StatusBar bar) {
Daniel Sandlerefb0faf2012-10-10 14:15:34 -070098 mBar = bar;
99 }
100
Jorim Jaggiecc798e2014-05-26 18:14:37 +0200101 public void setScrimController(ScrimController scrimController) {
102 mScrimController = scrimController;
103 }
104
Daniel Sandlerefb0faf2012-10-10 14:15:34 -0700105 @Override
Jorim Jaggiacea1002014-05-10 01:30:10 +0200106 public void onFinishInflate() {
John Spurlocke6f0a712013-09-03 16:23:49 -0400107 mBarTransitions.init();
Alan Viverette51efddb2017-04-05 10:00:01 -0400108 mBattery = findViewById(R.id.battery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500109 mCutoutSpace = findViewById(R.id.cutout_space_view);
Beverly40770652019-02-15 15:49:49 -0500110 mCenterIconSpace = findViewById(R.id.centered_icon_area);
Evan Laird17a96ba2018-05-23 18:21:56 -0400111
112 updateResources();
Jason Monkaa573e92017-01-27 17:00:29 -0500113 }
114
115 @Override
116 protected void onAttachedToWindow() {
117 super.onAttachedToWindow();
118 // Always have Battery meters in the status bar observe the dark/light modes.
119 Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500120 if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
Adrian Roosedfab3b2018-03-08 18:39:20 +0100121 updateLayoutForCutout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500122 }
Jason Monkaa573e92017-01-27 17:00:29 -0500123 }
124
125 @Override
126 protected void onDetachedFromWindow() {
127 super.onDetachedFromWindow();
128 Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500129 mDisplayCutout = null;
130 }
131
132 @Override
133 protected void onConfigurationChanged(Configuration newConfig) {
134 super.onConfigurationChanged(newConfig);
135
136 // May trigger cutout space layout-ing
137 if (updateOrientationAndCutout(newConfig.orientation)) {
Adrian Roosedfab3b2018-03-08 18:39:20 +0100138 updateLayoutForCutout();
139 requestLayout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500140 }
141 }
142
Adrian Roosedfab3b2018-03-08 18:39:20 +0100143 @Override
144 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
145 if (updateOrientationAndCutout(mLastOrientation)) {
146 updateLayoutForCutout();
147 requestLayout();
148 }
149 return super.onApplyWindowInsets(insets);
150 }
151
Evan Laird058c8ae2018-01-12 14:26:10 -0500152 /**
153 *
154 * @param newOrientation may pass NO_VALUE for no change
155 * @return boolean indicating if we need to update the cutout location / margins
156 */
157 private boolean updateOrientationAndCutout(int newOrientation) {
158 boolean changed = false;
159 if (newOrientation != NO_VALUE) {
160 if (mLastOrientation != newOrientation) {
161 changed = true;
162 mLastOrientation = newOrientation;
163 }
164 }
165
Adrian Roosedfab3b2018-03-08 18:39:20 +0100166 if (!Objects.equals(getRootWindowInsets().getDisplayCutout(), mDisplayCutout)) {
167 changed = true;
168 mDisplayCutout = getRootWindowInsets().getDisplayCutout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500169 }
170
171 return changed;
Daniel Sandlerefb0faf2012-10-10 14:15:34 -0700172 }
173
Daniel Sandler1c1edaa2012-08-14 11:14:45 -0400174 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800175 public boolean panelEnabled() {
Jason Monk297c04e2018-08-23 17:16:59 -0400176 return mCommandQueue.panelsEnabled();
Daniel Sandler1e8feef2012-08-16 11:37:41 -0400177 }
178
179 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800180 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
Alan Viverette5b046752015-01-08 11:13:06 -0800181 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700182 // The status bar is very small so augment the view that the user is touching
183 // with the content of the status bar a whole. This way an accessibility service
184 // may announce the current item as well as the entire content if appropriate.
185 AccessibilityEvent record = AccessibilityEvent.obtain();
186 onInitializeAccessibilityEvent(record);
187 dispatchPopulateAccessibilityEvent(record);
188 event.appendRecord(record);
189 return true;
190 }
191 return false;
192 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400193
194 @Override
195 public void onPanelPeeked() {
196 super.onPanelPeeked();
Jorim Jaggifa505a72014-04-28 20:04:11 +0200197 mBar.makeExpandedVisible(false);
Daniel Sandler67eab792012-10-02 17:08:23 -0400198 }
199
200 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800201 public void onPanelCollapsed() {
202 super.onPanelCollapsed();
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200203 // Close the status bar in the next frame so we can show the end of the animation.
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200204 post(mHideExpandedRunnable);
Xiaohui Chen9f967112016-01-07 14:14:06 -0800205 mIsFullyOpenedPanel = false;
Daniel Sandler8e72c9e2012-08-15 00:09:26 -0400206 }
207
Selim Cinek80c2abe2015-06-17 15:37:30 -0700208 public void removePendingHideExpandedRunnables() {
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200209 removeCallbacks(mHideExpandedRunnable);
Selim Cinek80c2abe2015-06-17 15:37:30 -0700210 }
211
Daniel Sandler8e72c9e2012-08-15 00:09:26 -0400212 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800213 public void onPanelFullyOpened() {
214 super.onPanelFullyOpened();
215 if (!mIsFullyOpenedPanel) {
216 mPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700217 }
Xiaohui Chen9f967112016-01-07 14:14:06 -0800218 mIsFullyOpenedPanel = true;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400219 }
220
221 @Override
222 public boolean onTouchEvent(MotionEvent event) {
Chris Wren64161cc2012-12-17 16:49:30 -0500223 boolean barConsumedEvent = mBar.interceptTouchEvent(event);
224
225 if (DEBUG_GESTURES) {
226 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
227 EventLog.writeEvent(EventLogTags.SYSUI_PANELBAR_TOUCH,
228 event.getActionMasked(), (int) event.getX(), (int) event.getY(),
229 barConsumedEvent ? 1 : 0);
230 }
231 }
232
233 return barConsumedEvent || super.onTouchEvent(event);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400234 }
235
236 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800237 public void onTrackingStarted() {
238 super.onTrackingStarted();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200239 mBar.onTrackingStarted();
Jorim Jaggiecc798e2014-05-26 18:14:37 +0200240 mScrimController.onTrackingStarted();
Selim Cinek529c5322016-04-06 20:03:45 -0700241 removePendingHideExpandedRunnables();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200242 }
243
244 @Override
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200245 public void onClosingFinished() {
246 super.onClosingFinished();
247 mBar.onClosingFinished();
248 }
249
250 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800251 public void onTrackingStopped(boolean expand) {
252 super.onTrackingStopped(expand);
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200253 mBar.onTrackingStopped(expand);
254 }
255
256 @Override
257 public void onExpandingFinished() {
258 super.onExpandingFinished();
259 mScrimController.onExpandingFinished();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200260 }
261
262 @Override
Daniel Sandler08d05e32012-08-08 16:39:54 -0400263 public boolean onInterceptTouchEvent(MotionEvent event) {
264 return mBar.interceptTouchEvent(event) || super.onInterceptTouchEvent(event);
265 }
266
267 @Override
Selim Cinek3d395c62015-06-16 19:37:37 -0700268 public void panelScrimMinFractionChanged(float minFraction) {
269 if (mMinFraction != minFraction) {
270 mMinFraction = minFraction;
271 updateScrimFraction();
272 }
273 }
274
275 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800276 public void panelExpansionChanged(float frac, boolean expanded) {
277 super.panelExpansionChanged(frac, expanded);
Selim Cinek3d395c62015-06-16 19:37:37 -0700278 updateScrimFraction();
Matthew Ng78f88d12018-01-23 12:39:55 -0800279 if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
Sunny Goyal0ff57922019-05-02 15:22:01 -0700280 mBar.getNavigationBarView().onPanelExpandedChange();
Matthew Ng78f88d12018-01-23 12:39:55 -0800281 }
Selim Cinek3d395c62015-06-16 19:37:37 -0700282 }
283
284 private void updateScrimFraction() {
Selim Cinekae76b4c2017-03-07 16:49:43 -0800285 float scrimFraction = mPanelFraction;
286 if (mMinFraction < 1.0f) {
287 scrimFraction = Math.max((mPanelFraction - mMinFraction) / (1.0f - mMinFraction),
288 0);
289 }
Selim Cinek3d395c62015-06-16 19:37:37 -0700290 mScrimController.setPanelExpansion(scrimFraction);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400291 }
Selim Cinek3e7592d2016-04-11 09:35:54 +0800292
Adrian Roos22af6502018-02-22 16:57:08 +0100293 public void updateResources() {
Evan Laird17a96ba2018-05-23 18:21:56 -0400294 mCutoutSideNudge = getResources().getDimensionPixelSize(
295 R.dimen.display_cutout_margin_consumption);
296
Selim Cinek3e7592d2016-04-11 09:35:54 +0800297 ViewGroup.LayoutParams layoutParams = getLayoutParams();
298 layoutParams.height = getResources().getDimensionPixelSize(
299 R.dimen.status_bar_height);
300 setLayoutParams(layoutParams);
301 }
Evan Laird058c8ae2018-01-12 14:26:10 -0500302
303 private void updateLayoutForCutout() {
Adrian Roos13836052018-03-15 21:06:37 +0100304 Pair<Integer, Integer> cornerCutoutMargins = cornerCutoutMargins(mDisplayCutout,
305 getDisplay());
306 updateCutoutLocation(cornerCutoutMargins);
307 updateSafeInsets(cornerCutoutMargins);
Evan Laird058c8ae2018-01-12 14:26:10 -0500308 }
309
Adrian Roos13836052018-03-15 21:06:37 +0100310 private void updateCutoutLocation(Pair<Integer, Integer> cornerCutoutMargins) {
Evan Lairdcda685d2018-01-22 18:25:17 -0500311 // Not all layouts have a cutout (e.g., Car)
312 if (mCutoutSpace == null) {
313 return;
314 }
315
Evan Laird058c8ae2018-01-12 14:26:10 -0500316 if (mDisplayCutout == null || mDisplayCutout.isEmpty()
Adrian Roos13836052018-03-15 21:06:37 +0100317 || mLastOrientation != ORIENTATION_PORTRAIT || cornerCutoutMargins != null) {
Beverly40770652019-02-15 15:49:49 -0500318 mCenterIconSpace.setVisibility(View.VISIBLE);
Evan Laird058c8ae2018-01-12 14:26:10 -0500319 mCutoutSpace.setVisibility(View.GONE);
320 return;
321 }
322
Beverly40770652019-02-15 15:49:49 -0500323 mCenterIconSpace.setVisibility(View.GONE);
Evan Laird058c8ae2018-01-12 14:26:10 -0500324 mCutoutSpace.setVisibility(View.VISIBLE);
325 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams();
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100326
327 Rect bounds = new Rect();
328 boundsFromDirection(mDisplayCutout, Gravity.TOP, bounds);
329
Evan Laird17a96ba2018-05-23 18:21:56 -0400330 bounds.left = bounds.left + mCutoutSideNudge;
331 bounds.right = bounds.right - mCutoutSideNudge;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100332 lp.width = bounds.width();
333 lp.height = bounds.height();
Evan Laird058c8ae2018-01-12 14:26:10 -0500334 }
335
Adrian Roos13836052018-03-15 21:06:37 +0100336 private void updateSafeInsets(Pair<Integer, Integer> cornerCutoutMargins) {
Evan Laird058c8ae2018-01-12 14:26:10 -0500337 // Depending on our rotation, we may have to work around a cutout in the middle of the view,
338 // or letterboxing from the right or left sides.
339
340 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
felkachang7749c9a2018-06-11 15:56:15 +0800341 if (mDisplayCutout == null || mDisplayCutout.isEmpty()
342 || mLastOrientation != ORIENTATION_PORTRAIT || cornerCutoutMargins == null) {
Evan Laird058c8ae2018-01-12 14:26:10 -0500343 lp.leftMargin = 0;
344 lp.rightMargin = 0;
345 return;
346 }
347
felkachang7749c9a2018-06-11 15:56:15 +0800348 lp.leftMargin = Math.max(lp.leftMargin, cornerCutoutMargins.first);
349 lp.rightMargin = Math.max(lp.rightMargin, cornerCutoutMargins.second);
Adrian Roos13836052018-03-15 21:06:37 +0100350
felkachang7749c9a2018-06-11 15:56:15 +0800351 // If we're already inset enough (e.g. on the status bar side), we can have 0 margin
352 WindowInsets insets = getRootWindowInsets();
353 int leftInset = insets.getSystemWindowInsetLeft();
354 int rightInset = insets.getSystemWindowInsetRight();
355 if (lp.leftMargin <= leftInset) {
356 lp.leftMargin = 0;
357 }
358 if (lp.rightMargin <= rightInset) {
359 lp.rightMargin = 0;
Adrian Roos13836052018-03-15 21:06:37 +0100360 }
361 }
362
363 public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
364 Display display) {
365 if (cutout == null) {
366 return null;
367 }
368 Point size = new Point();
369 display.getRealSize(size);
370
371 Rect bounds = new Rect();
372 boundsFromDirection(cutout, Gravity.TOP, bounds);
373
374 if (bounds.left <= 0) {
375 return new Pair<>(bounds.right, 0);
376 }
377 if (bounds.right >= size.x) {
378 return new Pair<>(0, size.x - bounds.left);
379 }
380 return null;
Evan Laird058c8ae2018-01-12 14:26:10 -0500381 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700382}