blob: e2b85fa0ac00ce739402ce6335e92127e6fd8629 [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.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent;
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.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManagerGlobal;
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.CoreStartable;
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.settings.DisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
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 implements CoreStartable, CommandQueue.Callbacks {
private static final String TAG = "WindowMagnification";
private final ModeSwitchesController mModeSwitchesController;
private final Context mContext;
private final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
private final CommandQueue mCommandQueue;
private final OverviewProxyService mOverviewProxyService;
private final DisplayTracker mDisplayTracker;
private final AccessibilityLogger mA11yLogger;
private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
private SysUiState mSysUiState;
private static class ControllerSupplier extends
DisplayIdIndexSupplier<WindowMagnificationController> {
private final Context mContext;
private final Handler mHandler;
private final WindowMagnifierCallback mWindowMagnifierCallback;
private final SysUiState mSysUiState;
private final SecureSettings mSecureSettings;
ControllerSupplier(Context context, Handler handler,
WindowMagnifierCallback windowMagnifierCallback,
DisplayManager displayManager, SysUiState sysUiState,
SecureSettings secureSettings) {
super(displayManager);
mContext = context;
mHandler = handler;
mWindowMagnifierCallback = windowMagnifierCallback;
mSysUiState = sysUiState;
mSecureSettings = secureSettings;
}
@Override
protected WindowMagnificationController createInstance(Display display) {
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
windowContext.setTheme(com.android.systemui.R.style.Theme_SystemUI);
return new WindowMagnificationController(
windowContext,
mHandler,
new WindowMagnificationAnimationController(windowContext),
new SfVsyncFrameCallbackProvider(),
null,
new SurfaceControl.Transaction(),
mWindowMagnifierCallback,
mSysUiState,
WindowManagerGlobal::getWindowSession,
mSecureSettings);
}
}
@VisibleForTesting
DisplayIdIndexSupplier<WindowMagnificationController> mMagnificationControllerSupplier;
private static class SettingsSupplier extends
DisplayIdIndexSupplier<MagnificationSettingsController> {
private final Context mContext;
private final MagnificationSettingsController.Callback mSettingsControllerCallback;
private final SecureSettings mSecureSettings;
SettingsSupplier(Context context,
MagnificationSettingsController.Callback settingsControllerCallback,
DisplayManager displayManager,
SecureSettings secureSettings) {
super(displayManager);
mContext = context;
mSettingsControllerCallback = settingsControllerCallback;
mSecureSettings = secureSettings;
}
@Override
protected MagnificationSettingsController createInstance(Display display) {
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
windowContext.setTheme(com.android.systemui.R.style.Theme_SystemUI);
return new MagnificationSettingsController(
windowContext,
new SfVsyncFrameCallbackProvider(),
mSettingsControllerCallback,
mSecureSettings);
}
}
@VisibleForTesting
DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier;
@Inject
public WindowMagnification(Context context, @Main Handler mainHandler,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
SysUiState sysUiState, OverviewProxyService overviewProxyService,
SecureSettings secureSettings, DisplayTracker displayTracker,
DisplayManager displayManager, AccessibilityLogger a11yLogger) {
mContext = context;
mHandler = mainHandler;
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mCommandQueue = commandQueue;
mModeSwitchesController = modeSwitchesController;
mSysUiState = sysUiState;
mOverviewProxyService = overviewProxyService;
mDisplayTracker = displayTracker;
mA11yLogger = a11yLogger;
mMagnificationControllerSupplier = new ControllerSupplier(context,
mHandler, mWindowMagnifierCallback,
displayManager, sysUiState, secureSettings);
mMagnificationSettingsSupplier = new SettingsSupplier(context,
mMagnificationSettingsControllerCallback, displayManager, secureSettings);
mModeSwitchesController.setClickListenerDelegate(
displayId -> mHandler.post(() -> {
showMagnificationSettingsPanel(displayId);
}));
}
@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 WindowMagnificationController controller =
mMagnificationControllerSupplier.valueAt(mDisplayTracker.getDefaultDisplayId());
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(mDisplayTracker.getDefaultDisplayId());
}
}
@MainThread
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY,
@Nullable IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.enableWindowMagnification(scale, centerX, centerY,
magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, callback);
}
}
@MainThread
void setScale(int displayId, float scale) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.setScale(scale);
}
}
@MainThread
void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
final WindowMagnificationController windowMagnificationcontroller =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationcontroller != null) {
windowMagnificationcontroller.moveWindowMagnifier(offsetX, offsetY);
}
}
@MainThread
void moveWindowMagnifierToPositionInternal(int displayId, float positionX, float positionY,
IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.moveWindowMagnifierToPosition(positionX, positionY,
callback);
}
}
@MainThread
void disableWindowMagnification(int displayId,
@Nullable IRemoteMagnificationAnimationCallback callback) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.deleteWindowMagnification(callback);
}
}
@MainThread
void showMagnificationSettingsPanel(int displayId) {
final MagnificationSettingsController magnificationSettingsController =
mMagnificationSettingsSupplier.get(displayId);
if (magnificationSettingsController != null) {
magnificationSettingsController.showMagnificationSettings();
}
}
@MainThread
void hideMagnificationSettingsPanel(int displayId) {
final MagnificationSettingsController magnificationSettingsController =
mMagnificationSettingsSupplier.get(displayId);
if (magnificationSettingsController != null) {
magnificationSettingsController.closeMagnificationSettings();
}
}
boolean isMagnificationSettingsPanelShowing(int displayId) {
final MagnificationSettingsController magnificationSettingsController =
mMagnificationSettingsSupplier.get(displayId);
if (magnificationSettingsController != null) {
return magnificationSettingsController.isMagnificationSettingsShowing();
}
return false;
}
@MainThread
void showMagnificationButton(int displayId, int magnificationMode) {
// not to show mode switch button if settings panel is already showing to
// prevent settings panel be covered by the button.
if (isMagnificationSettingsPanelShowing(displayId)) {
return;
}
mModeSwitchesController.showButton(displayId, magnificationMode);
}
@MainThread
void removeMagnificationButton(int displayId) {
mModeSwitchesController.removeButton(displayId);
}
@VisibleForTesting
final WindowMagnifierCallback mWindowMagnifierCallback = new WindowMagnifierCallback() {
@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 onMove(int displayId) {
if (mWindowMagnificationConnectionImpl != null) {
mWindowMagnificationConnectionImpl.onMove(displayId);
}
}
@Override
public void onClickSettingsButton(int displayId) {
mHandler.post(() -> {
showMagnificationSettingsPanel(displayId);
});
}
};
@VisibleForTesting
final MagnificationSettingsController.Callback mMagnificationSettingsControllerCallback =
new MagnificationSettingsController.Callback() {
@Override
public void onSetMagnifierSize(int displayId, int index) {
mHandler.post(() -> onSetMagnifierSizeInternal(displayId, index));
mA11yLogger.log(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_WINDOW_SIZE_SELECTED);
}
@Override
public void onSetDiagonalScrolling(int displayId, boolean enable) {
mHandler.post(() -> onSetDiagonalScrollingInternal(displayId, enable));
}
@Override
public void onEditMagnifierSizeMode(int displayId, boolean enable) {
mHandler.post(() -> onEditMagnifierSizeModeInternal(displayId, enable));
mA11yLogger.log(enable
? MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_SIZE_EDITING_ACTIVATED
: MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_SIZE_EDITING_DEACTIVATED);
}
@Override
public void onMagnifierScale(int displayId, float scale) {
if (mWindowMagnificationConnectionImpl != null) {
mWindowMagnificationConnectionImpl.onPerformScaleAction(displayId, scale);
}
}
@Override
public void onModeSwitch(int displayId, int newMode) {
mHandler.post(() -> onModeSwitchInternal(displayId, newMode));
}
@Override
public void onSettingsPanelVisibilityChanged(int displayId, boolean shown) {
mHandler.post(() -> onSettingsPanelVisibilityChangedInternal(displayId, shown));
mA11yLogger.log(shown
? MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_OPENED
: MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_CLOSED);
}
};
@MainThread
private void onSetMagnifierSizeInternal(int displayId, int index) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.changeMagnificationSize(index);
}
}
@MainThread
private void onSetDiagonalScrollingInternal(int displayId, boolean enable) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
windowMagnificationController.setDiagonalScrolling(enable);
}
}
@MainThread
private void onEditMagnifierSizeModeInternal(int displayId, boolean enable) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null && windowMagnificationController.isActivated()) {
windowMagnificationController.setEditMagnifierSizeMode(enable);
}
}
@MainThread
private void onModeSwitchInternal(int displayId, int newMode) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
final boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
final boolean isSwitchToWindowMode = (newMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
final boolean changed = isSwitchToWindowMode ^ isWindowMagnifierActivated;
if (changed) {
final MagnificationSettingsController magnificationSettingsController =
mMagnificationSettingsSupplier.get(displayId);
if (magnificationSettingsController != null) {
magnificationSettingsController.closeMagnificationSettings();
}
if (mWindowMagnificationConnectionImpl != null) {
mWindowMagnificationConnectionImpl.onChangeMagnificationMode(displayId, newMode);
}
}
}
@MainThread
private void onSettingsPanelVisibilityChangedInternal(int displayId, boolean shown) {
final WindowMagnificationController windowMagnificationController =
mMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null && windowMagnificationController.isActivated()) {
windowMagnificationController.updateDragHandleResourcesIfNeeded(shown);
}
}
@Override
public void requestWindowMagnificationConnection(boolean connect) {
if (connect) {
setWindowMagnificationConnection();
} else {
clearWindowMagnificationConnection();
}
}
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println(TAG);
mMagnificationControllerSupplier.forEach(
magnificationController -> magnificationController.dump(pw));
}
private void setWindowMagnificationConnection() {
if (mWindowMagnificationConnectionImpl == null) {
mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
mHandler);
}
mAccessibilityManager.setWindowMagnificationConnection(
mWindowMagnificationConnectionImpl);
}
private void clearWindowMagnificationConnection() {
mAccessibilityManager.setWindowMagnificationConnection(null);
//TODO: destroy controllers.
}
}