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

Commit df131d85 authored by Anna Bauza's avatar Anna Bauza
Browse files

Add broadcast to open UserSwitcher Dialog from outside of SystemUI

Bug: 409328841
Test: atest UserDialogReceiverTest
Flag: com.android.systemui.multiuser_open_user_switcher_dialog
Change-Id: I44a23f75360f4ac272438c1bfbc34de28782d9d1
parent 4d1f3b8e
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -1119,6 +1119,15 @@
            </intent-filter>
        </receiver>

        <receiver android:name=".user.UserDialogReceiver"
            android:exported="true"
            android:featureFlag="com.android.systemui.multiuser_open_user_switcher_dialog"
            android:permission="android.permission.CREATE_USERS">
            <intent-filter>
                <action android:name="com.android.systemui.action.LAUNCH_USER_SWITCHER_DIALOG" />
            </intent-filter>
        </receiver>

        <receiver android:name=".accessibility.hearingaid.HearingDevicesDialogReceiver"
            android:exported="false">
            <intent-filter android:priority="1">
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.user

import android.content.Intent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

/** Tests for [UserDialogReceiver]. */
@SmallTest
@RunWith(AndroidJUnit4::class)
class UserDialogReceiverTest : SysuiTestCase() {

    // Mock the interactor dependency to verify interactions.
    @Mock private lateinit var mockUserSwitcherInteractor: UserSwitcherInteractor

    // The instance of the BroadcastReceiver we are testing.
    private lateinit var userDialogReceiver: UserDialogReceiver

    @Before
    fun setUp() {
        // Initialize mocks created with the @Mock annotation.
        MockitoAnnotations.initMocks(this)
        // Create an instance of the receiver with the mocked dependency.
        userDialogReceiver = UserDialogReceiver(mockUserSwitcherInteractor)
    }

    /**
     * Verifies that when the correct action is received, the user switcher dialog is shown. This
     * assumes the system has already granted permission and allowed the broadcast.
     */
    @Test
    fun onReceive_withLaunchUserSwitcherAction_showsUserSwitcher() {
        // Arrange: Create an intent with the specific action the receiver listens for.
        val intent = Intent(UserDialogReceiver.LAUNCH_USER_SWITCHER_DIALOG)

        // Act: Trigger the onReceive method.
        userDialogReceiver.onReceive(context, intent)

        // Assert: Verify that the interactor's showUserSwitcher method was called correctly.
        verify(mockUserSwitcherInteractor).showUserSwitcher(null, context)
    }

    /** Verifies that if an unknown action is received, the interactor is not called. */
    @Test
    fun onReceive_withUnknownAction_doesNotShowUserSwitcher() {
        // Arrange: Create an intent with an action that the receiver should ignore.
        val intent = Intent("com.android.systemui.action.SOME_OTHER_ACTION")

        // Act: Trigger the onReceive method.
        userDialogReceiver.onReceive(context, intent)

        // Assert: Verify that the showUserSwitcher method was never called.
        verify(mockUserSwitcherInteractor, never()).showUserSwitcher(any(), any())
    }

    /** Verifies that if the intent action is null, the interactor is not called. */
    @Test
    fun onReceive_withNullAction_doesNotShowUserSwitcher() {
        // Arrange: Create an intent with no action set.
        val intent = Intent()

        // Act: Trigger the onReceive method.
        userDialogReceiver.onReceive(context, intent)

        // Assert: Verify that the showUserSwitcher method was never called.
        verify(mockUserSwitcherInteractor, never()).showUserSwitcher(any(), any())
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import com.android.systemui.screenshot.SmartActionsReceiver;
import com.android.systemui.user.UserDialogReceiver;

import dagger.Binds;
import dagger.Module;
@@ -81,6 +82,16 @@ public abstract class DefaultBroadcastReceiverBinder {
    public abstract BroadcastReceiver bindGuestResetOrExitSessionReceiver(
            GuestResetOrExitSessionReceiver broadcastReceiver);


    /**
     *
     */
    @Binds
    @IntoMap
    @ClassKey(UserDialogReceiver.class)
    public abstract BroadcastReceiver bindUserDialogReceiver(
            UserDialogReceiver broadcastReceiver);

    /**
     *
     */
+46 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.user

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import javax.inject.Inject

class UserDialogReceiver
@Inject
constructor(private val userSwitcherInteractor: UserSwitcherInteractor) : BroadcastReceiver() {
    companion object {
        private const val TAG = "UserDialogReceiver"
        const val LAUNCH_USER_SWITCHER_DIALOG =
            "com.android.systemui.action.LAUNCH_USER_SWITCHER_DIALOG"
    }

    override fun onReceive(context: Context, intent: Intent) {
        try {
            when (intent.getAction()) {
                LAUNCH_USER_SWITCHER_DIALOG -> {
                    userSwitcherInteractor.showUserSwitcher(null, context)
                }
                else -> Log.e(TAG, "Unknown action " + intent.getAction())
            }
        } catch (e: Exception) {
            Log.e(TAG, "Exception: ", e)
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -548,7 +548,7 @@ constructor(
     * If [context] is provided, the dialog will be created from that context. If not provided, the
     * shade context will be used.
     */
    fun showUserSwitcher(expandable: Expandable, context: Context? = null) {
    fun showUserSwitcher(expandable: Expandable?, context: Context? = null) {
        if (featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
            showDialog(ShowDialogRequestModel.ShowUserSwitcherFullscreenDialog(expandable, context))
        } else {