blob: 1359f74d0b3a550fa04d347054a611263e9577b9 [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
Adrian Roos6a4fa0e2018-03-05 19:50:16 +010019import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
20
Evan Laird058c8ae2018-01-12 14:26:10 -050021import android.annotation.Nullable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Context;
Evan Laird058c8ae2018-01-12 14:26:10 -050023import android.content.res.Configuration;
24import android.graphics.Rect;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.util.AttributeSet;
Chris Wren64161cc2012-12-17 16:49:30 -050026import android.util.EventLog;
Adrian Roos13836052018-03-15 21:06:37 +010027import android.util.Pair;
Evan Laird058c8ae2018-01-12 14:26:10 -050028import android.view.DisplayCutout;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +010029import android.view.Gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.view.MotionEvent;
31import android.view.View;
Selim Cinek3e7592d2016-04-11 09:35:54 +080032import android.view.ViewGroup;
Adrian Roosedfab3b2018-03-08 18:39:20 +010033import android.view.WindowInsets;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070034import android.view.accessibility.AccessibilityEvent;
Evan Laird058c8ae2018-01-12 14:26:10 -050035import android.widget.LinearLayout;
Adrian Roos13836052018-03-15 21:06:37 +010036
Jason Monkaa573e92017-01-27 17:00:29 -050037import com.android.systemui.Dependency;
Chris Wren64161cc2012-12-17 16:49:30 -050038import com.android.systemui.EventLogTags;
Selim Cinek3e7592d2016-04-11 09:35:54 +080039import com.android.systemui.R;
Beverly1be62f42018-12-19 17:17:48 -050040import com.android.systemui.plugins.DarkIconDispatcher;
41import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
Jason Monk297c04e2018-08-23 17:16:59 -040042import com.android.systemui.statusbar.CommandQueue;
Adrian Roosedfab3b2018-03-08 18:39:20 +010043
44import java.util.Objects;
Joe Onorato79de0c52010-05-26 17:03:26 -040045
Daniel Sandler08d05e32012-08-08 16:39:54 -040046public class PhoneStatusBarView extends PanelBar {
Joe Onoratofd52b182010-11-10 18:00:52 -080047 private static final String TAG = "PhoneStatusBarView";
Jason Monk2a6ea9c2017-01-26 11:14:51 -050048 private static final boolean DEBUG = StatusBar.DEBUG;
Christoph Studer934025e2014-11-25 12:53:55 +010049 private static final boolean DEBUG_GESTURES = false;
Evan Laird058c8ae2018-01-12 14:26:10 -050050 private static final int NO_VALUE = Integer.MIN_VALUE;
Jason Monk297c04e2018-08-23 17:16:59 -040051 private final CommandQueue mCommandQueue;
Daniel Sandler198a0302012-08-17 16:04:31 -040052
Jason Monk2a6ea9c2017-01-26 11:14:51 -050053 StatusBar mBar;
Daniel Sandler5a8aefa2012-09-25 01:21:12 -040054
Xiaohui Chen9f967112016-01-07 14:14:06 -080055 boolean mIsFullyOpenedPanel = false;
Jorim Jaggiecc798e2014-05-26 18:14:37 +020056 private ScrimController mScrimController;
Selim Cinek3d395c62015-06-16 19:37:37 -070057 private float mMinFraction;
Selim Cinek80c2abe2015-06-17 15:37:30 -070058 private Runnable mHideExpandedRunnable = new Runnable() {
59 @Override
60 public void run() {
Selim Cinek529c5322016-04-06 20:03:45 -070061 if (mPanelFraction == 0.0f) {
62 mBar.makeExpandedInvisible();
63 }
Selim Cinek80c2abe2015-06-17 15:37:30 -070064 }
65 };
Jason Monkaa573e92017-01-27 17:00:29 -050066 private DarkReceiver mBattery;
Evan Laird058c8ae2018-01-12 14:26:10 -050067 private int mLastOrientation;
Evan Lairdcda685d2018-01-22 18:25:17 -050068 @Nullable
Beverly40770652019-02-15 15:49:49 -050069 private View mCenterIconSpace;
70 @Nullable
Evan Laird058c8ae2018-01-12 14:26:10 -050071 private View mCutoutSpace;
72 @Nullable
73 private DisplayCutout mDisplayCutout;
Fabian Kozynski786974f2019-07-25 12:44:06 -040074
Evan Laird17a96ba2018-05-23 18:21:56 -040075 /**
76 * Draw this many pixels into the left/right side of the cutout to optimally use the space
77 */
78 private int mCutoutSideNudge = 0;
Selim Cinekd21232e2019-06-20 14:15:59 -070079 private boolean mHeadsUpVisible;
Joe Onorato119a4012010-06-30 14:49:51 -040080
shawnlinbd6d0ad2020-01-29 18:38:49 +080081 private int mRoundedCornerPadding = 0;
82
Joe Onoratofd52b182010-11-10 18:00:52 -080083 public PhoneStatusBarView(Context context, AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 super(context, attrs);
Dave Mankoffbcaca8a2019-10-31 18:04:08 -040085 mCommandQueue = Dependency.get(CommandQueue.class);
John Spurlocke932e302013-08-12 10:16:29 -040086 }
87
Jason Monk2a6ea9c2017-01-26 11:14:51 -050088 public void setBar(StatusBar bar) {
Daniel Sandlerefb0faf2012-10-10 14:15:34 -070089 mBar = bar;
90 }
91
Jorim Jaggiecc798e2014-05-26 18:14:37 +020092 public void setScrimController(ScrimController scrimController) {
93 mScrimController = scrimController;
94 }
95
Daniel Sandlerefb0faf2012-10-10 14:15:34 -070096 @Override
Jorim Jaggiacea1002014-05-10 01:30:10 +020097 public void onFinishInflate() {
Alan Viverette51efddb2017-04-05 10:00:01 -040098 mBattery = findViewById(R.id.battery);
Evan Laird058c8ae2018-01-12 14:26:10 -050099 mCutoutSpace = findViewById(R.id.cutout_space_view);
Beverly40770652019-02-15 15:49:49 -0500100 mCenterIconSpace = findViewById(R.id.centered_icon_area);
Evan Laird17a96ba2018-05-23 18:21:56 -0400101
102 updateResources();
Jason Monkaa573e92017-01-27 17:00:29 -0500103 }
104
105 @Override
106 protected void onAttachedToWindow() {
107 super.onAttachedToWindow();
108 // Always have Battery meters in the status bar observe the dark/light modes.
109 Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500110 if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
Adrian Roosedfab3b2018-03-08 18:39:20 +0100111 updateLayoutForCutout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500112 }
Jason Monkaa573e92017-01-27 17:00:29 -0500113 }
114
115 @Override
116 protected void onDetachedFromWindow() {
117 super.onDetachedFromWindow();
118 Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
Evan Laird058c8ae2018-01-12 14:26:10 -0500119 mDisplayCutout = null;
120 }
121
122 @Override
123 protected void onConfigurationChanged(Configuration newConfig) {
124 super.onConfigurationChanged(newConfig);
125
126 // May trigger cutout space layout-ing
127 if (updateOrientationAndCutout(newConfig.orientation)) {
Adrian Roosedfab3b2018-03-08 18:39:20 +0100128 updateLayoutForCutout();
129 requestLayout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500130 }
131 }
132
Adrian Roosedfab3b2018-03-08 18:39:20 +0100133 @Override
134 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
135 if (updateOrientationAndCutout(mLastOrientation)) {
136 updateLayoutForCutout();
137 requestLayout();
138 }
139 return super.onApplyWindowInsets(insets);
140 }
141
Evan Laird058c8ae2018-01-12 14:26:10 -0500142 /**
143 *
144 * @param newOrientation may pass NO_VALUE for no change
145 * @return boolean indicating if we need to update the cutout location / margins
146 */
147 private boolean updateOrientationAndCutout(int newOrientation) {
148 boolean changed = false;
149 if (newOrientation != NO_VALUE) {
150 if (mLastOrientation != newOrientation) {
151 changed = true;
152 mLastOrientation = newOrientation;
153 }
154 }
155
Adrian Roosedfab3b2018-03-08 18:39:20 +0100156 if (!Objects.equals(getRootWindowInsets().getDisplayCutout(), mDisplayCutout)) {
157 changed = true;
158 mDisplayCutout = getRootWindowInsets().getDisplayCutout();
Evan Laird058c8ae2018-01-12 14:26:10 -0500159 }
160
161 return changed;
Daniel Sandlerefb0faf2012-10-10 14:15:34 -0700162 }
163
Daniel Sandler1c1edaa2012-08-14 11:14:45 -0400164 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800165 public boolean panelEnabled() {
Jason Monk297c04e2018-08-23 17:16:59 -0400166 return mCommandQueue.panelsEnabled();
Daniel Sandler1e8feef2012-08-16 11:37:41 -0400167 }
168
169 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800170 public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
Alan Viverette5b046752015-01-08 11:13:06 -0800171 if (super.onRequestSendAccessibilityEventInternal(child, event)) {
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700172 // The status bar is very small so augment the view that the user is touching
173 // with the content of the status bar a whole. This way an accessibility service
174 // may announce the current item as well as the entire content if appropriate.
175 AccessibilityEvent record = AccessibilityEvent.obtain();
176 onInitializeAccessibilityEvent(record);
177 dispatchPopulateAccessibilityEvent(record);
178 event.appendRecord(record);
179 return true;
180 }
181 return false;
182 }
Daniel Sandler08d05e32012-08-08 16:39:54 -0400183
184 @Override
185 public void onPanelPeeked() {
186 super.onPanelPeeked();
Jorim Jaggifa505a72014-04-28 20:04:11 +0200187 mBar.makeExpandedVisible(false);
Daniel Sandler67eab792012-10-02 17:08:23 -0400188 }
189
190 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800191 public void onPanelCollapsed() {
192 super.onPanelCollapsed();
Jorim Jaggi2580a9762014-06-25 03:08:25 +0200193 // Close the status bar in the next frame so we can show the end of the animation.
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200194 post(mHideExpandedRunnable);
Xiaohui Chen9f967112016-01-07 14:14:06 -0800195 mIsFullyOpenedPanel = false;
Daniel Sandler8e72c9e2012-08-15 00:09:26 -0400196 }
197
Selim Cinek80c2abe2015-06-17 15:37:30 -0700198 public void removePendingHideExpandedRunnables() {
Jorim Jaggi45e2d8f2017-05-16 14:23:19 +0200199 removeCallbacks(mHideExpandedRunnable);
Selim Cinek80c2abe2015-06-17 15:37:30 -0700200 }
201
Daniel Sandler8e72c9e2012-08-15 00:09:26 -0400202 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800203 public void onPanelFullyOpened() {
204 super.onPanelFullyOpened();
205 if (!mIsFullyOpenedPanel) {
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500206 mPanel.getView().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
Casey Burkhardtbac221f2012-10-03 18:13:58 -0700207 }
Xiaohui Chen9f967112016-01-07 14:14:06 -0800208 mIsFullyOpenedPanel = true;
Daniel Sandler08d05e32012-08-08 16:39:54 -0400209 }
210
211 @Override
212 public boolean onTouchEvent(MotionEvent event) {
Chris Wren64161cc2012-12-17 16:49:30 -0500213 boolean barConsumedEvent = mBar.interceptTouchEvent(event);
214
215 if (DEBUG_GESTURES) {
216 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
217 EventLog.writeEvent(EventLogTags.SYSUI_PANELBAR_TOUCH,
218 event.getActionMasked(), (int) event.getX(), (int) event.getY(),
219 barConsumedEvent ? 1 : 0);
220 }
221 }
222
223 return barConsumedEvent || super.onTouchEvent(event);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400224 }
225
226 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800227 public void onTrackingStarted() {
228 super.onTrackingStarted();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200229 mBar.onTrackingStarted();
Jorim Jaggiecc798e2014-05-26 18:14:37 +0200230 mScrimController.onTrackingStarted();
Selim Cinek529c5322016-04-06 20:03:45 -0700231 removePendingHideExpandedRunnables();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200232 }
233
234 @Override
Selim Cinekdbbcfbe2014-10-24 17:52:35 +0200235 public void onClosingFinished() {
236 super.onClosingFinished();
237 mBar.onClosingFinished();
238 }
239
240 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800241 public void onTrackingStopped(boolean expand) {
242 super.onTrackingStopped(expand);
Jorim Jaggi2fbad7b2014-05-26 22:38:00 +0200243 mBar.onTrackingStopped(expand);
244 }
245
246 @Override
247 public void onExpandingFinished() {
248 super.onExpandingFinished();
249 mScrimController.onExpandingFinished();
Jorim Jaggie70d31f2014-04-24 22:08:30 +0200250 }
251
252 @Override
Daniel Sandler08d05e32012-08-08 16:39:54 -0400253 public boolean onInterceptTouchEvent(MotionEvent event) {
254 return mBar.interceptTouchEvent(event) || super.onInterceptTouchEvent(event);
255 }
256
257 @Override
Selim Cinek3d395c62015-06-16 19:37:37 -0700258 public void panelScrimMinFractionChanged(float minFraction) {
259 if (mMinFraction != minFraction) {
260 mMinFraction = minFraction;
261 updateScrimFraction();
262 }
263 }
264
265 @Override
Xiaohui Chen9f967112016-01-07 14:14:06 -0800266 public void panelExpansionChanged(float frac, boolean expanded) {
267 super.panelExpansionChanged(frac, expanded);
Selim Cinek3d395c62015-06-16 19:37:37 -0700268 updateScrimFraction();
Matthew Ng78f88d12018-01-23 12:39:55 -0800269 if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) {
Winson Chung5b6a1fe2019-07-26 16:38:08 -0700270 mBar.getNavigationBarView().onStatusBarPanelStateChanged();
Matthew Ng78f88d12018-01-23 12:39:55 -0800271 }
Selim Cinek3d395c62015-06-16 19:37:37 -0700272 }
273
274 private void updateScrimFraction() {
Selim Cinekae76b4c2017-03-07 16:49:43 -0800275 float scrimFraction = mPanelFraction;
276 if (mMinFraction < 1.0f) {
277 scrimFraction = Math.max((mPanelFraction - mMinFraction) / (1.0f - mMinFraction),
278 0);
279 }
Selim Cinek3d395c62015-06-16 19:37:37 -0700280 mScrimController.setPanelExpansion(scrimFraction);
Daniel Sandler08d05e32012-08-08 16:39:54 -0400281 }
Selim Cinek3e7592d2016-04-11 09:35:54 +0800282
Adrian Roos22af6502018-02-22 16:57:08 +0100283 public void updateResources() {
Evan Laird17a96ba2018-05-23 18:21:56 -0400284 mCutoutSideNudge = getResources().getDimensionPixelSize(
285 R.dimen.display_cutout_margin_consumption);
shawnlinbd6d0ad2020-01-29 18:38:49 +0800286 mRoundedCornerPadding = getResources().getDimensionPixelSize(
287 R.dimen.rounded_corner_content_padding);
Evan Laird17a96ba2018-05-23 18:21:56 -0400288
shawnlinbd6d0ad2020-01-29 18:38:49 +0800289 updateStatusBarHeight();
290 }
291
292 private void updateStatusBarHeight() {
293 final int waterfallTopInset =
294 mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800295 ViewGroup.LayoutParams layoutParams = getLayoutParams();
shawnlinbd6d0ad2020-01-29 18:38:49 +0800296 layoutParams.height =
297 getResources().getDimensionPixelSize(R.dimen.status_bar_height) - waterfallTopInset;
Selim Cinek3e7592d2016-04-11 09:35:54 +0800298 setLayoutParams(layoutParams);
299 }
Evan Laird058c8ae2018-01-12 14:26:10 -0500300
301 private void updateLayoutForCutout() {
shawnlinbd6d0ad2020-01-29 18:38:49 +0800302 updateStatusBarHeight();
303 Pair<Integer, Integer> cornerCutoutMargins =
304 StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay());
Adrian Roos13836052018-03-15 21:06:37 +0100305 updateCutoutLocation(cornerCutoutMargins);
306 updateSafeInsets(cornerCutoutMargins);
Evan Laird058c8ae2018-01-12 14:26:10 -0500307 }
308
Adrian Roos13836052018-03-15 21:06:37 +0100309 private void updateCutoutLocation(Pair<Integer, Integer> cornerCutoutMargins) {
Evan Lairdcda685d2018-01-22 18:25:17 -0500310 // Not all layouts have a cutout (e.g., Car)
311 if (mCutoutSpace == null) {
312 return;
313 }
314
shawnlin63dfae22020-02-16 17:15:07 +0800315 if (mDisplayCutout == null || mDisplayCutout.isEmpty() || cornerCutoutMargins != null) {
Beverly40770652019-02-15 15:49:49 -0500316 mCenterIconSpace.setVisibility(View.VISIBLE);
Evan Laird058c8ae2018-01-12 14:26:10 -0500317 mCutoutSpace.setVisibility(View.GONE);
318 return;
319 }
320
Beverly40770652019-02-15 15:49:49 -0500321 mCenterIconSpace.setVisibility(View.GONE);
Evan Laird058c8ae2018-01-12 14:26:10 -0500322 mCutoutSpace.setVisibility(View.VISIBLE);
323 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams();
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100324
325 Rect bounds = new Rect();
326 boundsFromDirection(mDisplayCutout, Gravity.TOP, bounds);
327
Evan Laird17a96ba2018-05-23 18:21:56 -0400328 bounds.left = bounds.left + mCutoutSideNudge;
329 bounds.right = bounds.right - mCutoutSideNudge;
Adrian Roos6a4fa0e2018-03-05 19:50:16 +0100330 lp.width = bounds.width();
331 lp.height = bounds.height();
Evan Laird058c8ae2018-01-12 14:26:10 -0500332 }
333
Adrian Roos13836052018-03-15 21:06:37 +0100334 private void updateSafeInsets(Pair<Integer, Integer> cornerCutoutMargins) {
Evan Laird058c8ae2018-01-12 14:26:10 -0500335 // Depending on our rotation, we may have to work around a cutout in the middle of the view,
336 // or letterboxing from the right or left sides.
337
shawnlinbd6d0ad2020-01-29 18:38:49 +0800338 Pair<Integer, Integer> padding =
339 StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
340 mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding);
Evan Laird058c8ae2018-01-12 14:26:10 -0500341
shawnlinbd6d0ad2020-01-29 18:38:49 +0800342 setPadding(padding.first, getPaddingTop(), padding.second, getPaddingBottom());
Evan Laird058c8ae2018-01-12 14:26:10 -0500343 }
Selim Cinekd21232e2019-06-20 14:15:59 -0700344
345 public void setHeadsUpVisible(boolean headsUpVisible) {
346 mHeadsUpVisible = headsUpVisible;
347 updateVisibility();
348 }
349
350 @Override
351 protected boolean shouldPanelBeVisible() {
352 return mHeadsUpVisible || super.shouldPanelBeVisible();
353 }
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700354}