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

Commit 74ba0bfe authored by Darrell Shi's avatar Darrell Shi Committed by Android (Google) Code Review
Browse files

Merge "Support Glanceable Hub widget configuration on HSUM" into main

parents 7045c543 6e1c8dc0
Loading
Loading
Loading
Loading
+99 −3
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.content.Intent
import android.content.IntentSender
import android.os.Binder
import android.os.UserHandle
import android.testing.TestableLooper
import android.widget.RemoteViews
@@ -29,6 +31,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IConfigureWidgetCallback
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -43,11 +46,13 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -164,7 +169,7 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
        }

    @Test
    fun addWidget_getWidgetUpdate() =
    fun addWidget_noConfigurationCallback_getWidgetUpdate() =
        testScope.runTest {
            setupWidgets()

@@ -180,7 +185,7 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()

            // Add a widget
            service.addWidget(ComponentName("pkg_4", "cls_4"), UserHandle.of(0), 3)
            service.addWidget(ComponentName("pkg_4", "cls_4"), UserHandle.of(0), 3, null)
            runCurrent()

            // Verify an update pushed with widget 4 added
@@ -191,6 +196,71 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
            assertThat(widgets?.get(3)?.has(4, "pkg_4/cls_4", 3, 3)).isTrue()
        }

    @Test
    fun addWidget_withConfigurationCallback_configurationFails_doNotAddWidget() =
        testScope.runTest {
            setupWidgets()

            // Bind service
            val binder = underTest.onBind(Intent())
            val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)

            // Verify the update is as expected
            val widgets by collectLastValue(service.listenForWidgetUpdates())
            assertThat(widgets).hasSize(3)
            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()

            // Add a widget with a configuration callback that fails
            service.addWidget(
                ComponentName("pkg_4", "cls_4"),
                UserHandle.of(0),
                3,
                createConfigureWidgetCallback(success = false),
            )
            runCurrent()

            // Verify that widget 4 is not added
            assertThat(widgets).hasSize(3)
            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
        }

    @Test
    fun addWidget_withConfigurationCallback_configurationSucceeds_addWidget() =
        testScope.runTest {
            setupWidgets()

            // Bind service
            val binder = underTest.onBind(Intent())
            val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)

            // Verify the update is as expected
            val widgets by collectLastValue(service.listenForWidgetUpdates())
            assertThat(widgets).hasSize(3)
            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()

            // Add a widget with a configuration callback that fails
            service.addWidget(
                ComponentName("pkg_4", "cls_4"),
                UserHandle.of(0),
                3,
                createConfigureWidgetCallback(success = true),
            )
            runCurrent()

            // Verify that widget 4 is added
            assertThat(widgets).hasSize(4)
            assertThat(widgets?.get(0)?.has(1, "pkg_1/cls_1", 0, 3)).isTrue()
            assertThat(widgets?.get(1)?.has(2, "pkg_2/cls_2", 1, 3)).isTrue()
            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
            assertThat(widgets?.get(3)?.has(4, "pkg_4/cls_4", 3, 3)).isTrue()
        }

    @Test
    fun deleteWidget_getWidgetUpdate() =
        testScope.runTest {
@@ -271,6 +341,21 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
            assertThat(widgets?.get(2)?.has(3, "pkg_3/cls_3", 2, 6)).isTrue()
        }

    @Test
    fun getIntentSenderForConfigureActivity() =
        testScope.runTest {
            val expected = IntentSender(Binder())
            whenever(appWidgetHost.getIntentSenderForConfigureActivity(anyInt(), anyInt()))
                .thenReturn(expected)

            // Bind service
            val binder = underTest.onBind(Intent())
            val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)

            val actual = service.getIntentSenderForConfigureActivity(1)
            assertThat(actual).isEqualTo(expected)
        }

    private fun setupWidgets() {
        widgetRepository.addWidget(
            appWidgetId = 1,
@@ -293,7 +378,7 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
    }

    private fun IGlanceableHubWidgetManagerService.listenForWidgetUpdates() =
        conflatedCallbackFlow<List<CommunalWidgetContentModel>> {
        conflatedCallbackFlow {
            val listener =
                object : IGlanceableHubWidgetsListener.Stub() {
                    override fun onWidgetsUpdated(widgets: List<CommunalWidgetContentModel>) {
@@ -316,4 +401,15 @@ class GlanceableHubWidgetManagerServiceTest : SysuiTestCase() {
            this.rank == rank &&
            this.spanY == spanY
    }

    private fun createConfigureWidgetCallback(success: Boolean): IConfigureWidgetCallback {
        return object : IConfigureWidgetCallback.Stub() {
            override fun onConfigureWidget(
                appWidgetId: Int,
                resultReceiver: IConfigureWidgetCallback.IResultReceiver?,
            ) {
                resultReceiver?.onResult(success)
            }
        }
    }
}
+132 −11
Original line number Diff line number Diff line
@@ -18,17 +18,18 @@ package com.android.systemui.communal.widgets

import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.IntentSender
import android.os.Binder
import android.os.OutcomeReceiver
import androidx.activity.ComponentActivity
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.shared.model.fakeGlanceableHubMultiUserHelper
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
@@ -38,15 +39,22 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class WidgetConfigurationControllerTest : SysuiTestCase() {
    @Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
    @Mock private lateinit var ownerActivity: ComponentActivity
    private val appWidgetHost = mock<CommunalAppWidgetHost>()
    private val ownerActivity = mock<ComponentActivity>()

    private val outcomeReceiverCaptor = argumentCaptor<OutcomeReceiver<IntentSender?, Throwable>>()

    private val kosmos = testKosmos()

@@ -54,18 +62,19 @@ class WidgetConfigurationControllerTest : SysuiTestCase() {

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        underTest =
            WidgetConfigurationController(
                ownerActivity,
                { appWidgetHost },
                kosmos.testDispatcher,
                kosmos.fakeGlanceableHubMultiUserHelper,
                { kosmos.mockGlanceableHubWidgetManager },
                kosmos.fakeExecutor,
            )
    }

    @Test
    fun configurationFailsWhenActivityNotFound() =
    fun configureWidget_activityNotFound_returnsFalse() =
        with(kosmos) {
            testScope.runTest {
                whenever(
@@ -84,13 +93,97 @@ class WidgetConfigurationControllerTest : SysuiTestCase() {
        }

    @Test
    fun configurationFails() =
    fun configureWidget_configurationFails_returnsFalse() =
        with(kosmos) {
            testScope.runTest {
                val result = async { underTest.configureWidget(123) }
                runCurrent()
                assertThat(result.isCompleted).isFalse()

                underTest.setConfigurationResult(Activity.RESULT_CANCELED)
                runCurrent()

                assertThat(result.await()).isFalse()
                result.cancel()
            }
        }

    @Test
    fun configureWidget_configurationSucceeds_returnsTrue() =
        with(kosmos) {
            testScope.runTest {
                val result = async { underTest.configureWidget(123) }
                runCurrent()
                assertThat(result.isCompleted).isFalse()

                underTest.setConfigurationResult(Activity.RESULT_OK)
                runCurrent()

                assertThat(result.await()).isTrue()
                result.cancel()
            }
        }

    @Test
    fun configureWidget_headlessSystemUser_activityNotFound_returnsFalse() =
        with(kosmos) {
            testScope.runTest {
                fakeGlanceableHubMultiUserHelper.setIsInHeadlessSystemUser(true)

                // Activity not found
                whenever(
                        mockGlanceableHubWidgetManager.getIntentSenderForConfigureActivity(
                            anyInt(),
                            outcomeReceiverCaptor.capture(),
                            any(),
                        )
                    )
                    .then { outcomeReceiverCaptor.firstValue.onError(ActivityNotFoundException()) }

                val result = async { underTest.configureWidget(123) }
                runCurrent()

                assertThat(result.await()).isFalse()
                result.cancel()
            }
        }

    @Test
    fun configureWidget_headlessSystemUser_intentSenderNull_returnsFalse() =
        with(kosmos) {
            testScope.runTest {
                fakeGlanceableHubMultiUserHelper.setIsInHeadlessSystemUser(true)

                prepareIntentSender(null)

                assertThat(underTest.configureWidget(123)).isFalse()
            }
        }

    @Test
    fun configureWidget_headlessSystemUser_configurationFails_returnsFalse() =
        with(kosmos) {
            testScope.runTest {
                fakeGlanceableHubMultiUserHelper.setIsInHeadlessSystemUser(true)

                val intentSender = IntentSender(Binder())
                prepareIntentSender(intentSender)

                val result = async { underTest.configureWidget(123) }
                runCurrent()
                assertThat(result.isCompleted).isFalse()

                verify(ownerActivity)
                    .startIntentSenderForResult(
                        eq(intentSender),
                        eq(WidgetConfigurationController.REQUEST_CODE),
                        anyOrNull(),
                        anyInt(),
                        anyInt(),
                        anyInt(),
                        any(),
                    )

                underTest.setConfigurationResult(Activity.RESULT_CANCELED)
                runCurrent()

@@ -100,13 +193,29 @@ class WidgetConfigurationControllerTest : SysuiTestCase() {
        }

    @Test
    fun configurationSuccessful() =
    fun configureWidget_headlessSystemUser_configurationSucceeds_returnsTrue() =
        with(kosmos) {
            testScope.runTest {
                fakeGlanceableHubMultiUserHelper.setIsInHeadlessSystemUser(true)

                val intentSender = IntentSender(Binder())
                prepareIntentSender(intentSender)

                val result = async { underTest.configureWidget(123) }
                runCurrent()
                assertThat(result.isCompleted).isFalse()

                verify(ownerActivity)
                    .startIntentSenderForResult(
                        eq(intentSender),
                        eq(WidgetConfigurationController.REQUEST_CODE),
                        anyOrNull(),
                        anyInt(),
                        anyInt(),
                        anyInt(),
                        any(),
                    )

                underTest.setConfigurationResult(Activity.RESULT_OK)
                runCurrent()

@@ -114,4 +223,16 @@ class WidgetConfigurationControllerTest : SysuiTestCase() {
                result.cancel()
            }
        }

    private fun prepareIntentSender(intentSender: IntentSender?) =
        with(kosmos) {
            whenever(
                    mockGlanceableHubWidgetManager.getIntentSenderForConfigureActivity(
                        anyInt(),
                        outcomeReceiverCaptor.capture(),
                        any(),
                    )
                )
                .then { outcomeReceiverCaptor.firstValue.onResult(intentSender) }
        }
}
+80 −11
Original line number Diff line number Diff line
@@ -19,7 +19,10 @@ package com.android.systemui.communal.widgets
import android.appwidget.AppWidgetHost.AppWidgetHostListener
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.content.IntentSender
import android.os.IBinder
import android.os.OutcomeReceiver
import android.os.RemoteException
import android.os.UserHandle
import android.widget.RemoteViews
import com.android.server.servicewatcher.ServiceWatcher
@@ -27,14 +30,19 @@ import com.android.server.servicewatcher.ServiceWatcher.ServiceListener
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IAppWidgetHostListener
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IConfigureWidgetCallback
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.launch

/**
 * Manages updates to Glanceable Hub widgets and requests to edit them from the headless system
@@ -47,6 +55,8 @@ import kotlinx.coroutines.channels.awaitClose
class GlanceableHubWidgetManager
@Inject
constructor(
    @Background private val bgExecutor: Executor,
    @Background private val bgScope: CoroutineScope,
    glanceableHubMultiUserHelper: GlanceableHubMultiUserHelper,
    @CommunalLog logBuffer: LogBuffer,
    serviceWatcherFactory: ServiceWatcherFactory<GlanceableHubWidgetManagerServiceInfo?>,
@@ -101,8 +111,7 @@ constructor(
        rank: Int?,
        configurator: WidgetConfigurator?,
    ) = runOnService { service ->
        // TODO(b/375036327): Add support for widget configuration
        service.addWidget(provider, user, rank ?: -1)
        service.addWidget(provider, user, rank ?: -1, createIConfigureWidgetCallback(configurator))
    }

    /** Requests the foreground user to delete a widget. */
@@ -129,7 +138,42 @@ constructor(
            )
        }

    /**
     * Requests the foreground user for the [IntentSender] to start a configuration activity for the
     * widget.
     *
     * @param appWidgetId Id of the widget to configure.
     * @param outcomeReceiver Callback for receiving the result or error.
     * @param executor Executor to run the callback on.
     */
    fun getIntentSenderForConfigureActivity(
        appWidgetId: Int,
        outcomeReceiver: OutcomeReceiver<IntentSender?, Throwable>,
        executor: Executor,
    ) {
        bgExecutor.execute {
            serviceWatcher.runOnBinder(
                object : ServiceWatcher.BinderOperation {
                    override fun run(binder: IBinder?) {
                        val service = IGlanceableHubWidgetManagerService.Stub.asInterface(binder)
                        try {
                            val result = service.getIntentSenderForConfigureActivity(appWidgetId)
                            executor.execute { outcomeReceiver.onResult(result) }
                        } catch (e: RemoteException) {
                            executor.execute { outcomeReceiver.onError(e) }
                        }
                    }

                    override fun onError(t: Throwable?) {
                        t?.let { executor.execute { outcomeReceiver.onError(t) } }
                    }
                }
            )
        }
    }

    private fun runOnService(block: (IGlanceableHubWidgetManagerService) -> Unit) {
        bgExecutor.execute {
            serviceWatcher.runOnBinder(
                object : ServiceWatcher.BinderOperation {
                    override fun run(binder: IBinder?) {
@@ -142,6 +186,7 @@ constructor(
                }
            )
        }
    }

    private fun createIAppWidgetHostListener(
        listener: AppWidgetHostListener
@@ -165,6 +210,30 @@ constructor(
        }
    }

    private fun createIConfigureWidgetCallback(
        configurator: WidgetConfigurator?
    ): IConfigureWidgetCallback? {
        return configurator?.let {
            object : IConfigureWidgetCallback.Stub() {
                override fun onConfigureWidget(
                    appWidgetId: Int,
                    resultReceiver: IConfigureWidgetCallback.IResultReceiver?,
                ) {
                    bgScope.launch {
                        val success = configurator.configureWidget(appWidgetId)
                        try {
                            resultReceiver?.onResult(success)
                        } catch (e: RemoteException) {
                            logger.e({ "Error reporting widget configuration result: $str1" }) {
                                str1 = e.localizedMessage
                            }
                        }
                    }
                }
            }
        }
    }

    companion object {
        private const val TAG = "GlanceableHubWidgetManager"
    }
+60 −5
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.appwidget.AppWidgetHost.AppWidgetHostListener
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
import android.content.Intent
import android.content.IntentSender
import android.os.IBinder
import android.os.RemoteCallbackList
import android.os.RemoteException
@@ -30,11 +31,13 @@ import androidx.lifecycle.lifecycleScope
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IAppWidgetHostListener
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IConfigureWidgetCallback
import com.android.systemui.communal.widgets.IGlanceableHubWidgetManagerService.IGlanceableHubWidgetsListener
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -131,7 +134,12 @@ constructor(
        appWidgetHost.setListener(appWidgetId, createListener(listener))
    }

    private fun addWidgetInternal(provider: ComponentName?, user: UserHandle?, rank: Int) {
    private fun addWidgetInternal(
        provider: ComponentName?,
        user: UserHandle?,
        rank: Int,
        callback: IConfigureWidgetCallback?,
    ) {
        if (provider == null) {
            throw IllegalStateException("Provider cannot be null")
        }
@@ -140,8 +148,29 @@ constructor(
            throw IllegalStateException("User cannot be null")
        }

        // TODO(b/375036327): Add support for widget configuration
        widgetRepository.addWidget(provider, user, rank, configurator = null)
        val configurator =
            callback?.let {
                WidgetConfigurator { appWidgetId ->
                    try {
                        val result = CompletableDeferred<Boolean>()
                        val resultReceiver =
                            object : IConfigureWidgetCallback.IResultReceiver.Stub() {
                                override fun onResult(success: Boolean) {
                                    result.complete(success)
                                }
                            }

                        callback.onConfigureWidget(appWidgetId, resultReceiver)
                        result.await()
                    } catch (e: RemoteException) {
                        logger.e({ "Error configuring widget: $str1" }) {
                            str1 = e.localizedMessage
                        }
                        false
                    }
                }
            }
        widgetRepository.addWidget(provider, user, rank, configurator)
    }

    private fun deleteWidgetInternal(appWidgetId: Int) {
@@ -177,6 +206,17 @@ constructor(
        widgetRepository.resizeWidget(appWidgetId, spanY, appWidgetIds.zip(ranks).toMap())
    }

    private fun getIntentSenderForConfigureActivityInternal(appWidgetId: Int): IntentSender? {
        return try {
            appWidgetHost.getIntentSenderForConfigureActivity(appWidgetId, /* intentFlags= */ 0)
        } catch (e: IntentSender.SendIntentException) {
            logger.e({ "Error getting intent sender for configure activity" }) {
                str1 = e.localizedMessage
            }
            null
        }
    }

    private fun createListener(listener: IAppWidgetHostListener): AppWidgetHostListener {
        return object : AppWidgetHostListener {
            override fun onUpdateProviderInfo(appWidget: AppWidgetProviderInfo?) {
@@ -250,11 +290,16 @@ constructor(
            }
        }

        override fun addWidget(provider: ComponentName?, user: UserHandle?, rank: Int) {
        override fun addWidget(
            provider: ComponentName?,
            user: UserHandle?,
            rank: Int,
            callback: IConfigureWidgetCallback?,
        ) {
            val iden = clearCallingIdentity()

            try {
                addWidgetInternal(provider, user, rank)
                addWidgetInternal(provider, user, rank, callback)
            } finally {
                restoreCallingIdentity(iden)
            }
@@ -294,6 +339,16 @@ constructor(
                restoreCallingIdentity(iden)
            }
        }

        override fun getIntentSenderForConfigureActivity(appWidgetId: Int): IntentSender? {
            val iden = clearCallingIdentity()

            try {
                return getIntentSenderForConfigureActivityInternal(appWidgetId)
            } finally {
                restoreCallingIdentity(iden)
            }
        }
    }

    /**
+17 −1
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ package com.android.systemui.communal.widgets;

import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.IntentSender;
import android.os.UserHandle;
import android.widget.RemoteViews;
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel;
@@ -21,7 +22,8 @@ interface IGlanceableHubWidgetManagerService {
    oneway void setAppWidgetHostListener(int appWidgetId, in IAppWidgetHostListener listener);

    // Requests to add a widget in the Glanceable Hub.
    oneway void addWidget(in ComponentName provider, in UserHandle user, int rank);
    oneway void addWidget(in ComponentName provider, in UserHandle user, int rank,
        in IConfigureWidgetCallback callback);

    // Requests to delete a widget from the Glanceable Hub.
    oneway void deleteWidget(int appWidgetId);
@@ -32,6 +34,9 @@ interface IGlanceableHubWidgetManagerService {
    // Requests to resize a widget in the Glanceable Hub.
    oneway void resizeWidget(int appWidgetId, int spanY, in int[] appWidgetIds, in int[] ranks);

    // Returns the [IntentSender] for launching the configuration activity of the given widget.
    IntentSender getIntentSenderForConfigureActivity(int appWidgetId);

    // Listener for Glanceable Hub widget updates
    oneway interface IGlanceableHubWidgetsListener {
        // Called when widgets have updated.
@@ -48,4 +53,15 @@ interface IGlanceableHubWidgetManagerService {

        void onViewDataChanged(int viewId);
    }

    oneway interface IConfigureWidgetCallback {
        // Called when the given widget should launch its configuration activity. The caller should
        // report the result through the [IResultReceiver].
        void onConfigureWidget(int appWidgetId, in IResultReceiver resultReceiver);

        interface IResultReceiver {
            // Called when the widget configuration operation returns a result.
            void onResult(boolean success);
        }
    }
}
Loading