Merge "feat(#AlwaysOnMagnifier): Add haptic and buffer zone when panning scale to persisted scale" into udc-dev
This commit is contained in:
@@ -44,6 +44,8 @@ import android.os.Handler;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.MathUtils;
|
import android.util.MathUtils;
|
||||||
@@ -279,7 +281,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
|
|||||||
return mTempPointerProperties;
|
return mTempPointerProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transitionTo(State state) {
|
@VisibleForTesting
|
||||||
|
void transitionTo(State state) {
|
||||||
if (DEBUG_STATE_TRANSITIONS) {
|
if (DEBUG_STATE_TRANSITIONS) {
|
||||||
Slog.i(mLogTag,
|
Slog.i(mLogTag,
|
||||||
(State.nameOf(mCurrentState) + " -> " + State.nameOf(state)
|
(State.nameOf(mCurrentState) + " -> " + State.nameOf(state)
|
||||||
@@ -287,6 +290,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
|
|||||||
.replace(getClass().getName(), ""));
|
.replace(getClass().getName(), ""));
|
||||||
}
|
}
|
||||||
mPreviousState = mCurrentState;
|
mPreviousState = mCurrentState;
|
||||||
|
if (state == mPanningScalingState) {
|
||||||
|
mPanningScalingState.prepareForState();
|
||||||
|
}
|
||||||
mCurrentState = state;
|
mCurrentState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,18 +323,34 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
|
|||||||
final class PanningScalingState extends SimpleOnGestureListener
|
final class PanningScalingState extends SimpleOnGestureListener
|
||||||
implements OnScaleGestureListener, State {
|
implements OnScaleGestureListener, State {
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
private final ScaleGestureDetector mScaleGestureDetector;
|
private final ScaleGestureDetector mScaleGestureDetector;
|
||||||
private final GestureDetector mScrollGestureDetector;
|
private final GestureDetector mScrollGestureDetector;
|
||||||
final float mScalingThreshold;
|
final float mScalingThreshold;
|
||||||
|
|
||||||
float mInitialScaleFactor = -1;
|
float mInitialScaleFactor = -1;
|
||||||
boolean mScaling;
|
@VisibleForTesting boolean mScaling;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether it needs to detect the target scale passes
|
||||||
|
* {@link FullScreenMagnificationController#getPersistedScale} during panning scale.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting boolean mDetectingPassPersistedScale;
|
||||||
|
|
||||||
|
// The threshold for relative difference from given scale to persisted scale. If the
|
||||||
|
// difference >= threshold, we can start detecting if the scale passes the persisted
|
||||||
|
// scale during panning.
|
||||||
|
@VisibleForTesting static final float CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD = 0.2f;
|
||||||
|
// The threshold for relative difference from given scale to persisted scale. If the
|
||||||
|
// difference < threshold, we can decide that the scale passes the persisted scale.
|
||||||
|
@VisibleForTesting static final float PASSING_PERSISTED_SCALE_THRESHOLD = 0.01f;
|
||||||
|
|
||||||
PanningScalingState(Context context) {
|
PanningScalingState(Context context) {
|
||||||
final TypedValue scaleValue = new TypedValue();
|
final TypedValue scaleValue = new TypedValue();
|
||||||
context.getResources().getValue(
|
context.getResources().getValue(
|
||||||
R.dimen.config_screen_magnification_scaling_threshold,
|
R.dimen.config_screen_magnification_scaling_threshold,
|
||||||
scaleValue, false);
|
scaleValue, false);
|
||||||
|
mContext = context;
|
||||||
mScalingThreshold = scaleValue.getFloat();
|
mScalingThreshold = scaleValue.getFloat();
|
||||||
mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
|
mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
|
||||||
mScaleGestureDetector.setQuickScaleEnabled(false);
|
mScaleGestureDetector.setQuickScaleEnabled(false);
|
||||||
@@ -351,12 +373,59 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void prepareForState() {
|
||||||
|
checkShouldDetectPassPersistedScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkShouldDetectPassPersistedScale() {
|
||||||
|
if (mDetectingPassPersistedScale) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final float currentScale =
|
||||||
|
mFullScreenMagnificationController.getScale(mDisplayId);
|
||||||
|
final float persistedScale =
|
||||||
|
mFullScreenMagnificationController.getPersistedScale(mDisplayId);
|
||||||
|
|
||||||
|
mDetectingPassPersistedScale =
|
||||||
|
(abs(currentScale - persistedScale) / persistedScale)
|
||||||
|
>= CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
public void persistScaleAndTransitionTo(State state) {
|
public void persistScaleAndTransitionTo(State state) {
|
||||||
mFullScreenMagnificationController.persistScale(mDisplayId);
|
mFullScreenMagnificationController.persistScale(mDisplayId);
|
||||||
clear();
|
clear();
|
||||||
transitionTo(state);
|
transitionTo(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setScaleAndClearIfNeeded(float scale, float pivotX, float pivotY) {
|
||||||
|
if (mDetectingPassPersistedScale) {
|
||||||
|
final float persistedScale =
|
||||||
|
mFullScreenMagnificationController.getPersistedScale(mDisplayId);
|
||||||
|
// If the scale passes the persisted scale during panning, perform a vibration
|
||||||
|
// feedback to user. Also, call {@link clear} to create a buffer zone so that
|
||||||
|
// user needs to panning more than {@link mScalingThreshold} to change scale again.
|
||||||
|
if (abs(scale - persistedScale) / persistedScale
|
||||||
|
< PASSING_PERSISTED_SCALE_THRESHOLD) {
|
||||||
|
scale = persistedScale;
|
||||||
|
final Vibrator vibrator = mContext.getSystemService(Vibrator.class);
|
||||||
|
if (vibrator != null) {
|
||||||
|
vibrator.vibrate(
|
||||||
|
VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
|
||||||
|
}
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
|
||||||
|
mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
|
||||||
|
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
|
||||||
|
|
||||||
|
checkShouldDetectPassPersistedScale();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onScroll(MotionEvent first, MotionEvent second,
|
public boolean onScroll(MotionEvent first, MotionEvent second,
|
||||||
float distanceX, float distanceY) {
|
float distanceX, float distanceY) {
|
||||||
@@ -402,11 +471,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
|
|||||||
scale = targetScale;
|
scale = targetScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
final float pivotX = detector.getFocusX();
|
setScaleAndClearIfNeeded(scale, detector.getFocusX(), detector.getFocusY());
|
||||||
final float pivotY = detector.getFocusY();
|
|
||||||
if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
|
|
||||||
mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
|
|
||||||
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
|
|
||||||
return /* handled: */ true;
|
return /* handled: */ true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,6 +489,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
|
|||||||
public void clear() {
|
public void clear() {
|
||||||
mInitialScaleFactor = -1;
|
mInitialScaleFactor = -1;
|
||||||
mScaling = false;
|
mScaling = false;
|
||||||
|
mDetectingPassPersistedScale = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ import android.annotation.NonNull;
|
|||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.testing.TestableContext;
|
import android.testing.TestableContext;
|
||||||
import android.util.DebugUtils;
|
import android.util.DebugUtils;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
@@ -507,6 +509,91 @@ public class FullScreenMagnificationGestureHandlerTest {
|
|||||||
verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
|
verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransitToPanningState_scaleDifferenceOverThreshold_startDetecting() {
|
||||||
|
final float scale = 2.0f;
|
||||||
|
final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
|
||||||
|
.CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
|
||||||
|
final float persistedScale = (1.0f + threshold) * scale + 1.0f;
|
||||||
|
mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
|
||||||
|
DEFAULT_Y, /* animate= */ false,
|
||||||
|
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
|
||||||
|
mFullScreenMagnificationController.persistScale(DISPLAY_0);
|
||||||
|
mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
|
||||||
|
DEFAULT_Y, /* animate= */ false,
|
||||||
|
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
|
||||||
|
|
||||||
|
mMgh.transitionTo(mMgh.mPanningScalingState);
|
||||||
|
|
||||||
|
assertTrue(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
|
||||||
|
|
||||||
|
mMgh.clearAndTransitionToStateDetecting();
|
||||||
|
mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransitToPanningState_scaleDifferenceLessThanThreshold_doNotDetect() {
|
||||||
|
final float scale = 2.0f;
|
||||||
|
final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
|
||||||
|
.CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
|
||||||
|
final float persistedScale = (1.0f + threshold) * scale - 0.1f;
|
||||||
|
mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
|
||||||
|
DEFAULT_Y, /* animate= */ false,
|
||||||
|
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
|
||||||
|
mFullScreenMagnificationController.persistScale(DISPLAY_0);
|
||||||
|
mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
|
||||||
|
DEFAULT_Y, /* animate= */ false,
|
||||||
|
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
|
||||||
|
|
||||||
|
mMgh.transitionTo(mMgh.mPanningScalingState);
|
||||||
|
|
||||||
|
assertFalse(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
|
||||||
|
|
||||||
|
mMgh.clearAndTransitionToStateDetecting();
|
||||||
|
mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPanningScaleToPersistedScale_detecting_vibrateAndClear() {
|
||||||
|
Vibrator vibrator = mock(Vibrator.class);
|
||||||
|
mContext.addMockSystemService(Vibrator.class, vibrator);
|
||||||
|
|
||||||
|
mMgh.mPanningScalingState.mDetectingPassPersistedScale = true;
|
||||||
|
|
||||||
|
final float persistedScale =
|
||||||
|
mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);
|
||||||
|
|
||||||
|
mMgh.transitionTo(mMgh.mPanningScalingState);
|
||||||
|
mMgh.mPanningScalingState.setScaleAndClearIfNeeded(persistedScale, DEFAULT_X, DEFAULT_Y);
|
||||||
|
|
||||||
|
verify(vibrator).vibrate(any(VibrationEffect.class));
|
||||||
|
assertFalse(mMgh.mPanningScalingState.mScaling);
|
||||||
|
|
||||||
|
mMgh.clearAndTransitionToStateDetecting();
|
||||||
|
mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPanningScaleOverThreshold_notDetecting_startDetecting() {
|
||||||
|
final float persistedScale =
|
||||||
|
mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);
|
||||||
|
|
||||||
|
mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
|
||||||
|
DEFAULT_Y, /* animate= */ false,
|
||||||
|
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
|
||||||
|
mMgh.transitionTo(mMgh.mPanningScalingState);
|
||||||
|
|
||||||
|
final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
|
||||||
|
.CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
|
||||||
|
final float scale = (1.0f + threshold) * persistedScale + 1.0f;
|
||||||
|
mMgh.mPanningScalingState.setScaleAndClearIfNeeded(scale, DEFAULT_X, DEFAULT_Y);
|
||||||
|
|
||||||
|
assertTrue(mMgh.mPanningScalingState.mDetectingPassPersistedScale);
|
||||||
|
|
||||||
|
mMgh.clearAndTransitionToStateDetecting();
|
||||||
|
mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
private void assertActionsInOrder(List<MotionEvent> actualEvents,
|
private void assertActionsInOrder(List<MotionEvent> actualEvents,
|
||||||
List<Integer> expectedActions) {
|
List<Integer> expectedActions) {
|
||||||
assertTrue(actualEvents.size() == expectedActions.size());
|
assertTrue(actualEvents.size() == expectedActions.size());
|
||||||
|
|||||||
Reference in New Issue
Block a user