summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java70
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java172
2 files changed, 206 insertions, 36 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 34f721c94ed2..128af3702c49 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -20,8 +20,13 @@ import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
+import android.graphics.PointF;
import android.provider.Settings;
+import android.util.MathUtils;
import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.ImageView;
@@ -31,7 +36,8 @@ import com.android.systemui.R;
/**
* Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
* {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
- * The button UI would automatically be dismissed after displaying for a period of time.
+ * The button icon is movable by dragging. And the button UI would automatically be dismissed after
+ * displaying for a period of time.
*/
class MagnificationModeSwitch {
@@ -41,6 +47,10 @@ class MagnificationModeSwitch {
private final Context mContext;
private final WindowManager mWindowManager;
private final ImageView mImageView;
+ private final PointF mLastDown = new PointF();
+ private final PointF mLastDrag = new PointF();
+ private final int mTapTimeout = ViewConfiguration.getTapTimeout();
+ private final int mTouchSlop;
private int mMagnificationMode = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
private final WindowManager.LayoutParams mParams;
private boolean mIsVisible = false;
@@ -56,13 +66,10 @@ class MagnificationModeSwitch {
Context.WINDOW_SERVICE);
mParams = createLayoutParams();
mImageView = imageView;
+ mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
applyResourcesValues();
- mImageView.setOnClickListener(
- view -> {
- removeButton();
- toggleMagnificationMode();
- });
mImageView.setImageResource(getIconResId(mMagnificationMode));
+ mImageView.setOnTouchListener(this::onTouch);
}
private void applyResourcesValues() {
@@ -71,13 +78,59 @@ class MagnificationModeSwitch {
mImageView.setPadding(padding, padding, padding, padding);
}
+ private boolean onTouch(View v, MotionEvent event) {
+ if (!mIsVisible || mImageView == null) {
+ return false;
+ }
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mImageView.setAlpha(1.0f);
+ mImageView.animate().cancel();
+ mLastDown.set(event.getRawX(), event.getRawY());
+ mLastDrag.set(event.getRawX(), event.getRawY());
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ // Move the button position.
+ moveButton(event.getRawX() - mLastDrag.x,
+ event.getRawY() - mLastDrag.y);
+ mLastDrag.set(event.getRawX(), event.getRawY());
+ return true;
+ case MotionEvent.ACTION_UP:
+ // Single tap to toggle magnification mode and the button position will be reset
+ // after the action is performed.
+ final float distance = MathUtils.dist(mLastDown.x, mLastDown.y,
+ event.getRawX(), event.getRawY());
+ if ((event.getEventTime() - event.getDownTime()) <= mTapTimeout
+ && distance <= mTouchSlop) {
+ handleSingleTap();
+ } else {
+ showButton(mMagnificationMode);
+ }
+ return true;
+ case MotionEvent.ACTION_CANCEL:
+ showButton(mMagnificationMode);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void moveButton(float offsetX, float offsetY) {
+ mParams.x -= offsetX;
+ mParams.y -= offsetY;
+ mWindowManager.updateViewLayout(mImageView, mParams);
+ }
+
void removeButton() {
if (!mIsVisible) {
return;
}
mImageView.animate().cancel();
mWindowManager.removeView(mImageView);
+ // Reset button status.
mIsVisible = false;
+ mParams.x = 0;
+ mParams.y = 0;
}
void showButton(int mode) {
@@ -120,6 +173,11 @@ class MagnificationModeSwitch {
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, newMode);
}
+ private void handleSingleTap() {
+ removeButton();
+ toggleMagnificationMode();
+ }
+
private static ImageView createView(Context context) {
ImageView imageView = new ImageView(context);
imageView.setClickable(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index ee151c441b68..cdbc647f152b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -18,6 +18,10 @@ package com.android.systemui.accessibility;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId;
@@ -27,6 +31,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -35,7 +40,9 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewPropertyAnimator;
import android.view.WindowManager;
import android.widget.ImageView;
@@ -48,35 +55,38 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class MagnificationModeSwitchTest extends SysuiTestCase {
- @Mock
- private ImageView mMockImageView;
+ private ImageView mSpyImageView;
@Mock
private WindowManager mWindowManager;
@Mock
private ViewPropertyAnimator mViewPropertyAnimator;
private MagnificationModeSwitch mMagnificationModeSwitch;
+ @Captor
+ private ArgumentCaptor<View.OnTouchListener> mTouchListenerCaptor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ WindowManager wm = mContext.getSystemService(WindowManager.class);
+ doAnswer(invocation ->
+ wm.getMaximumWindowMetrics()
+ ).when(mWindowManager).getMaximumWindowMetrics();
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+ mSpyImageView = Mockito.spy(new ImageView(mContext));
+ doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener(
+ mTouchListenerCaptor.capture());
+ initMockImageViewAndAnimator();
- when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
- when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
- when(mViewPropertyAnimator.setStartDelay(anyLong())).thenReturn(mViewPropertyAnimator);
- when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
- mViewPropertyAnimator);
-
- when(mMockImageView.animate()).thenReturn(mViewPropertyAnimator);
-
- mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mMockImageView);
+ mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView);
}
@Test
@@ -85,7 +95,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
mMagnificationModeSwitch.removeButton();
- verify(mWindowManager).removeView(mMockImageView);
+ verify(mWindowManager).removeView(mSpyImageView);
// First invocation is in showButton.
verify(mViewPropertyAnimator, times(2)).cancel();
}
@@ -94,22 +104,19 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
public void showWindowModeButton_fullscreenMode_addViewAndSetImageResource() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- verify(mMockImageView).setAlpha(1.0f);
- verify(mMockImageView).setImageResource(
+ verify(mSpyImageView).setAlpha(1.0f);
+ verify(mSpyImageView).setImageResource(
getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
- verify(mViewPropertyAnimator).cancel();
- verify(mViewPropertyAnimator).setDuration(anyLong());
- verify(mViewPropertyAnimator).setStartDelay(anyLong());
- verify(mViewPropertyAnimator).alpha(anyFloat());
+ assertShowButtonAnimation();
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
verify(mViewPropertyAnimator).withEndAction(captor.capture());
- verify(mWindowManager).addView(eq(mMockImageView), any(WindowManager.LayoutParams.class));
+ verify(mWindowManager).addView(eq(mSpyImageView), any(WindowManager.LayoutParams.class));
captor.getValue().run();
// First invocation is in showButton.
verify(mViewPropertyAnimator, times(2)).cancel();
- verify(mWindowManager).removeView(mMockImageView);
+ verify(mWindowManager).removeView(mSpyImageView);
}
@Test
@@ -117,26 +124,131 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
- verify(mMockImageView, times(2)).setImageResource(
+ verify(mSpyImageView, times(2)).setImageResource(
getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
}
@Test
- public void performClick_fullscreenMode_removeViewAndChangeSettingsValue() {
- ArgumentCaptor<View.OnClickListener> captor = ArgumentCaptor.forClass(
- View.OnClickListener.class);
- verify(mMockImageView).setOnClickListener(captor.capture());
+ public void performSingleTap_fullscreenMode_removeViewAndChangeSettingsValue() {
mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ resetMockImageViewAndAnimator();
- captor.getValue().onClick(mMockImageView);
+ // Perform a single-tap
+ final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
+ listener.onTouch(mSpyImageView, MotionEvent.obtain(
+ 0, 0, ACTION_DOWN, 100, 100, 0));
+ verify(mViewPropertyAnimator).cancel();
- // First invocation is in showButton.
- verify(mViewPropertyAnimator, times(2)).cancel();
- verify(mMockImageView).setImageResource(
+ resetMockImageViewAndAnimator();
+ listener.onTouch(mSpyImageView, MotionEvent.obtain(
+ 0, ViewConfiguration.getTapTimeout(), ACTION_UP, 100, 100, 0));
+ verify(mViewPropertyAnimator).cancel();
+ verify(mSpyImageView).setImageResource(
getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
- verify(mWindowManager).removeView(mMockImageView);
+ verify(mWindowManager).removeView(mSpyImageView);
final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, actualMode);
}
+
+ @Test
+ public void showMagnificationButton_performDragging_updateViewLayout() {
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ resetMockImageViewAndAnimator();
+
+ // Perform dragging
+ final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
+ final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+ listener.onTouch(mSpyImageView, MotionEvent.obtain(
+ 0, 0, ACTION_DOWN, 100, 100, 0));
+ verify(mSpyImageView).setAlpha(1.0f);
+ verify(mViewPropertyAnimator).cancel();
+
+ listener.onTouch(mSpyImageView, MotionEvent.obtain(
+ 0, ViewConfiguration.getTapTimeout(), ACTION_MOVE, 100 + offset, 100, 0));
+ verify(mWindowManager).updateViewLayout(eq(mSpyImageView),
+ any(WindowManager.LayoutParams.class));
+
+ resetMockImageViewAndAnimator();
+ listener.onTouch(mSpyImageView, MotionEvent.obtain(
+ 0, ViewConfiguration.getTapTimeout() + 10, ACTION_UP, 100 + offset, 100, 0));
+ verify(mSpyImageView).setAlpha(1.0f);
+ assertModeUnchanged(previousMode);
+ assertShowButtonAnimation();
+ }
+
+ @Test
+ public void performSingleTapActionCanceled_showButtonAnimation() {
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ resetMockImageViewAndAnimator();
+
+ // Perform single tap
+ final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
+ final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+ listener.onTouch(mSpyImageView, MotionEvent.obtain(
+ 0, 0, ACTION_DOWN, 100, 100, 0));
+
+ resetMockImageViewAndAnimator();
+ listener.onTouch(mSpyImageView, MotionEvent.obtain(
+ 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100, 100, 0));
+ verify(mSpyImageView).setAlpha(1.0f);
+ assertModeUnchanged(previousMode);
+ assertShowButtonAnimation();
+ }
+
+ @Test
+ public void performDraggingActionCanceled_showButtonAnimation() {
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ resetMockImageViewAndAnimator();
+
+ // Perform dragging
+ final View.OnTouchListener listener = mTouchListenerCaptor.getValue();
+ final int offset = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ final int previousMode = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+ listener.onTouch(mSpyImageView, MotionEvent.obtain(
+ 0, 0, ACTION_DOWN, 100, 100, 0));
+ listener.onTouch(mSpyImageView, MotionEvent.obtain(
+ 0, ViewConfiguration.getTapTimeout(), ACTION_MOVE, 100 + offset, 100, 0));
+
+ resetMockImageViewAndAnimator();
+ listener.onTouch(mSpyImageView, MotionEvent.obtain(
+ 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100 + offset, 100, 0));
+ verify(mSpyImageView).setAlpha(1.0f);
+ assertModeUnchanged(previousMode);
+ assertShowButtonAnimation();
+ }
+
+ private void assertModeUnchanged(int expectedMode) {
+ final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+ assertEquals(expectedMode, actualMode);
+ }
+
+ private void assertShowButtonAnimation() {
+ verify(mViewPropertyAnimator).cancel();
+ verify(mViewPropertyAnimator).setDuration(anyLong());
+ verify(mViewPropertyAnimator).setStartDelay(anyLong());
+ verify(mViewPropertyAnimator).alpha(anyFloat());
+ verify(mViewPropertyAnimator).start();
+ }
+
+ private void initMockImageViewAndAnimator() {
+ when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
+ when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
+ when(mViewPropertyAnimator.setStartDelay(anyLong())).thenReturn(mViewPropertyAnimator);
+ when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
+ mViewPropertyAnimator);
+
+ when(mSpyImageView.animate()).thenReturn(mViewPropertyAnimator);
+ }
+
+ private void resetMockImageViewAndAnimator() {
+ Mockito.reset(mViewPropertyAnimator);
+ Mockito.reset(mSpyImageView);
+ initMockImageViewAndAnimator();
+ }
}