Merge "Add help button on the top right corner of more settings page" into main

This commit is contained in:
Haijie Hong
2024-08-28 05:35:57 +00:00
committed by Android (Google) Code Review
8 changed files with 224 additions and 28 deletions

View File

@@ -101,6 +101,12 @@ open class BluetoothFeatureProviderImpl : BluetoothFeatureProvider {
bluetoothAdapter: BluetoothAdapter,
cachedDevice: CachedBluetoothDevice
): DeviceDetailsFragmentFormatter {
return DeviceDetailsFragmentFormatterImpl(context, fragment, bluetoothAdapter, cachedDevice)
return DeviceDetailsFragmentFormatterImpl(
context,
fragment,
bluetoothAdapter,
cachedDevice,
Dispatchers.IO
)
}
}

View File

@@ -66,4 +66,11 @@ sealed interface DeviceSettingPreferenceModel {
data class MoreSettingsPreference(
@DeviceSettingId override val id: Int,
) : DeviceSettingPreferenceModel
/** Models a help button on the top right corner of the fragment. */
data class HelpPreference(
@DeviceSettingId override val id: Int,
val icon: DeviceSettingIcon,
val onClick: (() -> Unit),
) : DeviceSettingPreferenceModel
}

View File

@@ -52,10 +52,15 @@ import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
import com.android.settingslib.spa.widget.ui.Footer
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
/** Handles device details fragment layout according to config. */
@@ -65,6 +70,11 @@ interface DeviceDetailsFragmentFormatter {
/** Updates device details fragment layout. */
fun updateLayout(fragmentType: FragmentTypeModel)
/** Gets the menu items of the fragment. */
fun getMenuItem(
fragmentType: FragmentTypeModel
): Flow<DeviceSettingPreferenceModel.HelpPreference?>
}
@OptIn(ExperimentalCoroutinesApi::class)
@@ -72,7 +82,8 @@ class DeviceDetailsFragmentFormatterImpl(
private val context: Context,
private val fragment: SettingsPreferenceFragment,
bluetoothAdapter: BluetoothAdapter,
private val cachedDevice: CachedBluetoothDevice
private val cachedDevice: CachedBluetoothDevice,
private val backgroundCoroutineContext: CoroutineContext,
) : DeviceDetailsFragmentFormatter {
private val repository =
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
@@ -88,6 +99,7 @@ class DeviceDetailsFragmentFormatterImpl(
repository,
spatialAudioInteractor,
cachedDevice,
backgroundCoroutineContext,
))
.get(BluetoothDeviceDetailsViewModel::class.java)
@@ -135,6 +147,19 @@ class DeviceDetailsFragmentFormatterImpl(
fragment.preferenceScreen.addPreference(Preference(context).apply { order = 10000 })
}
override fun getMenuItem(
fragmentType: FragmentTypeModel
): Flow<DeviceSettingPreferenceModel.HelpPreference?> = flow {
val t = viewModel.getHelpItem(fragmentType)
t?.let { item ->
emitAll(
viewModel.getDeviceSetting(cachedDevice, item.settingId).map {
it as? DeviceSettingPreferenceModel.HelpPreference
})
} ?: emit(null)
}
@Composable
private fun buildPreference(layout: DeviceSettingLayout, row: Int) {
val contents by
@@ -174,6 +199,7 @@ class DeviceDetailsFragmentFormatterImpl(
is DeviceSettingPreferenceModel.MoreSettingsPreference -> {
buildMoreSettingsPreference()
}
is DeviceSettingPreferenceModel.HelpPreference -> {}
null -> {}
}
}

View File

@@ -19,26 +19,65 @@ package com.android.settings.bluetooth.ui.view
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.content.Context
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.lifecycle.lifecycleScope
import com.android.settings.R
import com.android.settings.bluetooth.BluetoothDetailsProfilesController
import com.android.settings.bluetooth.Utils
import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
import com.android.settings.bluetooth.ui.model.FragmentTypeModel
import com.android.settings.dashboard.DashboardFragment
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
import com.android.settingslib.core.AbstractPreferenceController
import com.android.settingslib.core.lifecycle.LifecycleObserver
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class DeviceDetailsMoreSettingsFragment : DashboardFragment() {
private lateinit var formatter: DeviceDetailsFragmentFormatter
private lateinit var localBluetoothManager: LocalBluetoothManager
private lateinit var cachedDevice: CachedBluetoothDevice
private lateinit var helpItem: StateFlow<DeviceSettingPreferenceModel.HelpPreference?>
// TODO(b/343317785): add metrics category
override fun getMetricsCategory(): Int = 0
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
lifecycleScope.launch {
helpItem.filterNotNull().collect { item ->
val iconRes = item.icon as? DeviceSettingIcon.ResourceIcon ?: return@collect
val item: MenuItem =
menu.add(0, MENU_HELP_ITEM_ID, 0, R.string.bluetooth_device_tip_support)
item.setIcon(iconRes.resId)
item.icon?.setColorFilter(
resources.getColor(
com.android.settingslib.widget.theme.R.color
.settingslib_materialColorOnSurface),
PorterDuff.Mode.SRC_ATOP)
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
}
}
}
override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
if (menuItem.itemId == MENU_HELP_ITEM_ID) {
helpItem.value?.let { it.onClick() }
return true
}
return super.onOptionsItemSelected(menuItem)
}
override fun getPreferenceScreenResId(): Int {
return R.xml.bluetooth_device_more_settings_fragment
}
@@ -78,6 +117,10 @@ class DeviceDetailsMoreSettingsFragment : DashboardFragment() {
formatter =
featureFactory.bluetoothFeatureProvider.getDeviceDetailsFragmentFormatter(
requireContext(), this, bluetoothManager.adapter, cachedDevice)
helpItem =
formatter
.getMenuItem(FragmentTypeModel.DeviceDetailsMoreSettingsFragment)
.stateIn(lifecycleScope, SharingStarted.WhileSubscribed(), initialValue = null)
return listOf(
BluetoothDetailsProfilesController(
context, this, localBluetoothManager, cachedDevice, settingsLifecycle))
@@ -88,5 +131,6 @@ class DeviceDetailsMoreSettingsFragment : DashboardFragment() {
companion object {
const val TAG: String = "DeviceMoreSettingsFrg"
const val KEY_DEVICE_ADDRESS: String = "device_address"
const val MENU_HELP_ITEM_ID = Menu.FIRST
}
}

View File

@@ -21,6 +21,7 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.R
import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow
@@ -30,8 +31,10 @@ 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.DeviceSettingConfigItemModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
@@ -47,10 +50,11 @@ class BluetoothDeviceDetailsViewModel(
private val deviceSettingRepository: DeviceSettingRepository,
private val spatialAudioInteractor: SpatialAudioInteractor,
private val cachedDevice: CachedBluetoothDevice,
backgroundCoroutineContext: CoroutineContext,
) : AndroidViewModel(application){
private val items =
viewModelScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
viewModelScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
deviceSettingRepository.getDeviceSettingsConfig(cachedDevice)
}
@@ -61,6 +65,13 @@ class BluetoothDeviceDetailsViewModel(
items.await()?.moreSettingsItems
}
suspend fun getHelpItem(fragment: FragmentTypeModel): DeviceSettingConfigItemModel? =
when (fragment) {
is FragmentTypeModel.DeviceDetailsMainFragment -> null
is FragmentTypeModel.DeviceDetailsMoreSettingsFragment ->
items.await()?.moreSettingsHelpItem
}
fun getDeviceSetting(
cachedDevice: CachedBluetoothDevice,
@DeviceSettingId settingId: Int
@@ -101,6 +112,13 @@ class BluetoothDeviceDetailsViewModel(
}
is DeviceSettingModel.FooterPreference ->
DeviceSettingPreferenceModel.FooterPreference(id = id, footerText = footerText)
is DeviceSettingModel.HelpPreference ->
DeviceSettingPreferenceModel.HelpPreference(
id = id,
icon = DeviceSettingIcon.ResourceIcon(R.drawable.ic_help),
onClick = {
application.startActivity(intent)
})
is DeviceSettingModel.MultiTogglePreference ->
DeviceSettingPreferenceModel.MultiTogglePreference(
id = id,
@@ -163,11 +181,14 @@ class BluetoothDeviceDetailsViewModel(
private val deviceSettingRepository: DeviceSettingRepository,
private val spatialAudioInteractor: SpatialAudioInteractor,
private val cachedDevice: CachedBluetoothDevice,
private val backgroundCoroutineContext: CoroutineContext,
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return BluetoothDeviceDetailsViewModel(
application, deviceSettingRepository, spatialAudioInteractor, cachedDevice)
application, deviceSettingRepository, spatialAudioInteractor,
cachedDevice,
backgroundCoroutineContext)
as T
}
}