Loading packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java +8 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -85,6 +84,7 @@ public class EnableZenModeDialog { @VisibleForTesting protected Context mContext; private final int mThemeResId; @VisibleForTesting protected TextView mZenAlarmWarning; @VisibleForTesting Loading @@ -97,10 +97,15 @@ public class EnableZenModeDialog { protected LayoutInflater mLayoutInflater; public EnableZenModeDialog(Context context) { this(context, 0); } public EnableZenModeDialog(Context context, int themeResId) { mContext = context; mThemeResId = themeResId; } public Dialog createDialog() { public AlertDialog createDialog() { mNotificationManager = (NotificationManager) mContext. getSystemService(Context.NOTIFICATION_SERVICE); mForeverId = Condition.newId(mContext).appendPath("forever").build(); Loading @@ -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, Loading packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +31 −12 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -84,6 +85,7 @@ public class DndTile extends QSTileImpl<BooleanState> { private final DndDetailAdapter mDetailAdapter; private final SharedPreferences mSharedPreferences; private final SecureSetting mSettingZenDuration; private final DialogLaunchAnimator mDialogLaunchAnimator; private boolean mListening; private boolean mShowingDetail; Loading @@ -100,7 +102,8 @@ public class DndTile extends QSTileImpl<BooleanState> { QSLogger qsLogger, ZenModeController zenModeController, @Main SharedPreferences sharedPreferences, SecureSettings secureSettings SecureSettings secureSettings, DialogLaunchAnimator dialogLaunchAnimator ) { super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); Loading @@ -108,6 +111,7 @@ public class DndTile extends QSTileImpl<BooleanState> { mSharedPreferences = sharedPreferences; mDetailAdapter = new DndDetailAdapter(); mController.observe(getLifecycle(), mZenCallback); mDialogLaunchAnimator = dialogLaunchAnimator; mSettingZenDuration = new SecureSetting(secureSettings, mUiHandler, Settings.Secure.ZEN_DURATION, getHost().getUserId()) { @Override Loading @@ -117,8 +121,6 @@ public class DndTile extends QSTileImpl<BooleanState> { }; } public static void setVisible(Context context, boolean visible) { Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible); } Loading Loading @@ -187,14 +189,17 @@ public class DndTile extends QSTileImpl<BooleanState> { 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: Loading @@ -209,6 +214,20 @@ public class DndTile extends QSTileImpl<BooleanState> { } } 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()) { Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +23 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -303,13 +305,32 @@ public class SystemUIDialog extends AlertDialog implements ListenableDialog, * the screen off / close system dialogs broadcast. * <p> * <strong>Note:</strong> 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. * <p> * <strong>Note:</strong> 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(); } Loading packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt +49 −5 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) Loading Loading @@ -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 Loading @@ -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, Loading @@ -102,7 +117,8 @@ class DndTileTest : SysuiTestCase() { qsLogger, zenModeController, sharedPreferences, secureSettings secureSettings, dialogLaunchAnimator ) } Loading Loading @@ -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 Loading
packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java +8 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -85,6 +84,7 @@ public class EnableZenModeDialog { @VisibleForTesting protected Context mContext; private final int mThemeResId; @VisibleForTesting protected TextView mZenAlarmWarning; @VisibleForTesting Loading @@ -97,10 +97,15 @@ public class EnableZenModeDialog { protected LayoutInflater mLayoutInflater; public EnableZenModeDialog(Context context) { this(context, 0); } public EnableZenModeDialog(Context context, int themeResId) { mContext = context; mThemeResId = themeResId; } public Dialog createDialog() { public AlertDialog createDialog() { mNotificationManager = (NotificationManager) mContext. getSystemService(Context.NOTIFICATION_SERVICE); mForeverId = Condition.newId(mContext).appendPath("forever").build(); Loading @@ -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, Loading
packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +31 −12 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -84,6 +85,7 @@ public class DndTile extends QSTileImpl<BooleanState> { private final DndDetailAdapter mDetailAdapter; private final SharedPreferences mSharedPreferences; private final SecureSetting mSettingZenDuration; private final DialogLaunchAnimator mDialogLaunchAnimator; private boolean mListening; private boolean mShowingDetail; Loading @@ -100,7 +102,8 @@ public class DndTile extends QSTileImpl<BooleanState> { QSLogger qsLogger, ZenModeController zenModeController, @Main SharedPreferences sharedPreferences, SecureSettings secureSettings SecureSettings secureSettings, DialogLaunchAnimator dialogLaunchAnimator ) { super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); Loading @@ -108,6 +111,7 @@ public class DndTile extends QSTileImpl<BooleanState> { mSharedPreferences = sharedPreferences; mDetailAdapter = new DndDetailAdapter(); mController.observe(getLifecycle(), mZenCallback); mDialogLaunchAnimator = dialogLaunchAnimator; mSettingZenDuration = new SecureSetting(secureSettings, mUiHandler, Settings.Secure.ZEN_DURATION, getHost().getUserId()) { @Override Loading @@ -117,8 +121,6 @@ public class DndTile extends QSTileImpl<BooleanState> { }; } public static void setVisible(Context context, boolean visible) { Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible); } Loading Loading @@ -187,14 +189,17 @@ public class DndTile extends QSTileImpl<BooleanState> { 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: Loading @@ -209,6 +214,20 @@ public class DndTile extends QSTileImpl<BooleanState> { } } 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()) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +23 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -303,13 +305,32 @@ public class SystemUIDialog extends AlertDialog implements ListenableDialog, * the screen off / close system dialogs broadcast. * <p> * <strong>Note:</strong> 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. * <p> * <strong>Note:</strong> 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(); } Loading
packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt +49 −5 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) Loading Loading @@ -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 Loading @@ -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, Loading @@ -102,7 +117,8 @@ class DndTileTest : SysuiTestCase() { qsLogger, zenModeController, sharedPreferences, secureSettings secureSettings, dialogLaunchAnimator ) } Loading Loading @@ -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