Loading services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java +10 −55 Original line number Diff line number Diff line Loading @@ -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. * * <p>{@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. * * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor, * DeviceConfig.OnPropertiesChangedListener)} */ public static void removeOnChangedListener( @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) { DeviceConfig.removeOnPropertiesChangedListener(onChangedListener); @Override boolean getDefaultValue() { return false; } } services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +71 −33 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -110,6 +110,7 @@ public class FullScreenMagnificationController implements private boolean mAlwaysOnMagnificationEnabled = false; private final DisplayManagerInternal mDisplayManagerInternal; private final MagnificationThumbnailFeatureFlag mMagnificationThumbnailFeatureFlag; @NonNull private final Supplier<MagnificationThumbnail> mThumbnailSupplier; /** Loading Loading @@ -177,9 +178,7 @@ public class FullScreenMagnificationController implements mDisplayId, mMagnificationRegion); mMagnificationRegion.getBounds(mMagnificationBounds); if (mMagnificationThumbnail == null) { mMagnificationThumbnail = mThumbnailSupplier.get(); } createThumbnailIfSupported(); return true; } Loading Loading @@ -207,7 +206,7 @@ public class FullScreenMagnificationController implements mRegistered = false; unregisterCallbackLocked(mDisplayId, delete); destroyThumbNail(); destroyThumbnail(); } mUnregisterPending = false; } Loading Loading @@ -345,7 +344,7 @@ public class FullScreenMagnificationController implements mMagnificationRegion.set(magnified); mMagnificationRegion.getBounds(mMagnificationBounds); refreshThumbNail(getScale(), getCenterX(), getCenterY()); refreshThumbnail(getScale(), getCenterX(), getCenterY()); // It's possible that our magnification spec is invalid with the new bounds. // Adjust the current spec's offsets if necessary. Loading Loading @@ -405,9 +404,9 @@ public class FullScreenMagnificationController implements } if (isActivated()) { updateThumbNail(scale, centerX, centerY); updateThumbnail(scale, centerX, centerY); } else { hideThumbNail(); hideThumbnail(); } } Loading Loading @@ -538,7 +537,7 @@ public class FullScreenMagnificationController implements mIdOfLastServiceToMagnify = INVALID_SERVICE_ID; sendSpecToAnimation(spec, animationCallback); hideThumbNail(); hideThumbnail(); return changed; } Loading Loading @@ -596,16 +595,16 @@ public class FullScreenMagnificationController implements } @GuardedBy("mLock") void updateThumbNail(float scale, float centerX, float centerY) { void updateThumbnail(float scale, float centerX, float centerY) { if (mMagnificationThumbnail != null) { mMagnificationThumbnail.updateThumbNail(scale, centerX, centerY); mMagnificationThumbnail.updateThumbnail(scale, centerX, centerY); } } @GuardedBy("mLock") void refreshThumbNail(float scale, float centerX, float centerY) { void refreshThumbnail(float scale, float centerX, float centerY) { if (mMagnificationThumbnail != null) { mMagnificationThumbnail.setThumbNailBounds( mMagnificationThumbnail.setThumbnailBounds( mMagnificationBounds, scale, centerX, Loading @@ -615,20 +614,38 @@ public class FullScreenMagnificationController implements } @GuardedBy("mLock") void hideThumbNail() { void hideThumbnail() { if (mMagnificationThumbnail != null) { mMagnificationThumbnail.hideThumbNail(); mMagnificationThumbnail.hideThumbnail(); } } @GuardedBy("mLock") void createThumbnailIfSupported() { if (mMagnificationThumbnail == null) { mMagnificationThumbnail = mThumbnailSupplier.get(); // We call refreshThumbnail when the thumbnail is just created to set current // magnification bounds to thumbnail. It to prevent the thumbnail size has not yet // updated properly and thus shows with huge size. (b/276314641) refreshThumbnail(getScale(), getCenterX(), getCenterY()); } } @GuardedBy("mLock") void destroyThumbNail() { void destroyThumbnail() { if (mMagnificationThumbnail != null) { hideThumbNail(); hideThumbnail(); mMagnificationThumbnail = null; } } void onThumbnailFeatureFlagChanged() { synchronized (mLock) { destroyThumbnail(); createThumbnailIfSupported(); } } /** * Updates the current magnification spec. * Loading Loading @@ -768,20 +785,7 @@ public class FullScreenMagnificationController implements lock, magnificationInfoChangedCallback, scaleProvider, () -> { if (DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACCESSIBILITY, "enable_magnifier_thumbnail", /* defaultValue= */ false)) { return new MagnificationThumbnail( context, context.getSystemService(WindowManager.class), new Handler(context.getMainLooper()) ); } return null; }); /* thumbnailSupplier= */ null); } /** Constructor for tests */ Loading @@ -791,7 +795,7 @@ public class FullScreenMagnificationController implements @NonNull Object lock, @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback, @NonNull MagnificationScaleProvider scaleProvider, @NonNull Supplier<MagnificationThumbnail> thumbnailSupplier) { Supplier<MagnificationThumbnail> thumbnailSupplier) { mControllerCtx = ctx; mLock = lock; mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId(); Loading @@ -799,7 +803,41 @@ public class FullScreenMagnificationController implements addInfoChangedCallback(magnificationInfoChangedCallback); mScaleProvider = scaleProvider; mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mMagnificationThumbnailFeatureFlag = new MagnificationThumbnailFeatureFlag(); mMagnificationThumbnailFeatureFlag.addOnChangedListener( ConcurrentUtils.DIRECT_EXECUTOR, this::onMagnificationThumbnailFeatureFlagChanged); if (thumbnailSupplier != null) { mThumbnailSupplier = thumbnailSupplier; } else { mThumbnailSupplier = () -> { if (mMagnificationThumbnailFeatureFlag.isFeatureFlagEnabled()) { return new MagnificationThumbnail( ctx.getContext(), ctx.getContext().getSystemService(WindowManager.class), new Handler(ctx.getContext().getMainLooper()) ); } return null; }; } } private void onMagnificationThumbnailFeatureFlagChanged() { synchronized (mLock) { for (int i = 0; i < mDisplays.size(); i++) { onMagnificationThumbnailFeatureFlagChanged(mDisplays.keyAt(i)); } } } private void onMagnificationThumbnailFeatureFlagChanged(int displayId) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return; } display.onThumbnailFeatureFlagChanged(); } } /** Loading services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +4 −2 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb private final SparseArray<DisableMagnificationCallback> mMagnificationEndRunnableSparseArray = new SparseArray(); private final AlwaysOnMagnificationFeatureFlag mAlwaysOnMagnificationFeatureFlag; private final MagnificationScaleProvider mScaleProvider; private FullScreenMagnificationController mFullScreenMagnificationController; private WindowMagnificationManager mWindowMagnificationMgr; Loading Loading @@ -151,7 +152,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb mSupportWindowMagnification = context.getPackageManager().hasSystemFeature( FEATURE_WINDOW_MAGNIFICATION); AlwaysOnMagnificationFeatureFlag.addOnChangedListener( mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag(); mAlwaysOnMagnificationFeatureFlag.addOnChangedListener( ConcurrentUtils.DIRECT_EXECUTOR, mAms::updateAlwaysOnMagnification); } Loading Loading @@ -710,7 +712,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb } public boolean isAlwaysOnMagnificationFeatureFlagEnabled() { return AlwaysOnMagnificationFeatureFlag.isAlwaysOnMagnificationEnabled(); return mAlwaysOnMagnificationFeatureFlag.isFeatureFlagEnabled(); } private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked( Loading services/accessibility/java/com/android/server/accessibility/magnification/MagnificationFeatureFlagBase.java 0 → 100644 +115 −0 Original line number Diff line number Diff line /* * 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.annotation.NonNull; import android.os.Binder; import android.provider.DeviceConfig; import com.android.internal.annotations.VisibleForTesting; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; /** * Abstract base class to encapsulates the feature flags for magnification features. * {@see DeviceConfig} * * @hide */ abstract class MagnificationFeatureFlagBase { abstract String getNamespace(); abstract String getFeatureName(); abstract boolean getDefaultValue(); private void clearCallingIdentifyAndTryCatch(Runnable tryBlock, Runnable catchBlock) { try { Binder.withCleanCallingIdentity(() -> tryBlock.run()); } catch (Throwable throwable) { catchBlock.run(); } } /** Returns true iff the feature flag is readable and enabled */ public boolean isFeatureFlagEnabled() { AtomicBoolean isEnabled = new AtomicBoolean(getDefaultValue()); clearCallingIdentifyAndTryCatch( () -> isEnabled.set(DeviceConfig.getBoolean( getNamespace(), getFeatureName(), getDefaultValue())), () -> isEnabled.set(getDefaultValue())); return isEnabled.get(); } /** Sets the feature flag. Only used for testing; requires shell permissions. */ @VisibleForTesting public boolean setFeatureFlagEnabled(boolean isEnabled) { AtomicBoolean success = new AtomicBoolean(getDefaultValue()); clearCallingIdentifyAndTryCatch( () -> success.set(DeviceConfig.setProperty( getNamespace(), getFeatureName(), Boolean.toString(isEnabled), /* makeDefault= */ false)), () -> success.set(getDefaultValue())); return success.get(); } /** * Adds a listener for when the feature flag changes. * * <p>{@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. * * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor, * DeviceConfig.OnPropertiesChangedListener)} */ public void removeOnChangedListener( @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) { DeviceConfig.removeOnPropertiesChangedListener(onChangedListener); } } services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java +53 −39 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading @@ -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; } /** Loading @@ -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); } Loading @@ -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); Loading @@ -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) { Loading @@ -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 Loading Loading @@ -231,7 +235,7 @@ public class MagnificationThumbnail { } }); mThumbNailAnimator.start(); mThumbnailAnimator.start(); } /** Loading @@ -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); } } Loading Loading
services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java +10 −55 Original line number Diff line number Diff line Loading @@ -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. * * <p>{@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. * * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor, * DeviceConfig.OnPropertiesChangedListener)} */ public static void removeOnChangedListener( @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) { DeviceConfig.removeOnPropertiesChangedListener(onChangedListener); @Override boolean getDefaultValue() { return false; } }
services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +71 −33 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -110,6 +110,7 @@ public class FullScreenMagnificationController implements private boolean mAlwaysOnMagnificationEnabled = false; private final DisplayManagerInternal mDisplayManagerInternal; private final MagnificationThumbnailFeatureFlag mMagnificationThumbnailFeatureFlag; @NonNull private final Supplier<MagnificationThumbnail> mThumbnailSupplier; /** Loading Loading @@ -177,9 +178,7 @@ public class FullScreenMagnificationController implements mDisplayId, mMagnificationRegion); mMagnificationRegion.getBounds(mMagnificationBounds); if (mMagnificationThumbnail == null) { mMagnificationThumbnail = mThumbnailSupplier.get(); } createThumbnailIfSupported(); return true; } Loading Loading @@ -207,7 +206,7 @@ public class FullScreenMagnificationController implements mRegistered = false; unregisterCallbackLocked(mDisplayId, delete); destroyThumbNail(); destroyThumbnail(); } mUnregisterPending = false; } Loading Loading @@ -345,7 +344,7 @@ public class FullScreenMagnificationController implements mMagnificationRegion.set(magnified); mMagnificationRegion.getBounds(mMagnificationBounds); refreshThumbNail(getScale(), getCenterX(), getCenterY()); refreshThumbnail(getScale(), getCenterX(), getCenterY()); // It's possible that our magnification spec is invalid with the new bounds. // Adjust the current spec's offsets if necessary. Loading Loading @@ -405,9 +404,9 @@ public class FullScreenMagnificationController implements } if (isActivated()) { updateThumbNail(scale, centerX, centerY); updateThumbnail(scale, centerX, centerY); } else { hideThumbNail(); hideThumbnail(); } } Loading Loading @@ -538,7 +537,7 @@ public class FullScreenMagnificationController implements mIdOfLastServiceToMagnify = INVALID_SERVICE_ID; sendSpecToAnimation(spec, animationCallback); hideThumbNail(); hideThumbnail(); return changed; } Loading Loading @@ -596,16 +595,16 @@ public class FullScreenMagnificationController implements } @GuardedBy("mLock") void updateThumbNail(float scale, float centerX, float centerY) { void updateThumbnail(float scale, float centerX, float centerY) { if (mMagnificationThumbnail != null) { mMagnificationThumbnail.updateThumbNail(scale, centerX, centerY); mMagnificationThumbnail.updateThumbnail(scale, centerX, centerY); } } @GuardedBy("mLock") void refreshThumbNail(float scale, float centerX, float centerY) { void refreshThumbnail(float scale, float centerX, float centerY) { if (mMagnificationThumbnail != null) { mMagnificationThumbnail.setThumbNailBounds( mMagnificationThumbnail.setThumbnailBounds( mMagnificationBounds, scale, centerX, Loading @@ -615,20 +614,38 @@ public class FullScreenMagnificationController implements } @GuardedBy("mLock") void hideThumbNail() { void hideThumbnail() { if (mMagnificationThumbnail != null) { mMagnificationThumbnail.hideThumbNail(); mMagnificationThumbnail.hideThumbnail(); } } @GuardedBy("mLock") void createThumbnailIfSupported() { if (mMagnificationThumbnail == null) { mMagnificationThumbnail = mThumbnailSupplier.get(); // We call refreshThumbnail when the thumbnail is just created to set current // magnification bounds to thumbnail. It to prevent the thumbnail size has not yet // updated properly and thus shows with huge size. (b/276314641) refreshThumbnail(getScale(), getCenterX(), getCenterY()); } } @GuardedBy("mLock") void destroyThumbNail() { void destroyThumbnail() { if (mMagnificationThumbnail != null) { hideThumbNail(); hideThumbnail(); mMagnificationThumbnail = null; } } void onThumbnailFeatureFlagChanged() { synchronized (mLock) { destroyThumbnail(); createThumbnailIfSupported(); } } /** * Updates the current magnification spec. * Loading Loading @@ -768,20 +785,7 @@ public class FullScreenMagnificationController implements lock, magnificationInfoChangedCallback, scaleProvider, () -> { if (DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACCESSIBILITY, "enable_magnifier_thumbnail", /* defaultValue= */ false)) { return new MagnificationThumbnail( context, context.getSystemService(WindowManager.class), new Handler(context.getMainLooper()) ); } return null; }); /* thumbnailSupplier= */ null); } /** Constructor for tests */ Loading @@ -791,7 +795,7 @@ public class FullScreenMagnificationController implements @NonNull Object lock, @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback, @NonNull MagnificationScaleProvider scaleProvider, @NonNull Supplier<MagnificationThumbnail> thumbnailSupplier) { Supplier<MagnificationThumbnail> thumbnailSupplier) { mControllerCtx = ctx; mLock = lock; mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId(); Loading @@ -799,7 +803,41 @@ public class FullScreenMagnificationController implements addInfoChangedCallback(magnificationInfoChangedCallback); mScaleProvider = scaleProvider; mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mMagnificationThumbnailFeatureFlag = new MagnificationThumbnailFeatureFlag(); mMagnificationThumbnailFeatureFlag.addOnChangedListener( ConcurrentUtils.DIRECT_EXECUTOR, this::onMagnificationThumbnailFeatureFlagChanged); if (thumbnailSupplier != null) { mThumbnailSupplier = thumbnailSupplier; } else { mThumbnailSupplier = () -> { if (mMagnificationThumbnailFeatureFlag.isFeatureFlagEnabled()) { return new MagnificationThumbnail( ctx.getContext(), ctx.getContext().getSystemService(WindowManager.class), new Handler(ctx.getContext().getMainLooper()) ); } return null; }; } } private void onMagnificationThumbnailFeatureFlagChanged() { synchronized (mLock) { for (int i = 0; i < mDisplays.size(); i++) { onMagnificationThumbnailFeatureFlagChanged(mDisplays.keyAt(i)); } } } private void onMagnificationThumbnailFeatureFlagChanged(int displayId) { synchronized (mLock) { final DisplayMagnification display = mDisplays.get(displayId); if (display == null) { return; } display.onThumbnailFeatureFlagChanged(); } } /** Loading
services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +4 −2 Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb private final SparseArray<DisableMagnificationCallback> mMagnificationEndRunnableSparseArray = new SparseArray(); private final AlwaysOnMagnificationFeatureFlag mAlwaysOnMagnificationFeatureFlag; private final MagnificationScaleProvider mScaleProvider; private FullScreenMagnificationController mFullScreenMagnificationController; private WindowMagnificationManager mWindowMagnificationMgr; Loading Loading @@ -151,7 +152,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb mSupportWindowMagnification = context.getPackageManager().hasSystemFeature( FEATURE_WINDOW_MAGNIFICATION); AlwaysOnMagnificationFeatureFlag.addOnChangedListener( mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag(); mAlwaysOnMagnificationFeatureFlag.addOnChangedListener( ConcurrentUtils.DIRECT_EXECUTOR, mAms::updateAlwaysOnMagnification); } Loading Loading @@ -710,7 +712,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb } public boolean isAlwaysOnMagnificationFeatureFlagEnabled() { return AlwaysOnMagnificationFeatureFlag.isAlwaysOnMagnificationEnabled(); return mAlwaysOnMagnificationFeatureFlag.isFeatureFlagEnabled(); } private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked( Loading
services/accessibility/java/com/android/server/accessibility/magnification/MagnificationFeatureFlagBase.java 0 → 100644 +115 −0 Original line number Diff line number Diff line /* * 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.annotation.NonNull; import android.os.Binder; import android.provider.DeviceConfig; import com.android.internal.annotations.VisibleForTesting; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; /** * Abstract base class to encapsulates the feature flags for magnification features. * {@see DeviceConfig} * * @hide */ abstract class MagnificationFeatureFlagBase { abstract String getNamespace(); abstract String getFeatureName(); abstract boolean getDefaultValue(); private void clearCallingIdentifyAndTryCatch(Runnable tryBlock, Runnable catchBlock) { try { Binder.withCleanCallingIdentity(() -> tryBlock.run()); } catch (Throwable throwable) { catchBlock.run(); } } /** Returns true iff the feature flag is readable and enabled */ public boolean isFeatureFlagEnabled() { AtomicBoolean isEnabled = new AtomicBoolean(getDefaultValue()); clearCallingIdentifyAndTryCatch( () -> isEnabled.set(DeviceConfig.getBoolean( getNamespace(), getFeatureName(), getDefaultValue())), () -> isEnabled.set(getDefaultValue())); return isEnabled.get(); } /** Sets the feature flag. Only used for testing; requires shell permissions. */ @VisibleForTesting public boolean setFeatureFlagEnabled(boolean isEnabled) { AtomicBoolean success = new AtomicBoolean(getDefaultValue()); clearCallingIdentifyAndTryCatch( () -> success.set(DeviceConfig.setProperty( getNamespace(), getFeatureName(), Boolean.toString(isEnabled), /* makeDefault= */ false)), () -> success.set(getDefaultValue())); return success.get(); } /** * Adds a listener for when the feature flag changes. * * <p>{@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. * * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor, * DeviceConfig.OnPropertiesChangedListener)} */ public void removeOnChangedListener( @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) { DeviceConfig.removeOnPropertiesChangedListener(onChangedListener); } }
services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java +53 −39 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading @@ -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; } /** Loading @@ -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); } Loading @@ -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); Loading @@ -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) { Loading @@ -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 Loading Loading @@ -231,7 +235,7 @@ public class MagnificationThumbnail { } }); mThumbNailAnimator.start(); mThumbnailAnimator.start(); } /** Loading @@ -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); } } Loading