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 mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) == 0; 89 if (mContext.isNavBarForceVisible()) { 90 if (mIsImmersiveMode) { 91 startIconDimming(); 92 } else { 93 startIconUndimming(); 94 } 95 mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate( 96 mKidsModeAccessibilityDelegate); 97 mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate( 98 mKidsModeAccessibilityDelegate); 99 } else { 100 mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(null); 101 mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(null); 102 } 103 } 104 105 /** Clean up animations. */ onDestroy()106 public void onDestroy() { 107 startIconUndimming(); 108 mControllers.navbarButtonsViewController.setHomeButtonAccessibilityDelegate(null); 109 mControllers.navbarButtonsViewController.setBackButtonAccessibilityDelegate(null); 110 } 111 startIconUndimming()112 private void startIconUndimming() { 113 mHandler.removeCallbacks(mDimmingRunnable); 114 mHandler.removeCallbacks(mUndimmingRunnable); 115 mHandler.post(mUndimmingRunnable); 116 } 117 undimIcons()118 private void undimIcons() { 119 mIconAlphaForDimming.animateToValue(NAV_BAR_ICONS_UNDIM_PCT).setDuration( 120 NAV_BAR_ICONS_UNDIM_ANIMATION_DURATION_MS).start(); 121 } 122 startIconDimming()123 private void startIconDimming() { 124 mHandler.removeCallbacks(mDimmingRunnable); 125 int accessibilityDimmingTimeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis( 126 mContext, NAV_BAR_ICONS_DIM_ANIMATION_START_DELAY_MS, 127 (FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS)); 128 mHandler.postDelayed(mDimmingRunnable, accessibilityDimmingTimeout); 129 } 130 dimIcons()131 private void dimIcons() { 132 mIconAlphaForDimming.animateToValue(NAV_BAR_ICONS_DIM_PCT).setDuration( 133 NAV_BAR_ICONS_DIM_ANIMATION_DURATION_MS).start(); 134 } 135 136 /** 137 * Returns whether the taskbar is always visible in immersive mode. 138 */ isNavbarShownInImmersiveMode()139 private boolean isNavbarShownInImmersiveMode() { 140 return mIsImmersiveMode && mContext.isNavBarForceVisible(); 141 } 142 updateIconDimmingAlpha()143 private void updateIconDimmingAlpha() { 144 if (mControllers == null || mControllers.navbarButtonsViewController == null) { 145 return; 146 } 147 148 MultiPropertyFactory<View> ba = 149 mControllers.navbarButtonsViewController.getBackButtonAlpha(); 150 if (ba != null) { 151 ba.get(ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value); 152 } 153 MultiPropertyFactory<View> ha = 154 mControllers.navbarButtonsViewController.getHomeButtonAlpha(); 155 if (ba != null) { 156 ha.get(ALPHA_INDEX_IMMERSIVE_MODE).setValue(mIconAlphaForDimming.value); 157 } 158 } 159 160 @Override onControllerInterceptTouchEvent(MotionEvent ev)161 public boolean onControllerInterceptTouchEvent(MotionEvent ev) { 162 if (!isNavbarShownInImmersiveMode()) { 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