Loading packages/SystemUI/src/com/android/systemui/media/remedia/data/repository/MediaRepository.kt +103 −70 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.graphics.Color import com.android.internal.annotations.GuardedBy import com.android.internal.logging.InstanceId import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon Loading @@ -56,6 +57,8 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext /** A repository that holds the state of current media on the device. */ Loading Loading @@ -97,6 +100,7 @@ constructor( override var shouldScrollToFirst by mutableStateOf(false) @GuardedBy("mediaMutex") private var sortedMedia = TreeMap<MediaSortKeyModel, MediaDataModel>(comparator) // To store active controllers and their callbacks Loading @@ -104,21 +108,28 @@ constructor( private val mediaCallbacks = mutableMapOf<InstanceId, MediaController.Callback>() // To store active polling jobs private val positionPollers = mutableMapOf<InstanceId, Job>() private val mediaMutex = Mutex() override fun addCurrentUserMediaEntry(data: MediaData): UpdateArtInfoModel? { return super.addCurrentUserMediaEntry(data).also { updateModel -> addToSortedMedia(data, updateModel) applicationScope.launch { mediaMutex.withLock { addToSortedMediaLocked(data, updateModel) } } } } override fun removeCurrentUserMediaEntry(key: InstanceId): MediaData? { return super.removeCurrentUserMediaEntry(key)?.also { removeFromSortedMedia(it) } return super.removeCurrentUserMediaEntry(key)?.also { applicationScope.launch { mediaMutex.withLock { removeFromSortedMediaLocked(it) } } } } override fun removeCurrentUserMediaEntry(key: InstanceId, data: MediaData): Boolean { return super.removeCurrentUserMediaEntry(key, data).also { if (it) { removeFromSortedMedia(data) applicationScope.launch { mediaMutex.withLock { removeFromSortedMediaLocked(data) } } } } } Loading @@ -126,23 +137,33 @@ constructor( override fun clearCurrentUserMedia() { val userEntries = LinkedHashMap<InstanceId, MediaData>(mutableUserEntries.value) mutableUserEntries.value = LinkedHashMap() userEntries.forEach { removeFromSortedMedia(it.value) } applicationScope.launch { mediaMutex.withLock { userEntries.forEach { removeFromSortedMediaLocked(it.value) } } } } override fun seek(sessionKey: InstanceId, to: Long) { activeControllers[sessionKey]?.let { controller -> controller.transportControls.seekTo(to) applicationScope.launch { mediaMutex.withLock { currentMedia .find { it.instanceId == sessionKey } ?.let { latestModel -> updateMediaModelInState(latestModel) { it.copy(positionMs = to) } updateMediaModelInStateLocked(latestModel) { it.copy(positionMs = to) } } } } } } override fun reorderMedia() { applicationScope.launch { mediaMutex.withLock { currentMedia.clear() currentMedia.addAll(sortedMedia.values.toList()) } } currentCarouselIndex = 0 } Loading @@ -154,7 +175,8 @@ constructor( shouldScrollToFirst = false } private fun addToSortedMedia(data: MediaData, updateModel: UpdateArtInfoModel?) { @GuardedBy("mediaMutex") private suspend fun addToSortedMediaLocked(data: MediaData, updateModel: UpdateArtInfoModel?) { val sortedMap = TreeMap<MediaSortKeyModel, MediaDataModel>(comparator) val currentModel = sortedMedia.values.find { it.instanceId == data.instanceId } Loading @@ -173,8 +195,6 @@ constructor( systemClock.currentTimeMillis(), instanceId, ) applicationScope.launch { val controller = if (currentModel != null && currentModel.token == token) { activeControllers[currentModel.instanceId] Loading @@ -183,8 +203,7 @@ constructor( currentModel?.instanceId?.let { clearControllerState(it) } token?.let { MediaController(applicationContext, it) } } val (icon, background) = getIconAndBackground(mediaData, currentModel, updateModel) val (icon, background) = getIconAndBackground(mediaData, currentModel, updateModel) val mediaModel = toDataModel(controller, icon, background) sortedMap[sortKey] = mediaModel controller?.let { setupController(mediaModel, it) } Loading Loading @@ -215,9 +234,9 @@ constructor( } } } } private fun removeFromSortedMedia(data: MediaData) { @GuardedBy("mediaMutex") private fun removeFromSortedMediaLocked(data: MediaData) { currentMedia.removeIf { model -> data.instanceId == model.instanceId } sortedMedia = TreeMap<MediaSortKeyModel, MediaDataModel>(comparator).apply { Loading Loading @@ -390,15 +409,23 @@ constructor( } override fun onMetadataChanged(metadata: MediaMetadata?) { val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION) ?: 0L applicationScope.launch { mediaMutex.withLock { val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION) ?: 0L currentMedia .find { it.instanceId == dataModel.instanceId } ?.let { latestModel -> updateMediaModelInState(latestModel) { model -> updateMediaModelInStateLocked(latestModel) { model -> val canBeScrubbed = controller.playbackState?.state != PlaybackState.STATE_NONE && duration > 0L model.copy(canBeScrubbed = canBeScrubbed, durationMs = duration) controller.playbackState?.state != PlaybackState.STATE_NONE && duration > 0L model.copy( canBeScrubbed = canBeScrubbed, durationMs = duration, ) } } } } } Loading Loading @@ -465,11 +492,14 @@ constructor( } private fun checkPlaybackPosition(instanceId: InstanceId, playbackState: PlaybackState?) { applicationScope.launch { mediaMutex.withLock { currentMedia .find { it.instanceId == instanceId } ?.let { latestModel -> val newPosition = playbackState?.computeActualPosition(latestModel.durationMs) updateMediaModelInState(latestModel) { val newPosition = playbackState?.computeActualPosition(latestModel.durationMs) updateMediaModelInStateLocked(latestModel) { if (newPosition != null && newPosition <= latestModel.durationMs) { it.copy(positionMs = newPosition) } else { Loading @@ -478,6 +508,8 @@ constructor( } } } } } private fun clearControllerState(instanceId: InstanceId) { positionPollers[instanceId]?.cancel() Loading @@ -487,7 +519,8 @@ constructor( mediaCallbacks.remove(instanceId) } private fun updateMediaModelInState( @GuardedBy("mediaMutex") private fun updateMediaModelInStateLocked( oldModel: MediaDataModel, updateBlock: (MediaDataModel) -> MediaDataModel, ) { Loading Loading
packages/SystemUI/src/com/android/systemui/media/remedia/data/repository/MediaRepository.kt +103 −70 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.graphics.Color import com.android.internal.annotations.GuardedBy import com.android.internal.logging.InstanceId import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon Loading @@ -56,6 +57,8 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext /** A repository that holds the state of current media on the device. */ Loading Loading @@ -97,6 +100,7 @@ constructor( override var shouldScrollToFirst by mutableStateOf(false) @GuardedBy("mediaMutex") private var sortedMedia = TreeMap<MediaSortKeyModel, MediaDataModel>(comparator) // To store active controllers and their callbacks Loading @@ -104,21 +108,28 @@ constructor( private val mediaCallbacks = mutableMapOf<InstanceId, MediaController.Callback>() // To store active polling jobs private val positionPollers = mutableMapOf<InstanceId, Job>() private val mediaMutex = Mutex() override fun addCurrentUserMediaEntry(data: MediaData): UpdateArtInfoModel? { return super.addCurrentUserMediaEntry(data).also { updateModel -> addToSortedMedia(data, updateModel) applicationScope.launch { mediaMutex.withLock { addToSortedMediaLocked(data, updateModel) } } } } override fun removeCurrentUserMediaEntry(key: InstanceId): MediaData? { return super.removeCurrentUserMediaEntry(key)?.also { removeFromSortedMedia(it) } return super.removeCurrentUserMediaEntry(key)?.also { applicationScope.launch { mediaMutex.withLock { removeFromSortedMediaLocked(it) } } } } override fun removeCurrentUserMediaEntry(key: InstanceId, data: MediaData): Boolean { return super.removeCurrentUserMediaEntry(key, data).also { if (it) { removeFromSortedMedia(data) applicationScope.launch { mediaMutex.withLock { removeFromSortedMediaLocked(data) } } } } } Loading @@ -126,23 +137,33 @@ constructor( override fun clearCurrentUserMedia() { val userEntries = LinkedHashMap<InstanceId, MediaData>(mutableUserEntries.value) mutableUserEntries.value = LinkedHashMap() userEntries.forEach { removeFromSortedMedia(it.value) } applicationScope.launch { mediaMutex.withLock { userEntries.forEach { removeFromSortedMediaLocked(it.value) } } } } override fun seek(sessionKey: InstanceId, to: Long) { activeControllers[sessionKey]?.let { controller -> controller.transportControls.seekTo(to) applicationScope.launch { mediaMutex.withLock { currentMedia .find { it.instanceId == sessionKey } ?.let { latestModel -> updateMediaModelInState(latestModel) { it.copy(positionMs = to) } updateMediaModelInStateLocked(latestModel) { it.copy(positionMs = to) } } } } } } override fun reorderMedia() { applicationScope.launch { mediaMutex.withLock { currentMedia.clear() currentMedia.addAll(sortedMedia.values.toList()) } } currentCarouselIndex = 0 } Loading @@ -154,7 +175,8 @@ constructor( shouldScrollToFirst = false } private fun addToSortedMedia(data: MediaData, updateModel: UpdateArtInfoModel?) { @GuardedBy("mediaMutex") private suspend fun addToSortedMediaLocked(data: MediaData, updateModel: UpdateArtInfoModel?) { val sortedMap = TreeMap<MediaSortKeyModel, MediaDataModel>(comparator) val currentModel = sortedMedia.values.find { it.instanceId == data.instanceId } Loading @@ -173,8 +195,6 @@ constructor( systemClock.currentTimeMillis(), instanceId, ) applicationScope.launch { val controller = if (currentModel != null && currentModel.token == token) { activeControllers[currentModel.instanceId] Loading @@ -183,8 +203,7 @@ constructor( currentModel?.instanceId?.let { clearControllerState(it) } token?.let { MediaController(applicationContext, it) } } val (icon, background) = getIconAndBackground(mediaData, currentModel, updateModel) val (icon, background) = getIconAndBackground(mediaData, currentModel, updateModel) val mediaModel = toDataModel(controller, icon, background) sortedMap[sortKey] = mediaModel controller?.let { setupController(mediaModel, it) } Loading Loading @@ -215,9 +234,9 @@ constructor( } } } } private fun removeFromSortedMedia(data: MediaData) { @GuardedBy("mediaMutex") private fun removeFromSortedMediaLocked(data: MediaData) { currentMedia.removeIf { model -> data.instanceId == model.instanceId } sortedMedia = TreeMap<MediaSortKeyModel, MediaDataModel>(comparator).apply { Loading Loading @@ -390,15 +409,23 @@ constructor( } override fun onMetadataChanged(metadata: MediaMetadata?) { val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION) ?: 0L applicationScope.launch { mediaMutex.withLock { val duration = metadata?.getLong(MediaMetadata.METADATA_KEY_DURATION) ?: 0L currentMedia .find { it.instanceId == dataModel.instanceId } ?.let { latestModel -> updateMediaModelInState(latestModel) { model -> updateMediaModelInStateLocked(latestModel) { model -> val canBeScrubbed = controller.playbackState?.state != PlaybackState.STATE_NONE && duration > 0L model.copy(canBeScrubbed = canBeScrubbed, durationMs = duration) controller.playbackState?.state != PlaybackState.STATE_NONE && duration > 0L model.copy( canBeScrubbed = canBeScrubbed, durationMs = duration, ) } } } } } Loading Loading @@ -465,11 +492,14 @@ constructor( } private fun checkPlaybackPosition(instanceId: InstanceId, playbackState: PlaybackState?) { applicationScope.launch { mediaMutex.withLock { currentMedia .find { it.instanceId == instanceId } ?.let { latestModel -> val newPosition = playbackState?.computeActualPosition(latestModel.durationMs) updateMediaModelInState(latestModel) { val newPosition = playbackState?.computeActualPosition(latestModel.durationMs) updateMediaModelInStateLocked(latestModel) { if (newPosition != null && newPosition <= latestModel.durationMs) { it.copy(positionMs = newPosition) } else { Loading @@ -478,6 +508,8 @@ constructor( } } } } } private fun clearControllerState(instanceId: InstanceId) { positionPollers[instanceId]?.cancel() Loading @@ -487,7 +519,8 @@ constructor( mediaCallbacks.remove(instanceId) } private fun updateMediaModelInState( @GuardedBy("mediaMutex") private fun updateMediaModelInStateLocked( oldModel: MediaDataModel, updateBlock: (MediaDataModel) -> MediaDataModel, ) { Loading