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

Commit e3e4f1aa authored by andrewxu's avatar andrewxu
Browse files

[Flexiglass] Implement the composable edit scene

This CL creates an edit scene with compose views.

Bug: 420960164
Flag: com.android.systemui.scene_container
Change-Id: I1d2839ed004fd0e1562faddcce75a09f20b51a5e
parent c92d8d32
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
/*
 * Copyright 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.scene

import com.android.systemui.qs.ui.composable.EditModeScene
import com.android.systemui.scene.ui.composable.Scene
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet

@Module
interface EditSceneModule {
    @Binds @IntoSet fun editModeScene(scene: EditModeScene): Scene
}
+77 −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.qs.ui.composable

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.qs.ui.viewmodel.EditModeSceneActionsViewModel
import com.android.systemui.qs.ui.viewmodel.EditModeSceneContentViewModel
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.Scene
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import com.android.compose.animation.scene.ContentScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.shade.ui.composable.Shade
import androidx.compose.ui.res.colorResource
import com.android.systemui.qs.panels.ui.compose.EditMode
import com.android.systemui.res.R

@SysUISingleton
class EditModeScene
@Inject
constructor(
    private val contentViewModelFactory: EditModeSceneContentViewModel.Factory,
    private val actionsViewModelFactory: EditModeSceneActionsViewModel.Factory,
) : ExclusiveActivatable(), Scene {
    override val key = Scenes.QSEditMode

    private val actionsViewModel: EditModeSceneActionsViewModel by lazy {
        actionsViewModelFactory.create()
    }

    override suspend fun onActivated() {
        actionsViewModel.activate()
    }

    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions

    @Composable
    override fun ContentScope.Content(modifier: Modifier) {
        val viewModel = rememberViewModel("edit_mode_scene_view_model") {
            contentViewModelFactory.create()
        }

        Box(
            modifier =
                Modifier
                    .fillMaxSize()
                    .element(Shade.Elements.BackgroundScrim)
                    .background(colorResource(R.color.shade_scrim_background_dark))
        )

        EditMode(viewModel.editModeViewModel, modifier.testTag("edit_mode_scene"))
    }
}
 No newline at end of file
+53 −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.qs.ui.viewmodel

import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation


class EditModeSceneActionsViewModel
@AssistedInject
constructor(
    private val sceneBackInteractor: SceneBackInteractor
) : UserActionsViewModel() {
    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
        sceneBackInteractor.backScene.collect {
            setActions(
                buildMap {
                    put(Swipe.Up, UserActionResult(Scenes.Gone))
                    if (it != null) {
                        put(Back, UserActionResult(it))
                    }
                }
            )
        }
    }

    @AssistedFactory
    interface Factory {
        fun create(): EditModeSceneActionsViewModel
    }
}
+61 −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.qs.ui.viewmodel

import com.android.systemui.qs.composefragment.SceneKeys
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.util.kotlin.sample
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

class EditModeSceneContentViewModel
@AssistedInject
constructor(
    val editModeViewModel: EditModeViewModel,
    private val sceneBackInteractor: SceneBackInteractor,
    private val sceneInteractor: SceneInteractor,
) : ExclusiveActivatable() {
    override suspend fun onActivated() {
        coroutineScope {
            editModeViewModel.isEditing
                .filterNot { it }
                .sample(sceneBackInteractor.backScene)
                .onEach { backScene ->
                    sceneInteractor.changeScene(
                        toScene = backScene ?: SceneKeys.EditMode,
                        loggingReason = "Exiting edit mode"
                    )
                }
                .launchIn(this)

            awaitCancellation()
        }
    }

    @AssistedFactory
    interface Factory {
        fun create(): EditModeSceneContentViewModel
    }
}
 No newline at end of file
+7 −1
Original line number Diff line number Diff line
@@ -36,9 +36,11 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

/**
 * Models UI state needed for rendering the content of the quick settings scene.
@@ -84,6 +86,10 @@ constructor(
            launch { hydrator.activate() }

            launch { qsContainerViewModel.activate() }
            qsContainerViewModel.editModeViewModel.isEditing
                .filter { it }
                .onEach { sceneInteractor.changeScene(Scenes.QSEditMode, loggingReason = "") }
                .launchIn(this)

            launch(context = mainDispatcher) {
                shadeModeInteractor.shadeMode.collect { shadeMode ->
Loading