diff --git a/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/AccessibilityInsightsForAndroidService.java b/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/AccessibilityInsightsForAndroidService.java index e1fa8a0f..8e76f9cc 100644 --- a/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/AccessibilityInsightsForAndroidService.java +++ b/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/AccessibilityInsightsForAndroidService.java @@ -116,7 +116,7 @@ protected void onServiceConnected() { LayoutParamGenerator layoutParamGenerator = new LayoutParamGenerator(this::getRealDisplayMetrics); focusVisualizationCanvas = new FocusVisualizationCanvas(this); focusVisualizer = new FocusVisualizer(new FocusVisualizerStyles(), focusVisualizationCanvas); - focusVisualizerController = new FocusVisualizerController(focusVisualizer, focusVisualizationStateManager, new UIThreadRunner(), windowManager, layoutParamGenerator, focusVisualizationCanvas); + focusVisualizerController = new FocusVisualizerController(focusVisualizer, focusVisualizationStateManager, new UIThreadRunner(), windowManager, layoutParamGenerator, focusVisualizationCanvas, new DateProvider()); accessibilityEventDispatcher = new AccessibilityEventDispatcher(); deviceOrientationHandler = new DeviceOrientationHandler(getResources().getConfiguration().orientation); diff --git a/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/DateProvider.java b/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/DateProvider.java new file mode 100644 index 00000000..40927c6d --- /dev/null +++ b/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/DateProvider.java @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package com.microsoft.accessibilityinsightsforandroidservice; + +import java.util.Date; + +public class DateProvider { + public Date get() { + return new Date(); + } +} diff --git a/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/FocusVisualizerController.java b/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/FocusVisualizerController.java index 7ba93c64..bd797641 100644 --- a/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/FocusVisualizerController.java +++ b/AccessibilityInsightsForAndroidService/app/src/main/java/com/microsoft/accessibilityinsightsforandroidservice/FocusVisualizerController.java @@ -6,6 +6,7 @@ import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import java.util.Date; public class FocusVisualizerController { private FocusVisualizer focusVisualizer; @@ -15,6 +16,9 @@ public class FocusVisualizerController { private LayoutParamGenerator layoutParamGenerator; private FocusVisualizationCanvas focusVisualizationCanvas; private AccessibilityNodeInfo lastEventSource; + private DateProvider dateProvider; + private Date lastOrientationChange; + private long maximumOrientationChangeDelay = 1000; public FocusVisualizerController( FocusVisualizer focusVisualizer, @@ -22,20 +26,23 @@ public FocusVisualizerController( UIThreadRunner uiThreadRunner, WindowManager windowManager, LayoutParamGenerator layoutParamGenerator, - FocusVisualizationCanvas focusVisualizationCanvas) { + FocusVisualizationCanvas focusVisualizationCanvas, + DateProvider dateProvider) { this.focusVisualizer = focusVisualizer; this.focusVisualizationStateManager = focusVisualizationStateManager; this.uiThreadRunner = uiThreadRunner; this.windowManager = windowManager; this.layoutParamGenerator = layoutParamGenerator; this.focusVisualizationCanvas = focusVisualizationCanvas; + this.dateProvider = dateProvider; this.focusVisualizationStateManager.subscribe(this::onFocusVisualizationStateChange); + this.lastOrientationChange = dateProvider.get(); } public void onFocusEvent(AccessibilityEvent event) { lastEventSource = event.getSource(); - - if (focusVisualizationStateManager.getState() == false) { + if (focusVisualizationStateManager.getState() == false + || ignoreFocusEventDueToRecentOrientationChange()) { return; } @@ -62,7 +69,7 @@ public void onOrientationChanged(Integer orientation) { if (focusVisualizationStateManager.getState() == false) { return; } - + lastOrientationChange = dateProvider.get(); windowManager.updateViewLayout(focusVisualizationCanvas, layoutParamGenerator.get()); focusVisualizer.resetVisualizations(); } @@ -86,4 +93,12 @@ private void removeFocusVisualizationToScreen() { focusVisualizer.resetVisualizations(); windowManager.removeView(focusVisualizationCanvas); } + + private boolean ignoreFocusEventDueToRecentOrientationChange() { + Date currentTime = dateProvider.get(); + long cur = currentTime.getTime(); + long last = lastOrientationChange.getTime(); + long timeSinceLastOrientationChange = cur - last; + return timeSinceLastOrientationChange < maximumOrientationChangeDelay; + } } diff --git a/AccessibilityInsightsForAndroidService/app/src/test/java/com/microsoft/accessibilityinsightsforandroidservice/FocusVisualizerControllerTest.java b/AccessibilityInsightsForAndroidService/app/src/test/java/com/microsoft/accessibilityinsightsforandroidservice/FocusVisualizerControllerTest.java index b6c5595d..d2f0ce24 100644 --- a/AccessibilityInsightsForAndroidService/app/src/test/java/com/microsoft/accessibilityinsightsforandroidservice/FocusVisualizerControllerTest.java +++ b/AccessibilityInsightsForAndroidService/app/src/test/java/com/microsoft/accessibilityinsightsforandroidservice/FocusVisualizerControllerTest.java @@ -4,6 +4,7 @@ package com.microsoft.accessibilityinsightsforandroidservice; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -12,8 +13,7 @@ import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; +import java.util.Date; import java.util.function.Consumer; import org.junit.Assert; import org.junit.Before; @@ -25,8 +25,6 @@ @RunWith(PowerMockRunner.class) public class FocusVisualizerControllerTest { - private static final ScheduledExecutorService mainThread = - Executors.newSingleThreadScheduledExecutor(); @Mock FocusVisualizer focusVisualizerMock; @Mock FocusVisualizationStateManager focusVisualizationStateManagerMock; @Mock AccessibilityEvent accessibilityEventMock; @@ -36,6 +34,9 @@ public class FocusVisualizerControllerTest { @Mock FocusVisualizationCanvas focusVisualizationCanvas; @Mock WindowManager.LayoutParams layoutParams; @Mock AccessibilityNodeInfo accessibilityNodeInfo; + @Mock DateProvider dateProvider; + @Mock Date oldDateMock; + @Mock Date newDateMock; Consumer listener; FocusVisualizerController testSubject; @@ -44,6 +45,7 @@ public class FocusVisualizerControllerTest { public void prepare() { when(layoutParamGenerator.get()).thenReturn(layoutParams); listener = null; + when(dateProvider.get()).thenReturn(oldDateMock); testSubject = new FocusVisualizerController( focusVisualizerMock, @@ -51,7 +53,8 @@ public void prepare() { uiThreadRunner, windowManager, layoutParamGenerator, - focusVisualizationCanvas); + focusVisualizationCanvas, + dateProvider); } @Test @@ -67,9 +70,25 @@ public void onFocusEventDoesNotCallVisualizerIfStateIsFalse() { } @Test - public void onFocusEventCallsVisualizerIfStateIsTrue() { + public void onFocusEventDoesNotCallVisualizerIfOrientationChangedRecently() { + reset(dateProvider); + when(dateProvider.get()).thenReturn(newDateMock); + when(focusVisualizationStateManagerMock.getState()).thenReturn(true); + when(oldDateMock.getTime()).thenReturn((long) 500); + when(newDateMock.getTime()).thenReturn((long) 501); + testSubject.onFocusEvent(accessibilityEventMock); + verify(focusVisualizerMock, times(0)).addNewFocusedElement(accessibilityNodeInfo); + } + + @Test + public void onFocusEventCallsVisualizerIfStateIsTrueAndOrientationHasNotChangedRecently() + throws Exception { + reset(dateProvider); + when(dateProvider.get()).thenReturn(newDateMock); when(focusVisualizationStateManagerMock.getState()).thenReturn(true); when(accessibilityEventMock.getSource()).thenReturn(accessibilityNodeInfo); + when(oldDateMock.getTime()).thenReturn((long) 500); + when(newDateMock.getTime()).thenReturn((long) 10000); testSubject.onFocusEvent(accessibilityEventMock); verify(focusVisualizerMock, times(1)).addNewFocusedElement(accessibilityNodeInfo); } @@ -145,7 +164,8 @@ public void onFocusVisualizationStateChangeToEnabledAddsVisualization() { uiThreadRunner, windowManager, layoutParamGenerator, - focusVisualizationCanvas); + focusVisualizationCanvas, + dateProvider); verify(windowManager).addView(focusVisualizationCanvas, layoutParams); } @@ -178,7 +198,8 @@ public void onFocusVisualizationStateChangeToEnabledAddsVisualizationWithLastEve uiThreadRunner, windowManager, layoutParamGenerator, - focusVisualizationCanvas); + focusVisualizationCanvas, + dateProvider); testSubject.onFocusEvent(accessibilityEventMock); listener.accept(true); @@ -214,7 +235,8 @@ public void onFocusVisualizationStateChangToDisabledRemovesVisualizations() { uiThreadRunner, windowManager, layoutParamGenerator, - focusVisualizationCanvas); + focusVisualizationCanvas, + dateProvider); verify(focusVisualizerMock).resetVisualizations(); }