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