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

Commit 57116756 authored by Lucas Silva's avatar Lucas Silva
Browse files

Create widget host views in background thread

Creation of app widget host views requires IPCs to system_server to get
the remote views to show. This change offloads this to a background
thread in order to ensure smooth scrolling of the grid.

Bug: 356393319
Test: flashed device and opened hub
Flag: com.android.systemui.communal_hub
Change-Id: Ia52ba05148505ed95da2de53a2ec289e79e89d4c
parent e1ab7920
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
@@ -46,6 +47,7 @@ constructor(
    private val bottomAreaSection: BottomAreaSection,
    private val ambientStatusBarSection: AmbientStatusBarSection,
    private val communalPopupSection: CommunalPopupSection,
    private val widgetSection: CommunalAppWidgetSection,
) {

    @Composable
@@ -63,6 +65,7 @@ constructor(
                            viewModel = viewModel,
                            interactionHandler = interactionHandler,
                            dialogFactory = dialogFactory,
                            widgetSection = widgetSection,
                            modifier = Modifier.element(Communal.Elements.Grid)
                        )
                    }
+20 −38
Original line number Diff line number Diff line
@@ -19,10 +19,7 @@ package com.android.systemui.communal.ui.compose
import android.content.Context
import android.content.res.Configuration
import android.graphics.drawable.Icon
import android.os.Bundle
import android.util.SizeF
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
import android.widget.FrameLayout
import android.widget.RemoteViews
import androidx.annotation.VisibleForTesting
@@ -105,7 +102,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
@@ -168,6 +164,7 @@ import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
import com.android.systemui.communal.ui.compose.extensions.observeTaps
import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@@ -179,11 +176,12 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import kotlinx.coroutines.launch

@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CommunalHub(
    modifier: Modifier = Modifier,
    viewModel: BaseCommunalViewModel,
    widgetSection: CommunalAppWidgetSection,
    interactionHandler: RemoteViews.InteractionHandler? = null,
    dialogFactory: SystemUIDialogFactory? = null,
    widgetConfigurator: WidgetConfigurator? = null,
@@ -382,6 +380,7 @@ fun CommunalHub(
                            selectedKey = selectedKey,
                            widgetConfigurator = widgetConfigurator,
                            interactionHandler = interactionHandler,
                            widgetSection = widgetSection,
                        )
                    }
                }
@@ -631,6 +630,7 @@ private fun BoxScope.CommunalHubLazyGrid(
    updateDragPositionForRemove: (offset: Offset) -> Boolean,
    widgetConfigurator: WidgetConfigurator?,
    interactionHandler: RemoteViews.InteractionHandler?,
    widgetSection: CommunalAppWidgetSection,
) {
    var gridModifier =
        Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) }
@@ -718,18 +718,20 @@ private fun BoxScope.CommunalHubLazyGrid(
                        index = index,
                        contentListState = contentListState,
                        interactionHandler = interactionHandler,
                        widgetSection = widgetSection,
                    )
                }
            } else {
                CommunalContent(
                    modifier = cardModifier.animateItem(),
                    model = list[index],
                    viewModel = viewModel,
                    size = size,
                    selected = false,
                    modifier = cardModifier.animateItem(),
                    index = index,
                    contentListState = contentListState,
                    interactionHandler = interactionHandler,
                    widgetSection = widgetSection,
                )
            }
        }
@@ -971,6 +973,7 @@ private fun CommunalContent(
    index: Int,
    contentListState: ContentListState,
    interactionHandler: RemoteViews.InteractionHandler?,
    widgetSection: CommunalAppWidgetSection,
) {
    when (model) {
        is CommunalContentModel.WidgetContent.Widget ->
@@ -982,7 +985,8 @@ private fun CommunalContent(
                widgetConfigurator,
                modifier,
                index,
                contentListState
                contentListState,
                widgetSection,
            )
        is CommunalContentModel.WidgetPlaceholder -> HighlightedItem(modifier)
        is CommunalContentModel.WidgetContent.DisabledWidget ->
@@ -1115,9 +1119,9 @@ private fun WidgetContent(
    modifier: Modifier = Modifier,
    index: Int,
    contentListState: ContentListState,
    widgetSection: CommunalAppWidgetSection,
) {
    val context = LocalContext.current
    val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
    val accessibilityLabel =
        remember(model, context) {
            model.providerInfo.loadLabel(context.packageManager).toString().trim()
@@ -1204,36 +1208,14 @@ private fun WidgetContent(
                    }
                }
    ) {
        AndroidView(
        with(widgetSection) {
            Widget(
                viewModel = viewModel,
                model = model,
                size = size,
                modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
            factory = { context ->
                model.appWidgetHost
                    .createViewForCommunal(context, model.appWidgetId, model.providerInfo)
                    .apply {
                        updateAppWidgetSize(
                            /* newOptions = */ Bundle(),
                            /* minWidth = */ size.width.toInt(),
                            /* minHeight = */ size.height.toInt(),
                            /* maxWidth = */ size.width.toInt(),
                            /* maxHeight = */ size.height.toInt(),
                            /* ignorePadding = */ true
            )
                        accessibilityDelegate = viewModel.widgetAccessibilityDelegate
        }
            },
            update = {
                it.apply {
                    importantForAccessibility =
                        if (isFocusable) {
                            IMPORTANT_FOR_ACCESSIBILITY_AUTO
                        } else {
                            IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                        }
                }
            },
            // For reusing composition in lazy lists.
            onReset = {},
        )
        if (
            viewModel is CommunalEditModeViewModel &&
                model.reconfigurable &&
+9 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.dagger.SysUISingleton
@@ -42,6 +43,7 @@ constructor(
    private val viewModel: CommunalViewModel,
    private val dialogFactory: SystemUIDialogFactory,
    private val interactionHandler: WidgetInteractionHandler,
    private val widgetSection: CommunalAppWidgetSection,
) : ComposableScene {
    override val key = Scenes.Communal

@@ -55,6 +57,12 @@ constructor(

    @Composable
    override fun SceneScope.Content(modifier: Modifier) {
        CommunalHub(modifier, viewModel, interactionHandler, dialogFactory)
        CommunalHub(
            modifier = modifier,
            viewModel = viewModel,
            interactionHandler = interactionHandler,
            widgetSection = widgetSection,
            dialogFactory = dialogFactory,
        )
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -278,4 +278,7 @@
    <!-- Id for the udfps accessibility overlay -->
    <item type="id" name="udfps_accessibility_overlay" />
    <item type="id" name="udfps_accessibility_overlay_top_guideline" />

    <!-- Ids for communal hub widgets -->
    <item type="id" name="communal_widget_disposable_tag"/>
</resources>
+69 −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.communal.ui.binder

import android.content.Context
import android.util.SizeF
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import com.android.app.tracing.coroutines.launch
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.util.WidgetViewFactory
import com.android.systemui.util.kotlin.DisposableHandles
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle

object CommunalAppWidgetHostViewBinder {
    private const val TAG = "CommunalAppWidgetHostViewBinder"

    fun bind(
        context: Context,
        applicationScope: CoroutineScope,
        container: FrameLayout,
        model: CommunalContentModel.WidgetContent.Widget,
        size: SizeF,
        factory: WidgetViewFactory,
    ): DisposableHandle {
        val disposables = DisposableHandles()

        val loadingJob =
            applicationScope.launch("$TAG#createWidgetView") {
                val widget = factory.createWidget(context, model, size)
                // TODO(b/358662507): Remove this workaround
                (container.parent as? ViewGroup)?.let { parent ->
                    val index = parent.indexOfChild(container)
                    parent.removeView(container)
                    parent.addView(container, index)
                }
                container.setView(widget)
            }

        disposables += DisposableHandle { loadingJob.cancel() }
        disposables += DisposableHandle { container.removeAllViews() }

        return disposables
    }
}

private fun ViewGroup.setView(view: View) {
    if (view.parent == this) {
        return
    }
    (view.parent as? ViewGroup)?.removeView(view)
    addView(view)
}
Loading