blob: 1a2a4253e761b5ba522c10f9cce8c9b41be6554e [file] [log] [blame]
/*
* 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.communal.domain.interactor
import android.app.smartspace.SmartspaceTarget
import android.appwidget.AppWidgetHost
import android.content.ComponentName
import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
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.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
/** Encapsulates business-logic related to communal mode. */
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class CommunalInteractor
@Inject
constructor(
private val communalRepository: CommunalRepository,
private val widgetRepository: CommunalWidgetRepository,
mediaRepository: CommunalMediaRepository,
smartspaceRepository: SmartspaceRepository,
keyguardInteractor: KeyguardInteractor,
private val appWidgetHost: AppWidgetHost,
private val editWidgetsActivityStarter: EditWidgetsActivityStarter
) {
/** Whether communal features are enabled. */
val isCommunalEnabled: Boolean
get() = communalRepository.isCommunalEnabled
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through
* [onSceneChanged].
*/
val desiredScene: StateFlow<CommunalSceneKey> = communalRepository.desiredScene
/** Transition state of the hub mode. */
val transitionState: StateFlow<ObservableCommunalTransitionState> =
communalRepository.transitionState
/**
* Updates the transition state of the hub [SceneTransitionLayout].
*
* Note that you must call is with `null` when the UI is done or risk a memory leak.
*/
fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
communalRepository.setTransitionState(transitionState)
}
/**
* Flow that emits a boolean if the communal UI is showing, ie. the [desiredScene] is the
* [CommunalSceneKey.Communal].
*/
val isCommunalShowing: Flow<Boolean> =
communalRepository.desiredScene.map { it == CommunalSceneKey.Communal }
val isKeyguardVisible: Flow<Boolean> = keyguardInteractor.isKeyguardVisible
/** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */
fun onSceneChanged(newScene: CommunalSceneKey) {
communalRepository.setDesiredScene(newScene)
}
/** Show the widget editor Activity. */
fun showWidgetEditor() {
editWidgetsActivityStarter.startActivity()
}
/** Add a widget at the specified position. */
fun addWidget(componentName: ComponentName, priority: Int) =
widgetRepository.addWidget(componentName, priority)
/** Delete a widget by id. */
fun deleteWidget(id: Int) = widgetRepository.deleteWidget(id)
/** Reorder widgets. The order in the list will be their display order in the hub. */
fun updateWidgetOrder(ids: List<Int>) = widgetRepository.updateWidgetOrder(ids)
/** A list of widget content to be displayed in the communal hub. */
val widgetContent: Flow<List<CommunalContentModel.Widget>> =
widgetRepository.communalWidgets.map { widgets ->
widgets.map Widget@{ widget ->
return@Widget CommunalContentModel.Widget(
appWidgetId = widget.appWidgetId,
providerInfo = widget.providerInfo,
appWidgetHost = appWidgetHost,
)
}
}
/** A flow of available smartspace content. Currently only showing timer targets. */
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. */
val tutorialContent: List<CommunalContentModel.Tutorial> =
listOf(
CommunalContentModel.Tutorial(id = 0, CommunalContentSize.FULL),
CommunalContentModel.Tutorial(id = 1, CommunalContentSize.THIRD),
CommunalContentModel.Tutorial(id = 2, CommunalContentSize.THIRD),
CommunalContentModel.Tutorial(id = 3, CommunalContentSize.THIRD),
CommunalContentModel.Tutorial(id = 4, CommunalContentSize.HALF),
CommunalContentModel.Tutorial(id = 5, CommunalContentSize.HALF),
CommunalContentModel.Tutorial(id = 6, CommunalContentSize.HALF),
CommunalContentModel.Tutorial(id = 7, CommunalContentSize.HALF),
)
val umoContent: Flow<List<CommunalContentModel.Umo>> =
mediaRepository.mediaPlaying.flatMapLatest { mediaPlaying ->
if (mediaPlaying) {
// TODO(b/310254801): support HALF and FULL layouts
flowOf(listOf(CommunalContentModel.Umo(CommunalContentSize.THIRD)))
} else {
flowOf(emptyList())
}
}
}