diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java b/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java index 16d2e6b47a54c..93531ddea005b 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java @@ -16,76 +16,31 @@ package com.android.server.accessibility.magnification; -import android.annotation.NonNull; import android.provider.DeviceConfig; -import com.android.internal.annotations.VisibleForTesting; - -import java.util.concurrent.Executor; - /** * Encapsulates the feature flags for always on magnification. {@see DeviceConfig} * * @hide */ -public class AlwaysOnMagnificationFeatureFlag { +public class AlwaysOnMagnificationFeatureFlag extends MagnificationFeatureFlagBase { private static final String NAMESPACE = DeviceConfig.NAMESPACE_WINDOW_MANAGER; private static final String FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION = "AlwaysOnMagnifier__enable_always_on_magnifier"; - private AlwaysOnMagnificationFeatureFlag() {} - - /** Returns true if the feature flag is enabled for always on magnification */ - public static boolean isAlwaysOnMagnificationEnabled() { - return DeviceConfig.getBoolean( - NAMESPACE, - FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION, - /* defaultValue= */ false); + @Override + String getNamespace() { + return NAMESPACE; } - /** Sets the feature flag. Only used for testing; requires shell permissions. */ - @VisibleForTesting - public static boolean setAlwaysOnMagnificationEnabled(boolean isEnabled) { - return DeviceConfig.setProperty( - NAMESPACE, - FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION, - Boolean.toString(isEnabled), - /* makeDefault= */ false); + @Override + String getFeatureName() { + return FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION; } - /** - * Adds a listener for when the feature flag changes. - * - *
{@see DeviceConfig#addOnPropertiesChangedListener( - * String, Executor, DeviceConfig.OnPropertiesChangedListener)} - */ - @NonNull - public static DeviceConfig.OnPropertiesChangedListener addOnChangedListener( - @NonNull Executor executor, @NonNull Runnable listener) { - DeviceConfig.OnPropertiesChangedListener onChangedListener = - properties -> { - if (properties.getKeyset().contains( - FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION)) { - listener.run(); - } - }; - DeviceConfig.addOnPropertiesChangedListener( - NAMESPACE, - executor, - onChangedListener); - - return onChangedListener; - } - - /** - * Remove a listener for when the feature flag changes. - * - *
{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor,
- * DeviceConfig.OnPropertiesChangedListener)}
- */
- public static void removeOnChangedListener(
- @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) {
- DeviceConfig.removeOnPropertiesChangedListener(onChangedListener);
+ @Override
+ boolean getDefaultValue() {
+ return false;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index ed8a35f45176e..fbc7b3cbc63b1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -38,7 +38,6 @@ import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.Message;
-import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.MathUtils;
@@ -57,6 +56,7 @@ import com.android.internal.R;
import com.android.internal.accessibility.common.MagnificationConstants;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService;
@@ -110,6 +110,7 @@ public class FullScreenMagnificationController implements
private boolean mAlwaysOnMagnificationEnabled = false;
private final DisplayManagerInternal mDisplayManagerInternal;
+ private final MagnificationThumbnailFeatureFlag mMagnificationThumbnailFeatureFlag;
@NonNull private final Supplier {@see DeviceConfig#addOnPropertiesChangedListener(
+ * String, Executor, DeviceConfig.OnPropertiesChangedListener)}
+ */
+ @NonNull
+ public DeviceConfig.OnPropertiesChangedListener addOnChangedListener(
+ @NonNull Executor executor, @NonNull Runnable listener) {
+ DeviceConfig.OnPropertiesChangedListener onChangedListener =
+ properties -> {
+ if (properties.getKeyset().contains(
+ getFeatureName())) {
+ listener.run();
+ }
+ };
+
+ clearCallingIdentifyAndTryCatch(
+ () -> DeviceConfig.addOnPropertiesChangedListener(
+ getNamespace(),
+ executor,
+ onChangedListener),
+ () -> {});
+
+ return onChangedListener;
+ }
+
+ /**
+ * Remove a listener for when the feature flag changes.
+ *
+ * {@see DeviceConfig#addOnPropertiesChangedListener(String, Executor,
+ * DeviceConfig.OnPropertiesChangedListener)}
+ */
+ public void removeOnChangedListener(
+ @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) {
+ DeviceConfig.removeOnPropertiesChangedListener(onChangedListener);
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
index 5a783f47ccdc9..03fa93d8a3bc4 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
@@ -58,7 +58,9 @@ public class MagnificationThumbnail {
@VisibleForTesting
public final FrameLayout mThumbnailLayout;
- private final View mThumbNailView;
+ private final View mThumbnailView;
+ private int mThumbnailWidth;
+ private int mThumbnailHeight;
private final WindowManager.LayoutParams mBackgroundParams;
private boolean mVisible = false;
@@ -66,7 +68,7 @@ public class MagnificationThumbnail {
private static final float ASPECT_RATIO = 14f;
private static final float BG_ASPECT_RATIO = ASPECT_RATIO / 2f;
- private ObjectAnimator mThumbNailAnimator;
+ private ObjectAnimator mThumbnailAnimator;
private boolean mIsFadingIn;
/**
@@ -79,9 +81,11 @@ public class MagnificationThumbnail {
mWindowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
mThumbnailLayout = (FrameLayout) LayoutInflater.from(mContext)
.inflate(R.layout.thumbnail_background_view, /* root: */ null);
- mThumbNailView =
+ mThumbnailView =
mThumbnailLayout.findViewById(R.id.accessibility_magnification_thumbnail_view);
mBackgroundParams = createLayoutParams();
+ mThumbnailWidth = 0;
+ mThumbnailHeight = 0;
}
/**
@@ -90,35 +94,35 @@ public class MagnificationThumbnail {
* @param currentBounds the current magnification bounds
*/
@AnyThread
- public void setThumbNailBounds(Rect currentBounds, float scale, float centerX, float centerY) {
+ public void setThumbnailBounds(Rect currentBounds, float scale, float centerX, float centerY) {
if (DEBUG) {
- Log.d(LOG_TAG, "setThumbNailBounds " + currentBounds);
+ Log.d(LOG_TAG, "setThumbnailBounds " + currentBounds);
}
mHandler.post(() -> {
mWindowBounds = currentBounds;
setBackgroundBounds();
if (mVisible) {
- updateThumbNailMainThread(scale, centerX, centerY);
+ updateThumbnailMainThread(scale, centerX, centerY);
}
});
}
private void setBackgroundBounds() {
Point magnificationBoundary = getMagnificationThumbnailPadding(mContext);
- final int thumbNailWidth = (int) (mWindowBounds.width() / BG_ASPECT_RATIO);
- final int thumbNailHeight = (int) (mWindowBounds.height() / BG_ASPECT_RATIO);
+ mThumbnailWidth = (int) (mWindowBounds.width() / BG_ASPECT_RATIO);
+ mThumbnailHeight = (int) (mWindowBounds.height() / BG_ASPECT_RATIO);
int initX = magnificationBoundary.x;
int initY = magnificationBoundary.y;
- mBackgroundParams.width = thumbNailWidth;
- mBackgroundParams.height = thumbNailHeight;
+ mBackgroundParams.width = mThumbnailWidth;
+ mBackgroundParams.height = mThumbnailHeight;
mBackgroundParams.x = initX;
mBackgroundParams.y = initY;
}
@MainThread
- private void showThumbNail() {
+ private void showThumbnail() {
if (DEBUG) {
- Log.d(LOG_TAG, "showThumbNail " + mVisible);
+ Log.d(LOG_TAG, "showThumbnail " + mVisible);
}
animateThumbnail(true);
}
@@ -127,14 +131,14 @@ public class MagnificationThumbnail {
* Hides thumbnail and removes the view from the window when finished animating.
*/
@AnyThread
- public void hideThumbNail() {
- mHandler.post(this::hideThumbNailMainThread);
+ public void hideThumbnail() {
+ mHandler.post(this::hideThumbnailMainThread);
}
@MainThread
- private void hideThumbNailMainThread() {
+ private void hideThumbnailMainThread() {
if (DEBUG) {
- Log.d(LOG_TAG, "hideThumbNail " + mVisible);
+ Log.d(LOG_TAG, "hideThumbnail " + mVisible);
}
if (mVisible) {
animateThumbnail(false);
@@ -155,14 +159,14 @@ public class MagnificationThumbnail {
+ " fadeIn: " + fadeIn
+ " mVisible: " + mVisible
+ " isFadingIn: " + mIsFadingIn
- + " isRunning: " + mThumbNailAnimator
+ + " isRunning: " + mThumbnailAnimator
);
}
// Reset countdown to hide automatically
- mHandler.removeCallbacks(this::hideThumbNailMainThread);
+ mHandler.removeCallbacks(this::hideThumbnailMainThread);
if (fadeIn) {
- mHandler.postDelayed(this::hideThumbNailMainThread, LINGER_DURATION_MS);
+ mHandler.postDelayed(this::hideThumbnailMainThread, LINGER_DURATION_MS);
}
if (fadeIn == mIsFadingIn) {
@@ -175,18 +179,18 @@ public class MagnificationThumbnail {
mVisible = true;
}
- if (mThumbNailAnimator != null) {
- mThumbNailAnimator.cancel();
+ if (mThumbnailAnimator != null) {
+ mThumbnailAnimator.cancel();
}
- mThumbNailAnimator = ObjectAnimator.ofFloat(
+ mThumbnailAnimator = ObjectAnimator.ofFloat(
mThumbnailLayout,
"alpha",
fadeIn ? 1f : 0f
);
- mThumbNailAnimator.setDuration(
+ mThumbnailAnimator.setDuration(
fadeIn ? FADE_IN_ANIMATION_DURATION_MS : FADE_OUT_ANIMATION_DURATION_MS
);
- mThumbNailAnimator.addListener(new Animator.AnimatorListener() {
+ mThumbnailAnimator.addListener(new Animator.AnimatorListener() {
private boolean mIsCancelled;
@Override
@@ -231,7 +235,7 @@ public class MagnificationThumbnail {
}
});
- mThumbNailAnimator.start();
+ mThumbnailAnimator.start();
}
/**
@@ -246,38 +250,48 @@ public class MagnificationThumbnail {
* of the viewport, or {@link Float#NaN} to leave unchanged
*/
@AnyThread
- public void updateThumbNail(float scale, float centerX, float centerY) {
- mHandler.post(() -> updateThumbNailMainThread(scale, centerX, centerY));
+ public void updateThumbnail(float scale, float centerX, float centerY) {
+ mHandler.post(() -> updateThumbnailMainThread(scale, centerX, centerY));
}
@MainThread
- private void updateThumbNailMainThread(float scale, float centerX, float centerY) {
+ private void updateThumbnailMainThread(float scale, float centerX, float centerY) {
// Restart the fadeout countdown (or show if it's hidden)
- showThumbNail();
+ showThumbnail();
- var scaleDown = Float.isNaN(scale) ? mThumbNailView.getScaleX() : 1f / scale;
+ var scaleDown = Float.isNaN(scale) ? mThumbnailView.getScaleX() : 1f / scale;
if (!Float.isNaN(scale)) {
- mThumbNailView.setScaleX(scaleDown);
- mThumbNailView.setScaleY(scaleDown);
+ mThumbnailView.setScaleX(scaleDown);
+ mThumbnailView.setScaleY(scaleDown);
+ }
+ float thumbnailWidth;
+ float thumbnailHeight;
+ if (mThumbnailView.getWidth() == 0 || mThumbnailView.getHeight() == 0) {
+ // if the thumbnail view size is not updated correctly, we just use the cached values.
+ thumbnailWidth = mThumbnailWidth;
+ thumbnailHeight = mThumbnailHeight;
+ } else {
+ thumbnailWidth = mThumbnailView.getWidth();
+ thumbnailHeight = mThumbnailView.getHeight();
}
if (!Float.isNaN(centerX)) {
- var padding = mThumbNailView.getPaddingTop();
+ var padding = mThumbnailView.getPaddingTop();
var ratio = 1f / BG_ASPECT_RATIO;
- var centerXScaled = centerX * ratio - (mThumbNailView.getWidth() / 2f + padding);
- var centerYScaled = centerY * ratio - (mThumbNailView.getHeight() / 2f + padding);
+ var centerXScaled = centerX * ratio - (thumbnailWidth / 2f + padding);
+ var centerYScaled = centerY * ratio - (thumbnailHeight / 2f + padding);
if (DEBUG) {
Log.d(
LOG_TAG,
- "updateThumbNail centerXScaled : " + centerXScaled
+ "updateThumbnail centerXScaled : " + centerXScaled
+ " centerYScaled : " + centerYScaled
- + " getTranslationX : " + mThumbNailView.getTranslationX()
+ + " getTranslationX : " + mThumbnailView.getTranslationX()
+ " ratio : " + ratio
);
}
- mThumbNailView.setTranslationX(centerXScaled);
- mThumbNailView.setTranslationY(centerYScaled);
+ mThumbnailView.setTranslationX(centerXScaled);
+ mThumbnailView.setTranslationY(centerYScaled);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java
new file mode 100644
index 0000000000000..519f31b86f78a
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.server.accessibility.magnification;
+
+import android.provider.DeviceConfig;
+
+/**
+ * Encapsulates the feature flags for magnification thumbnail. {@see DeviceConfig}
+ *
+ * @hide
+ */
+public class MagnificationThumbnailFeatureFlag extends MagnificationFeatureFlagBase {
+
+ private static final String NAMESPACE = DeviceConfig.NAMESPACE_ACCESSIBILITY;
+ private static final String FEATURE_NAME_ENABLE_MAGNIFIER_THUMBNAIL =
+ "enable_magnifier_thumbnail";
+
+ @Override
+ String getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ String getFeatureName() {
+ return FEATURE_NAME_ENABLE_MAGNIFIER_THUMBNAIL;
+ }
+
+ @Override
+ boolean getDefaultValue() {
+ return false;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index f1ad577fde885..a01c7bdd51444 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -200,7 +200,7 @@ public class FullScreenMagnificationControllerTest {
assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_1));
- verify(mMockThumbnail, times(2)).hideThumbNail();
+ verify(mMockThumbnail, times(2)).hideThumbnail();
}
@Test
@@ -538,7 +538,10 @@ public class FullScreenMagnificationControllerTest {
mConfigCaptor.capture());
assertConfigEquals(config, mConfigCaptor.getValue());
- verify(mMockThumbnail).setThumbNailBounds(any(), anyFloat(), anyFloat(), anyFloat());
+ // The first time is triggered when the thumbnail is just created.
+ // The second time is triggered when the magnification region changed.
+ verify(mMockThumbnail, times(2)).setThumbnailBounds(
+ any(), anyFloat(), anyFloat(), anyFloat());
}
@Test
@@ -909,7 +912,7 @@ public class FullScreenMagnificationControllerTest {
verifyNoMoreInteractions(mMockWindowManager);
verify(mMockThumbnail)
- .updateThumbNail(eq(scale), eq(startCenter.x), eq(startCenter.y));
+ .updateThumbnail(eq(scale), eq(startCenter.x), eq(startCenter.y));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
index 60c8148180da4..3baa102b882b4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
@@ -66,14 +66,14 @@ public class MagnificationThumbnailTest {
@Test
public void updateThumbnailShows() {
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2f,
/* centerX= */ 5,
/* centerY= */ 10
));
idle();
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2.2f,
/* centerX= */ 15,
/* centerY= */ 50
@@ -86,7 +86,7 @@ public class MagnificationThumbnailTest {
@Test
public void updateThumbnailLingersThenHidesAfterTimeout() throws InterruptedException {
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2f,
/* centerX= */ 5,
/* centerY= */ 10
@@ -103,14 +103,14 @@ public class MagnificationThumbnailTest {
@Test
public void hideThumbnailRemoves() throws InterruptedException {
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2f,
/* centerX= */ 5,
/* centerY= */ 10
));
idle();
- runOnMainSync(() -> mMagnificationThumbnail.hideThumbNail());
+ runOnMainSync(() -> mMagnificationThumbnail.hideThumbnail());
idle();
// Wait for the fade out animation
@@ -122,10 +122,10 @@ public class MagnificationThumbnailTest {
@Test
public void hideShowHideShowHideRemoves() throws InterruptedException {
- runOnMainSync(() -> mMagnificationThumbnail.hideThumbNail());
+ runOnMainSync(() -> mMagnificationThumbnail.hideThumbnail());
idle();
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2f,
/* centerX= */ 5,
/* centerY= */ 10
@@ -135,17 +135,17 @@ public class MagnificationThumbnailTest {
// Wait for the fade in animation
Thread.sleep(200L);
- runOnMainSync(() -> mMagnificationThumbnail.hideThumbNail());
+ runOnMainSync(() -> mMagnificationThumbnail.hideThumbnail());
idle();
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2f,
/* centerX= */ 5,
/* centerY= */ 10
));
idle();
- runOnMainSync(() -> mMagnificationThumbnail.hideThumbNail());
+ runOnMainSync(() -> mMagnificationThumbnail.hideThumbnail());
idle();
@@ -158,7 +158,7 @@ public class MagnificationThumbnailTest {
@Test
public void hideWithoutShowDoesNothing() throws InterruptedException {
- runOnMainSync(() -> mMagnificationThumbnail.hideThumbNail());
+ runOnMainSync(() -> mMagnificationThumbnail.hideThumbnail());
idle();
// Wait for the fade out animation
@@ -172,7 +172,7 @@ public class MagnificationThumbnailTest {
@Test
public void whenHidden_setBoundsDoesNotShow() throws InterruptedException {
- runOnMainSync(() -> mMagnificationThumbnail.setThumbNailBounds(
+ runOnMainSync(() -> mMagnificationThumbnail.setThumbnailBounds(
new Rect(),
/* scale= */ 2f,
/* centerX= */ 5,