diff options
5 files changed, 226 insertions, 49 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/AlertDialogWithDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/AlertDialogWithDelegateTest.kt new file mode 100644 index 000000000000..5389fc29eaf1 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/AlertDialogWithDelegateTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.phone + +import android.app.AlertDialog +import android.content.res.Configuration +import android.testing.TestableLooper.RunWithLooper +import android.view.WindowManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R +import com.android.systemui.util.mockito.mock +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito + +@RunWith(AndroidJUnit4::class) +@RunWithLooper +@SmallTest +class AlertDialogWithDelegateTest : SysuiTestCase() { + + private val delegate = mock<DialogDelegate<AlertDialog>>() + + @Test + fun delegateIsCalled_inCorrectOrder() { + val configuration = Configuration() + val inOrder = Mockito.inOrder(delegate) + val dialog = createDialog() + + dialog.show() + dialog.onWindowFocusChanged(/* hasFocus= */ true) + dialog.onConfigurationChanged(configuration) + dialog.dismiss() + + inOrder.verify(delegate).beforeCreate(dialog, /* savedInstanceState= */ null) + inOrder.verify(delegate).onCreate(dialog, /* savedInstanceState= */ null) + inOrder.verify(delegate).onStart(dialog) + inOrder.verify(delegate).onWindowFocusChanged(dialog, /* hasFocus= */ true) + inOrder.verify(delegate).onConfigurationChanged(dialog, configuration) + inOrder.verify(delegate).onStop(dialog) + } + + private fun createDialog(): AlertDialogWithDelegate = + AlertDialogWithDelegate(context, R.style.Theme_SystemUI_Dialog, delegate).apply { + window?.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java index 162008808791..0b922a844690 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java @@ -29,22 +29,28 @@ import static org.mockito.Mockito.when; import android.content.BroadcastReceiver; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Configuration; import android.testing.TestableLooper.RunWithLooper; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.model.SysUiState; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; +import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.concurrent.atomic.AtomicBoolean; @@ -58,6 +64,8 @@ public class SystemUIDialogTest extends SysuiTestCase { private FeatureFlags mFeatureFlags; @Mock private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private DialogDelegate<SystemUIDialog> mDelegate; @Before public void setup() { @@ -143,4 +151,35 @@ public class SystemUIDialogTest extends SysuiTestCase { assertThat(calledStart.get()).isTrue(); assertThat(calledStop.get()).isTrue(); } + + @Test + public void delegateIsCalled_inCorrectOrder() { + Configuration configuration = new Configuration(); + InOrder inOrder = Mockito.inOrder(mDelegate); + SystemUIDialog dialog = createDialogWithDelegate(); + + dialog.show(); + dialog.onWindowFocusChanged(/* hasFocus= */ true); + dialog.onConfigurationChanged(configuration); + dialog.dismiss(); + + inOrder.verify(mDelegate).beforeCreate(dialog, /* savedInstanceState= */ null); + inOrder.verify(mDelegate).onCreate(dialog, /* savedInstanceState= */ null); + inOrder.verify(mDelegate).onStart(dialog); + inOrder.verify(mDelegate).onWindowFocusChanged(dialog, /* hasFocus= */ true); + inOrder.verify(mDelegate).onConfigurationChanged(dialog, configuration); + inOrder.verify(mDelegate).onStop(dialog); + } + + private SystemUIDialog createDialogWithDelegate() { + SystemUIDialog.Factory factory = new SystemUIDialog.Factory( + getContext(), + mFeatureFlags, + Dependency.get(SystemUIDialogManager.class), + Dependency.get(SysUiState.class), + Dependency.get(BroadcastDispatcher.class), + Dependency.get(DialogLaunchAnimator.class) + ); + return factory.create(mDelegate); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AlertDialogWithDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AlertDialogWithDelegate.kt new file mode 100644 index 000000000000..6211e1755217 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AlertDialogWithDelegate.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import android.app.AlertDialog +import android.content.Context +import android.content.res.Configuration +import android.os.Bundle +import android.view.ViewRootImpl +import android.view.ViewRootImpl.ConfigChangedCallback +import androidx.annotation.StyleRes + +/** + * Implementation of [AlertDialog] that takes as parameter a [DialogDelegate]. + * + * Can be used when composition is preferred over inheritance. + */ +class AlertDialogWithDelegate( + context: Context, + @StyleRes theme: Int, + private val delegate: DialogDelegate<AlertDialog> +) : AlertDialog(context, theme), ConfigChangedCallback { + + override fun onCreate(savedInstanceState: Bundle?) { + delegate.beforeCreate(dialog = this, savedInstanceState) + super.onCreate(savedInstanceState) + delegate.onCreate(dialog = this, savedInstanceState) + } + + override fun onConfigurationChanged(configuration: Configuration) { + delegate.onConfigurationChanged(dialog = this, configuration) + } + + override fun onStart() { + super.onStart() + ViewRootImpl.addConfigCallback(this) + delegate.onStart(dialog = this) + } + + override fun onStop() { + super.onStop() + ViewRootImpl.removeConfigCallback(this) + delegate.onStop(dialog = this) + } + + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + delegate.onWindowFocusChanged(dialog = this, hasFocus) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DialogDelegate.kt new file mode 100644 index 000000000000..b56baeefda19 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DialogDelegate.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import android.app.Dialog +import android.content.res.Configuration +import android.os.Bundle +import android.view.ViewRootImpl + +/** + * A delegate class that should be implemented in place of subclassing [Dialog]. + * + * To use with [SystemUIDialog], implement this interface and then pass an instance of your + * implementation to [SystemUIDialog.Factory.create]. + */ +interface DialogDelegate<T : Dialog> { + /** Called before [Dialog.onCreate] is called. */ + fun beforeCreate(dialog: T, savedInstanceState: Bundle?) {} + + /** Called after [Dialog.onCreate] is called. */ + fun onCreate(dialog: T, savedInstanceState: Bundle?) {} + + /** Called after [Dialog.onStart] is called. */ + fun onStart(dialog: T) {} + + /** Called after [Dialog.onStop] is called. */ + fun onStop(dialog: T) {} + + /** Called after [Dialog.onWindowFocusChanged] is called. */ + fun onWindowFocusChanged(dialog: T, hasFocus: Boolean) {} + + /** Called as part of [ViewRootImpl.ConfigChangedCallback.onConfigurationChanged]. */ + fun onConfigurationChanged(dialog: T, configuration: Configuration) {} +} 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 2558645e3f64..824de01ca59f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java @@ -42,13 +42,13 @@ import android.view.WindowManager.LayoutParams; import androidx.annotation.Nullable; import com.android.systemui.Dependency; -import com.android.systemui.res.R; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.model.SysUiState; +import com.android.systemui.res.R; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.util.DialogKt; @@ -60,7 +60,7 @@ import javax.inject.Inject; /** * Class for dialogs that should appear over panels and keyguard. * - * DO NOT SUBCLASS THIS. See {@link SystemUIDialog.Delegate} for an interface that enables + * DO NOT SUBCLASS THIS. See {@link DialogDelegate} for an interface that enables * customizing behavior via composition instead of inheritance. Clients should implement the * Delegate class and then pass their implementation into the SystemUIDialog constructor. * @@ -79,7 +79,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh private final Context mContext; private final FeatureFlags mFeatureFlags; - private final Delegate mDelegate; + private final DialogDelegate<SystemUIDialog> mDelegate; @Nullable private final DismissReceiver mDismissReceiver; private final Handler mHandler = new Handler(); private final SystemUIDialogManager mDialogManager; @@ -110,7 +110,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh Dependency.get(SysUiState.class), Dependency.get(BroadcastDispatcher.class), Dependency.get(DialogLaunchAnimator.class), - new Delegate() {}); + new DialogDelegate<>() {}); } @Inject @@ -129,7 +129,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh sysUiState, broadcastDispatcher, dialogLaunchAnimator, - new Delegate(){}); + new DialogDelegate<>(){}); } public static class Factory { @@ -156,7 +156,11 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh mDialogLaunchAnimator = dialogLaunchAnimator; } - public SystemUIDialog create(Delegate delegate) { + /** + * Creates a new instance of {@link SystemUIDialog} with {@code delegate} as the {@link + * DialogDelegate}. + */ + public SystemUIDialog create(DialogDelegate<SystemUIDialog> delegate) { return new SystemUIDialog( mContext, DEFAULT_THEME, @@ -188,7 +192,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh sysUiState, broadcastDispatcher, dialogLaunchAnimator, - new Delegate(){}); + new DialogDelegate<>(){}); } public SystemUIDialog( @@ -200,7 +204,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh SysUiState sysUiState, BroadcastDispatcher broadcastDispatcher, DialogLaunchAnimator dialogLaunchAnimator, - Delegate delegate) { + DialogDelegate<SystemUIDialog> delegate) { super(context, theme); mContext = context; mFeatureFlags = featureFlags; @@ -309,7 +313,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh * should override this method instead. */ protected void start() { - mDelegate.start(this); + mDelegate.onStart(this); } @Override @@ -333,7 +337,7 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh * should override this method instead. */ protected void stop() { - mDelegate.stop(this); + mDelegate.onStop(this); } @Override @@ -584,43 +588,4 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh mDialog.dismiss(); } } - - /** - * A delegate class that should be implemented in place of sublcassing {@link SystemUIDialog}. - * - * Implement this interface and then pass an instance of your implementation to - * {@link SystemUIDialog.Factory#create(Delegate)}. - */ - public interface Delegate { - /** - * Called before {@link AlertDialog#onCreate} is called. - */ - default void beforeCreate(SystemUIDialog dialog, Bundle savedInstanceState) {} - - /** - * Called after {@link AlertDialog#onCreate} is called. - */ - default void onCreate(SystemUIDialog dialog, Bundle savedInstanceState) {} - - /** - * Called after {@link AlertDialog#onStart} is called. - */ - default void start(SystemUIDialog dialog) {} - - /** - * Called after {@link AlertDialog#onStop} is called. - */ - default void stop(SystemUIDialog dialog) {} - - /** - * Called after {@link AlertDialog#onWindowFocusChanged(boolean)} is called. - */ - default void onWindowFocusChanged(SystemUIDialog dialog, boolean hasFocus) {} - - /** - * Called as part of - * {@link ViewRootImpl.ConfigChangedCallback#onConfigurationChanged(Configuration)}. - */ - default void onConfigurationChanged(SystemUIDialog dialog, Configuration configuration) {} - } } |