blob: cee395bd9a6a8ff2b659705c3434251a5440f97f [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.accessibility;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.accessibility.IWindowMagnificationConnection;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import javax.inject.Inject;
/**
* Class to handle the interaction with
* {@link com.android.server.accessibility.AccessibilityManagerService}. It invokes
* {@link AccessibilityManager#setWindowMagnificationConnection(IWindowMagnificationConnection)}
* when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
*/
@SysUISingleton
public class WindowMagnification extends SystemUI implements WindowMagnifierCallback,
CommandQueue.Callbacks {
private static final String TAG = "WindowMagnification";
private final ModeSwitchesController mModeSwitchesController;
private final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
private final CommandQueue mCommandQueue;
private final OverviewProxyService mOverviewProxyService;
private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
private Configuration mLastConfiguration;
private SysUiState mSysUiState;
private static class AnimationControllerSupplier extends
DisplayIdIndexSupplier<WindowMagnificationAnimationController> {
private final Context mContext;
private final Handler mHandler;
private final WindowMagnifierCallback mWindowMagnifierCallback;
private final SysUiState mSysUiState;
AnimationControllerSupplier(Context context, Handler handler,
WindowMagnifierCallback windowMagnifierCallback,
DisplayManager displayManager, SysUiState sysUiState) {
super(displayManager);
mContext = context;
mHandler = handler;
mWindowMagnifierCallback = windowMagnifierCallback;
mSysUiState = sysUiState;
}
@Override
protected WindowMagnificationAnimationController createInstance(Display display) {
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
final WindowMagnificationController controller = new WindowMagnificationController(
mContext,
mHandler, new SfVsyncFrameCallbackProvider(), null,
new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState);
return new WindowMagnificationAnimationController(windowContext, controller);
}
}
@VisibleForTesting
DisplayIdIndexSupplier<WindowMagnificationAnimationController> mAnimationControllerSupplier;
@Inject
public WindowMagnification(Context context, @Main Handler mainHandler,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
SysUiState sysUiState, OverviewProxyService overviewProxyService) {
super(context);
mHandler = mainHandler;
mLastConfiguration = new Configuration(context.getResources().getConfiguration());
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mCommandQueue = commandQueue;
mModeSwitchesController = modeSwitchesController;
mSysUiState = sysUiState;
mOverviewProxyService = overviewProxyService;
mAnimationControllerSupplier = new AnimationControllerSupplier(context,
mHandler, this, context.getSystemService(DisplayManager.class), sysUiState);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
final int configDiff = newConfig.diff(mLastConfiguration);
mLastConfiguration.setTo(newConfig);
mAnimationControllerSupplier.forEach(
animationController -> animationController.onConfigurationChanged(configDiff));
if (mModeSwitchesController != null) {
mModeSwitchesController.onConfigurationChanged(configDiff);
}
}
@Override
public void start() {
mCommandQueue.addCallback(this);
mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
@Override
public void onConnectionChanged(boolean isConnected) {
if (isConnected) {
updateSysUiStateFlag();
}
}
});
}
private void updateSysUiStateFlag() {
//TODO(b/187510533): support multi-display once SysuiState supports it.
final WindowMagnificationAnimationController controller =
mAnimationControllerSupplier.valueAt(Display.DEFAULT_DISPLAY);
if (controller != null) {
controller.updateSysUiStateFlag();
} else {
// The instance is initialized when there is an IPC request. Considering
// self-crash cases, we need to reset the flag in such situation.
mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
.commitUpdate(Display.DEFAULT_DISPLAY);
}
}
@MainThread
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
@Nullable IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationAnimationController windowMagnificationAnimationController =
mAnimationControllerSupplier.get(displayId);
if (windowMagnificationAnimationController != null) {
windowMagnificationAnimationController.enableWindowMagnification(scale, centerX,
centerY, callback);
}
}
@MainThread
void setScale(int displayId, float scale) {
final WindowMagnificationAnimationController windowMagnificationAnimationController =
mAnimationControllerSupplier.get(displayId);
if (windowMagnificationAnimationController != null) {
windowMagnificationAnimationController.setScale(scale);
}
}
@MainThread
void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
final WindowMagnificationAnimationController windowMagnificationAnimationController =
mAnimationControllerSupplier.get(displayId);
if (windowMagnificationAnimationController != null) {
windowMagnificationAnimationController.moveWindowMagnifier(offsetX, offsetY);
}
}
@MainThread
void disableWindowMagnification(int displayId,
@Nullable IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationAnimationController windowMagnificationAnimationController =
mAnimationControllerSupplier.get(displayId);
if (windowMagnificationAnimationController != null) {
windowMagnificationAnimationController.deleteWindowMagnification(callback);
}
}
@Override
public void onWindowMagnifierBoundsChanged(int displayId, Rect frame) {
if (mWindowMagnificationConnectionImpl != null) {
mWindowMagnificationConnectionImpl.onWindowMagnifierBoundsChanged(displayId, frame);
}
}
@Override
public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
if (mWindowMagnificationConnectionImpl != null) {
mWindowMagnificationConnectionImpl.onSourceBoundsChanged(displayId, sourceBounds);
}
}
@Override
public void onPerformScaleAction(int displayId, float scale) {
if (mWindowMagnificationConnectionImpl != null) {
mWindowMagnificationConnectionImpl.onPerformScaleAction(displayId, scale);
}
}
@Override
public void onAccessibilityActionPerformed(int displayId) {
if (mWindowMagnificationConnectionImpl != null) {
mWindowMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
}
}
@Override
public void requestWindowMagnificationConnection(boolean connect) {
if (connect) {
setWindowMagnificationConnection();
} else {
clearWindowMagnificationConnection();
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(TAG);
mAnimationControllerSupplier.forEach(
animationController -> animationController.dump(pw));
}
private void setWindowMagnificationConnection() {
if (mWindowMagnificationConnectionImpl == null) {
mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
mHandler, mModeSwitchesController);
}
mAccessibilityManager.setWindowMagnificationConnection(
mWindowMagnificationConnectionImpl);
}
private void clearWindowMagnificationConnection() {
mAccessibilityManager.setWindowMagnificationConnection(null);
//TODO: destroy controllers.
}
}