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

Commit a909a87f authored by Anton Potapov's avatar Anton Potapov
Browse files

Add basic ANC device settings infrastructure

Flag: com.android.systemui.volume_redesign
Bug: 439511163
Test: passes presubmits
Change-Id: I91d771c536b0d05fc65669f9baa84b1f288ce7d9
parent 278ca97a
Loading
Loading
Loading
Loading
+55 −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.volume.panel.component.anc.domain.interactor

import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.onStart

@VolumePanelScope
class AncDeviceSettingInteractor
@Inject
constructor(
    private val audioOutputInteractor: AudioOutputInteractor,
    private val deviceSettingRepository: DeviceSettingRepository,
) {

    fun getSetting(): Flow<DeviceSettingModel?> {
        return audioOutputInteractor.currentAudioDevice.flatMapLatest {
            if (it is AudioOutputDevice.Bluetooth) {
                getSettingForDevice(it.cachedBluetoothDevice)
            } else {
                flowOf(null)
            }
        }
    }

    private fun getSettingForDevice(device: CachedBluetoothDevice): Flow<DeviceSettingModel?> {
        return deviceSettingRepository
            .getDeviceSetting(device, DeviceSettingId.DEVICE_SETTING_ID_ANC)
            .onStart { emit(null) }
    }
}
+45 −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.volume.panel.component.anc.ui.viewmodel

import androidx.compose.runtime.getValue
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.volume.panel.component.anc.domain.interactor.AncDeviceSettingInteractor
import com.android.systemui.volume.panel.component.devicesetting.ui.viewmodel.DeviceSettingComponentViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

class AncSettingViewModel @AssistedInject constructor(interactor: AncDeviceSettingInteractor) :
    ExclusiveActivatable(), DeviceSettingComponentViewModel {

    private val hydrator = Hydrator("AncSettingViewModel")

    override val setting: DeviceSettingModel? by
        hydrator.hydratedStateOf("ancSetting", null, interactor.getSetting())

    override suspend fun onActivated(): Nothing {
        hydrator.activate()
    }

    @AssistedFactory
    interface Factory {

        fun create(): AncSettingViewModel
    }
}
+22 −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.bluetooth.devicesettings.data.repository

import com.android.systemui.kosmos.Kosmos

val Kosmos.fakeDeviceSettingRepository by Kosmos.Fixture { FakeDeviceSettingRepository() }
var Kosmos.deviceSettingRepository by Kosmos.Fixture { fakeDeviceSettingRepository }
+69 −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.bluetooth.devicesettings.data.repository

import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update

class FakeDeviceSettingRepository : DeviceSettingRepository {

    private val settingsConfigs = mutableMapOf<CachedBluetoothDevice, DeviceSettingConfigModel>()
    private val deviceSettings =
        mutableMapOf<DeviceSettingsKey, MutableStateFlow<DeviceSettingModel?>>()

    override suspend fun getDeviceSettingsConfig(
        cachedDevice: CachedBluetoothDevice
    ): DeviceSettingConfigModel? {
        return settingsConfigs[cachedDevice]
    }

    fun setDeviceSettingsConfig(
        cachedDevice: CachedBluetoothDevice,
        config: DeviceSettingConfigModel,
    ) {
        settingsConfigs[cachedDevice] = config
    }

    override fun getDeviceSetting(
        cachedDevice: CachedBluetoothDevice,
        settingId: Int,
    ): Flow<DeviceSettingModel?> =
        deviceSettings
            .getOrPut(DeviceSettingsKey(cachedDevice, settingId)) { MutableStateFlow(null) }
            .asStateFlow()

    fun updateDeviceSetting(
        cachedDevice: CachedBluetoothDevice,
        settingId: Int,
        update: (DeviceSettingModel?) -> DeviceSettingModel?,
    ) {
        deviceSettings
            .getOrPut(DeviceSettingsKey(cachedDevice, settingId)) { MutableStateFlow(null) }
            .update(update)
    }

    private data class DeviceSettingsKey(
        val cachedDevice: CachedBluetoothDevice,
        val settingId: Int,
    )
}
+29 −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.volume.panel.component.anc.domain.interactor

import com.android.systemui.bluetooth.devicesettings.data.repository.deviceSettingRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.volume.domain.interactor.audioOutputInteractor

val Kosmos.ancDeviceSettingInteractor by
    Kosmos.Fixture {
        AncDeviceSettingInteractor(
            audioOutputInteractor = audioOutputInteractor,
            deviceSettingRepository = deviceSettingRepository,
        )
    }