Loading services/accessibility/accessibility.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,13 @@ flag { bug: "257274411" } flag { name: "enable_magnification_one_finger_panning_gesture" namespace: "accessibility" description: "Whether to allow easy-mode (one finger panning gesture) for magnification" bug: "282039824" } flag { name: "fix_drag_pointer_when_ending_drag" namespace: "accessibility" Loading services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +20 −19 Original line number Diff line number Diff line Loading @@ -167,7 +167,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH }) public @interface OverscrollState {} @VisibleForTesting boolean mIsSinglePanningEnabled; @VisibleForTesting final OneFingerPanningSettingsProvider mOneFingerPanningSettingsProvider; private final FullScreenMagnificationVibrationHelper mFullScreenMagnificationVibrationHelper; Loading Loading @@ -201,7 +201,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH displayId, fullScreenMagnificationVibrationHelper, /* magnificationLogger= */ null, ViewConfiguration.get(context)); ViewConfiguration.get(context), new OneFingerPanningSettingsProvider( context, Flags.enableMagnificationOneFingerPanningGesture() )); } /** Constructor for tests. */ Loading @@ -218,7 +222,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH int displayId, FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper, MagnificationLogger magnificationLogger, ViewConfiguration viewConfiguration) { ViewConfiguration viewConfiguration, OneFingerPanningSettingsProvider oneFingerPanningSettingsProvider ) { super(displayId, detectSingleFingerTripleTap, detectTwoFingerTripleTap, detectShortcutTrigger, trace, callback); if (DEBUG_ALL) { Loading Loading @@ -301,9 +307,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH mPanningScalingState = new PanningScalingState(context); mSinglePanningState = new SinglePanningState(context); mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper; setSinglePanningEnabled( context.getResources() .getBoolean(R.bool.config_enable_a11y_magnification_single_panning)); mOneFingerPanningSettingsProvider = oneFingerPanningSettingsProvider; mOverscrollHandler = new OverscrollHandler(); mIsWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); Loading @@ -317,11 +321,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH transitionTo(mDetectingState); } @VisibleForTesting void setSinglePanningEnabled(boolean isEnabled) { mIsSinglePanningEnabled = isEnabled; } @Override void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (event.getActionMasked() == ACTION_DOWN) { Loading Loading @@ -361,6 +360,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH Slog.i(mLogTag, "onDestroy(); delayed = " + MotionEventInfo.toString(mDetectingState.mDelayedEventQueue)); } mOneFingerPanningSettingsProvider.unregister(); if (mScreenStateReceiver != null) { mScreenStateReceiver.unregister(); Loading Loading @@ -524,7 +524,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH && event.getPointerCount() == 2 // includes the pointer currently being released && mPreviousState == mViewportDraggingState) { // if feature flag is enabled, currently only true on watches if (mIsSinglePanningEnabled) { if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) { mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded(); mOverscrollHandler.clearEdgeState(); } Loading @@ -532,7 +532,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } else if (action == ACTION_UP || action == ACTION_CANCEL) { onPanningFinished(event); // if feature flag is enabled, currently only true on watches if (mIsSinglePanningEnabled) { if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) { mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded(); mOverscrollHandler.clearEdgeState(); } Loading Loading @@ -611,7 +611,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH onPan(second); mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX, distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); if (mIsSinglePanningEnabled) { if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) { mOverscrollHandler.onScrollStateChanged(first, second); } return /* event consumed: */ true; Loading Loading @@ -1000,7 +1000,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH && event.getPointerCount() == 2) { transitionToViewportDraggingStateAndClear(event); } else if (isActivated() && event.getPointerCount() == 2) { if (mIsSinglePanningEnabled if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled() && overscrollState(event, mFirstPointerDownLocation) == OVERSCROLL_VERTICAL_EDGE) { transitionToDelegatingStateAndClear(); Loading @@ -1008,7 +1008,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH //Primary pointer is swiping, so transit to PanningScalingState transitToPanningScalingStateAndClear(); } } else if (mIsSinglePanningEnabled } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled() && isActivated() && event.getPointerCount() == 1) { if (overscrollState(event, mFirstPointerDownLocation) Loading Loading @@ -1255,7 +1255,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) { transitionToViewportDraggingStateAndClear(event); } else if (isActivated() && event.getPointerCount() == 2) { if (mIsSinglePanningEnabled if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled() && overscrollState(event, mFirstPointerDownLocation) == OVERSCROLL_VERTICAL_EDGE) { transitionToDelegatingStateAndClear(); Loading @@ -1263,7 +1263,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH //Primary pointer is swiping, so transit to PanningScalingState transitToPanningScalingStateAndClear(); } } else if (mIsSinglePanningEnabled } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled() && isActivated() && event.getPointerCount() == 1) { if (overscrollState(event, mFirstPointerDownLocation) Loading Loading @@ -1633,7 +1633,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH + ", mPreviousState=" + State.nameOf(mPreviousState) + ", mMagnificationController=" + mFullScreenMagnificationController + ", mDisplayId=" + mDisplayId + ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled + ", mIsSinglePanningEnabled=" + mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled() + ", mOverscrollHandler=" + mOverscrollHandler + '}'; } Loading services/accessibility/java/com/android/server/accessibility/magnification/OneFingerPanningSettingsProvider.java 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; import android.net.Uri; import android.provider.Settings; import androidx.annotation.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.atomic.AtomicBoolean; /** * Provider for secure settings {@link Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED}. */ public class OneFingerPanningSettingsProvider { @VisibleForTesting static final String KEY = Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED; private static final Uri URI = Settings.Secure.getUriFor(KEY); private AtomicBoolean mCached = new AtomicBoolean(); @VisibleForTesting ContentObserver mObserver; @VisibleForTesting ContentResolver mContentResolver; @Retention(RetentionPolicy.SOURCE) public @interface State { int OFF = 0; int ON = 1; } public OneFingerPanningSettingsProvider( Context context, boolean featureFlagEnabled ) { var defaultValue = isOneFingerPanningEnabledDefault(context); if (featureFlagEnabled) { mContentResolver = context.getContentResolver(); mObserver = new ContentObserver(context.getMainThreadHandler()) { @Override public void onChange(boolean selfChange) { mCached.set(isOneFingerPanningEnabledInSetting(context, defaultValue)); } }; mCached.set(isOneFingerPanningEnabledInSetting(context, defaultValue)); mContentResolver.registerContentObserver(URI, false, mObserver); } else { mCached.set(defaultValue); } } /** Returns whether one finger panning is enabled.. */ public boolean isOneFingerPanningEnabled() { return mCached.get(); } /** Unregister content observer for listening to secure settings. */ public void unregister() { if (mContentResolver != null) { mContentResolver.unregisterContentObserver(mObserver); } mContentResolver = null; } private boolean isOneFingerPanningEnabledInSetting(Context context, boolean defaultValue) { return State.ON == Settings.Secure.getIntForUser( mContentResolver, KEY, (defaultValue ? State.ON : State.OFF), context.getUserId()); } @VisibleForTesting static boolean isOneFingerPanningEnabledDefault(Context context) { boolean oneFingerPanningDefaultValue; try { oneFingerPanningDefaultValue = context.getResources().getBoolean( com.android.internal.R.bool.config_enable_a11y_magnification_single_panning); } catch (Resources.NotFoundException e) { oneFingerPanningDefaultValue = false; } return oneFingerPanningDefaultValue; } } services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +25 −18 Original line number Diff line number Diff line Loading @@ -189,6 +189,8 @@ public class FullScreenMagnificationGestureHandlerTest { FullScreenMagnificationVibrationHelper mMockFullScreenMagnificationVibrationHelper; @Mock FullScreenMagnificationGestureHandler.MagnificationLogger mMockMagnificationLogger; @Mock OneFingerPanningSettingsProvider mMockOneFingerPanningSettingsProvider; @Rule public final TestableContext mContext = new TestableContext(getInstrumentation().getContext()); Loading Loading @@ -266,6 +268,7 @@ public class FullScreenMagnificationGestureHandlerTest { mMgh.onDestroy(); mFullScreenMagnificationController.unregister(DISPLAY_0); verify(mWindowMagnificationPromptController).onDestroy(); verify(mMockOneFingerPanningSettingsProvider).unregister(); Settings.Secure.putFloatForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, mOriginalMagnificationPersistedScale, Loading @@ -288,11 +291,10 @@ public class FullScreenMagnificationGestureHandlerTest { DISPLAY_0, mMockFullScreenMagnificationVibrationHelper, mMockMagnificationLogger, ViewConfiguration.get(mContext)); ViewConfiguration.get(mContext), mMockOneFingerPanningSettingsProvider); if (isWatch()) { h.setSinglePanningEnabled(true); } else { h.setSinglePanningEnabled(false); enableOneFingerPanning(true); } mHandler = new TestHandler(h.mDetectingState, mClock) { @Override Loading Loading @@ -607,8 +609,8 @@ public class FullScreenMagnificationGestureHandlerTest { @Test @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) public void testTwoFingerTap_StateIsActivated_shouldInDelegating() { assumeTrue(mMgh.mIsSinglePanningEnabled); mMgh.setSinglePanningEnabled(false); assumeTrue(isWatch()); enableOneFingerPanning(false); goFromStateIdleTo(STATE_ACTIVATED); allowEventDelegation(); Loading @@ -623,8 +625,8 @@ public class FullScreenMagnificationGestureHandlerTest { @Test @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) public void testTwoFingerTap_StateIsIdle_shouldInDelegating() { assumeTrue(mMgh.mIsSinglePanningEnabled); mMgh.setSinglePanningEnabled(false); assumeTrue(isWatch()); enableOneFingerPanning(false); goFromStateIdleTo(STATE_IDLE); allowEventDelegation(); Loading Loading @@ -830,7 +832,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test public void testActionUpNotAtEdge_singlePanningState_detectingState() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); send(upEvent()); Loading @@ -841,8 +843,8 @@ public class FullScreenMagnificationGestureHandlerTest { @Test public void testScroll_SinglePanningDisabled_delegatingState() { assumeTrue(mMgh.mIsSinglePanningEnabled); mMgh.setSinglePanningEnabled(false); assumeTrue(isWatch()); enableOneFingerPanning(false); goFromStateIdleTo(STATE_ACTIVATED); allowEventDelegation(); Loading @@ -854,7 +856,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test @FlakyTest public void testScroll_singleHorizontalPanningAndAtEdge_leftEdgeOverscroll() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); float centerY = (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f; Loading @@ -878,7 +880,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test @FlakyTest public void testScroll_singleHorizontalPanningAndAtEdge_rightEdgeOverscroll() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); float centerY = (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f; Loading @@ -902,7 +904,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test @FlakyTest public void testScroll_singleVerticalPanningAndAtEdge_verticalOverscroll() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); float centerX = (INITIAL_MAGNIFICATION_BOUNDS.right + INITIAL_MAGNIFICATION_BOUNDS.left) / 2.0f; Loading @@ -924,7 +926,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test public void testScroll_singlePanningAndAtEdge_noOverscroll() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); float centerY = (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f; Loading @@ -946,7 +948,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test public void testScroll_singleHorizontalPanningAndAtEdge_vibrate() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); mFullScreenMagnificationController.setCenter( DISPLAY_0, Loading @@ -970,7 +972,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test public void testScroll_singleVerticalPanningAndAtEdge_doNotVibrate() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); mFullScreenMagnificationController.setCenter( DISPLAY_0, Loading @@ -993,8 +995,9 @@ public class FullScreenMagnificationGestureHandlerTest { } @Test @RequiresFlagsEnabled(Flags.FLAG_FULLSCREEN_FLING_GESTURE) public void singleFinger_testScrollAfterMagnified_startsFling() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_ACTIVATED); swipeAndHold(); Loading Loading @@ -1274,6 +1277,10 @@ public class FullScreenMagnificationGestureHandlerTest { mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false); } private void enableOneFingerPanning(boolean enable) { when(mMockOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()).thenReturn(enable); } private void assertActionsInOrder(List<MotionEvent> actualEvents, List<Integer> expectedActions) { assertTrue(actualEvents.size() == expectedActions.size()); Loading services/tests/servicestests/src/com/android/server/accessibility/magnification/OneFingerPanningSettingsProviderTest.java 0 → 100644 +153 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import android.provider.Settings; import android.testing.TestableContext; import androidx.test.runner.AndroidJUnit4; import com.android.server.accessibility.magnification.OneFingerPanningSettingsProvider.State; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class OneFingerPanningSettingsProviderTest { @Rule public final TestableContext mContext = new TestableContext(getInstrumentation().getContext()); private boolean mDefaultValue; private boolean mOriginalIsOneFingerPanningEnabled; private OneFingerPanningSettingsProvider mProvider; @Before public void setup() { mDefaultValue = OneFingerPanningSettingsProvider.isOneFingerPanningEnabledDefault(mContext); mOriginalIsOneFingerPanningEnabled = isSecureSettingsEnabled(); } @After public void tearDown() { enableSecureSettings(mOriginalIsOneFingerPanningEnabled); if (mProvider != null) { mProvider.unregister(); } } @Test public void isOneFingerPanningEnabled_flagDisabled_matchesDefault() { mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ false); assertThat(mProvider.isOneFingerPanningEnabled()).isEqualTo(mDefaultValue); } @Test public void isOneFingerPanningEnabled_flagEnabledSettingEnabled_true() { enableSecureSettings(true); mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true); assertTrue(mProvider.isOneFingerPanningEnabled()); } @Test public void isOneFingerPanningEnabled_flagEnabledSettingDisabled_false() { enableSecureSettings(false); mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true); assertFalse(mProvider.isOneFingerPanningEnabled()); } @Test public void isOneFingerPanningEnabled_flagEnabledSettingsFalse_false() { mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true); // Simulate observer triggered. enableSecureSettings(false); mProvider.mObserver.onChange(/* selfChange= */ false); assertFalse(mProvider.isOneFingerPanningEnabled()); } @Test public void isOneFingerPanningEnabled_flagEnabledSettingsTrue_true() { mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true); // Simulate observer triggered. enableSecureSettings(true); mProvider.mObserver.onChange(/* selfChange= */ false); assertTrue(mProvider.isOneFingerPanningEnabled()); } @Test public void isOneFingerPanningEnabled_flagDisabledSettingsChanges_valueUnchanged() { mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ false); var previousValue = mProvider.isOneFingerPanningEnabled(); enableSecureSettings(!previousValue); assertThat(mProvider.isOneFingerPanningEnabled()).isEqualTo(previousValue); assertThat(mProvider.isOneFingerPanningEnabled()).isEqualTo(mDefaultValue); } @Test public void unregister_featureEnabled_contentResolverNull() { var provider = new OneFingerPanningSettingsProvider( mContext, /* featureFlagEnabled */ true); provider.unregister(); assertThat(provider.mContentResolver).isNull(); } @Test public void unregister_featureDisabled_noError() { var provider = new OneFingerPanningSettingsProvider( mContext, /* featureFlagEnabled */ false); provider.unregister(); } private void enableSecureSettings(boolean enable) { Settings.Secure.putIntForUser( mContext.getContentResolver(), OneFingerPanningSettingsProvider.KEY, enable ? State.ON : State.OFF, mContext.getUserId()); } private boolean isSecureSettingsEnabled() { return State.ON == Settings.Secure.getIntForUser( mContext.getContentResolver(), OneFingerPanningSettingsProvider.KEY, mDefaultValue ? State.ON : State.OFF, mContext.getUserId()); } } Loading
services/accessibility/accessibility.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,13 @@ flag { bug: "257274411" } flag { name: "enable_magnification_one_finger_panning_gesture" namespace: "accessibility" description: "Whether to allow easy-mode (one finger panning gesture) for magnification" bug: "282039824" } flag { name: "fix_drag_pointer_when_ending_drag" namespace: "accessibility" Loading
services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +20 −19 Original line number Diff line number Diff line Loading @@ -167,7 +167,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH }) public @interface OverscrollState {} @VisibleForTesting boolean mIsSinglePanningEnabled; @VisibleForTesting final OneFingerPanningSettingsProvider mOneFingerPanningSettingsProvider; private final FullScreenMagnificationVibrationHelper mFullScreenMagnificationVibrationHelper; Loading Loading @@ -201,7 +201,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH displayId, fullScreenMagnificationVibrationHelper, /* magnificationLogger= */ null, ViewConfiguration.get(context)); ViewConfiguration.get(context), new OneFingerPanningSettingsProvider( context, Flags.enableMagnificationOneFingerPanningGesture() )); } /** Constructor for tests. */ Loading @@ -218,7 +222,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH int displayId, FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper, MagnificationLogger magnificationLogger, ViewConfiguration viewConfiguration) { ViewConfiguration viewConfiguration, OneFingerPanningSettingsProvider oneFingerPanningSettingsProvider ) { super(displayId, detectSingleFingerTripleTap, detectTwoFingerTripleTap, detectShortcutTrigger, trace, callback); if (DEBUG_ALL) { Loading Loading @@ -301,9 +307,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH mPanningScalingState = new PanningScalingState(context); mSinglePanningState = new SinglePanningState(context); mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper; setSinglePanningEnabled( context.getResources() .getBoolean(R.bool.config_enable_a11y_magnification_single_panning)); mOneFingerPanningSettingsProvider = oneFingerPanningSettingsProvider; mOverscrollHandler = new OverscrollHandler(); mIsWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); Loading @@ -317,11 +321,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH transitionTo(mDetectingState); } @VisibleForTesting void setSinglePanningEnabled(boolean isEnabled) { mIsSinglePanningEnabled = isEnabled; } @Override void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (event.getActionMasked() == ACTION_DOWN) { Loading Loading @@ -361,6 +360,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH Slog.i(mLogTag, "onDestroy(); delayed = " + MotionEventInfo.toString(mDetectingState.mDelayedEventQueue)); } mOneFingerPanningSettingsProvider.unregister(); if (mScreenStateReceiver != null) { mScreenStateReceiver.unregister(); Loading Loading @@ -524,7 +524,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH && event.getPointerCount() == 2 // includes the pointer currently being released && mPreviousState == mViewportDraggingState) { // if feature flag is enabled, currently only true on watches if (mIsSinglePanningEnabled) { if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) { mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded(); mOverscrollHandler.clearEdgeState(); } Loading @@ -532,7 +532,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } else if (action == ACTION_UP || action == ACTION_CANCEL) { onPanningFinished(event); // if feature flag is enabled, currently only true on watches if (mIsSinglePanningEnabled) { if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) { mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded(); mOverscrollHandler.clearEdgeState(); } Loading Loading @@ -611,7 +611,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH onPan(second); mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX, distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); if (mIsSinglePanningEnabled) { if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) { mOverscrollHandler.onScrollStateChanged(first, second); } return /* event consumed: */ true; Loading Loading @@ -1000,7 +1000,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH && event.getPointerCount() == 2) { transitionToViewportDraggingStateAndClear(event); } else if (isActivated() && event.getPointerCount() == 2) { if (mIsSinglePanningEnabled if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled() && overscrollState(event, mFirstPointerDownLocation) == OVERSCROLL_VERTICAL_EDGE) { transitionToDelegatingStateAndClear(); Loading @@ -1008,7 +1008,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH //Primary pointer is swiping, so transit to PanningScalingState transitToPanningScalingStateAndClear(); } } else if (mIsSinglePanningEnabled } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled() && isActivated() && event.getPointerCount() == 1) { if (overscrollState(event, mFirstPointerDownLocation) Loading Loading @@ -1255,7 +1255,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) { transitionToViewportDraggingStateAndClear(event); } else if (isActivated() && event.getPointerCount() == 2) { if (mIsSinglePanningEnabled if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled() && overscrollState(event, mFirstPointerDownLocation) == OVERSCROLL_VERTICAL_EDGE) { transitionToDelegatingStateAndClear(); Loading @@ -1263,7 +1263,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH //Primary pointer is swiping, so transit to PanningScalingState transitToPanningScalingStateAndClear(); } } else if (mIsSinglePanningEnabled } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled() && isActivated() && event.getPointerCount() == 1) { if (overscrollState(event, mFirstPointerDownLocation) Loading Loading @@ -1633,7 +1633,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH + ", mPreviousState=" + State.nameOf(mPreviousState) + ", mMagnificationController=" + mFullScreenMagnificationController + ", mDisplayId=" + mDisplayId + ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled + ", mIsSinglePanningEnabled=" + mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled() + ", mOverscrollHandler=" + mOverscrollHandler + '}'; } Loading
services/accessibility/java/com/android/server/accessibility/magnification/OneFingerPanningSettingsProvider.java 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; import android.net.Uri; import android.provider.Settings; import androidx.annotation.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.atomic.AtomicBoolean; /** * Provider for secure settings {@link Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED}. */ public class OneFingerPanningSettingsProvider { @VisibleForTesting static final String KEY = Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED; private static final Uri URI = Settings.Secure.getUriFor(KEY); private AtomicBoolean mCached = new AtomicBoolean(); @VisibleForTesting ContentObserver mObserver; @VisibleForTesting ContentResolver mContentResolver; @Retention(RetentionPolicy.SOURCE) public @interface State { int OFF = 0; int ON = 1; } public OneFingerPanningSettingsProvider( Context context, boolean featureFlagEnabled ) { var defaultValue = isOneFingerPanningEnabledDefault(context); if (featureFlagEnabled) { mContentResolver = context.getContentResolver(); mObserver = new ContentObserver(context.getMainThreadHandler()) { @Override public void onChange(boolean selfChange) { mCached.set(isOneFingerPanningEnabledInSetting(context, defaultValue)); } }; mCached.set(isOneFingerPanningEnabledInSetting(context, defaultValue)); mContentResolver.registerContentObserver(URI, false, mObserver); } else { mCached.set(defaultValue); } } /** Returns whether one finger panning is enabled.. */ public boolean isOneFingerPanningEnabled() { return mCached.get(); } /** Unregister content observer for listening to secure settings. */ public void unregister() { if (mContentResolver != null) { mContentResolver.unregisterContentObserver(mObserver); } mContentResolver = null; } private boolean isOneFingerPanningEnabledInSetting(Context context, boolean defaultValue) { return State.ON == Settings.Secure.getIntForUser( mContentResolver, KEY, (defaultValue ? State.ON : State.OFF), context.getUserId()); } @VisibleForTesting static boolean isOneFingerPanningEnabledDefault(Context context) { boolean oneFingerPanningDefaultValue; try { oneFingerPanningDefaultValue = context.getResources().getBoolean( com.android.internal.R.bool.config_enable_a11y_magnification_single_panning); } catch (Resources.NotFoundException e) { oneFingerPanningDefaultValue = false; } return oneFingerPanningDefaultValue; } }
services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +25 −18 Original line number Diff line number Diff line Loading @@ -189,6 +189,8 @@ public class FullScreenMagnificationGestureHandlerTest { FullScreenMagnificationVibrationHelper mMockFullScreenMagnificationVibrationHelper; @Mock FullScreenMagnificationGestureHandler.MagnificationLogger mMockMagnificationLogger; @Mock OneFingerPanningSettingsProvider mMockOneFingerPanningSettingsProvider; @Rule public final TestableContext mContext = new TestableContext(getInstrumentation().getContext()); Loading Loading @@ -266,6 +268,7 @@ public class FullScreenMagnificationGestureHandlerTest { mMgh.onDestroy(); mFullScreenMagnificationController.unregister(DISPLAY_0); verify(mWindowMagnificationPromptController).onDestroy(); verify(mMockOneFingerPanningSettingsProvider).unregister(); Settings.Secure.putFloatForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, mOriginalMagnificationPersistedScale, Loading @@ -288,11 +291,10 @@ public class FullScreenMagnificationGestureHandlerTest { DISPLAY_0, mMockFullScreenMagnificationVibrationHelper, mMockMagnificationLogger, ViewConfiguration.get(mContext)); ViewConfiguration.get(mContext), mMockOneFingerPanningSettingsProvider); if (isWatch()) { h.setSinglePanningEnabled(true); } else { h.setSinglePanningEnabled(false); enableOneFingerPanning(true); } mHandler = new TestHandler(h.mDetectingState, mClock) { @Override Loading Loading @@ -607,8 +609,8 @@ public class FullScreenMagnificationGestureHandlerTest { @Test @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) public void testTwoFingerTap_StateIsActivated_shouldInDelegating() { assumeTrue(mMgh.mIsSinglePanningEnabled); mMgh.setSinglePanningEnabled(false); assumeTrue(isWatch()); enableOneFingerPanning(false); goFromStateIdleTo(STATE_ACTIVATED); allowEventDelegation(); Loading @@ -623,8 +625,8 @@ public class FullScreenMagnificationGestureHandlerTest { @Test @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) public void testTwoFingerTap_StateIsIdle_shouldInDelegating() { assumeTrue(mMgh.mIsSinglePanningEnabled); mMgh.setSinglePanningEnabled(false); assumeTrue(isWatch()); enableOneFingerPanning(false); goFromStateIdleTo(STATE_IDLE); allowEventDelegation(); Loading Loading @@ -830,7 +832,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test public void testActionUpNotAtEdge_singlePanningState_detectingState() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); send(upEvent()); Loading @@ -841,8 +843,8 @@ public class FullScreenMagnificationGestureHandlerTest { @Test public void testScroll_SinglePanningDisabled_delegatingState() { assumeTrue(mMgh.mIsSinglePanningEnabled); mMgh.setSinglePanningEnabled(false); assumeTrue(isWatch()); enableOneFingerPanning(false); goFromStateIdleTo(STATE_ACTIVATED); allowEventDelegation(); Loading @@ -854,7 +856,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test @FlakyTest public void testScroll_singleHorizontalPanningAndAtEdge_leftEdgeOverscroll() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); float centerY = (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f; Loading @@ -878,7 +880,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test @FlakyTest public void testScroll_singleHorizontalPanningAndAtEdge_rightEdgeOverscroll() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); float centerY = (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f; Loading @@ -902,7 +904,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test @FlakyTest public void testScroll_singleVerticalPanningAndAtEdge_verticalOverscroll() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); float centerX = (INITIAL_MAGNIFICATION_BOUNDS.right + INITIAL_MAGNIFICATION_BOUNDS.left) / 2.0f; Loading @@ -924,7 +926,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test public void testScroll_singlePanningAndAtEdge_noOverscroll() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); float centerY = (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f; Loading @@ -946,7 +948,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test public void testScroll_singleHorizontalPanningAndAtEdge_vibrate() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); mFullScreenMagnificationController.setCenter( DISPLAY_0, Loading @@ -970,7 +972,7 @@ public class FullScreenMagnificationGestureHandlerTest { @Test public void testScroll_singleVerticalPanningAndAtEdge_doNotVibrate() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_SINGLE_PANNING); mFullScreenMagnificationController.setCenter( DISPLAY_0, Loading @@ -993,8 +995,9 @@ public class FullScreenMagnificationGestureHandlerTest { } @Test @RequiresFlagsEnabled(Flags.FLAG_FULLSCREEN_FLING_GESTURE) public void singleFinger_testScrollAfterMagnified_startsFling() { assumeTrue(mMgh.mIsSinglePanningEnabled); assumeTrue(isWatch()); goFromStateIdleTo(STATE_ACTIVATED); swipeAndHold(); Loading Loading @@ -1274,6 +1277,10 @@ public class FullScreenMagnificationGestureHandlerTest { mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false); } private void enableOneFingerPanning(boolean enable) { when(mMockOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()).thenReturn(enable); } private void assertActionsInOrder(List<MotionEvent> actualEvents, List<Integer> expectedActions) { assertTrue(actualEvents.size() == expectedActions.size()); Loading
services/tests/servicestests/src/com/android/server/accessibility/magnification/OneFingerPanningSettingsProviderTest.java 0 → 100644 +153 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import android.provider.Settings; import android.testing.TestableContext; import androidx.test.runner.AndroidJUnit4; import com.android.server.accessibility.magnification.OneFingerPanningSettingsProvider.State; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class OneFingerPanningSettingsProviderTest { @Rule public final TestableContext mContext = new TestableContext(getInstrumentation().getContext()); private boolean mDefaultValue; private boolean mOriginalIsOneFingerPanningEnabled; private OneFingerPanningSettingsProvider mProvider; @Before public void setup() { mDefaultValue = OneFingerPanningSettingsProvider.isOneFingerPanningEnabledDefault(mContext); mOriginalIsOneFingerPanningEnabled = isSecureSettingsEnabled(); } @After public void tearDown() { enableSecureSettings(mOriginalIsOneFingerPanningEnabled); if (mProvider != null) { mProvider.unregister(); } } @Test public void isOneFingerPanningEnabled_flagDisabled_matchesDefault() { mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ false); assertThat(mProvider.isOneFingerPanningEnabled()).isEqualTo(mDefaultValue); } @Test public void isOneFingerPanningEnabled_flagEnabledSettingEnabled_true() { enableSecureSettings(true); mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true); assertTrue(mProvider.isOneFingerPanningEnabled()); } @Test public void isOneFingerPanningEnabled_flagEnabledSettingDisabled_false() { enableSecureSettings(false); mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true); assertFalse(mProvider.isOneFingerPanningEnabled()); } @Test public void isOneFingerPanningEnabled_flagEnabledSettingsFalse_false() { mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true); // Simulate observer triggered. enableSecureSettings(false); mProvider.mObserver.onChange(/* selfChange= */ false); assertFalse(mProvider.isOneFingerPanningEnabled()); } @Test public void isOneFingerPanningEnabled_flagEnabledSettingsTrue_true() { mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ true); // Simulate observer triggered. enableSecureSettings(true); mProvider.mObserver.onChange(/* selfChange= */ false); assertTrue(mProvider.isOneFingerPanningEnabled()); } @Test public void isOneFingerPanningEnabled_flagDisabledSettingsChanges_valueUnchanged() { mProvider = new OneFingerPanningSettingsProvider(mContext, /* featureFlagEnabled */ false); var previousValue = mProvider.isOneFingerPanningEnabled(); enableSecureSettings(!previousValue); assertThat(mProvider.isOneFingerPanningEnabled()).isEqualTo(previousValue); assertThat(mProvider.isOneFingerPanningEnabled()).isEqualTo(mDefaultValue); } @Test public void unregister_featureEnabled_contentResolverNull() { var provider = new OneFingerPanningSettingsProvider( mContext, /* featureFlagEnabled */ true); provider.unregister(); assertThat(provider.mContentResolver).isNull(); } @Test public void unregister_featureDisabled_noError() { var provider = new OneFingerPanningSettingsProvider( mContext, /* featureFlagEnabled */ false); provider.unregister(); } private void enableSecureSettings(boolean enable) { Settings.Secure.putIntForUser( mContext.getContentResolver(), OneFingerPanningSettingsProvider.KEY, enable ? State.ON : State.OFF, mContext.getUserId()); } private boolean isSecureSettingsEnabled() { return State.ON == Settings.Secure.getIntForUser( mContext.getContentResolver(), OneFingerPanningSettingsProvider.KEY, mDefaultValue ? State.ON : State.OFF, mContext.getUserId()); } }