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

Commit c094ec2f authored by Darrell Shi's avatar Darrell Shi Committed by William Xiao
Browse files

Smartspace in communal hub

This change introduces smartspace timers in the communal hub.

Test: atest CommunalInteractorTest
Bug: 306680050
Fix: 306680050
Flag: ACONFIG com.android.systemui.communal_hub DEVELOPMENT

Change-Id: I17be18049a637ef6ae3f480bc4180ac260f27461
parent b2eedb98
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -18,11 +18,14 @@ package com.android.systemui.communal.ui.compose

import android.os.Bundle
import android.util.SizeF
import android.widget.FrameLayout
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
@@ -68,6 +71,7 @@ fun CommunalHub(
                span = { index -> GridItemSpan(communalContent[index].size.span) },
            ) { index ->
                CommunalContent(
                    modifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth),
                    model = communalContent[index],
                    deleteOnClick = viewModel::onDeleteWidget,
                    size =
@@ -96,6 +100,7 @@ private fun CommunalContent(
) {
    when (model) {
        is CommunalContentModel.Widget -> WidgetContent(model, size, deleteOnClick, modifier)
        is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
        is CommunalContentModel.Tutorial -> TutorialContent(modifier)
    }
}
@@ -130,6 +135,21 @@ private fun WidgetContent(
    }
}

@Composable
private fun SmartspaceContent(
    model: CommunalContentModel.Smartspace,
    modifier: Modifier = Modifier,
) {
    AndroidView(
        modifier = modifier,
        factory = { context ->
            FrameLayout(context).apply { addView(model.remoteViews.apply(context, this)) }
        },
        // For reusing composition in lazy lists.
        onReset = {}
    )
}

@Composable
private fun TutorialContent(modifier: Modifier = Modifier) {
    Card(modifier = modifier, content = {})
+29 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.communal.domain.interactor

import android.app.smartspace.SmartspaceTarget
import android.appwidget.AppWidgetHost
import android.content.ComponentName
import com.android.systemui.communal.data.repository.CommunalRepository
@@ -25,10 +26,12 @@ import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -40,6 +43,7 @@ class CommunalInteractor
constructor(
    private val communalRepository: CommunalRepository,
    private val widgetRepository: CommunalWidgetRepository,
    smartspaceRepository: SmartspaceRepository,
    tutorialInteractor: CommunalTutorialInteractor,
    private val appWidgetHost: AppWidgetHost,
) {
@@ -83,7 +87,9 @@ constructor(
            if (isTutorialMode) {
                return@flatMapLatest flowOf(tutorialContent)
            }
            widgetContent
            combine(smartspaceContent, widgetContent) { smartspace, widgets ->
                smartspace + widgets
            }
        }

    /** A list of widget content to be displayed in the communal hub. */
@@ -98,6 +104,28 @@ constructor(
            }
        }

    /** A flow of available smartspace content. Currently only showing timer targets. */
    private val smartspaceContent: Flow<List<CommunalContentModel.Smartspace>> =
        if (!smartspaceRepository.isSmartspaceRemoteViewsEnabled) {
            flowOf(emptyList())
        } else {
            smartspaceRepository.lockscreenSmartspaceTargets.map { targets ->
                targets
                    .filter { target ->
                        target.featureType == SmartspaceTarget.FEATURE_TIMER &&
                            target.remoteViews != null
                    }
                    .map Target@{ target ->
                        return@Target CommunalContentModel.Smartspace(
                            smartspaceTargetId = target.smartspaceTargetId,
                            remoteViews = target.remoteViews!!,
                            // Smartspace always as HALF for now.
                            size = CommunalContentSize.HALF,
                        )
                    }
            }
        }

    /** A list of tutorial content to be displayed in the communal hub in tutorial mode. */
    private val tutorialContent: List<CommunalContentModel.Tutorial> =
        listOf(
+9 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.communal.domain.model

import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetProviderInfo
import android.widget.RemoteViews
import com.android.systemui.communal.shared.model.CommunalContentSize

/** Encapsulates data for a communal content. */
@@ -44,4 +45,12 @@ sealed interface CommunalContentModel {
    ) : CommunalContentModel {
        override val key = "tutorial_$id"
    }

    class Smartspace(
        smartspaceTargetId: String,
        val remoteViews: RemoteViews,
        override val size: CommunalContentSize,
    ) : CommunalContentModel {
        override val key = "smartspace_$smartspaceTargetId"
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -18,13 +18,15 @@ package com.android.systemui.smartspace.dagger
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
import com.android.systemui.smartspace.data.repository.SmartspaceRepositoryModule
import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
import dagger.Binds
import dagger.BindsOptionalOf
import dagger.Module
import javax.inject.Named

@Module(subcomponents = [SmartspaceViewComponent::class])
@Module(subcomponents = [SmartspaceViewComponent::class],
    includes = [SmartspaceRepositoryModule::class])
abstract class SmartspaceModule {
    @Module
    companion object {
+68 −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.smartspace.data.repository

import android.app.smartspace.SmartspaceTarget
import android.os.Parcelable
import android.widget.RemoteViews
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart

interface SmartspaceRepository {
    /** Whether [RemoteViews] are passed through smartspace targets. */
    val isSmartspaceRemoteViewsEnabled: Boolean

    /** Smartspace targets for the lockscreen surface. */
    val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>>
}

@SysUISingleton
class SmartspaceRepositoryImpl
@Inject
constructor(
    private val lockscreenSmartspaceController: LockscreenSmartspaceController,
) : SmartspaceRepository, BcSmartspaceDataPlugin.SmartspaceTargetListener {

    override val isSmartspaceRemoteViewsEnabled: Boolean
        get() = android.app.smartspace.flags.Flags.remoteViews()

    private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> =
        MutableStateFlow(emptyList())
    override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> =
        _lockscreenSmartspaceTargets
            .onStart {
                lockscreenSmartspaceController.addListener(listener = this@SmartspaceRepositoryImpl)
            }
            .onCompletion {
                lockscreenSmartspaceController.removeListener(
                    listener = this@SmartspaceRepositoryImpl
                )
            }

    override fun onSmartspaceTargetsUpdated(targetsNullable: MutableList<out Parcelable>?) {
        targetsNullable?.let { targets ->
            _lockscreenSmartspaceTargets.value = targets.filterIsInstance<SmartspaceTarget>()
        }
            ?: run { _lockscreenSmartspaceTargets.value = emptyList() }
    }
}
Loading