From 9ac796de8d2b44f6aafadd0ab695e42605db79bc Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Mon, 15 Nov 2021 16:58:36 -0500 Subject: [PATCH] [DO NOT MERGE] Add animation for Dnd dialog Also, use SystemUI dialog theme. Test: manual Test: atest DndTileTest Fixes: 206454318 Fixes: 206730944 Change-Id: I599935e28ab6d6f6ad1fa497f4eeea652b74dad6 --- .../notification/EnableZenModeDialog.java | 13 +++-- .../android/systemui/qs/tiles/DndTile.java | 43 ++++++++++----- .../statusbar/phone/SystemUIDialog.java | 25 ++++++++- .../android/systemui/qs/tiles/DndTileTest.kt | 54 +++++++++++++++++-- 4 files changed, 112 insertions(+), 23 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java index a210e90a3cfcf..8b17be1e8ec96 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java @@ -19,7 +19,6 @@ package com.android.settingslib.notification; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.AlertDialog; -import android.app.Dialog; import android.app.NotificationManager; import android.content.Context; import android.content.DialogInterface; @@ -85,6 +84,7 @@ public class EnableZenModeDialog { @VisibleForTesting protected Context mContext; + private final int mThemeResId; @VisibleForTesting protected TextView mZenAlarmWarning; @VisibleForTesting @@ -97,10 +97,15 @@ public class EnableZenModeDialog { protected LayoutInflater mLayoutInflater; public EnableZenModeDialog(Context context) { - mContext = context; + this(context, 0); } - public Dialog createDialog() { + public EnableZenModeDialog(Context context, int themeResId) { + mContext = context; + mThemeResId = themeResId; + } + + public AlertDialog createDialog() { mNotificationManager = (NotificationManager) mContext. getSystemService(Context.NOTIFICATION_SERVICE); mForeverId = Condition.newId(mContext).appendPath("forever").build(); @@ -108,7 +113,7 @@ public class EnableZenModeDialog { mUserId = mContext.getUserId(); mAttached = false; - final AlertDialog.Builder builder = new AlertDialog.Builder(mContext) + final AlertDialog.Builder builder = new AlertDialog.Builder(mContext, mThemeResId) .setTitle(R.string.zen_mode_settings_turn_on_dialog_title) .setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.zen_mode_enable_dialog_turn_on, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index b1cd68e5185c8..3163c5f5a3c9e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; import static android.provider.Settings.Global.ZEN_MODE_OFF; +import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.Intent; @@ -41,7 +42,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.ViewGroup; -import android.view.WindowManager; import android.widget.Switch; import android.widget.Toast; @@ -53,6 +53,7 @@ import com.android.settingslib.notification.EnableZenModeDialog; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SysUIToast; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; @@ -84,6 +85,7 @@ public class DndTile extends QSTileImpl { private final DndDetailAdapter mDetailAdapter; private final SharedPreferences mSharedPreferences; private final SecureSetting mSettingZenDuration; + private final DialogLaunchAnimator mDialogLaunchAnimator; private boolean mListening; private boolean mShowingDetail; @@ -100,7 +102,8 @@ public class DndTile extends QSTileImpl { QSLogger qsLogger, ZenModeController zenModeController, @Main SharedPreferences sharedPreferences, - SecureSettings secureSettings + SecureSettings secureSettings, + DialogLaunchAnimator dialogLaunchAnimator ) { super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); @@ -108,6 +111,7 @@ public class DndTile extends QSTileImpl { mSharedPreferences = sharedPreferences; mDetailAdapter = new DndDetailAdapter(); mController.observe(getLifecycle(), mZenCallback); + mDialogLaunchAnimator = dialogLaunchAnimator; mSettingZenDuration = new SecureSetting(secureSettings, mUiHandler, Settings.Secure.ZEN_DURATION, getHost().getUserId()) { @Override @@ -117,8 +121,6 @@ public class DndTile extends QSTileImpl { }; } - - public static void setVisible(Context context, boolean visible) { Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible); } @@ -187,14 +189,17 @@ public class DndTile extends QSTileImpl { switch (zenDuration) { case Settings.Secure.ZEN_DURATION_PROMPT: mUiHandler.post(() -> { - Dialog mDialog = new EnableZenModeDialog(mContext).createDialog(); - mDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - SystemUIDialog.setShowForAllUsers(mDialog, true); - SystemUIDialog.registerDismissListener(mDialog); - SystemUIDialog.setWindowOnTop(mDialog); - mUiHandler.post(() -> mDialog.show()); - mHost.collapsePanels(); + Dialog dialog = makeZenModeDialog(); + if (view != null) { + final Dialog hostDialog = + mDialogLaunchAnimator.showFromView(dialog, view, false); + setDialogListeners(dialog, hostDialog); + } else { + // If we are not launching with animator, register default + // dismiss listener + SystemUIDialog.registerDismissListener(dialog); + dialog.show(); + } }); break; case Settings.Secure.ZEN_DURATION_FOREVER: @@ -209,6 +214,20 @@ public class DndTile extends QSTileImpl { } } + private Dialog makeZenModeDialog() { + AlertDialog dialog = new EnableZenModeDialog(mContext, R.style.Theme_SystemUI_Dialog) + .createDialog(); + SystemUIDialog.applyFlags(dialog); + SystemUIDialog.setShowForAllUsers(dialog, true); + return dialog; + } + + private void setDialogListeners(Dialog zenModeDialog, Dialog hostDialog) { + // Zen mode dialog is never hidden. + SystemUIDialog.registerDismissListener(zenModeDialog, hostDialog::dismiss); + zenModeDialog.setOnCancelListener(dialog -> hostDialog.cancel()); + } + @Override protected void handleSecondaryClick(@Nullable View view) { if (mController.isVolumeRestricted()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java index 1130ec24108a5..ed52a81751ddb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java @@ -36,6 +36,8 @@ import android.view.WindowInsets.Type; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import androidx.annotation.Nullable; + import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.animation.DialogListener; @@ -303,13 +305,32 @@ public class SystemUIDialog extends AlertDialog implements ListenableDialog, * the screen off / close system dialogs broadcast. *

* Note: Don't call dialog.setOnDismissListener() after - * calling this because it causes a leak of BroadcastReceiver. + * calling this because it causes a leak of BroadcastReceiver. Instead, call the version that + * takes an extra Runnable as a parameter. * * @param dialog The dialog to be associated with the listener. */ public static void registerDismissListener(Dialog dialog) { + registerDismissListener(dialog, null); + } + + + /** + * Registers a listener that dismisses the given dialog when it receives + * the screen off / close system dialogs broadcast. + *

+ * Note: Don't call dialog.setOnDismissListener() after + * calling this because it causes a leak of BroadcastReceiver. + * + * @param dialog The dialog to be associated with the listener. + * @param dismissAction An action to run when the dialog is dismissed. + */ + public static void registerDismissListener(Dialog dialog, @Nullable Runnable dismissAction) { DismissReceiver dismissReceiver = new DismissReceiver(dialog); - dialog.setOnDismissListener(d -> dismissReceiver.unregister()); + dialog.setOnDismissListener(d -> { + dismissReceiver.unregister(); + if (dismissAction != null) dismissAction.run(); + }); dismissReceiver.register(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt index f99703e2415d1..13df04ccef6f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt @@ -16,22 +16,28 @@ package com.android.systemui.qs.tiles +import android.app.Dialog import android.content.ContextWrapper import android.content.SharedPreferences import android.os.Handler import android.provider.Settings +import android.provider.Settings.Global.ZEN_MODE_OFF import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import android.view.View import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.logging.QSLogger import com.android.systemui.statusbar.policy.ZenModeController +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.SecureSettings import com.google.common.truth.Truth.assertThat @@ -40,9 +46,12 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.never +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.io.File +import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @@ -70,6 +79,10 @@ class DndTileTest : SysuiTestCase() { private lateinit var zenModeController: ZenModeController @Mock private lateinit var sharedPreferences: SharedPreferences + @Mock + private lateinit var dialogLaunchAnimator: DialogLaunchAnimator + @Mock + private lateinit var hostDialog: Dialog private lateinit var secureSettings: SecureSettings private lateinit var testableLooper: TestableLooper @@ -81,15 +94,17 @@ class DndTileTest : SysuiTestCase() { testableLooper = TestableLooper.get(this) secureSettings = FakeSettings() - Mockito.`when`(qsHost.userId).thenReturn(DEFAULT_USER) - Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) + whenever(qsHost.userId).thenReturn(DEFAULT_USER) + whenever(qsHost.uiEventLogger).thenReturn(uiEventLogger) + whenever(dialogLaunchAnimator.showFromView(any(), any(), anyBoolean())) + .thenReturn(hostDialog) val wrappedContext = object : ContextWrapper(context) { override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences { return sharedPreferences } } - Mockito.`when`(qsHost.context).thenReturn(wrappedContext) + whenever(qsHost.context).thenReturn(wrappedContext) tile = DndTile( qsHost, @@ -102,7 +117,8 @@ class DndTileTest : SysuiTestCase() { qsLogger, zenModeController, sharedPreferences, - secureSettings + secureSettings, + dialogLaunchAnimator ) } @@ -147,4 +163,32 @@ class DndTileTest : SysuiTestCase() { assertThat(tile.state.forceExpandIcon).isTrue() } + + @Test + fun testLaunchDialogFromViewWhenPrompt() { + whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF) + + secureSettings.putIntForUser(KEY, Settings.Secure.ZEN_DURATION_PROMPT, DEFAULT_USER) + testableLooper.processAllMessages() + + val view = View(context) + tile.handleClick(view) + testableLooper.processAllMessages() + + verify(dialogLaunchAnimator).showFromView(any(), eq(view), anyBoolean()) + } + + @Test + fun testNoLaunchDialogWhenNotPrompt() { + whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF) + + secureSettings.putIntForUser(KEY, 60, DEFAULT_USER) + testableLooper.processAllMessages() + + val view = View(context) + tile.handleClick(view) + testableLooper.processAllMessages() + + verify(dialogLaunchAnimator, never()).showFromView(any(), any(), anyBoolean()) + } } \ No newline at end of file