Rearrange bluetooth device details fragment according to config

BUG: 343317785
Test: atest DeviceDetailsFragmentFormatterTest
Flag: com.android.settings.flags.enable_bluetooth_device_details_polish
Change-Id: I440f85b2c402920e851915ff56fa7b3f5356e807
This commit is contained in:
Haijie Hong
2024-08-09 14:58:10 +08:00
parent 2a01b356ae
commit 41f7c222b6
13 changed files with 949 additions and 21 deletions

View File

@@ -101,7 +101,8 @@ public class BlockingPrefWithSliceController extends BasePreferenceController im
return mUri != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
public void setSliceUri(Uri uri) {
/** Sets Slice uri for the preference. */
public void setSliceUri(@Nullable Uri uri) {
mUri = uri;
mLiveData = SliceLiveData.fromUri(mContext, mUri, (int type, Throwable source) -> {
Log.w(TAG, "Slice may be null. uri = " + uri + ", error = " + type);

View File

@@ -43,10 +43,12 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
import com.android.settings.connecteddevice.stylus.StylusDevicesController;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.dashboard.RestrictedDashboardFragment;
@@ -60,9 +62,11 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment {
public static final String KEY_DEVICE_ADDRESS = "device_address";
@@ -98,6 +102,8 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
@VisibleForTesting
CachedBluetoothDevice mCachedDevice;
BluetoothAdapter mBluetoothAdapter;
@VisibleForTesting
DeviceDetailsFragmentFormatter mFormatter;
@Nullable
InputDevice mInputDevice;
@@ -214,18 +220,29 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
finish();
return;
}
use(AdvancedBluetoothDetailsHeaderController.class).init(mCachedDevice, this);
use(LeAudioBluetoothDetailsHeaderController.class).init(mCachedDevice, mManager, this);
use(KeyboardSettingsPreferenceController.class).init(mCachedDevice);
getController(
AdvancedBluetoothDetailsHeaderController.class,
controller -> controller.init(mCachedDevice, this));
getController(
LeAudioBluetoothDetailsHeaderController.class,
controller -> controller.init(mCachedDevice, mManager, this));
getController(
KeyboardSettingsPreferenceController.class,
controller -> controller.init(mCachedDevice));
final BluetoothFeatureProvider featureProvider =
FeatureFactory.getFeatureFactory().getBluetoothFeatureProvider();
final boolean sliceEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_SLICE_SETTINGS_ENABLED, true);
use(BlockingPrefWithSliceController.class).setSliceUri(sliceEnabled
? featureProvider.getBluetoothDeviceSettingsUri(mCachedDevice.getDevice())
: null);
getController(
BlockingPrefWithSliceController.class,
controller ->
controller.setSliceUri(
sliceEnabled
? featureProvider.getBluetoothDeviceSettingsUri(
mCachedDevice.getDevice())
: null));
mManager.getEventManager().registerCallback(mBluetoothCallback);
mBluetoothAdapter.addOnMetadataChangedListener(
@@ -257,21 +274,35 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
}
}
mExtraControlUriLoaded |= controlUri != null;
final SlicePreferenceController slicePreferenceController = use(
SlicePreferenceController.class);
slicePreferenceController.setSliceUri(sliceEnabled ? controlUri : null);
slicePreferenceController.onStart();
slicePreferenceController.displayPreference(getPreferenceScreen());
Uri finalControlUri = controlUri;
getController(SlicePreferenceController.class, controller -> {
controller.setSliceUri(sliceEnabled ? finalControlUri : null);
controller.onStart();
controller.displayPreference(getPreferenceScreen());
});
// Temporarily fix the issue that the page will be automatically scrolled to a wrong
// position when entering the page. This will make sure the bluetooth header is shown on top
// of the page.
use(LeAudioBluetoothDetailsHeaderController.class).displayPreference(
getPreferenceScreen());
use(AdvancedBluetoothDetailsHeaderController.class).displayPreference(
getPreferenceScreen());
use(BluetoothDetailsHeaderController.class).displayPreference(
getPreferenceScreen());
getController(
LeAudioBluetoothDetailsHeaderController.class,
controller -> controller.displayPreference(getPreferenceScreen()));
getController(
AdvancedBluetoothDetailsHeaderController.class,
controller -> controller.displayPreference(getPreferenceScreen()));
getController(
BluetoothDetailsHeaderController.class,
controller -> controller.displayPreference(getPreferenceScreen()));
}
protected <T extends AbstractPreferenceController> void getController(Class<T> clazz,
Consumer<T> action) {
T controller = use(clazz);
if (controller != null) {
action.accept(controller);
}
}
private final ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutListener =
@@ -308,6 +339,14 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
return view;
}
@Override
public void onCreatePreferences(@NonNull Bundle savedInstanceState, @NonNull String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
if (Flags.enableBluetoothDeviceDetailsPolish()) {
mFormatter.updateLayout();
}
}
@Override
public void onResume() {
super.onResume();
@@ -358,8 +397,30 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
return super.onOptionsItemSelected(menuItem);
}
@Override
protected void addPreferenceController(AbstractPreferenceController controller) {
if (Flags.enableBluetoothDeviceDetailsPolish()) {
List<String> keys = mFormatter.getVisiblePreferenceKeysForMainPage();
Lifecycle lifecycle = getSettingsLifecycle();
if (keys == null || keys.contains(controller.getPreferenceKey())) {
super.addPreferenceController(controller);
} else if (controller instanceof LifecycleObserver) {
lifecycle.removeObserver((LifecycleObserver) controller);
}
} else {
super.addPreferenceController(controller);
}
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
if (Flags.enableBluetoothDeviceDetailsPolish()) {
mFormatter =
FeatureFactory.getFeatureFactory()
.getBluetoothFeatureProvider()
.getDeviceDetailsFragmentFormatter(
requireContext(), this, mBluetoothAdapter, mCachedDevice);
}
ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
if (mCachedDevice != null) {

View File

@@ -16,15 +16,21 @@
package com.android.settings.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
import android.media.Spatializer;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleCoroutineScope;
import androidx.preference.Preference;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository;
import java.util.List;
import java.util.Set;
@@ -84,4 +90,19 @@ public interface BluetoothFeatureProvider {
*/
Set<String> getInvisibleProfilePreferenceKeys(
Context context, BluetoothDevice bluetoothDevice);
/** Gets DeviceSettingRepository. */
@NonNull
DeviceSettingRepository getDeviceSettingRepository(
@NonNull Context context,
@NonNull BluetoothAdapter bluetoothAdapter,
@NonNull LifecycleCoroutineScope scope);
/** Gets device details fragment layout formatter. */
@NonNull
DeviceDetailsFragmentFormatter getDeviceDetailsFragmentFormatter(
@NonNull Context context,
@NonNull SettingsPreferenceFragment fragment,
@NonNull BluetoothAdapter bluetoothAdapter,
@NonNull CachedBluetoothDevice cachedDevice);
}

View File

@@ -16,6 +16,9 @@
package com.android.settings.bluetooth;
import static com.android.settings.bluetooth.utils.DeviceSettingUtilsKt.createDeviceSettingRepository;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
@@ -23,10 +26,16 @@ import android.media.AudioManager;
import android.media.Spatializer;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleCoroutineScope;
import androidx.preference.Preference;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatterImpl;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -73,4 +82,24 @@ public class BluetoothFeatureProviderImpl implements BluetoothFeatureProvider {
Context context, BluetoothDevice bluetoothDevice) {
return ImmutableSet.of();
}
@Override
@NonNull
public DeviceSettingRepository getDeviceSettingRepository(
@NonNull Context context,
@NonNull BluetoothAdapter bluetoothAdapter,
@NonNull LifecycleCoroutineScope scope) {
return createDeviceSettingRepository(context, bluetoothAdapter, scope);
}
@Override
@NonNull
public DeviceDetailsFragmentFormatter getDeviceDetailsFragmentFormatter(
@NonNull Context context,
@NonNull SettingsPreferenceFragment fragment,
@NonNull BluetoothAdapter bluetoothAdapter,
@NonNull CachedBluetoothDevice cachedDevice) {
return new DeviceDetailsFragmentFormatterImpl(
context, fragment, bluetoothAdapter, cachedDevice);
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.settings.bluetooth.ui
package com.android.settings.bluetooth.ui.composable
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background

View File

@@ -0,0 +1,25 @@
/*
* 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.settings.bluetooth.ui.layout
import kotlinx.coroutines.flow.Flow
/** Represent the layout of device settings. */
data class DeviceSettingLayout(val rows: List<DeviceSettingLayoutRow>)
/** Represent a row in the layout. */
data class DeviceSettingLayoutRow(val settingIds: Flow<List<Int>>)

View File

@@ -0,0 +1,225 @@
/*
* 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.settings.bluetooth.ui.view
import android.bluetooth.BluetoothAdapter
import android.content.Context
import android.util.Log
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import com.android.settings.SettingsPreferenceFragment
import com.android.settings.bluetooth.ui.composable.MultiTogglePreferenceGroup
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
import com.android.settings.bluetooth.ui.viewmodel.BluetoothDeviceDetailsViewModel
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settings.spa.preference.ComposePreference
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.preference.Preference as SpaPreference
import com.android.settingslib.spa.widget.preference.PreferenceModel
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 kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
/** Handles device details fragment layout according to config. */
interface DeviceDetailsFragmentFormatter {
/** Gets keys of visible preferences in built-in preference in xml. */
fun getVisiblePreferenceKeysForMainPage(): List<String>?
/** Updates device details fragment layout. */
fun updateLayout()
}
@OptIn(ExperimentalCoroutinesApi::class)
class DeviceDetailsFragmentFormatterImpl(
private val context: Context,
private val fragment: SettingsPreferenceFragment,
bluetoothAdapter: BluetoothAdapter,
private val cachedDevice: CachedBluetoothDevice
) : DeviceDetailsFragmentFormatter {
private val repository =
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
context, bluetoothAdapter, fragment.lifecycleScope)
private val viewModel: BluetoothDeviceDetailsViewModel =
ViewModelProvider(
fragment,
BluetoothDeviceDetailsViewModel.Factory(
repository,
cachedDevice,
))
.get(BluetoothDeviceDetailsViewModel::class.java)
override fun getVisiblePreferenceKeysForMainPage(): List<String>? = runBlocking {
viewModel.getItems()?.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>()?.map {
it.preferenceKey
}
}
/** Updates bluetooth device details fragment layout. */
override fun updateLayout() = runBlocking {
val items = viewModel.getItems() ?: return@runBlocking
val layout = viewModel.getLayout() ?: return@runBlocking
val prefKeyToSettingId =
items
.filterIsInstance<DeviceSettingConfigItemModel.BuiltinItem>()
.associateBy({ it.preferenceKey }, { it.settingId })
val settingIdToXmlPreferences: MutableMap<Int, Preference> = HashMap()
for (i in 0 until fragment.preferenceScreen.preferenceCount) {
val pref = fragment.preferenceScreen.getPreference(i)
prefKeyToSettingId[pref.key]?.let { id -> settingIdToXmlPreferences[id] = pref }
}
fragment.preferenceScreen.removeAll()
for (row in items.indices) {
val settingId = items[row].settingId
if (settingIdToXmlPreferences.containsKey(settingId)) {
fragment.preferenceScreen.addPreference(
settingIdToXmlPreferences[settingId]!!.apply { order = row })
} else {
val pref =
ComposePreference(context)
.apply {
key = getPreferenceKey(settingId)
order = row
}
.also { pref -> pref.setContent { buildPreference(layout, row) } }
fragment.preferenceScreen.addPreference(pref)
}
}
}
@Composable
private fun buildPreference(layout: DeviceSettingLayout, row: Int) {
val contents by
remember(row) {
layout.rows[row].settingIds.flatMapLatest { settingIds ->
if (settingIds.isEmpty()) {
flowOf(emptyList<DeviceSettingModel>())
} else {
combine(
settingIds.map { settingId ->
viewModel.getDeviceSetting(cachedDevice, settingId)
}) {
it.toList()
}
}
}
}
.collectAsStateWithLifecycle(initialValue = listOf())
val settings = contents
when (settings.size) {
0 -> {}
1 -> {
when (val setting = settings[0]) {
is DeviceSettingModel.ActionSwitchPreference -> {
buildActionSwitchPreference(setting)
}
is DeviceSettingModel.MultiTogglePreference -> {
buildMultiTogglePreference(listOf(setting))
}
null -> {}
else -> {
Log.w(TAG, "Unknown preference type ${setting.id}, skip.")
}
}
}
else -> {
if (!settings.all { it is DeviceSettingModel.MultiTogglePreference }) {
return
}
buildMultiTogglePreference(
settings.filterIsInstance<DeviceSettingModel.MultiTogglePreference>())
}
}
}
@Composable
private fun buildMultiTogglePreference(prefs: List<DeviceSettingModel.MultiTogglePreference>) {
MultiTogglePreferenceGroup(prefs)
}
@Composable
private fun buildActionSwitchPreference(model: DeviceSettingModel.ActionSwitchPreference) {
if (model.switchState != null) {
val switchPrefModel =
object : SwitchPreferenceModel {
override val title = model.title
override val summary = { model.summary ?: "" }
override val checked = { model.switchState?.checked }
override val onCheckedChange = { newChecked: Boolean ->
model.updateState?.invoke(
DeviceSettingStateModel.ActionSwitchPreferenceState(newChecked))
Unit
}
override val icon = @Composable { deviceSettingIcon(model) }
}
if (model.intent != null) {
TwoTargetSwitchPreference(switchPrefModel) { context.startActivity(model.intent) }
} else {
SwitchPreference(switchPrefModel)
}
} else {
SpaPreference(
object : PreferenceModel {
override val title = model.title
override val summary = { model.summary ?: "" }
override val onClick = {
model.intent?.let { context.startActivity(it) }
Unit
}
override val icon = @Composable { deviceSettingIcon(model) }
})
}
}
@Composable
private fun deviceSettingIcon(model: DeviceSettingModel.ActionSwitchPreference) {
model.icon?.let { bitmap ->
Icon(
bitmap.asImageBitmap(),
contentDescription = null,
modifier = Modifier.size(SettingsDimension.itemIconSize),
tint = LocalContentColor.current)
}
}
private fun getPreferenceKey(settingId: Int) = "DEVICE_SETTING_${settingId}"
companion object {
const val TAG = "DeviceDetailsFormatter"
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.settings.bluetooth.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow
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.DeviceSettingModel
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
class BluetoothDeviceDetailsViewModel(
private val deviceSettingRepository: DeviceSettingRepository,
private val cachedDevice: CachedBluetoothDevice,
) : ViewModel() {
private val items =
viewModelScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
deviceSettingRepository.getDeviceSettingsConfig(cachedDevice)
}
suspend fun getItems(): List<DeviceSettingConfigItemModel>? = items.await()?.mainItems
fun getDeviceSetting(cachedDevice: CachedBluetoothDevice, @DeviceSettingId settingId: Int) =
deviceSettingRepository.getDeviceSetting(cachedDevice, settingId)
suspend fun getLayout(): DeviceSettingLayout? {
val configItems = getItems() ?: return null
val idToDeviceSetting =
configItems
.filterIsInstance<DeviceSettingConfigItemModel.AppProvidedItem>()
.associateBy({ it.settingId }, { getDeviceSetting(cachedDevice, it.settingId) })
val configDeviceSetting =
configItems.map { idToDeviceSetting[it.settingId] ?: flowOf(null) }
val positionToSettingIds =
combine(configDeviceSetting) { settings ->
val positionMapping = mutableMapOf<Int, List<Int>>()
var multiToggleSettingIds: MutableList<Int>? = null
for (i in settings.indices) {
val configItem = configItems[i]
val setting = settings[i]
val isXmlPreference = configItem is DeviceSettingConfigItemModel.BuiltinItem
if (!isXmlPreference && setting == null) {
continue
}
if (setting !is DeviceSettingModel.MultiTogglePreference) {
multiToggleSettingIds = null
positionMapping[i] = listOf(configItem.settingId)
continue
}
if (multiToggleSettingIds != null) {
multiToggleSettingIds.add(setting.id)
} else {
multiToggleSettingIds = mutableListOf(setting.id)
positionMapping[i] = multiToggleSettingIds
}
}
positionMapping
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = mapOf())
return DeviceSettingLayout(
configItems.indices.map { idx ->
DeviceSettingLayoutRow(positionToSettingIds.map { it[idx] ?: emptyList() })
})
}
class Factory(
private val deviceSettingRepository: DeviceSettingRepository,
private val cachedDevice: CachedBluetoothDevice,
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
return BluetoothDeviceDetailsViewModel(deviceSettingRepository, cachedDevice) as T
}
}
companion object {
private const val TAG = "BluetoothDeviceDetailsViewModel"
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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.settings.bluetooth.utils
import android.bluetooth.BluetoothAdapter
import android.content.Context
import androidx.lifecycle.LifecycleCoroutineScope
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepositoryImpl
import kotlinx.coroutines.Dispatchers
fun createDeviceSettingRepository(
context: Context,
bluetoothAdapter: BluetoothAdapter,
coroutineScope: LifecycleCoroutineScope
) = DeviceSettingRepositoryImpl(context, bluetoothAdapter, coroutineScope, Dispatchers.IO)

View File

@@ -20,6 +20,7 @@ import android.content.Context;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
@@ -61,7 +62,8 @@ public class SlicePreferenceController extends BasePreferenceController implemen
return mUri != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
public void setSliceUri(Uri uri) {
/** Sets Slice uri for the preference. */
public void setSliceUri(@Nullable Uri uri) {
mUri = uri;
mLiveData = SliceLiveData.fromUri(mContext, mUri, (int type, Throwable source) -> {
Log.w(TAG, "Slice may be null. uri = " + uri + ", error = " + type);