blob: e949ade28b59727bcaa33bd4b31f5f91602a9b3c [file] [log] [blame]
James O'Learycbea84d2019-03-14 11:18:24 -04001/*
2 * Copyright (C) 2019 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.phone;
18
19import android.annotation.NonNull;
20import android.content.Context;
21import android.content.res.Configuration;
22import android.content.res.Resources;
23import android.graphics.Rect;
24import android.view.View;
25import android.view.ViewTreeObserver;
26import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
27
28import com.android.systemui.Dependency;
29import com.android.systemui.assist.AssistManager;
30import com.android.systemui.bubbles.BubbleController;
31import com.android.systemui.statusbar.policy.ConfigurationController;
32import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
33
34/**
35 * Manages what parts of the status bar are touchable. Clients are primarily UI that displays in the
36 * status bar even though the UI doesn't look like part of the status bar.
37 */
38public final class StatusBarTouchableRegionManager implements
39 OnComputeInternalInsetsListener, ConfigurationListener {
40
41 private final AssistManager mAssistManager = Dependency.get(AssistManager.class);
42 private final BubbleController mBubbleController = Dependency.get(BubbleController.class);
43 private final Context mContext;
44 private final HeadsUpManagerPhone mHeadsUpManager;
45 private boolean mIsStatusBarExpanded = false;
46 private boolean mShouldAdjustInsets = false;
47 private final StatusBar mStatusBar;
48 private int mStatusBarHeight;
49 private final View mStatusBarWindowView;
50 private boolean mForceCollapsedUntilLayout = false;
51
52 public StatusBarTouchableRegionManager(@NonNull Context context,
53 HeadsUpManagerPhone headsUpManager,
54 @NonNull StatusBar statusBar,
55 @NonNull View statusBarWindowView) {
56 mContext = context;
57 mHeadsUpManager = headsUpManager;
58 mStatusBar = statusBar;
59 mStatusBarWindowView = statusBarWindowView;
60
61 initResources();
62
James O'Learycbea84d2019-03-14 11:18:24 -040063 mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
64 updateTouchableRegion();
65 });
66 Dependency.get(ConfigurationController.class).addCallback(this);
67 }
68
69 /**
70 * Set the touchable portion of the status bar based on what elements are visible.
71 */
72 public void updateTouchableRegion() {
73 boolean hasCutoutInset = (mStatusBarWindowView != null)
74 && (mStatusBarWindowView.getRootWindowInsets() != null)
75 && (mStatusBarWindowView.getRootWindowInsets().getDisplayCutout() != null);
76 boolean shouldObserve =
77 mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpManager.isHeadsUpGoingAway()
78 || mBubbleController.hasBubbles()
James O'Learycbea84d2019-03-14 11:18:24 -040079 || mForceCollapsedUntilLayout
80 || hasCutoutInset;
81 if (shouldObserve == mShouldAdjustInsets) {
82 return;
83 }
84
85 if (shouldObserve) {
86 mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
87 mStatusBarWindowView.requestLayout();
88 } else {
89 mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
90 }
91 mShouldAdjustInsets = shouldObserve;
92 }
93
94 /**
95 * Calls {@code updateTouchableRegion()} after a layout pass completes.
96 */
97 public void updateTouchableRegionAfterLayout() {
98 mForceCollapsedUntilLayout = true;
99 mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
100 @Override
101 public void onLayoutChange(View v, int left, int top, int right, int bottom,
102 int oldLeft,
103 int oldTop, int oldRight, int oldBottom) {
104 if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
105 mStatusBarWindowView.removeOnLayoutChangeListener(this);
106 mForceCollapsedUntilLayout = false;
107 updateTouchableRegion();
108 }
109 }
110 });
111 }
112
113 /**
114 * Notify that the status bar panel gets expanded or collapsed.
115 *
116 * @param isExpanded True to notify expanded, false to notify collapsed.
117 */
118 public void setIsStatusBarExpanded(boolean isExpanded) {
119 if (isExpanded != mIsStatusBarExpanded) {
120 mIsStatusBarExpanded = isExpanded;
121 if (isExpanded) {
122 // make sure our state is sane
123 mForceCollapsedUntilLayout = false;
124 }
125 updateTouchableRegion();
126 }
127 }
128
129 @Override
130 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
131 if (mIsStatusBarExpanded || mStatusBar.isBouncerShowing()) {
132 // The touchable region is always the full area when expanded
133 return;
134 }
135
136 mHeadsUpManager.updateTouchableRegion(info);
137
138 Rect bubbleRect = mBubbleController.getTouchableRegion();
139 if (bubbleRect != null) {
140 info.touchableRegion.union(bubbleRect);
141 }
James O'Learycbea84d2019-03-14 11:18:24 -0400142 }
143
144 @Override
145 public void onConfigChanged(Configuration newConfig) {
146 initResources();
147 }
148
149 @Override
150 public void onDensityOrFontScaleChanged() {
151 initResources();
152 }
153
154 @Override
155 public void onOverlayChanged() {
156 initResources();
157 }
158
159 private void initResources() {
160 Resources resources = mContext.getResources();
161 mStatusBarHeight = resources.getDimensionPixelSize(
162 com.android.internal.R.dimen.status_bar_height);
163 }
164}