diff options
| author | 2024-10-07 15:20:36 +0100 | |
|---|---|---|
| committer | 2024-10-10 15:26:57 +0000 | |
| commit | e8b7d81f1e292d1c1595be80df693083238b161c (patch) | |
| tree | 601c4243c133f33edae7c6ff24f4bca44dfab23a | |
| parent | fb9677f248f1ee88b1fa4f0ac02b70b7dfe7fcf5 (diff) | |
Add base logic for the Volume Dialog sliders.
This change creates barebone interactors, viewmodel and viewbinders for
the future changes to implement.
Flag: com.android.systemui.volume_redesign
Test: passes presubmits
Bug: 369992924
Change-Id: Ib701f846e2ea1c5371ae9d714cbf6eafeb1c518b
12 files changed, 402 insertions, 10 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java index b1736b16875d..c09509d8690a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java @@ -14,7 +14,6 @@ package com.android.systemui.plugins; -import android.annotation.IntegerRes; import android.content.ComponentName; import android.media.AudioManager; import android.media.AudioSystem; @@ -22,6 +21,8 @@ import android.os.Handler; import android.os.VibrationEffect; import android.util.SparseArray; +import androidx.annotation.StringRes; + import com.android.systemui.plugins.VolumeDialogController.Callbacks; import com.android.systemui.plugins.VolumeDialogController.State; import com.android.systemui.plugins.VolumeDialogController.StreamState; @@ -90,7 +91,7 @@ public interface VolumeDialogController { public int levelMax; public boolean muted; public boolean muteSupported; - public @IntegerRes int name; + public @StringRes int name; public String remoteLabel; public boolean routedToBluetooth; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 1f92bc1df9c8..bbd8f3dc7e82 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -59,7 +59,6 @@ import android.view.accessibility.CaptioningManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Observer; import com.android.internal.annotations.GuardedBy; @@ -110,8 +109,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa // It is safe to use 99 as the broadcast stream now. There are only 10+ default audio // streams defined in AudioSystem for now and audio team is in the middle of restructure, // no new default stream is preferred. - @VisibleForTesting static final int DYNAMIC_STREAM_BROADCAST = 99; - private static final int DYNAMIC_STREAM_REMOTE_START_INDEX = 100; + public static final int DYNAMIC_STREAM_BROADCAST = 99; + public static final int DYNAMIC_STREAM_REMOTE_START_INDEX = 100; private static final AudioAttributes SONIFICIATION_VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStateModel.kt index f1443e36d019..500cc0bf748c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStateModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStateModel.kt @@ -23,7 +23,7 @@ import com.android.systemui.plugins.VolumeDialogController /** Models a state of the Volume Dialog. */ data class VolumeDialogStateModel( - val states: Map<Int, VolumeDialogStreamStateModel>, + val states: Map<Int, VolumeDialogStreamModel>, val ringerModeInternal: Int = 0, val ringerModeExternal: Int = 0, val zenMode: Int = 0, @@ -39,7 +39,7 @@ data class VolumeDialogStateModel( constructor( legacyState: VolumeDialogController.State ) : this( - states = legacyState.states.mapToMap { VolumeDialogStreamStateModel(it) }, + states = legacyState.states.mapToMap { VolumeDialogStreamModel(it) }, ringerModeInternal = legacyState.ringerModeInternal, ringerModeExternal = legacyState.ringerModeExternal, zenMode = legacyState.zenMode, diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStreamStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStreamModel.kt index a9d367da41e4..26c96eabc65f 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStreamStateModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStreamModel.kt @@ -16,18 +16,18 @@ package com.android.systemui.volume.dialog.domain.model -import android.annotation.IntegerRes +import androidx.annotation.StringRes import com.android.systemui.plugins.VolumeDialogController /** Models a state of an audio stream of the Volume Dialog. */ -data class VolumeDialogStreamStateModel( +data class VolumeDialogStreamModel( val isDynamic: Boolean = false, val level: Int = 0, val levelMin: Int = 0, val levelMax: Int = 0, val muted: Boolean = false, val muteSupported: Boolean = false, - @IntegerRes val name: Int = 0, + @StringRes val name: Int = 0, val remoteLabel: String? = null, val routedToBluetooth: Boolean = false, ) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt new file mode 100644 index 000000000000..81507ba7dc60 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 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.volume.dialog.sliders.domain.interactor + +import com.android.systemui.plugins.VolumeDialogController +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope +import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor +import com.android.systemui.volume.dialog.domain.model.VolumeDialogStreamModel +import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.mapNotNull + +/** Operates a state of particular slider of the Volume Dialog. */ +class VolumeDialogSliderInteractor +@AssistedInject +constructor( + @Assisted private val sliderType: VolumeDialogSliderType, + volumeDialogStateInteractor: VolumeDialogStateInteractor, + private val volumeDialogController: VolumeDialogController, +) { + + val slider: Flow<VolumeDialogStreamModel> = + volumeDialogStateInteractor.volumeDialogState.mapNotNull { + it.states[sliderType.audioStream] + } + + fun setStreamVolume(userLevel: Int) { + volumeDialogController.setStreamVolume(sliderType.audioStream, userLevel) + } + + @VolumeDialogScope + @AssistedFactory + interface Factory { + + fun create(sliderType: VolumeDialogSliderType): VolumeDialogSliderInteractor + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt new file mode 100644 index 000000000000..325e4c9514cf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 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.volume.dialog.sliders.domain.interactor + +import com.android.systemui.volume.VolumeDialogControllerImpl +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope +import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor +import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType +import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSlidersModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** Provides a state for the Sliders section of the Volume Dialog. */ +@VolumeDialogScope +class VolumeDialogSlidersInteractor +@Inject +constructor(volumeDialogStateInteractor: VolumeDialogStateInteractor) { + + val sliders: Flow<VolumeDialogSlidersModel> = + volumeDialogStateInteractor.volumeDialogState.map { + val sliderTypes: List<VolumeDialogSliderType> = + it.states.keys.sortedWith(StreamsSorter).map { audioStream -> + when { + audioStream == VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST -> + VolumeDialogSliderType.AudioSharingStream(audioStream) + audioStream >= + VolumeDialogControllerImpl.DYNAMIC_STREAM_REMOTE_START_INDEX -> + VolumeDialogSliderType.RemoteMediaStream(audioStream) + else -> VolumeDialogSliderType.Stream(audioStream) + } + } + VolumeDialogSlidersModel( + slider = sliderTypes.first(), + floatingSliders = sliderTypes.drop(1), + ) + } + + private object StreamsSorter : Comparator<Int> { + + // TODO(b/369992924) order the streams + override fun compare(lhs: Int, rhs: Int): Int { + return lhs - rhs + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderType.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderType.kt new file mode 100644 index 000000000000..18a26891e904 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderType.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 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.volume.dialog.sliders.domain.model + +/** Models different possible audio sliders shown in the Volume Dialog. */ +sealed interface VolumeDialogSliderType { + + // VolumeDialogController uses the same model for every slider type. We need to follow the same + // logic until we refactor and decouple data and domain layers from the VolumeDialogController + // into separated interactors. + val audioStream: Int + + class Stream(override val audioStream: Int) : VolumeDialogSliderType + + class RemoteMediaStream(override val audioStream: Int) : VolumeDialogSliderType + + class AudioSharingStream(override val audioStream: Int) : VolumeDialogSliderType +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSlidersModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSlidersModel.kt new file mode 100644 index 000000000000..91a332830b75 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSlidersModel.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 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.volume.dialog.sliders.domain.model + +/** Models a state of the sliders section of the Volume Dialog. */ +data class VolumeDialogSlidersModel( + val slider: VolumeDialogSliderType, + val floatingSliders: List<VolumeDialogSliderType>, +) diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt new file mode 100644 index 000000000000..25a5f287c21f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 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.volume.dialog.sliders.ui + +import android.view.View +import androidx.lifecycle.viewmodel.compose.viewModel +import com.android.systemui.lifecycle.WindowLifecycleState +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.lifecycle.setSnapshotBinding +import com.android.systemui.lifecycle.viewModel +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope +import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.awaitCancellation + +class VolumeDialogSliderViewBinder +@AssistedInject +constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel) { + + fun bind(view: View) { + with(view) { + repeatWhenAttached { + viewModel( + traceName = "VolumeDialogSliderViewBinder", + minWindowLifecycleState = WindowLifecycleState.ATTACHED, + factory = { viewModelProvider() }, + ) { viewModel -> + setSnapshotBinding {} + + awaitCancellation() + } + } + } + } + + @AssistedFactory + @VolumeDialogScope + interface Factory { + + fun create( + viewModelProvider: () -> VolumeDialogSliderViewModel + ): VolumeDialogSliderViewBinder + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt new file mode 100644 index 000000000000..0a00f70b54f1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 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.volume.dialog.sliders.ui + +import android.view.View +import com.android.systemui.lifecycle.WindowLifecycleState +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.lifecycle.setSnapshotBinding +import com.android.systemui.lifecycle.viewModel +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope +import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel +import javax.inject.Inject +import kotlinx.coroutines.awaitCancellation + +@VolumeDialogScope +class VolumeDialogSlidersViewBinder +@Inject +constructor(private val viewModelFactory: VolumeDialogSlidersViewModel.Factory) { + + fun bind(view: View) { + with(view) { + repeatWhenAttached { + viewModel( + traceName = "VolumeDialogSlidersViewBinder", + minWindowLifecycleState = WindowLifecycleState.ATTACHED, + factory = { viewModelFactory.create() }, + ) { viewModel -> + setSnapshotBinding {} + + awaitCancellation() + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt new file mode 100644 index 000000000000..27b8f2f5adb7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 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.volume.dialog.sliders.ui.viewmodel + +import com.android.systemui.volume.dialog.domain.model.VolumeDialogStreamModel +import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.Flow + +class VolumeDialogSliderViewModel +@AssistedInject +constructor(@Assisted private val interactor: VolumeDialogSliderInteractor) { + + val model: Flow<VolumeDialogStreamModel> = interactor.slider + + fun setStreamVolume(volume: Int) { + interactor.setStreamVolume(volume) + } + + @AssistedFactory + interface Factory { + + fun create(interactor: VolumeDialogSliderInteractor): VolumeDialogSliderViewModel + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt new file mode 100644 index 000000000000..b5b292fa4a66 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 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.volume.dialog.sliders.ui.viewmodel + +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog +import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor +import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor +import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType +import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +class VolumeDialogSlidersViewModel +@AssistedInject +constructor( + @VolumeDialog coroutineScope: CoroutineScope, + private val slidersInteractor: VolumeDialogSlidersInteractor, + private val sliderInteractorFactory: VolumeDialogSliderInteractor.Factory, + private val sliderViewModelFactory: VolumeDialogSliderViewModel.Factory, + private val sliderViewBinderFactory: VolumeDialogSliderViewBinder.Factory, +) { + + val sliders: Flow<VolumeDialogSliderUiModel> = + slidersInteractor.sliders + .distinctUntilChanged() + .map { slidersModel -> + VolumeDialogSliderUiModel( + sliderViewBinder = createSliderViewBinder(slidersModel.slider), + floatingSliderViewBinders = + slidersModel.floatingSliders.map(::createSliderViewBinder), + ) + } + .stateIn(coroutineScope, SharingStarted.Eagerly, null) + .filterNotNull() + + private fun createSliderViewBinder(type: VolumeDialogSliderType): VolumeDialogSliderViewBinder = + sliderViewBinderFactory.create { + sliderViewModelFactory.create(sliderInteractorFactory.create(type)) + } + + @AssistedFactory + interface Factory { + + fun create(): VolumeDialogSlidersViewModel + } +} + +/** Models slider ui */ +data class VolumeDialogSliderUiModel( + val sliderViewBinder: VolumeDialogSliderViewBinder, + val floatingSliderViewBinders: List<VolumeDialogSliderViewBinder>, +) |