1 /* 2 * Copyright (C) 2019 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; 18 19 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; 20 21 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; 22 23 import android.annotation.MainThread; 24 import android.annotation.Nullable; 25 import android.content.Context; 26 import android.content.res.Configuration; 27 import android.graphics.Rect; 28 import android.hardware.display.DisplayManager; 29 import android.os.Handler; 30 import android.view.Display; 31 import android.view.SurfaceControl; 32 import android.view.accessibility.AccessibilityManager; 33 import android.view.accessibility.IRemoteMagnificationAnimationCallback; 34 import android.view.accessibility.IWindowMagnificationConnection; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.internal.graphics.SfVsyncFrameCallbackProvider; 38 import com.android.systemui.SystemUI; 39 import com.android.systemui.dagger.SysUISingleton; 40 import com.android.systemui.dagger.qualifiers.Main; 41 import com.android.systemui.model.SysUiState; 42 import com.android.systemui.recents.OverviewProxyService; 43 import com.android.systemui.statusbar.CommandQueue; 44 45 import java.io.FileDescriptor; 46 import java.io.PrintWriter; 47 48 import javax.inject.Inject; 49 50 /** 51 * Class to handle the interaction with 52 * {@link com.android.server.accessibility.AccessibilityManagerService}. It invokes 53 * {@link AccessibilityManager#setWindowMagnificationConnection(IWindowMagnificationConnection)} 54 * when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called. 55 */ 56 @SysUISingleton 57 public class WindowMagnification extends SystemUI implements WindowMagnifierCallback, 58 CommandQueue.Callbacks { 59 private static final String TAG = "WindowMagnification"; 60 61 private final ModeSwitchesController mModeSwitchesController; 62 private final Handler mHandler; 63 private final AccessibilityManager mAccessibilityManager; 64 private final CommandQueue mCommandQueue; 65 private final OverviewProxyService mOverviewProxyService; 66 67 private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl; 68 private Configuration mLastConfiguration; 69 private SysUiState mSysUiState; 70 71 private static class AnimationControllerSupplier extends 72 DisplayIdIndexSupplier<WindowMagnificationAnimationController> { 73 74 private final Context mContext; 75 private final Handler mHandler; 76 private final WindowMagnifierCallback mWindowMagnifierCallback; 77 private final SysUiState mSysUiState; 78 AnimationControllerSupplier(Context context, Handler handler, WindowMagnifierCallback windowMagnifierCallback, DisplayManager displayManager, SysUiState sysUiState)79 AnimationControllerSupplier(Context context, Handler handler, 80 WindowMagnifierCallback windowMagnifierCallback, 81 DisplayManager displayManager, SysUiState sysUiState) { 82 super(displayManager); 83 mContext = context; 84 mHandler = handler; 85 mWindowMagnifierCallback = windowMagnifierCallback; 86 mSysUiState = sysUiState; 87 } 88 89 @Override createInstance(Display display)90 protected WindowMagnificationAnimationController createInstance(Display display) { 91 final Context windowContext = mContext.createWindowContext(display, 92 TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null); 93 final WindowMagnificationController controller = new WindowMagnificationController( 94 windowContext, 95 mHandler, new SfVsyncFrameCallbackProvider(), null, 96 new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState); 97 return new WindowMagnificationAnimationController(windowContext, controller); 98 } 99 } 100 101 @VisibleForTesting 102 DisplayIdIndexSupplier<WindowMagnificationAnimationController> mAnimationControllerSupplier; 103 104 @Inject WindowMagnification(Context context, @Main Handler mainHandler, CommandQueue commandQueue, ModeSwitchesController modeSwitchesController, SysUiState sysUiState, OverviewProxyService overviewProxyService)105 public WindowMagnification(Context context, @Main Handler mainHandler, 106 CommandQueue commandQueue, ModeSwitchesController modeSwitchesController, 107 SysUiState sysUiState, OverviewProxyService overviewProxyService) { 108 super(context); 109 mHandler = mainHandler; 110 mLastConfiguration = new Configuration(context.getResources().getConfiguration()); 111 mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); 112 mCommandQueue = commandQueue; 113 mModeSwitchesController = modeSwitchesController; 114 mSysUiState = sysUiState; 115 mOverviewProxyService = overviewProxyService; 116 mAnimationControllerSupplier = new AnimationControllerSupplier(context, 117 mHandler, this, context.getSystemService(DisplayManager.class), sysUiState); 118 } 119 120 @Override onConfigurationChanged(Configuration newConfig)121 public void onConfigurationChanged(Configuration newConfig) { 122 final int configDiff = newConfig.diff(mLastConfiguration); 123 mLastConfiguration.setTo(newConfig); 124 mAnimationControllerSupplier.forEach( 125 animationController -> animationController.onConfigurationChanged(configDiff)); 126 if (mModeSwitchesController != null) { 127 mModeSwitchesController.onConfigurationChanged(configDiff); 128 } 129 } 130 131 @Override start()132 public void start() { 133 mCommandQueue.addCallback(this); 134 mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() { 135 @Override 136 public void onConnectionChanged(boolean isConnected) { 137 if (isConnected) { 138 updateSysUiStateFlag(); 139 } 140 } 141 }); 142 } 143 updateSysUiStateFlag()144 private void updateSysUiStateFlag() { 145 //TODO(b/187510533): support multi-display once SysuiState supports it. 146 final WindowMagnificationAnimationController controller = 147 mAnimationControllerSupplier.valueAt(Display.DEFAULT_DISPLAY); 148 if (controller != null) { 149 controller.updateSysUiStateFlag(); 150 } else { 151 // The instance is initialized when there is an IPC request. Considering 152 // self-crash cases, we need to reset the flag in such situation. 153 mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false) 154 .commitUpdate(Display.DEFAULT_DISPLAY); 155 } 156 } 157 158 @MainThread enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable IRemoteMagnificationAnimationCallback callback)159 void enableWindowMagnification(int displayId, float scale, float centerX, float centerY, 160 @Nullable IRemoteMagnificationAnimationCallback callback) { 161 final WindowMagnificationAnimationController windowMagnificationAnimationController = 162 mAnimationControllerSupplier.get(displayId); 163 if (windowMagnificationAnimationController != null) { 164 windowMagnificationAnimationController.enableWindowMagnification(scale, centerX, 165 centerY, callback); 166 } 167 } 168 169 @MainThread setScale(int displayId, float scale)170 void setScale(int displayId, float scale) { 171 final WindowMagnificationAnimationController windowMagnificationAnimationController = 172 mAnimationControllerSupplier.get(displayId); 173 if (windowMagnificationAnimationController != null) { 174 windowMagnificationAnimationController.setScale(scale); 175 } 176 } 177 178 @MainThread moveWindowMagnifier(int displayId, float offsetX, float offsetY)179 void moveWindowMagnifier(int displayId, float offsetX, float offsetY) { 180 final WindowMagnificationAnimationController windowMagnificationAnimationController = 181 mAnimationControllerSupplier.get(displayId); 182 if (windowMagnificationAnimationController != null) { 183 windowMagnificationAnimationController.moveWindowMagnifier(offsetX, offsetY); 184 } 185 } 186 187 @MainThread disableWindowMagnification(int displayId, @Nullable IRemoteMagnificationAnimationCallback callback)188 void disableWindowMagnification(int displayId, 189 @Nullable IRemoteMagnificationAnimationCallback callback) { 190 final WindowMagnificationAnimationController windowMagnificationAnimationController = 191 mAnimationControllerSupplier.get(displayId); 192 if (windowMagnificationAnimationController != null) { 193 windowMagnificationAnimationController.deleteWindowMagnification(callback); 194 } 195 } 196 197 @Override onWindowMagnifierBoundsChanged(int displayId, Rect frame)198 public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) { 199 if (mWindowMagnificationConnectionImpl != null) { 200 mWindowMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame); 201 } 202 } 203 204 @Override onSourceBoundsChanged(int displayId, Rect sourceBounds)205 public void onSourceBoundsChanged(int displayId, Rect sourceBounds) { 206 if (mWindowMagnificationConnectionImpl != null) { 207 mWindowMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds); 208 } 209 } 210 211 @Override onPerformScaleAction(int displayId, float scale)212 public void onPerformScaleAction(int displayId, float scale) { 213 if (mWindowMagnificationConnectionImpl != null) { 214 mWindowMagnificationConnectionImpl.onPerformScaleAction(displayId, scale); 215 } 216 } 217 218 @Override onAccessibilityActionPerformed(int displayId)219 public void onAccessibilityActionPerformed(int displayId) { 220 if (mWindowMagnificationConnectionImpl != null) { 221 mWindowMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId); 222 } 223 } 224 225 @Override requestWindowMagnificationConnection(boolean connect)226 public void requestWindowMagnificationConnection(boolean connect) { 227 if (connect) { 228 setWindowMagnificationConnection(); 229 } else { 230 clearWindowMagnificationConnection(); 231 } 232 } 233 234 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)235 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 236 pw.println(TAG); 237 mAnimationControllerSupplier.forEach( 238 animationController -> animationController.dump(pw)); 239 } 240 setWindowMagnificationConnection()241 private void setWindowMagnificationConnection() { 242 if (mWindowMagnificationConnectionImpl == null) { 243 mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this, 244 mHandler, mModeSwitchesController); 245 } 246 mAccessibilityManager.setWindowMagnificationConnection( 247 mWindowMagnificationConnectionImpl); 248 } 249 clearWindowMagnificationConnection()250 private void clearWindowMagnificationConnection() { 251 mAccessibilityManager.setWindowMagnificationConnection(null); 252 //TODO: destroy controllers. 253 } 254 } 255