diff options
3 files changed, 135 insertions, 2 deletions
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 572f6ff2745e..1a37e2ddcc4b 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -977,4 +977,13 @@ Width in pixels of the Side FPS sensor. --> <integer name="config_sfpsSensorWidth">200</integer> + + <!-- + They are service names that, if enabled, will cause the magnification settings button + to never hide after timeout. + --> + <string-array name="services_always_show_magnification_settings" translatable="false"> + <item>com.android.switchaccess.SwitchAccessService</item> + <item>com.google.android.apps.accessibility.voiceaccess.JustSpeakService</item> + </string-array> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java index 84f1395051eb..d9d9e3781242 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java @@ -20,12 +20,14 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.annotation.UiContext; import android.content.ComponentCallbacks; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; @@ -49,6 +51,8 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.res.R; import java.util.Collections; +import java.util.Optional; +import java.util.Set; /** * Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of @@ -315,10 +319,46 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, AccessibilityManager.FLAG_CONTENT_ICONS | AccessibilityManager.FLAG_CONTENT_CONTROLS); + if (shouldAlwaysShowSettings()) { + mUiTimeout = -1; + } } // Refresh the time slot of the fade-out task whenever this method is called. stopFadeOutAnimation(); - mImageView.postOnAnimationDelayed(mFadeOutAnimationTask, mUiTimeout); + if (mUiTimeout >= 0) { + mImageView.postOnAnimationDelayed(mFadeOutAnimationTask, mUiTimeout); + } + } + + private boolean shouldAlwaysShowSettings() { + try { + var serviceNamesArray = mContext.getResources().getStringArray( + R.array.services_always_show_magnification_settings); + if (serviceNamesArray.length == 0) { + return false; + } + Set serviceNamesSet = Set.of(serviceNamesArray); + + var serviceInfoList = mAccessibilityManager + .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK); + for (var serviceInfo : serviceInfoList) { + var serviceName = Optional.ofNullable(serviceInfo) + .map(AccessibilityServiceInfo::getResolveInfo) + .map(resolveInfo -> resolveInfo.serviceInfo) + .map(resolvedServiceInfo -> resolvedServiceInfo.name) + .orElse(null); + if (serviceName == null) { + continue; + } + + if (serviceNamesSet.contains(serviceName)) { + return true; + } + } + } catch (Resources.NotFoundException nfe) { + // No-op. Do not crash for not finding resources. + } + return false; } private void stopFadeOutAnimation() { 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 8fd2bd6ad762..1a885453eccb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java @@ -49,8 +49,11 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.graphics.Insets; import android.graphics.Rect; import android.os.Handler; @@ -71,8 +74,8 @@ import android.widget.ImageView; import androidx.test.filters.SmallTest; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.res.R; import org.junit.After; import org.junit.Before; @@ -186,6 +189,87 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { } @Test + public void showMagnificationButton_noA11yServicesRunning_postDelayedAnimationsWithTimeout() { + final int a11yTimeout = 12345; + when(mAccessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn( + a11yTimeout); + when(mAccessibilityManager.getEnabledAccessibilityServiceList(anyInt())) + .thenReturn(List.of()); + + mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + + verify(mAccessibilityManager).getRecommendedTimeoutMillis( + DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, AccessibilityManager.FLAG_CONTENT_ICONS + | AccessibilityManager.FLAG_CONTENT_CONTROLS); + verify(mSpyImageView).postOnAnimationDelayed(any(Runnable.class), eq((long) a11yTimeout)); + } + + @Test + public void showMagnificationButton_voiceAccessRunning_noTimeout() { + var serviceInfo = createServiceInfoWithName( + "com.google.android.apps.accessibility.voiceaccess.JustSpeakService"); + when(mAccessibilityManager.getEnabledAccessibilityServiceList(anyInt())) + .thenReturn(List.of(serviceInfo)); + + mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + + verify(mSpyImageView, never()).postOnAnimationDelayed(any(Runnable.class), anyLong()); + } + + @Test + public void showMagnificationButton_switchAccessRunning_noTimeout() { + var serviceInfo = createServiceInfoWithName( + "com.android.switchaccess.SwitchAccessService"); + when(mAccessibilityManager.getEnabledAccessibilityServiceList(anyInt())) + .thenReturn(List.of(serviceInfo)); + + mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + + verify(mSpyImageView, never()).postOnAnimationDelayed(any(Runnable.class), anyLong()); + } + + @Test + public void showMagnificationButton_switchAccessAndVoiceAccessBothRunning_noTimeout() { + var switchAccessServiceInfo = createServiceInfoWithName( + "com.android.switchaccess.SwitchAccessService"); + var voiceAccessServiceInfo = createServiceInfoWithName( + "com.google.android.apps.accessibility.voiceaccess.JustSpeakService"); + when(mAccessibilityManager.getEnabledAccessibilityServiceList(anyInt())) + .thenReturn(List.of(switchAccessServiceInfo, voiceAccessServiceInfo)); + + mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + + verify(mSpyImageView, never()).postOnAnimationDelayed(any(Runnable.class), anyLong()); + } + + @Test + public void showMagnificationButton_someOtherServiceRunning_postDelayedAnimationsWithTimeout() { + final int a11yTimeout = 12345; + when(mAccessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn( + a11yTimeout); + var serviceInfo1 = createServiceInfoWithName("com.test.someService1"); + var serviceInfo2 = createServiceInfoWithName("com.test.someService2"); + when(mAccessibilityManager.getEnabledAccessibilityServiceList(anyInt())) + .thenReturn(List.of(serviceInfo1, serviceInfo2)); + + mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + + verify(mAccessibilityManager).getRecommendedTimeoutMillis( + DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, AccessibilityManager.FLAG_CONTENT_ICONS + | AccessibilityManager.FLAG_CONTENT_CONTROLS); + verify(mSpyImageView).postOnAnimationDelayed(any(Runnable.class), eq((long) a11yTimeout)); + } + + private AccessibilityServiceInfo createServiceInfoWithName(String name) { + var resolveInfo = new ResolveInfo(); + resolveInfo.serviceInfo = new ServiceInfo(); + resolveInfo.serviceInfo.name = name; + var serviceInfo = new AccessibilityServiceInfo(); + serviceInfo.setResolveInfo(resolveInfo); + return serviceInfo; + } + + @Test public void showMagnificationButton_windowModeAndFadingOut_verifyAnimationEndAction() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); executeFadeOutAnimation(); |