diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index afc567e5de5c7..c88048c17160c 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -97,6 +97,12 @@ public interface WindowManagerPolicyConstants { */ String EXTRA_START_REASON = "android.intent.extra.EXTRA_START_REASON"; + /** + * Set to {@code true} when intent was invoked from pressing one of the brightness keys. + * @hide + */ + String EXTRA_FROM_BRIGHTNESS_KEY = "android.intent.extra.FROM_BRIGHTNESS_KEY"; + // TODO: move this to a more appropriate place. interface PointerEventListener { /** diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java index 5199bd43f9829..182e4569b549f 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java @@ -18,6 +18,7 @@ package com.android.systemui.settings.brightness; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY; import android.app.Activity; import android.graphics.Rect; @@ -29,8 +30,10 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; @@ -38,34 +41,42 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.List; -import java.util.concurrent.Executor; import javax.inject.Inject; /** A dialog that provides controls for adjusting the screen brightness. */ public class BrightnessDialog extends Activity { + @VisibleForTesting + static final int DIALOG_TIMEOUT_MILLIS = 3000; + private BrightnessController mBrightnessController; private final BrightnessSliderController.Factory mToggleSliderFactory; private final UserTracker mUserTracker; private final DisplayTracker mDisplayTracker; - private final Executor mMainExecutor; + private final DelayableExecutor mMainExecutor; private final Handler mBackgroundHandler; + private final AccessibilityManagerWrapper mAccessibilityMgr; + private Runnable mCancelTimeoutRunnable; @Inject public BrightnessDialog( UserTracker userTracker, DisplayTracker displayTracker, BrightnessSliderController.Factory factory, - @Main Executor mainExecutor, - @Background Handler bgHandler) { + @Main DelayableExecutor mainExecutor, + @Background Handler bgHandler, + AccessibilityManagerWrapper accessibilityMgr) { mUserTracker = userTracker; mDisplayTracker = displayTracker; mToggleSliderFactory = factory; mMainExecutor = mainExecutor; mBackgroundHandler = bgHandler; + mAccessibilityMgr = accessibilityMgr; } @@ -121,6 +132,14 @@ public class BrightnessDialog extends Activity { MetricsLogger.visible(this, MetricsEvent.BRIGHTNESS_DIALOG); } + @Override + protected void onResume() { + super.onResume(); + if (triggeredByBrightnessKey()) { + scheduleTimeout(); + } + } + @Override protected void onPause() { super.onPause(); @@ -139,9 +158,25 @@ public class BrightnessDialog extends Activity { if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) { + if (mCancelTimeoutRunnable != null) { + mCancelTimeoutRunnable.run(); + } finish(); } return super.onKeyDown(keyCode, event); } + + private boolean triggeredByBrightnessKey() { + return getIntent().getBooleanExtra(EXTRA_FROM_BRIGHTNESS_KEY, false); + } + + private void scheduleTimeout() { + if (mCancelTimeoutRunnable != null) { + mCancelTimeoutRunnable.run(); + } + final int timeout = mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS, + AccessibilityManager.FLAG_CONTENT_CONTROLS); + mCancelTimeoutRunnable = mMainExecutor.executeDelayed(this::finish, timeout); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt index 16751c937f9e2..5c35913f6e209 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt @@ -16,12 +16,14 @@ package com.android.systemui.settings.brightness +import android.content.Intent import android.graphics.Rect import android.os.Handler import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.view.ViewGroup +import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule import com.android.systemui.R @@ -29,15 +31,20 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.activity.SingleActivityFactory import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat -import java.util.concurrent.Executor import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.eq import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @@ -48,9 +55,12 @@ class BrightnessDialogTest : SysuiTestCase() { @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var brightnessSliderControllerFactory: BrightnessSliderController.Factory - @Mock private lateinit var mainExecutor: Executor @Mock private lateinit var backgroundHandler: Handler @Mock private lateinit var brightnessSliderController: BrightnessSliderController + @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper + + private val clock = FakeSystemClock() + private val mainExecutor = FakeExecutor(clock) private var displayTracker = FakeDisplayTracker(mContext) @@ -64,7 +74,8 @@ class BrightnessDialogTest : SysuiTestCase() { displayTracker, brightnessSliderControllerFactory, mainExecutor, - backgroundHandler + backgroundHandler, + accessibilityMgr ) }, /* initialTouchMode= */ false, @@ -77,8 +88,6 @@ class BrightnessDialogTest : SysuiTestCase() { `when`(brightnessSliderControllerFactory.create(any(), any())) .thenReturn(brightnessSliderController) `when`(brightnessSliderController.rootView).thenReturn(View(context)) - - activityRule.launchActivity(null) } @After @@ -88,6 +97,7 @@ class BrightnessDialogTest : SysuiTestCase() { @Test fun testGestureExclusion() { + activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) val frame = activityRule.activity.requireViewById(R.id.brightness_mirror_container) val lp = frame.layoutParams as ViewGroup.MarginLayoutParams @@ -104,18 +114,83 @@ class BrightnessDialogTest : SysuiTestCase() { .isEqualTo(Rect(-horizontalMargin, 0, frame.width + horizontalMargin, frame.height)) } + @Test + fun testTimeout() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG) + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true) + activityRule.launchActivity(intent) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong()) + assertThat(activityRule.activity.isFinishing()).isTrue() + } + + @Test + fun testRestartTimeout() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG) + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true) + activityRule.launchActivity(intent) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + // Restart the timeout + activityRule.activity.onResume() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + // The dialog should not have disappeared yet + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + assertThat(activityRule.activity.isFinishing()).isTrue() + } + + @Test + fun testNoTimeoutIfNotStartedByBrightnessKey() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong()) + assertThat(activityRule.activity.isFinishing()).isFalse() + } + class TestDialog( userTracker: UserTracker, displayTracker: FakeDisplayTracker, brightnessSliderControllerFactory: BrightnessSliderController.Factory, - mainExecutor: Executor, - backgroundHandler: Handler + mainExecutor: DelayableExecutor, + backgroundHandler: Handler, + accessibilityMgr: AccessibilityManagerWrapper ) : BrightnessDialog( userTracker, displayTracker, brightnessSliderControllerFactory, mainExecutor, - backgroundHandler + backgroundHandler, + accessibilityMgr ) } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 279a480842522..5cfbcaafe82ef 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3211,8 +3211,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { minLinearBrightness, maxLinearBrightness); mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness); - startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), - UserHandle.CURRENT_OR_SELF); + Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true); + startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); } return true; case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN: