• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.statusbar.phone;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.content.res.Configuration;
22 import android.content.res.Resources;
23 import android.graphics.Rect;
24 import android.view.View;
25 import android.view.ViewTreeObserver;
26 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
27 
28 import com.android.systemui.Dependency;
29 import com.android.systemui.assist.AssistManager;
30 import com.android.systemui.bubbles.BubbleController;
31 import com.android.systemui.statusbar.policy.ConfigurationController;
32 import 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  */
38 public 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 
StatusBarTouchableRegionManager(@onNull Context context, HeadsUpManagerPhone headsUpManager, @NonNull StatusBar statusBar, @NonNull View statusBarWindowView)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 
63         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      */
updateTouchableRegion()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()
79                         || 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      */
updateTouchableRegionAfterLayout()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      */
setIsStatusBarExpanded(boolean isExpanded)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
onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info)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         }
142     }
143 
144     @Override
onConfigChanged(Configuration newConfig)145     public void onConfigChanged(Configuration newConfig) {
146         initResources();
147     }
148 
149     @Override
onDensityOrFontScaleChanged()150     public void onDensityOrFontScaleChanged() {
151         initResources();
152     }
153 
154     @Override
onOverlayChanged()155     public void onOverlayChanged() {
156         initResources();
157     }
158 
initResources()159     private void initResources() {
160         Resources resources = mContext.getResources();
161         mStatusBarHeight = resources.getDimensionPixelSize(
162                 com.android.internal.R.dimen.status_bar_height);
163     }
164 }
165