Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7245b8fe authored by Chris Göllner's avatar Chris Göllner Committed by Android (Google) Code Review
Browse files

Merge "Extract SystemUIDialog.Delegate into DialogDelegate and make it generic" into main

parents c9ef0de2 7b76289f
Loading
Loading
Loading
Loading
+61 −0
Original line number Diff line number Diff line
/*
 * 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)
        }
}
+39 −0
Original line number Diff line number Diff line
@@ -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);
    }
}
+64 −0
Original line number Diff line number Diff line
/*
 * 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)
    }
}
+48 −0
Original line number Diff line number Diff line
/*
 * 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) {}
}
+14 −49
Original line number Diff line number Diff line
@@ -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) {}
    }
}