• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.launcher3.taskbar;
18 
19 import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
20 import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
21 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
22 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
23 
24 import static com.android.launcher3.taskbar.NavbarButtonsViewController.ALPHA_INDEX_IMMERSIVE_MODE;
25 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
26 
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.Looper;
30 import android.view.MotionEvent;
31 import android.view.View;
32 
33 import com.android.launcher3.anim.AnimatedFloat;
34 import com.android.launcher3.compat.AccessibilityManagerCompat;
35 import com.android.launcher3.util.MultiPropertyFactory;
36 import com.android.launcher3.util.TouchController;
37 import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
38 
39 /**
40  * Controller for taskbar when force visible in immersive mode is set.
41  */
42 public class TaskbarForceVisibleImmersiveController implements TouchController {
43     private static final int NAV_BAR_ICONS_DIM_ANIMATION_START_DELAY_MS = 4500;
44     private static final int NAV_BAR_ICONS_DIM_ANIMATION_DURATION_MS = 500;
45     private static final int NAV_BAR_ICONS_UNDIM_ANIMATION_DURATION_MS = 250;
46     private static final float NAV_BAR_ICONS_DIM_PCT = 0.15f;
47     private static final float NAV_BAR_ICONS_UNDIM_PCT = 1f;
48 
49     private final TaskbarActivityContext mContext;
50     private final Handler mHandler = new Handler(Looper.getMainLooper());
51     private final Runnable mDimmingRunnable = this::dimIcons;
52     private final Runnable mUndimmingRunnable = this::undimIcons;
53     private final AnimatedFloat mIconAlphaForDimming = new AnimatedFloat(
54             this::updateIconDimmingAlpha);
55     private final View.AccessibilityDelegate mKidsModeAccessibilityDelegate =
56             new View.AccessibilityDelegate() {
57                 @Override
58                 public boolean performAccessibilityAction(View host, int action, Bundle args) {
59                     if (action == ACTION_ACCESSIBILITY_FOCUS || action == ACTION_CLICK) {
60                         // Animate undimming of icons on an a11y event, followed by starting the
61                         // dimming animation (after its timeout has expired). Both can be called in
62                         // succession, as the playing of the two animations in a row is managed by
63                         // mHandler's message queue.
64                         startIconUndimming();
65                         startIconDimming();
66                     }
67                     return super.performAccessibilityAction(host, action, args);
68                 }
69             };
70 
71     // Initialized in init.
72     private TaskbarControllers mControllers;
73     private boolean mIsImmersiveMode;
74 
TaskbarForceVisibleImmersiveController(TaskbarActivityContext context)75     public TaskbarForceVisibleImmersiveController(TaskbarActivityContext context) {
76         mContext = context;
77     }
78 
79     /**
80      * Initialize controllers.
81      */
init(TaskbarControllers controllers)82     public void init(TaskbarControllers controllers) {
83         mControllers = controllers;
84     }
85 
86     /** Update values tracked via sysui flags. */
updateSysuiFlags(@ystemUiStateFlags long sysuiFlags)87     public void updateSysuiFlags(@SystemUiStateFlags long sysuiFlags) {
88         if (mContext.isPhoneMode()) {
89             return;
90         }
91         mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) == 0;
92         if (mContext.isNavBarForceVisible()) {
93             if (mIsImmersiveMode) {
94                 startIconDimming();
95             } else {
96                 startIconUndimming();
97             }
98             mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(
99                     mKidsModeAccessibilityDelegate);
100             mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(
101                     mKidsModeAccessibilityDelegate);
102         } else {
103             mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(null);
104             mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(null);
105         }
106     }
107 
108     /** Clean up animations. */
onDestroy()109     public void onDestroy() {
110         startIconUndimming();
111         if (mControllers != null) {
112             mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(null);
113             mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(null);
114         }
115     }
116 
startIconUndimming()117     private void startIconUndimming() {
118         mHandler.removeCallbacks(mDimmingRunnable);
119         mHandler.removeCallbacks(mUndimmingRunnable);
120         mHandler.post(mUndimmingRunnable);
121     }
122 
undimIcons()123     private void undimIcons() {
124         mIconAlphaForDimming.animateToValue(NAV_BAR_ICONS_UNDIM_PCT).setDuration(
125                 NAV_BAR_ICONS_UNDIM_ANIMATION_DURATION_MS).start();
126     }
127 
startIconDimming()128     private void startIconDimming() {
129         mHandler.removeCallbacks(mDimmingRunnable);
130         int accessibilityDimmingTimeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis(
131                 mContext, NAV_BAR_ICONS_DIM_ANIMATION_START_DELAY_MS,
132                 (FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS));
133         mHandler.postDelayed(mDimmingRunnable, accessibilityDimmingTimeout);
134     }
135 
dimIcons()136     private void dimIcons() {
137         mIconAlphaForDimming.animateToValue(NAV_BAR_ICONS_DIM_PCT).setDuration(
138                 NAV_BAR_ICONS_DIM_ANIMATION_DURATION_MS).start();
139     }
140 
141     /**
142      * Returns whether the taskbar is always visible in immersive mode.
143      */
isNavbarShownInImmersiveMode()144     private boolean isNavbarShownInImmersiveMode() {
145         return mIsImmersiveMode && mContext.isNavBarForceVisible();
146     }
147 
updateIconDimmingAlpha()148     private void updateIconDimmingAlpha() {
149         if (mControllers == null || mControllers.navbarButtonsViewController == null) {
150             return;
151         }
152 
153         MultiPropertyFactory<View> ba =
154                 mControllers.navbarButtonsViewController.getBackButtonAlpha();
155         if (ba != null) {
156             ba.get(ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value);
157         }
158         MultiPropertyFactory<View> ha =
159                 mControllers.navbarButtonsViewController.getHomeButtonAlpha();
160         if (ba != null) {
161             ha.get(ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value);
162         }
163     }
164 
165     @Override
onControllerInterceptTouchEvent(MotionEvent ev)166     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
167         if (!isNavbarShownInImmersiveMode()) {
168             return false;
169         }
170         return onControllerTouchEvent(ev);
171     }
172 
173     @Override
onControllerTouchEvent(MotionEvent ev)174     public boolean onControllerTouchEvent(MotionEvent ev) {
175         switch (ev.getAction()) {
176             case MotionEvent.ACTION_DOWN:
177                 startIconUndimming();
178                 break;
179             case MotionEvent.ACTION_UP:
180             case MotionEvent.ACTION_CANCEL:
181                 startIconDimming();
182                 break;
183         }
184         return false;
185     }
186 }
187