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

Commit f3490d1e authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[Media] Card carousel, top-level view-model, and interactor interface.

- Card carousel - the last, top-level composable for the UI element;
  including the Media(...) single top-level composable API
- Top-level view-model - MediaViewModel is the single Activatable
  view-model of the entire system
- Interactor interface - MediaInteractor defines the contract between
  the new UI and the rest of the system

Bug: 397989775
Test: tested in testbed app with fake data, see b/397989775 #comment4 for a screen recording of the entire thing in action in the compose gallery app
Test: once we have a real implementation of MediaInteractor, it's
worthwhile to add an integration unit test that uses the view-model, the
real interface, and a fake data at the top of the upstream of the stack
Flag: EXEMPT - the code isn't used anywhere yet

Change-Id: I6c474693e83c796914e81c49c628a4bb6d2c1258
parent 1b9e79be
Loading
Loading
Loading
Loading
+47 −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.media.remedia.domain.interactor

import android.graphics.Bitmap
import com.android.systemui.media.remedia.domain.model.MediaSessionModel

/**
 * Defines interface for classes that can provide business logic in the domain of the media controls
 * element.
 */
interface MediaInteractor {

    /** The list of sessions. Needs to be backed by a compose snapshot state. */
    val sessions: List<MediaSessionModel>

    /**
     * Loads, on a background thread, the media art for a session with the given [sessionToken].
     *
     * This will be called on the main thread. It's the responsibility of the implementation to move
     * its work off the main thread.
     */
    suspend fun loadArt(sessionToken: Any): Bitmap

    /** Seek to [to], in milliseconds on the media session with the given [sessionKey]. */
    fun seek(sessionKey: Any, to: Long)

    /** Hide the representation of the media session with the given [sessionKey]. */
    fun hide(sessionKey: Any)

    /** Open media settings. */
    fun openMediaSettings()
}
+27 −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.media.remedia.domain.model

import com.android.systemui.common.shared.model.Icon

sealed interface MediaActionModel {
    data class Action(val icon: Icon, val onClick: (() -> Unit)?) : MediaActionModel

    data object ReserveSpace : MediaActionModel

    data object None : MediaActionModel
}
+21 −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.media.remedia.domain.model

import com.android.systemui.common.shared.model.Icon

data class MediaOutputDeviceModel(val name: String, val icon: Icon, val isInProgress: Boolean)
+71 −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.media.remedia.domain.model

import androidx.compose.runtime.Stable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.media.remedia.shared.model.MediaCardActionButtonLayout
import com.android.systemui.media.remedia.shared.model.MediaSessionState

/** Data model representing a media session. */
@Stable
interface MediaSessionModel {
    /** Unique identifier. */
    val key: Any

    val appName: String

    val appIcon: Icon

    val title: String

    val subtitle: String

    val onClick: () -> Unit

    /**
     * Whether the session is currently active. Under some UIs, only currently active session should
     * be shown.
     */
    val isActive: Boolean

    /** Whether the session can be hidden/dismissed by the user. */
    val canBeHidden: Boolean

    /**
     * Whether the session currently supports scrubbing (e.g. moving to a different position iin the
     * playback.
     */
    val canBeScrubbed: Boolean

    val state: MediaSessionState

    /** The position of the playback within the current track. */
    val positionMs: Long

    /** The total duration of the current track. */
    val durationMs: Long

    val outputDevice: MediaOutputDeviceModel

    /** How to lay out the action buttons. */
    val actionButtonLayout: MediaCardActionButtonLayout
    val playPauseAction: MediaActionModel
    val leftAction: MediaActionModel
    val rightAction: MediaActionModel
    val additionalActions: List<MediaActionModel.Action>
}
+23 −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.media.remedia.shared.model

enum class MediaActionState {
    Enabled,
    Disabled,
    Hidden,
}
Loading