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