1 /* 2 * Copyright (C) 2021 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.systemui.accessibility.floatingmenu; 18 19 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; 20 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; 21 22 import android.content.Context; 23 import android.hardware.display.DisplayManager; 24 import android.os.UserHandle; 25 import android.text.TextUtils; 26 import android.view.Display; 27 import android.view.WindowManager; 28 import android.view.accessibility.AccessibilityManager; 29 30 import androidx.annotation.MainThread; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.keyguard.KeyguardUpdateMonitor; 34 import com.android.keyguard.KeyguardUpdateMonitorCallback; 35 import com.android.systemui.accessibility.AccessibilityButtonModeObserver; 36 import com.android.systemui.accessibility.AccessibilityButtonModeObserver.AccessibilityButtonMode; 37 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver; 38 import com.android.systemui.dagger.SysUISingleton; 39 import com.android.systemui.settings.DisplayTracker; 40 import com.android.systemui.util.settings.SecureSettings; 41 42 import javax.inject.Inject; 43 44 /** A controller to handle the lifecycle of accessibility floating menu. */ 45 @MainThread 46 @SysUISingleton 47 public class AccessibilityFloatingMenuController implements 48 AccessibilityButtonModeObserver.ModeChangedListener, 49 AccessibilityButtonTargetsObserver.TargetsChangedListener { 50 51 private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; 52 private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver; 53 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 54 55 private Context mContext; 56 private final WindowManager mWindowManager; 57 private final DisplayManager mDisplayManager; 58 private final AccessibilityManager mAccessibilityManager; 59 60 private final SecureSettings mSecureSettings; 61 private final DisplayTracker mDisplayTracker; 62 @VisibleForTesting 63 IAccessibilityFloatingMenu mFloatingMenu; 64 private int mBtnMode; 65 private String mBtnTargets; 66 private boolean mIsKeyguardVisible; 67 68 @VisibleForTesting 69 final KeyguardUpdateMonitorCallback mKeyguardCallback = new KeyguardUpdateMonitorCallback() { 70 71 @Override 72 public void onUserUnlocked() { 73 handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets); 74 } 75 76 @Override 77 public void onKeyguardVisibilityChanged(boolean visible) { 78 mIsKeyguardVisible = visible; 79 handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets); 80 } 81 82 @Override 83 public void onUserSwitching(int userId) { 84 destroyFloatingMenu(); 85 } 86 87 @Override 88 public void onUserSwitchComplete(int userId) { 89 mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0); 90 mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); 91 mBtnTargets = 92 mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets(); 93 handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets); 94 } 95 }; 96 97 @Inject AccessibilityFloatingMenuController(Context context, WindowManager windowManager, DisplayManager displayManager, AccessibilityManager accessibilityManager, AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver, AccessibilityButtonModeObserver accessibilityButtonModeObserver, KeyguardUpdateMonitor keyguardUpdateMonitor, SecureSettings secureSettings, DisplayTracker displayTracker)98 public AccessibilityFloatingMenuController(Context context, 99 WindowManager windowManager, 100 DisplayManager displayManager, 101 AccessibilityManager accessibilityManager, 102 AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver, 103 AccessibilityButtonModeObserver accessibilityButtonModeObserver, 104 KeyguardUpdateMonitor keyguardUpdateMonitor, 105 SecureSettings secureSettings, 106 DisplayTracker displayTracker) { 107 mContext = context; 108 mWindowManager = windowManager; 109 mDisplayManager = displayManager; 110 mAccessibilityManager = accessibilityManager; 111 mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver; 112 mAccessibilityButtonModeObserver = accessibilityButtonModeObserver; 113 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 114 mSecureSettings = secureSettings; 115 mDisplayTracker = displayTracker; 116 117 mIsKeyguardVisible = false; 118 } 119 120 /** 121 * Handles visibility of the accessibility floating menu when accessibility button mode changes. 122 * 123 * @param mode Current accessibility button mode. 124 */ 125 @Override onAccessibilityButtonModeChanged(@ccessibilityButtonMode int mode)126 public void onAccessibilityButtonModeChanged(@AccessibilityButtonMode int mode) { 127 mBtnMode = mode; 128 handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets); 129 } 130 131 /** 132 * Handles visibility of the accessibility floating menu when accessibility button targets 133 * changes. 134 * List should come from {@link android.provider.Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS}. 135 * @param targets Current accessibility button list. 136 */ 137 @Override onAccessibilityButtonTargetsChanged(String targets)138 public void onAccessibilityButtonTargetsChanged(String targets) { 139 mBtnTargets = targets; 140 handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets); 141 } 142 143 /** Initializes the AccessibilityFloatingMenuController configurations. */ init()144 public void init() { 145 mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); 146 mBtnTargets = mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets(); 147 registerContentObservers(); 148 } 149 registerContentObservers()150 private void registerContentObservers() { 151 mAccessibilityButtonModeObserver.addListener(this); 152 mAccessibilityButtonTargetsObserver.addListener(this); 153 mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); 154 } 155 156 /** 157 * Handles the accessibility floating menu visibility with the given values. 158 * 159 * @param keyguardVisible the keyguard visibility status. Not show the 160 * {@link MenuView} when keyguard appears. 161 * @param mode accessibility button mode {@link AccessibilityButtonMode} 162 * @param targets accessibility button list; it should comes from 163 * {@link android.provider.Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS}. 164 */ handleFloatingMenuVisibility(boolean keyguardVisible, @AccessibilityButtonMode int mode, String targets)165 private void handleFloatingMenuVisibility(boolean keyguardVisible, 166 @AccessibilityButtonMode int mode, String targets) { 167 if (keyguardVisible) { 168 destroyFloatingMenu(); 169 return; 170 } 171 172 if (shouldShowFloatingMenu(mode, targets)) { 173 showFloatingMenu(); 174 } else { 175 destroyFloatingMenu(); 176 } 177 } 178 shouldShowFloatingMenu(@ccessibilityButtonMode int mode, String targets)179 private boolean shouldShowFloatingMenu(@AccessibilityButtonMode int mode, String targets) { 180 return mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU && !TextUtils.isEmpty(targets); 181 } 182 showFloatingMenu()183 private void showFloatingMenu() { 184 if (mFloatingMenu == null) { 185 final Display defaultDisplay = mDisplayManager.getDisplay( 186 mDisplayTracker.getDefaultDisplayId()); 187 final Context windowContext = mContext.createWindowContext(defaultDisplay, 188 TYPE_NAVIGATION_BAR_PANEL, /* options= */ null); 189 mFloatingMenu = new MenuViewLayerController(windowContext, mWindowManager, 190 mAccessibilityManager, mSecureSettings); 191 } 192 193 mFloatingMenu.show(); 194 } 195 destroyFloatingMenu()196 private void destroyFloatingMenu() { 197 if (mFloatingMenu == null) { 198 return; 199 } 200 201 mFloatingMenu.hide(); 202 mFloatingMenu = null; 203 } 204 } 205