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