summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/drawable/accessibility_fullscreen_magnification_border_background.xml25
-rw-r--r--packages/SystemUI/res/layout/fullscreen_magnification_border.xml22
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java140
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java28
7 files changed, 354 insertions, 1 deletions
diff --git a/packages/SystemUI/res/drawable/accessibility_fullscreen_magnification_border_background.xml b/packages/SystemUI/res/drawable/accessibility_fullscreen_magnification_border_background.xml
new file mode 100644
index 000000000000..edfbe7bed588
--- /dev/null
+++ b/packages/SystemUI/res/drawable/accessibility_fullscreen_magnification_border_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@*android:dimen/rounded_corner_radius" />
+ <!-- Since the device corners are not perfectly rounded, we create the stroke with offset
+ to fill up the space between border and device corner -->
+ <stroke
+ android:color="@color/magnification_border_color"
+ android:width="@dimen/magnifier_border_width_fullscreen_with_offset"/>
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/fullscreen_magnification_border.xml b/packages/SystemUI/res/layout/fullscreen_magnification_border.xml
new file mode 100644
index 000000000000..5f738c041f54
--- /dev/null
+++ b/packages/SystemUI/res/layout/fullscreen_magnification_border.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/magnification_fullscreen_border"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="false"
+ android:background="@drawable/accessibility_fullscreen_magnification_border_background"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e004ee9fa157..29f4942e8b4a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1266,6 +1266,8 @@
<dimen name="magnifier_corner_radius">28dp</dimen>
<dimen name="magnifier_edit_corner_radius">16dp</dimen>
<dimen name="magnifier_edit_outer_corner_radius">18dp</dimen>
+ <dimen name="magnifier_border_width_fullscreen_with_offset">12dp</dimen>
+ <dimen name="magnifier_border_width_fullscreen">6dp</dimen>
<dimen name="magnifier_border_width">8dp</dimen>
<dimen name="magnifier_stroke_width">2dp</dimen>
<dimen name="magnifier_edit_dash_gap">20dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
new file mode 100644
index 000000000000..af8149f59a16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2024 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;
+
+import android.annotation.UiContext;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.AttachedSurfaceControl;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.annotation.UiThread;
+
+import com.android.systemui.res.R;
+
+import java.util.function.Supplier;
+
+class FullscreenMagnificationController {
+
+ private final Context mContext;
+ private final AccessibilityManager mAccessibilityManager;
+ private final WindowManager mWindowManager;
+ private Supplier<SurfaceControlViewHost> mScvhSupplier;
+ private SurfaceControlViewHost mSurfaceControlViewHost;
+ private Rect mWindowBounds;
+ private SurfaceControl.Transaction mTransaction;
+ private View mFullscreenBorder = null;
+ private int mBorderOffset;
+ private final int mDisplayId;
+ private static final Region sEmptyRegion = new Region();
+
+ FullscreenMagnificationController(
+ @UiContext Context context,
+ AccessibilityManager accessibilityManager,
+ WindowManager windowManager,
+ Supplier<SurfaceControlViewHost> scvhSupplier) {
+ mContext = context;
+ mAccessibilityManager = accessibilityManager;
+ mWindowManager = windowManager;
+ mWindowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ mTransaction = new SurfaceControl.Transaction();
+ mScvhSupplier = scvhSupplier;
+ mBorderOffset = mContext.getResources().getDimensionPixelSize(
+ R.dimen.magnifier_border_width_fullscreen_with_offset)
+ - mContext.getResources().getDimensionPixelSize(
+ R.dimen.magnifier_border_width_fullscreen);
+ mDisplayId = mContext.getDisplayId();
+ }
+
+ @UiThread
+ void onFullscreenMagnificationActivationChanged(boolean activated) {
+ if (activated) {
+ createFullscreenMagnificationBorder();
+ } else {
+ removeFullscreenMagnificationBorder();
+ }
+ }
+
+ @UiThread
+ private void removeFullscreenMagnificationBorder() {
+ if (mSurfaceControlViewHost != null) {
+ mSurfaceControlViewHost.release();
+ mSurfaceControlViewHost = null;
+ }
+
+ if (mFullscreenBorder != null) {
+ mFullscreenBorder = null;
+ }
+ }
+
+ /**
+ * Since the device corners are not perfectly rounded, we would like to create a thick stroke,
+ * and set negative offset to the border view to fill up the spaces between the border and the
+ * device corners.
+ */
+ @UiThread
+ private void createFullscreenMagnificationBorder() {
+ mFullscreenBorder = LayoutInflater.from(mContext)
+ .inflate(R.layout.fullscreen_magnification_border, null);
+ mSurfaceControlViewHost = mScvhSupplier.get();
+ mSurfaceControlViewHost.setView(mFullscreenBorder, getBorderLayoutParams());
+
+ SurfaceControl surfaceControl = mSurfaceControlViewHost
+ .getSurfacePackage().getSurfaceControl();
+
+ mTransaction
+ .setPosition(surfaceControl, -mBorderOffset, -mBorderOffset)
+ .setLayer(surfaceControl, Integer.MAX_VALUE)
+ .show(surfaceControl)
+ .apply();
+
+ mAccessibilityManager.attachAccessibilityOverlayToDisplay(mDisplayId, surfaceControl);
+
+ applyTouchableRegion();
+ }
+
+ private LayoutParams getBorderLayoutParams() {
+ LayoutParams params = new LayoutParams(
+ mWindowBounds.width() + 2 * mBorderOffset,
+ mWindowBounds.height() + 2 * mBorderOffset,
+ LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,
+ LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSPARENT);
+ params.setTrustedOverlay();
+ return params;
+ }
+
+ private void applyTouchableRegion() {
+ // Sometimes this can get posted and run after deleteWindowMagnification() is called.
+ if (mFullscreenBorder == null) return;
+
+ AttachedSurfaceControl surfaceControl = mSurfaceControlViewHost.getRootSurfaceControl();
+
+ // The touchable region of the mFullscreenBorder will be empty since we are going to allow
+ // all touch events to go through this view.
+ surfaceControl.setTouchableRegion(sEmptyRegion);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index 88fa2de186d0..70165f377a7c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -34,6 +34,7 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
+import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IMagnificationConnection;
@@ -134,6 +135,37 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
@VisibleForTesting
DisplayIdIndexSupplier<WindowMagnificationController> mWindowMagnificationControllerSupplier;
+ private static class FullscreenMagnificationControllerSupplier extends
+ DisplayIdIndexSupplier<FullscreenMagnificationController> {
+
+ private final Context mContext;
+
+ FullscreenMagnificationControllerSupplier(Context context, Handler handler,
+ DisplayManager displayManager, SysUiState sysUiState,
+ SecureSettings secureSettings) {
+ super(displayManager);
+ mContext = context;
+ }
+
+ @Override
+ protected FullscreenMagnificationController createInstance(Display display) {
+ final Context windowContext = mContext.createWindowContext(display,
+ TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
+ Supplier<SurfaceControlViewHost> scvhSupplier = () -> new SurfaceControlViewHost(
+ mContext, mContext.getDisplay(), new InputTransferToken(), TAG);
+ windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
+ return new FullscreenMagnificationController(
+ windowContext,
+ windowContext.getSystemService(AccessibilityManager.class),
+ windowContext.getSystemService(WindowManager.class),
+ scvhSupplier);
+ }
+ }
+
+ @VisibleForTesting
+ DisplayIdIndexSupplier<FullscreenMagnificationController>
+ mFullscreenMagnificationControllerSupplier;
+
private static class SettingsSupplier extends
DisplayIdIndexSupplier<MagnificationSettingsController> {
@@ -185,6 +217,8 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
mHandler, mWindowMagnifierCallback,
displayManager, sysUiState, secureSettings);
+ mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
+ context, mHandler, displayManager, sysUiState, secureSettings);
mMagnificationSettingsSupplier = new SettingsSupplier(context,
mMagnificationSettingsControllerCallback, displayManager, secureSettings);
@@ -273,8 +307,13 @@ public class Magnification implements CoreStartable, CommandQueue.Callbacks {
}
}
+ @MainThread
void onFullscreenMagnificationActivationChanged(int displayId, boolean activated) {
- // Do nothing
+ final FullscreenMagnificationController fullscreenMagnificationController =
+ mFullscreenMagnificationControllerSupplier.get(displayId);
+ if (fullscreenMagnificationController != null) {
+ fullscreenMagnificationController.onFullscreenMagnificationActivationChanged(activated);
+ }
}
@MainThread
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
new file mode 100644
index 000000000000..12f334ba08bb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControlViewHost;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+import android.window.InputTransferToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Supplier;
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class FullscreenMagnificationControllerTest extends SysuiTestCase {
+
+ private FullscreenMagnificationController mFullscreenMagnificationController;
+ private SurfaceControlViewHost mSurfaceControlViewHost;
+
+ @Before
+ public void setUp() {
+ getInstrumentation().runOnMainSync(() -> mSurfaceControlViewHost =
+ new SurfaceControlViewHost(mContext, mContext.getDisplay(),
+ new InputTransferToken(), "FullscreenMagnification"));
+
+ Supplier<SurfaceControlViewHost> scvhSupplier = () -> mSurfaceControlViewHost;
+
+ mFullscreenMagnificationController = new FullscreenMagnificationController(
+ mContext,
+ mContext.getSystemService(AccessibilityManager.class),
+ mContext.getSystemService(WindowManager.class),
+ scvhSupplier);
+ }
+
+ @After
+ public void tearDown() {
+ getInstrumentation().runOnMainSync(
+ () -> mFullscreenMagnificationController
+ .onFullscreenMagnificationActivationChanged(false));
+ }
+
+ @Test
+ public void onFullscreenMagnificationActivationChange_activated_visibleBorder() {
+ getInstrumentation().runOnMainSync(
+ () -> mFullscreenMagnificationController
+ .onFullscreenMagnificationActivationChanged(true)
+ );
+
+ // Wait for Rects updated.
+ waitForIdleSync();
+ assertThat(mSurfaceControlViewHost.getView().isVisibleToUser()).isTrue();
+ }
+
+ @Test
+ public void onFullscreenMagnificationActivationChange_deactivated_invisibleBorder() {
+ getInstrumentation().runOnMainSync(
+ () -> {
+ mFullscreenMagnificationController
+ .onFullscreenMagnificationActivationChanged(true);
+ mFullscreenMagnificationController
+ .onFullscreenMagnificationActivationChanged(false);
+ }
+ );
+
+ assertThat(mSurfaceControlViewHost.getView()).isNull();
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index bd49927c8b53..41d5d5d919e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -71,6 +71,8 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
@Mock
private WindowMagnificationController mWindowMagnificationController;
@Mock
+ private FullscreenMagnificationController mFullscreenMagnificationController;
+ @Mock
private MagnificationSettingsController mMagnificationSettingsController;
@Mock
private ModeSwitchesController mModeSwitchesController;
@@ -105,6 +107,9 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
mMagnification.mWindowMagnificationControllerSupplier =
new FakeWindowMagnificationControllerSupplier(
mContext.getSystemService(DisplayManager.class));
+ mMagnification.mFullscreenMagnificationControllerSupplier =
+ new FakeFullscreenMagnificationControllerSupplier(
+ mContext.getSystemService(DisplayManager.class));
mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
mContext.getSystemService(DisplayManager.class));
@@ -124,6 +129,15 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
}
@Test
+ public void onFullscreenMagnificationActivationChanged_passThrough() throws RemoteException {
+ mIMagnificationConnection.onFullscreenMagnificationActivationChanged(TEST_DISPLAY, true);
+ waitForIdleSync();
+
+ verify(mFullscreenMagnificationController)
+ .onFullscreenMagnificationActivationChanged(eq(true));
+ }
+
+ @Test
public void disableWindowMagnification_deleteWindowMagnification() throws RemoteException {
mIMagnificationConnection.disableWindowMagnification(TEST_DISPLAY,
mAnimationCallback);
@@ -215,6 +229,20 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
}
}
+
+ private class FakeFullscreenMagnificationControllerSupplier extends
+ DisplayIdIndexSupplier<FullscreenMagnificationController> {
+
+ FakeFullscreenMagnificationControllerSupplier(DisplayManager displayManager) {
+ super(displayManager);
+ }
+
+ @Override
+ protected FullscreenMagnificationController createInstance(Display display) {
+ return mFullscreenMagnificationController;
+ }
+ }
+
private class FakeSettingsSupplier extends
DisplayIdIndexSupplier<MagnificationSettingsController> {