blob: 7bc17765d9fc4813375fe14c980e63e388a7cc24 [file] [log] [blame]
Abhijoy Saha84590ea2020-01-14 12:31:00 -08001/*
2 * Copyright (C) 2020 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
Heemin Seog9b078d32020-04-21 15:07:32 -070017package com.android.systemui.car.window;
Abhijoy Saha84590ea2020-01-14 12:31:00 -080018
Abhijoy Sahaf0988b92020-06-12 23:08:44 +000019import static android.view.WindowInsets.Type.statusBars;
Heemin Seoge076c232021-02-26 14:03:01 -080020import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS;
Abhijoy Sahaf0988b92020-06-12 23:08:44 +000021
Abhijoy Saha84590ea2020-01-14 12:31:00 -080022import android.view.View;
23import android.view.ViewGroup;
24import android.view.ViewStub;
Abhijoy Sahaf0988b92020-06-12 23:08:44 +000025import android.view.WindowInsets;
Abhijoy Saha84590ea2020-01-14 12:31:00 -080026
Heemin Seoge076c232021-02-26 14:03:01 -080027import androidx.annotation.IdRes;
28
29import com.android.car.ui.FocusArea;
30
Abhijoy Saha84590ea2020-01-14 12:31:00 -080031/**
32 * Owns a {@link View} that is present in SystemUIOverlayWindow.
33 */
34public class OverlayViewController {
35 private final int mStubId;
36 private final OverlayViewGlobalStateController mOverlayViewGlobalStateController;
37
38 private View mLayout;
39
40 public OverlayViewController(int stubId,
41 OverlayViewGlobalStateController overlayViewGlobalStateController) {
42 mLayout = null;
43 mStubId = stubId;
44 mOverlayViewGlobalStateController = overlayViewGlobalStateController;
45 }
46
47 /**
48 * Shows content of {@link OverlayViewController}.
49 *
50 * Should be used to show view externally and in particular by {@link OverlayViewMediator}.
51 */
52 public final void start() {
53 mOverlayViewGlobalStateController.showView(/* viewController= */ this, this::show);
54 }
55
56 /**
57 * Hides content of {@link OverlayViewController}.
58 *
59 * Should be used to hide view externally and in particular by {@link OverlayViewMediator}.
60 */
61 public final void stop() {
62 mOverlayViewGlobalStateController.hideView(/* viewController= */ this, this::hide);
63 }
64
Abhijoy Saha84590ea2020-01-14 12:31:00 -080065 /**
66 * Inflate layout owned by controller.
67 */
68 public final void inflate(ViewGroup baseLayout) {
69 ViewStub viewStub = baseLayout.findViewById(mStubId);
70 mLayout = viewStub.inflate();
71 onFinishInflate();
72 }
73
74 /**
75 * Called once inflate finishes.
76 */
77 protected void onFinishInflate() {
78 // no-op
79 }
80
81 /**
Abhijoy Saha36451842020-04-23 22:41:39 +000082 * Returns {@code true} if layout owned by controller has been inflated.
Abhijoy Saha84590ea2020-01-14 12:31:00 -080083 */
84 public final boolean isInflated() {
85 return mLayout != null;
86 }
87
88 private void show() {
89 if (mLayout == null) {
90 // layout must be inflated before show() is called.
91 return;
92 }
93 showInternal();
94 }
95
96 /**
97 * Subclasses should override this method to implement reveal animations and implement logic
98 * specific to when the layout owned by the controller is shown.
99 *
100 * Should only be overridden by Superclass but not called by any {@link OverlayViewMediator}.
101 */
102 protected void showInternal() {
103 mLayout.setVisibility(View.VISIBLE);
104 }
105
106 private void hide() {
107 if (mLayout == null) {
108 // layout must be inflated before hide() is called.
109 return;
110 }
111 hideInternal();
112 }
113
114 /**
115 * Subclasses should override this method to implement conceal animations and implement logic
116 * specific to when the layout owned by the controller is hidden.
117 *
118 * Should only be overridden by Superclass but not called by any {@link OverlayViewMediator}.
119 */
120 protected void hideInternal() {
121 mLayout.setVisibility(View.GONE);
122 }
123
124 /**
125 * Provides access to layout owned by controller.
126 */
127 protected final View getLayout() {
128 return mLayout;
129 }
Heemin Seogb2c20a62020-03-23 17:35:07 -0700130
131 /** Returns the {@link OverlayViewGlobalStateController}. */
132 protected final OverlayViewGlobalStateController getOverlayViewGlobalStateController() {
133 return mOverlayViewGlobalStateController;
134 }
Abhijoy Saha36451842020-04-23 22:41:39 +0000135
Heemin Seoge076c232021-02-26 14:03:01 -0800136 /** Returns whether the view controlled by this controller is visible. */
137 public final boolean isVisible() {
138 return mLayout.getVisibility() == View.VISIBLE;
139 }
140
141 /**
142 * Returns the ID of the focus area that should receive focus when this view is the
143 * topmost view or {@link View#NO_ID} if there is no focus area.
144 */
145 @IdRes
146 protected int getFocusAreaViewId() {
147 return View.NO_ID;
148 }
149
150 /** Returns whether the view controlled by this controller has rotary focus. */
151 protected final boolean hasRotaryFocus() {
152 return !mLayout.isInTouchMode() && mLayout.hasFocus();
153 }
154
155 /**
156 * Sets whether this view allows rotary focus. This should be set to {@code true} for the
157 * topmost layer in the overlay window and {@code false} for the others.
158 */
159 public void setAllowRotaryFocus(boolean allowRotaryFocus) {
160 if (!isInflated()) {
161 return;
162 }
163
164 if (!(mLayout instanceof ViewGroup)) {
165 return;
166 }
167
168 ViewGroup viewGroup = (ViewGroup) mLayout;
169 viewGroup.setDescendantFocusability(allowRotaryFocus
170 ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
171 : ViewGroup.FOCUS_BLOCK_DESCENDANTS);
172 }
173
174 /**
175 * Refreshes the rotary focus in this view if we are in rotary mode. If the view already has
176 * rotary focus, it leaves the focus alone. Returns {@code true} if a new view was focused.
177 */
178 public boolean refreshRotaryFocusIfNeeded() {
179 if (mLayout.isInTouchMode()) {
180 return false;
181 }
182
183 if (hasRotaryFocus()) {
184 return false;
185 }
186
187 View view = mLayout.findViewById(getFocusAreaViewId());
188 if (view == null || !(view instanceof FocusArea)) {
189 return mLayout.requestFocus();
190 }
191
192 FocusArea focusArea = (FocusArea) view;
193 return focusArea.performAccessibilityAction(ACTION_FOCUS, /* arguments= */ null);
194 }
195
Abhijoy Saha36451842020-04-23 22:41:39 +0000196 /**
197 * Returns {@code true} if heads up notifications should be displayed over this view.
198 */
199 protected boolean shouldShowHUN() {
200 return true;
201 }
202
203 /**
Heemin Seogb6f4a0c2020-09-01 16:08:29 -0700204 * Returns {@code true} if navigation bar insets should be displayed over this view. Has no
205 * effect if {@link #shouldFocusWindow} returns {@code false}.
Abhijoy Saha36451842020-04-23 22:41:39 +0000206 */
Heemin Seogb6f4a0c2020-09-01 16:08:29 -0700207 protected boolean shouldShowNavigationBarInsets() {
Abhijoy Saha36451842020-04-23 22:41:39 +0000208 return false;
209 }
kwaky5dc67de2020-05-28 11:55:54 -0700210
211 /**
Heemin Seogb6f4a0c2020-09-01 16:08:29 -0700212 * Returns {@code true} if status bar insets should be displayed over this view. Has no
213 * effect if {@link #shouldFocusWindow} returns {@code false}.
Abhijoy Sahaf0988b92020-06-12 23:08:44 +0000214 */
Heemin Seogb6f4a0c2020-09-01 16:08:29 -0700215 protected boolean shouldShowStatusBarInsets() {
Abhijoy Sahaf0988b92020-06-12 23:08:44 +0000216 return false;
217 }
218
219 /**
kwaky5dc67de2020-05-28 11:55:54 -0700220 * Returns {@code true} if this view should be hidden during the occluded state.
221 */
222 protected boolean shouldShowWhenOccluded() {
223 return false;
224 }
Abhijoy Sahaf0988b92020-06-12 23:08:44 +0000225
226 /**
Heemin Seogb6f4a0c2020-09-01 16:08:29 -0700227 * Returns {@code true} if the window should be focued when this view is visible. Note that
228 * returning {@code false} here means that {@link #shouldShowStatusBarInsets} and
229 * {@link #shouldShowNavigationBarInsets} will have no effect.
230 */
231 protected boolean shouldFocusWindow() {
232 return true;
233 }
234
235 /**
Abhijoy Sahaf0988b92020-06-12 23:08:44 +0000236 * Returns the insets types to fit to the sysui overlay window when this
237 * {@link OverlayViewController} is in the foreground.
238 */
239 @WindowInsets.Type.InsetsType
240 protected int getInsetTypesToFit() {
241 return statusBars();
242 }
Abhijoy Saha84590ea2020-01-14 12:31:00 -0800243}