Snap for 12348750 from 1c756ec809
to 24Q4-release
Change-Id: I8dec62d80cea41f1b27722e8b5523456cacfb93b
This commit is contained in:
@@ -111,7 +111,10 @@ android_library {
|
|||||||
"keyboard_flags_lib",
|
"keyboard_flags_lib",
|
||||||
],
|
],
|
||||||
|
|
||||||
plugins: ["androidx.room_room-compiler-plugin"],
|
plugins: [
|
||||||
|
"SettingsLibMetadata-processor",
|
||||||
|
"androidx.room_room-compiler-plugin",
|
||||||
|
],
|
||||||
|
|
||||||
errorprone: {
|
errorprone: {
|
||||||
extra_check_modules: ["//external/nullaway:nullaway_plugin"],
|
extra_check_modules: ["//external/nullaway:nullaway_plugin"],
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings;
|
package com.android.settings;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.settingsCatalyst;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
@@ -42,13 +44,20 @@ import com.android.settings.overlay.FeatureFactoryImpl;
|
|||||||
import com.android.settings.spa.SettingsSpaEnvironment;
|
import com.android.settings.spa.SettingsSpaEnvironment;
|
||||||
import com.android.settingslib.applications.AppIconCacheManager;
|
import com.android.settingslib.applications.AppIconCacheManager;
|
||||||
import com.android.settingslib.datastore.BackupRestoreStorageManager;
|
import com.android.settingslib.datastore.BackupRestoreStorageManager;
|
||||||
|
import com.android.settingslib.metadata.PreferenceScreenMetadata;
|
||||||
|
import com.android.settingslib.metadata.PreferenceScreenRegistry;
|
||||||
|
import com.android.settingslib.metadata.ProvidePreferenceScreenOptions;
|
||||||
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory;
|
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory;
|
||||||
|
|
||||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/** Settings application which sets up activity embedding rules for the large screen device. */
|
/** Settings application which sets up activity embedding rules for the large screen device. */
|
||||||
|
@ProvidePreferenceScreenOptions(
|
||||||
|
codegenCollector = "com.android.settings/PreferenceScreenCollector/get"
|
||||||
|
)
|
||||||
public class SettingsApplication extends Application {
|
public class SettingsApplication extends Application {
|
||||||
|
|
||||||
private WeakReference<SettingsHomepageActivity> mHomeActivity = new WeakReference<>(null);
|
private WeakReference<SettingsHomepageActivity> mHomeActivity = new WeakReference<>(null);
|
||||||
@@ -64,6 +73,11 @@ public class SettingsApplication extends Application {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
|
if (settingsCatalyst()) {
|
||||||
|
PreferenceScreenRegistry.INSTANCE.setPreferenceScreensSupplier(
|
||||||
|
this::getPreferenceScreens);
|
||||||
|
}
|
||||||
|
|
||||||
BackupRestoreStorageManager.getInstance(this)
|
BackupRestoreStorageManager.getInstance(this)
|
||||||
.add(
|
.add(
|
||||||
new BatterySettingsStorage(this),
|
new BatterySettingsStorage(this),
|
||||||
@@ -90,6 +104,13 @@ public class SettingsApplication extends Application {
|
|||||||
registerActivityLifecycleCallbacks(new DeveloperOptionsActivityLifecycle());
|
registerActivityLifecycleCallbacks(new DeveloperOptionsActivityLifecycle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the screens using metadata. */
|
||||||
|
protected List<PreferenceScreenMetadata> getPreferenceScreens() {
|
||||||
|
// PreferenceScreenCollector is generated by annotation processor from classes annotated
|
||||||
|
// with @ProvidePreferenceScreen
|
||||||
|
return PreferenceScreenCollector.get(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTerminate() {
|
public void onTerminate() {
|
||||||
BackupRestoreStorageManager.getInstance(this).removeAll();
|
BackupRestoreStorageManager.getInstance(this).removeAll();
|
||||||
|
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* 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.connecteddevice.audiosharing;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||||
|
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||||
|
|
||||||
|
public class AudioSharingRetryDialogFragment extends InstrumentedDialogFragment {
|
||||||
|
private static final String TAG = "AudioSharingRetryDialog";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMetricsCategory() {
|
||||||
|
// TODO: add metrics
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the {@link AudioSharingRetryDialogFragment} dialog.
|
||||||
|
*
|
||||||
|
* @param host The Fragment this dialog will be hosted.
|
||||||
|
*/
|
||||||
|
public static void show(@Nullable Fragment host) {
|
||||||
|
if (host == null || !BluetoothUtils.isAudioSharingEnabled()) return;
|
||||||
|
final FragmentManager manager;
|
||||||
|
try {
|
||||||
|
manager = host.getChildFragmentManager();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
Log.d(TAG, "Fail to show dialog: " + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AlertDialog dialog = AudioSharingDialogHelper.getDialogIfShowing(manager, TAG);
|
||||||
|
if (dialog != null) {
|
||||||
|
Log.d(TAG, "Dialog is showing, return.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Show up the retry dialog.");
|
||||||
|
AudioSharingRetryDialogFragment dialogFrag = new AudioSharingRetryDialogFragment();
|
||||||
|
dialogFrag.show(manager, TAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||||
|
// TODO: put strings to res till they are finalized
|
||||||
|
AlertDialog dialog =
|
||||||
|
AudioSharingDialogFactory.newBuilder(getActivity())
|
||||||
|
.setTitle("Couldn't share audio")
|
||||||
|
.setTitleIcon(com.android.settings.R.drawable.ic_warning_24dp)
|
||||||
|
.setIsCustomBodyEnabled(true)
|
||||||
|
.setCustomMessage("Something went wrong. Please try again.")
|
||||||
|
.setPositiveButton(com.android.settings.R.string.okay, (d, w) -> {
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
dialog.setCanceledOnTouchOutside(true);
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
}
|
@@ -25,7 +25,6 @@ import android.bluetooth.BluetoothLeBroadcast;
|
|||||||
import android.bluetooth.BluetoothLeBroadcastAssistant;
|
import android.bluetooth.BluetoothLeBroadcastAssistant;
|
||||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||||
import android.bluetooth.BluetoothProfile;
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -379,7 +378,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
if (FeatureFlagUtils.isEnabled(
|
if (FeatureFlagUtils.isEnabled(
|
||||||
mContext,
|
mContext,
|
||||||
FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST)
|
FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST)
|
||||||
&& mAssistant.getAllConnectedDevices().isEmpty()) {
|
&& hasEmptyConnectedSink()) {
|
||||||
// Pop up dialog to ask users to connect at least one lea buds before audio sharing.
|
// Pop up dialog to ask users to connect at least one lea buds before audio sharing.
|
||||||
AudioSharingUtils.postOnMainThread(
|
AudioSharingUtils.postOnMainThread(
|
||||||
mContext,
|
mContext,
|
||||||
@@ -435,8 +434,12 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
|
public void onActiveDeviceChanged(@Nullable CachedBluetoothDevice activeDevice,
|
||||||
if (activeDevice != null && bluetoothProfile == BluetoothProfile.LE_AUDIO) {
|
int bluetoothProfile) {
|
||||||
|
if (activeDevice != null) {
|
||||||
|
Log.d(TAG, "onActiveDeviceChanged: device = "
|
||||||
|
+ activeDevice.getDevice().getAnonymizedAddress()
|
||||||
|
+ ", profile = " + bluetoothProfile);
|
||||||
updateSwitch();
|
updateSwitch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -536,13 +539,19 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
boolean isBroadcasting = BluetoothUtils.isBroadcasting(mBtManager);
|
boolean isBroadcasting = BluetoothUtils.isBroadcasting(mBtManager);
|
||||||
boolean hasActiveDevice =
|
boolean hasActiveDevice =
|
||||||
AudioSharingUtils.hasActiveConnectedLeadDevice(mBtManager);
|
AudioSharingUtils.hasActiveConnectedLeadDevice(mBtManager);
|
||||||
|
boolean hasEmptyConnectedDevice = hasEmptyConnectedSink();
|
||||||
boolean isStateReady =
|
boolean isStateReady =
|
||||||
isBluetoothOn()
|
isBluetoothOn()
|
||||||
&& AudioSharingUtils.isAudioSharingProfileReady(
|
&& AudioSharingUtils.isAudioSharingProfileReady(
|
||||||
mProfileManager)
|
mProfileManager)
|
||||||
|
&& (isBroadcasting
|
||||||
|
// Always enable toggle when no connected sink. We have
|
||||||
|
// dialog to guide users to connect compatible devices
|
||||||
|
// for audio sharing.
|
||||||
|
|| hasEmptyConnectedDevice
|
||||||
// Disable toggle till device gets active after
|
// Disable toggle till device gets active after
|
||||||
// broadcast ends.
|
// broadcast ends.
|
||||||
&& (isBroadcasting || hasActiveDevice);
|
|| hasActiveDevice);
|
||||||
AudioSharingUtils.postOnMainThread(
|
AudioSharingUtils.postOnMainThread(
|
||||||
mContext,
|
mContext,
|
||||||
() -> {
|
() -> {
|
||||||
@@ -566,6 +575,10 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
|
|||||||
return mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
|
return mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasEmptyConnectedSink() {
|
||||||
|
return mAssistant != null && mAssistant.getAllConnectedDevices().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
private void handleOnBroadcastReady() {
|
private void handleOnBroadcastReady() {
|
||||||
Pair<Integer, Object>[] eventData =
|
Pair<Integer, Object>[] eventData =
|
||||||
AudioSharingUtils.buildAudioSharingDialogEventData(
|
AudioSharingUtils.buildAudioSharingDialogEventData(
|
||||||
|
@@ -215,16 +215,13 @@ public class AudioSharingUtils {
|
|||||||
@Nullable LocalBluetoothManager localBtManager) {
|
@Nullable LocalBluetoothManager localBtManager) {
|
||||||
CachedBluetoothDeviceManager deviceManager =
|
CachedBluetoothDeviceManager deviceManager =
|
||||||
localBtManager == null ? null : localBtManager.getCachedDeviceManager();
|
localBtManager == null ? null : localBtManager.getCachedDeviceManager();
|
||||||
Map<Integer, List<BluetoothDevice>> groupedConnectedDevices =
|
if (deviceManager == null) {
|
||||||
fetchConnectedDevicesByGroupId(localBtManager);
|
Log.d(TAG, "hasActiveConnectedLeadDevice return false due to null device manager.");
|
||||||
for (List<BluetoothDevice> devices : groupedConnectedDevices.values()) {
|
|
||||||
CachedBluetoothDevice leadDevice = getLeadDevice(deviceManager, devices);
|
|
||||||
if (isActiveLeAudioDevice(leadDevice)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return deviceManager.getCachedDevicesCopy().stream().anyMatch(
|
||||||
|
BluetoothUtils::isActiveMediaDevice);
|
||||||
|
}
|
||||||
|
|
||||||
/** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
|
/** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
|
||||||
public static AudioSharingDeviceItem buildAudioSharingDeviceItem(
|
public static AudioSharingDeviceItem buildAudioSharingDeviceItem(
|
||||||
|
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.settings.dashboard;
|
package com.android.settings.dashboard;
|
||||||
|
|
||||||
|
import static com.android.settingslib.flags.Flags.settingsCatalyst;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
@@ -53,6 +55,7 @@ import com.android.settingslib.core.AbstractPreferenceController;
|
|||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
import com.android.settingslib.drawer.DashboardCategory;
|
import com.android.settingslib.drawer.DashboardCategory;
|
||||||
import com.android.settingslib.drawer.Tile;
|
import com.android.settingslib.drawer.Tile;
|
||||||
|
import com.android.settingslib.metadata.PreferenceScreenRegistry;
|
||||||
import com.android.settingslib.search.Indexable;
|
import com.android.settingslib.search.Indexable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -97,13 +100,17 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
|
|||||||
R.array.config_suppress_injected_tile_keys));
|
R.array.config_suppress_injected_tile_keys));
|
||||||
mDashboardFeatureProvider =
|
mDashboardFeatureProvider =
|
||||||
FeatureFactory.getFeatureFactory().getDashboardFeatureProvider();
|
FeatureFactory.getFeatureFactory().getDashboardFeatureProvider();
|
||||||
|
|
||||||
|
if (!usePreferenceScreenMetadata() || PreferenceScreenRegistry.INSTANCE.get(
|
||||||
|
getPreferenceScreenBindingKey(context)) == null) {
|
||||||
// Load preference controllers from code
|
// Load preference controllers from code
|
||||||
final List<AbstractPreferenceController> controllersFromCode =
|
final List<AbstractPreferenceController> controllersFromCode =
|
||||||
createPreferenceControllers(context);
|
createPreferenceControllers(context);
|
||||||
// Load preference controllers from xml definition
|
// Load preference controllers from xml definition
|
||||||
final List<BasePreferenceController> controllersFromXml = PreferenceControllerListHelper
|
final List<BasePreferenceController> controllersFromXml = PreferenceControllerListHelper
|
||||||
.getPreferenceControllersFromXml(context, getPreferenceScreenResId());
|
.getPreferenceControllersFromXml(context, getPreferenceScreenResId());
|
||||||
// Filter xml-based controllers in case a similar controller is created from code already.
|
// Filter xml-based controllers in case a similar controller is created from code
|
||||||
|
// already.
|
||||||
final List<BasePreferenceController> uniqueControllerFromXml =
|
final List<BasePreferenceController> uniqueControllerFromXml =
|
||||||
PreferenceControllerListHelper.filterControllers(
|
PreferenceControllerListHelper.filterControllers(
|
||||||
controllersFromXml, controllersFromCode);
|
controllersFromXml, controllersFromCode);
|
||||||
@@ -121,6 +128,7 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
|
|||||||
lifecycle.addObserver((LifecycleObserver) controller);
|
lifecycle.addObserver((LifecycleObserver) controller);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Set metrics category for BasePreferenceController.
|
// Set metrics category for BasePreferenceController.
|
||||||
final int metricCategory = getMetricsCategory();
|
final int metricCategory = getMetricsCategory();
|
||||||
@@ -272,6 +280,11 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final int getPreferenceScreenResId(@NonNull Context context) {
|
||||||
|
return getPreferenceScreenResId();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected abstract int getPreferenceScreenResId();
|
protected abstract int getPreferenceScreenResId();
|
||||||
|
|
||||||
@@ -364,12 +377,32 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
|
|||||||
if (resId <= 0) {
|
if (resId <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
PreferenceScreen screen;
|
||||||
|
if (usePreferenceScreenMetadata()) {
|
||||||
|
screen = createPreferenceScreen();
|
||||||
|
setPreferenceScreen(screen);
|
||||||
|
requireActivity().setTitle(screen.getTitle());
|
||||||
|
} else {
|
||||||
addPreferencesFromResource(resId);
|
addPreferencesFromResource(resId);
|
||||||
final PreferenceScreen screen = getPreferenceScreen();
|
screen = getPreferenceScreen();
|
||||||
|
}
|
||||||
screen.setOnExpandButtonClickListener(this);
|
screen.setOnExpandButtonClickListener(this);
|
||||||
displayResourceTilesToScreen(screen);
|
displayResourceTilesToScreen(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final boolean usePreferenceScreenMetadata() {
|
||||||
|
return settingsCatalyst() && enableCatalyst();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if settings catalyst should be enabled (e.g. check trunk stable flag) on current
|
||||||
|
* screen.
|
||||||
|
*/
|
||||||
|
protected boolean enableCatalyst() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform {@link AbstractPreferenceController#displayPreference(PreferenceScreen)}
|
* Perform {@link AbstractPreferenceController#displayPreference(PreferenceScreen)}
|
||||||
* on all {@link AbstractPreferenceController}s.
|
* on all {@link AbstractPreferenceController}s.
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
package com.android.settings.network.telephony
|
package com.android.settings.network.telephony
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
import android.os.PersistableBundle
|
import android.os.PersistableBundle
|
||||||
import android.telephony.CarrierConfigManager
|
import android.telephony.CarrierConfigManager
|
||||||
import android.telephony.SubscriptionManager
|
import android.telephony.SubscriptionManager
|
||||||
@@ -35,7 +36,8 @@ class CarrierConfigRepository(private val context: Context) {
|
|||||||
private enum class KeyType {
|
private enum class KeyType {
|
||||||
BOOLEAN,
|
BOOLEAN,
|
||||||
INT,
|
INT,
|
||||||
STRING
|
INT_ARRAY,
|
||||||
|
STRING,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CarrierConfigAccessor {
|
interface CarrierConfigAccessor {
|
||||||
@@ -43,17 +45,20 @@ class CarrierConfigRepository(private val context: Context) {
|
|||||||
|
|
||||||
fun getInt(key: String): Int
|
fun getInt(key: String): Int
|
||||||
|
|
||||||
|
fun getIntArray(key: String): IntArray?
|
||||||
|
|
||||||
fun getString(key: String): String?
|
fun getString(key: String): String?
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Accessor(private val cache: ConfigCache) : CarrierConfigAccessor {
|
private class Accessor(private val cache: ConfigCache) : CarrierConfigAccessor {
|
||||||
private val keysToRetrieve = mutableMapOf<String, KeyType>()
|
private val keysToRetrieve = mutableMapOf<String, KeyType>()
|
||||||
|
private var isKeysToRetrieveFrozen = false
|
||||||
|
|
||||||
override fun getBoolean(key: String): Boolean {
|
override fun getBoolean(key: String): Boolean {
|
||||||
checkBooleanKey(key)
|
checkBooleanKey(key)
|
||||||
val value = cache[key]
|
val value = cache[key]
|
||||||
return if (value == null) {
|
return if (value == null) {
|
||||||
keysToRetrieve += key to KeyType.BOOLEAN
|
addKeyToRetrieve(key, KeyType.BOOLEAN)
|
||||||
DefaultConfig.getBoolean(key)
|
DefaultConfig.getBoolean(key)
|
||||||
} else {
|
} else {
|
||||||
check(value is BooleanConfigValue) { "Boolean value type wrong" }
|
check(value is BooleanConfigValue) { "Boolean value type wrong" }
|
||||||
@@ -65,7 +70,7 @@ class CarrierConfigRepository(private val context: Context) {
|
|||||||
check(key.endsWith("_int")) { "Int key should ends with _int" }
|
check(key.endsWith("_int")) { "Int key should ends with _int" }
|
||||||
val value = cache[key]
|
val value = cache[key]
|
||||||
return if (value == null) {
|
return if (value == null) {
|
||||||
keysToRetrieve += key to KeyType.INT
|
addKeyToRetrieve(key, KeyType.INT)
|
||||||
DefaultConfig.getInt(key)
|
DefaultConfig.getInt(key)
|
||||||
} else {
|
} else {
|
||||||
check(value is IntConfigValue) { "Int value type wrong" }
|
check(value is IntConfigValue) { "Int value type wrong" }
|
||||||
@@ -73,11 +78,23 @@ class CarrierConfigRepository(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getIntArray(key: String): IntArray? {
|
||||||
|
checkIntArrayKey(key)
|
||||||
|
val value = cache[key]
|
||||||
|
return if (value == null) {
|
||||||
|
addKeyToRetrieve(key, KeyType.INT_ARRAY)
|
||||||
|
DefaultConfig.getIntArray(key)
|
||||||
|
} else {
|
||||||
|
check(value is IntArrayConfigValue) { "Int array value type wrong" }
|
||||||
|
value.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getString(key: String): String? {
|
override fun getString(key: String): String? {
|
||||||
check(key.endsWith("_string")) { "String key should ends with _string" }
|
check(key.endsWith("_string")) { "String key should ends with _string" }
|
||||||
val value = cache[key]
|
val value = cache[key]
|
||||||
return if (value == null) {
|
return if (value == null) {
|
||||||
keysToRetrieve += key to KeyType.STRING
|
addKeyToRetrieve(key, KeyType.STRING)
|
||||||
DefaultConfig.getString(key)
|
DefaultConfig.getString(key)
|
||||||
} else {
|
} else {
|
||||||
check(value is StringConfigValue) { "String value type wrong" }
|
check(value is StringConfigValue) { "String value type wrong" }
|
||||||
@@ -85,20 +102,35 @@ class CarrierConfigRepository(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKeysToRetrieve(): Map<String, KeyType> = keysToRetrieve
|
private fun addKeyToRetrieve(key: String, type: KeyType) {
|
||||||
|
if (keysToRetrieve.put(key, type) == null && Build.IS_DEBUGGABLE) {
|
||||||
|
check(!isKeysToRetrieveFrozen) { "implement error for key $key" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the keys to retrieve.
|
||||||
|
*
|
||||||
|
* After this function is called, the keys to retrieve is frozen.
|
||||||
|
*/
|
||||||
|
fun getAndFrozeKeysToRetrieve(): Map<String, KeyType> {
|
||||||
|
isKeysToRetrieveFrozen = true
|
||||||
|
return keysToRetrieve
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the configuration values for the given [subId].
|
* Gets the configuration values for the given [subId].
|
||||||
*
|
*
|
||||||
* Configuration values could be accessed in [block]. Note: [block] could be called multiple
|
* Configuration values could be accessed in [block]. Note: [block] could be called multiple
|
||||||
* times, so it should be pure function without side effort.
|
* times, so it should be pure function without side effort. Please also make sure every key is
|
||||||
|
* retrieved every time, for example, we need avoid expression shortcut.
|
||||||
*/
|
*/
|
||||||
fun <T> transformConfig(subId: Int, block: CarrierConfigAccessor.() -> T): T {
|
fun <T> transformConfig(subId: Int, block: CarrierConfigAccessor.() -> T): T {
|
||||||
val perSubCache = getPerSubCache(subId)
|
val perSubCache = getPerSubCache(subId)
|
||||||
val accessor = Accessor(perSubCache)
|
val accessor = Accessor(perSubCache)
|
||||||
val result = accessor.block()
|
val result = accessor.block()
|
||||||
val keysToRetrieve = accessor.getKeysToRetrieve()
|
val keysToRetrieve = accessor.getAndFrozeKeysToRetrieve()
|
||||||
// If all keys found in the first pass, no need to collect again
|
// If all keys found in the first pass, no need to collect again
|
||||||
if (keysToRetrieve.isEmpty()) return result
|
if (keysToRetrieve.isEmpty()) return result
|
||||||
|
|
||||||
@@ -113,6 +145,10 @@ class CarrierConfigRepository(private val context: Context) {
|
|||||||
/** Gets the configuration int for the given [subId] and [key]. */
|
/** Gets the configuration int for the given [subId] and [key]. */
|
||||||
fun getInt(subId: Int, key: String): Int = transformConfig(subId) { getInt(key) }
|
fun getInt(subId: Int, key: String): Int = transformConfig(subId) { getInt(key) }
|
||||||
|
|
||||||
|
/** Gets the configuration int array for the given [subId] and [key]. */
|
||||||
|
fun getIntArray(subId: Int, key: String): IntArray? =
|
||||||
|
transformConfig(subId) { getIntArray(key) }
|
||||||
|
|
||||||
/** Gets the configuration string for the given [subId] and [key]. */
|
/** Gets the configuration string for the given [subId] and [key]. */
|
||||||
fun getString(subId: Int, key: String): String? = transformConfig(subId) { getString(key) }
|
fun getString(subId: Int, key: String): String? = transformConfig(subId) { getString(key) }
|
||||||
|
|
||||||
@@ -122,6 +158,7 @@ class CarrierConfigRepository(private val context: Context) {
|
|||||||
when (type) {
|
when (type) {
|
||||||
KeyType.BOOLEAN -> this[key] = BooleanConfigValue(config.getBoolean(key))
|
KeyType.BOOLEAN -> this[key] = BooleanConfigValue(config.getBoolean(key))
|
||||||
KeyType.INT -> this[key] = IntConfigValue(config.getInt(key))
|
KeyType.INT -> this[key] = IntConfigValue(config.getInt(key))
|
||||||
|
KeyType.INT_ARRAY -> this[key] = IntArrayConfigValue(config.getIntArray(key))
|
||||||
KeyType.STRING -> this[key] = StringConfigValue(config.getString(key))
|
KeyType.STRING -> this[key] = StringConfigValue(config.getString(key))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,6 +232,10 @@ class CarrierConfigRepository(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkIntArrayKey(key: String) {
|
||||||
|
check(key.endsWith("_int_array")) { "Int array key should ends with _int_array" }
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
fun setBooleanForTest(subId: Int, key: String, value: Boolean) {
|
fun setBooleanForTest(subId: Int, key: String, value: Boolean) {
|
||||||
checkBooleanKey(key)
|
checkBooleanKey(key)
|
||||||
@@ -207,6 +248,12 @@ class CarrierConfigRepository(private val context: Context) {
|
|||||||
getPerSubCache(subId)[key] = IntConfigValue(value)
|
getPerSubCache(subId)[key] = IntConfigValue(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun setIntArrayForTest(subId: Int, key: String, value: IntArray?) {
|
||||||
|
checkIntArrayKey(key)
|
||||||
|
getPerSubCache(subId)[key] = IntArrayConfigValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
fun setStringForTest(subId: Int, key: String, value: String?) {
|
fun setStringForTest(subId: Int, key: String, value: String?) {
|
||||||
check(key.endsWith("_string")) { "String key should ends with _string" }
|
check(key.endsWith("_string")) { "String key should ends with _string" }
|
||||||
@@ -221,6 +268,8 @@ private data class BooleanConfigValue(val value: Boolean) : ConfigValue
|
|||||||
|
|
||||||
private data class IntConfigValue(val value: Int) : ConfigValue
|
private data class IntConfigValue(val value: Int) : ConfigValue
|
||||||
|
|
||||||
|
private class IntArrayConfigValue(val value: IntArray?) : ConfigValue
|
||||||
|
|
||||||
private data class StringConfigValue(val value: String?) : ConfigValue
|
private data class StringConfigValue(val value: String?) : ConfigValue
|
||||||
|
|
||||||
private typealias ConfigCache = ConcurrentHashMap<String, ConfigValue>
|
private typealias ConfigCache = ConcurrentHashMap<String, ConfigValue>
|
||||||
|
@@ -34,7 +34,6 @@ import androidx.preference.PreferenceScreen;
|
|||||||
import androidx.preference.TwoStatePreference;
|
import androidx.preference.TwoStatePreference;
|
||||||
|
|
||||||
import com.android.internal.telephony.flags.Flags;
|
import com.android.internal.telephony.flags.Flags;
|
||||||
import com.android.internal.telephony.util.ArrayUtils;
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.network.ims.VolteQueryImsState;
|
import com.android.settings.network.ims.VolteQueryImsState;
|
||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||||
@@ -56,8 +55,6 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc
|
|||||||
Preference mPreference;
|
Preference mPreference;
|
||||||
private PhoneCallStateTelephonyCallback mTelephonyCallback;
|
private PhoneCallStateTelephonyCallback mTelephonyCallback;
|
||||||
private boolean mShow5gLimitedDialog;
|
private boolean mShow5gLimitedDialog;
|
||||||
boolean mIsNrEnabledFromCarrierConfig;
|
|
||||||
private boolean mHas5gCapability;
|
|
||||||
private Integer mCallState;
|
private Integer mCallState;
|
||||||
private final List<On4gLteUpdateListener> m4gLteListeners;
|
private final List<On4gLteUpdateListener> m4gLteListeners;
|
||||||
|
|
||||||
@@ -94,9 +91,6 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc
|
|||||||
mShow5gLimitedDialog = carrierConfig.getBoolean(
|
mShow5gLimitedDialog = carrierConfig.getBoolean(
|
||||||
CarrierConfigManager.KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL);
|
CarrierConfigManager.KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL);
|
||||||
|
|
||||||
int[] nrAvailabilities = carrierConfig.getIntArray(
|
|
||||||
CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
|
|
||||||
mIsNrEnabledFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,10 +241,6 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc
|
|||||||
}
|
}
|
||||||
mTelephonyManager.registerTelephonyCallback(
|
mTelephonyManager.registerTelephonyCallback(
|
||||||
mContext.getMainExecutor(), mTelephonyCallback);
|
mContext.getMainExecutor(), mTelephonyCallback);
|
||||||
|
|
||||||
final long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily();
|
|
||||||
mHas5gCapability =
|
|
||||||
(supportedRadioBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregister() {
|
public void unregister() {
|
||||||
@@ -269,8 +259,7 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDialogNeeded() {
|
private boolean isDialogNeeded() {
|
||||||
Log.d(TAG, "Has5gCapability:" + mHas5gCapability);
|
return mShow5gLimitedDialog && new NrRepository(mContext).isNrAvailable(mSubId);
|
||||||
return mShow5gLimitedDialog && mHas5gCapability && mIsNrEnabledFromCarrierConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void show5gLimitedDialog(ImsMmTelManager imsMmTelManager) {
|
private void show5gLimitedDialog(ImsMmTelManager imsMmTelManager) {
|
||||||
|
@@ -25,31 +25,28 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
|
|
||||||
import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
|
import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem
|
||||||
|
import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult
|
||||||
import com.android.settings.spa.preference.ComposePreferenceController
|
import com.android.settings.spa.preference.ComposePreferenceController
|
||||||
import com.android.settingslib.spa.widget.preference.SwitchPreference
|
import com.android.settingslib.spa.widget.preference.SwitchPreference
|
||||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||||
import kotlinx.coroutines.flow.flowOf
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/** Preference controller for "Voice over NR". */
|
||||||
* Preference controller for "Voice over NR".
|
class NrAdvancedCallingPreferenceController
|
||||||
*/
|
@JvmOverloads
|
||||||
class NrAdvancedCallingPreferenceController @JvmOverloads constructor(
|
constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
key: String,
|
key: String,
|
||||||
|
private val voNrRepository: VoNrRepository = VoNrRepository(context),
|
||||||
private val callStateRepository: CallStateRepository = CallStateRepository(context),
|
private val callStateRepository: CallStateRepository = CallStateRepository(context),
|
||||||
) : ComposePreferenceController(context, key) {
|
) : ComposePreferenceController(context, key) {
|
||||||
private var subId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
private var subId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||||
private var repository: VoNrRepository? = null
|
|
||||||
private val searchItem = NrAdvancedCallingSearchItem(context)
|
private val searchItem = NrAdvancedCallingSearchItem(context)
|
||||||
|
|
||||||
/** Initial this PreferenceController. */
|
/** Initial this PreferenceController. */
|
||||||
@JvmOverloads
|
fun init(subId: Int) {
|
||||||
fun init(subId: Int, repository: VoNrRepository = VoNrRepository(mContext, subId)) {
|
|
||||||
this.subId = subId
|
this.subId = subId
|
||||||
this.repository = repository
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAvailabilityStatus() =
|
override fun getAvailabilityStatus() =
|
||||||
@@ -58,30 +55,32 @@ class NrAdvancedCallingPreferenceController @JvmOverloads constructor(
|
|||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val summary = stringResource(R.string.nr_advanced_calling_summary)
|
val summary = stringResource(R.string.nr_advanced_calling_summary)
|
||||||
val isInCall by remember { callStateRepository.isInCallFlow() }
|
val isInCall by
|
||||||
|
remember { callStateRepository.isInCallFlow() }
|
||||||
|
.collectAsStateWithLifecycle(initialValue = false)
|
||||||
|
val isVoNrEnabled by
|
||||||
|
remember { voNrRepository.isVoNrEnabledFlow(subId) }
|
||||||
.collectAsStateWithLifecycle(initialValue = false)
|
.collectAsStateWithLifecycle(initialValue = false)
|
||||||
val isEnabled by remember {
|
|
||||||
repository?.isVoNrEnabledFlow() ?: flowOf(false)
|
|
||||||
}.collectAsStateWithLifecycle(initialValue = false)
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
SwitchPreference(object : SwitchPreferenceModel {
|
SwitchPreference(
|
||||||
|
object : SwitchPreferenceModel {
|
||||||
override val title = stringResource(R.string.nr_advanced_calling_title)
|
override val title = stringResource(R.string.nr_advanced_calling_title)
|
||||||
override val summary = { summary }
|
override val summary = { summary }
|
||||||
override val changeable = { !isInCall }
|
override val changeable = { !isInCall }
|
||||||
override val checked = { isEnabled }
|
override val checked = { isVoNrEnabled }
|
||||||
override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
|
override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch { voNrRepository.setVoNrEnabled(subId, newChecked) }
|
||||||
repository?.setVoNrEnabled(newChecked)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
class NrAdvancedCallingSearchItem(private val context: Context) :
|
class NrAdvancedCallingSearchItem(private val context: Context) :
|
||||||
MobileNetworkSettingsSearchItem {
|
MobileNetworkSettingsSearchItem {
|
||||||
|
private val voNrRepository = VoNrRepository(context)
|
||||||
|
|
||||||
fun isAvailable(subId: Int): Boolean = VoNrRepository(context, subId).isVoNrAvailable()
|
fun isAvailable(subId: Int): Boolean = voNrRepository.isVoNrAvailable(subId)
|
||||||
|
|
||||||
override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
|
override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? {
|
||||||
if (!isAvailable(subId)) return null
|
if (!isAvailable(subId)) return null
|
||||||
|
50
src/com/android/settings/network/telephony/NrRepository.kt
Normal file
50
src/com/android/settings/network/telephony/NrRepository.kt
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.telephony.CarrierConfigManager
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
class NrRepository(private val context: Context) {
|
||||||
|
private val carrierConfigRepository = CarrierConfigRepository(context)
|
||||||
|
|
||||||
|
fun isNrAvailable(subId: Int): Boolean {
|
||||||
|
if (!SubscriptionManager.isValidSubscriptionId(subId) || !has5gCapability(subId)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val carrierNrAvailabilities =
|
||||||
|
carrierConfigRepository.getIntArray(
|
||||||
|
subId = subId,
|
||||||
|
key = CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
|
||||||
|
)
|
||||||
|
return carrierNrAvailabilities?.isNotEmpty() ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun has5gCapability(subId: Int): Boolean {
|
||||||
|
val telephonyManager = context.telephonyManager(subId)
|
||||||
|
return (telephonyManager.supportedRadioAccessFamily and
|
||||||
|
TelephonyManager.NETWORK_TYPE_BITMASK_NR > 0)
|
||||||
|
.also { Log.d(TAG, "[$subId] has5gCapability: $it") }
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
private const val TAG = "NrRepository"
|
||||||
|
}
|
||||||
|
}
|
@@ -19,7 +19,6 @@ package com.android.settings.network.telephony
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.telephony.CarrierConfigManager
|
import android.telephony.CarrierConfigManager
|
||||||
import android.telephony.SubscriptionManager
|
import android.telephony.SubscriptionManager
|
||||||
import android.telephony.TelephonyManager
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@@ -29,40 +28,40 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class VoNrRepository(private val context: Context, private val subId: Int) {
|
class VoNrRepository(
|
||||||
private val telephonyManager = context.telephonyManager(subId)
|
private val context: Context,
|
||||||
private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!!
|
private val nrRepository: NrRepository = NrRepository(context),
|
||||||
|
) {
|
||||||
|
private val carrierConfigRepository = CarrierConfigRepository(context)
|
||||||
|
|
||||||
fun isVoNrAvailable(): Boolean {
|
fun isVoNrAvailable(subId: Int): Boolean {
|
||||||
if (!SubscriptionManager.isValidSubscriptionId(subId) || !has5gCapability()) return false
|
if (!nrRepository.isNrAvailable(subId)) return false
|
||||||
val carrierConfig = carrierConfigManager.safeGetConfig(
|
data class Config(val isVoNrEnabled: Boolean, val isVoNrSettingVisibility: Boolean)
|
||||||
keys = listOf(
|
val carrierConfig =
|
||||||
CarrierConfigManager.KEY_VONR_ENABLED_BOOL,
|
carrierConfigRepository.transformConfig(subId) {
|
||||||
CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL,
|
Config(
|
||||||
CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
|
isVoNrEnabled = getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL),
|
||||||
),
|
isVoNrSettingVisibility =
|
||||||
subId = subId,
|
getBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL),
|
||||||
)
|
)
|
||||||
return carrierConfig.getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL) &&
|
}
|
||||||
carrierConfig.getBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL) &&
|
return carrierConfig.isVoNrEnabled && carrierConfig.isVoNrSettingVisibility
|
||||||
(carrierConfig.getIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY)
|
|
||||||
?.isNotEmpty() ?: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun has5gCapability() =
|
fun isVoNrEnabledFlow(subId: Int): Flow<Boolean> {
|
||||||
((telephonyManager.supportedRadioAccessFamily and
|
val telephonyManager = context.telephonyManager(subId)
|
||||||
TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0)
|
return context
|
||||||
.also { Log.d(TAG, "[$subId] has5gCapability: $it") }
|
.subscriptionsChangedFlow()
|
||||||
|
|
||||||
fun isVoNrEnabledFlow(): Flow<Boolean> = context.subscriptionsChangedFlow()
|
|
||||||
.map { telephonyManager.isVoNrEnabled }
|
.map { telephonyManager.isVoNrEnabled }
|
||||||
.conflate()
|
.conflate()
|
||||||
.onEach { Log.d(TAG, "[$subId] isVoNrEnabled: $it") }
|
.onEach { Log.d(TAG, "[$subId] isVoNrEnabled: $it") }
|
||||||
.flowOn(Dispatchers.Default)
|
.flowOn(Dispatchers.Default)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun setVoNrEnabled(enabled: Boolean) = withContext(Dispatchers.Default) {
|
suspend fun setVoNrEnabled(subId: Int, enabled: Boolean) =
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
if (!SubscriptionManager.isValidSubscriptionId(subId)) return@withContext
|
if (!SubscriptionManager.isValidSubscriptionId(subId)) return@withContext
|
||||||
val result = telephonyManager.setVoNrEnabled(enabled)
|
val result = context.telephonyManager(subId).setVoNrEnabled(enabled)
|
||||||
Log.d(TAG, "[$subId] setVoNrEnabled: $enabled, result: $result")
|
Log.d(TAG, "[$subId] setVoNrEnabled: $enabled, result: $result")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,6 +26,7 @@ import android.content.pm.ApplicationInfo;
|
|||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageItemInfo;
|
import android.content.pm.PackageItemInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.UserManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.webkit.UserPackage;
|
import android.webkit.UserPackage;
|
||||||
|
|
||||||
@@ -149,17 +150,20 @@ public class WebViewAppPicker extends DefaultAppPickerFragment {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
String getDisabledReason(WebViewUpdateServiceWrapper webviewUpdateServiceWrapper,
|
String getDisabledReason(WebViewUpdateServiceWrapper webviewUpdateServiceWrapper,
|
||||||
Context context, String packageName) {
|
Context context, String packageName) {
|
||||||
|
UserManager userManager = context.getSystemService(UserManager.class);
|
||||||
List<UserPackage> userPackages =
|
List<UserPackage> userPackages =
|
||||||
webviewUpdateServiceWrapper.getPackageInfosAllUsers(context, packageName);
|
webviewUpdateServiceWrapper.getPackageInfosAllUsers(context, packageName);
|
||||||
for (UserPackage userPackage : userPackages) {
|
for (UserPackage userPackage : userPackages) {
|
||||||
if (!userPackage.isInstalledPackage()) {
|
if (!userPackage.isInstalledPackage()) {
|
||||||
// Package uninstalled/hidden
|
// Package uninstalled/hidden
|
||||||
return context.getString(
|
return context.getString(
|
||||||
R.string.webview_uninstalled_for_user, userPackage.getUserInfo().name);
|
R.string.webview_uninstalled_for_user,
|
||||||
|
userManager.getUserInfo(userPackage.getUser().getIdentifier()).name);
|
||||||
} else if (!userPackage.isEnabledPackage()) {
|
} else if (!userPackage.isEnabledPackage()) {
|
||||||
// Package disabled
|
// Package disabled
|
||||||
return context.getString(
|
return context.getString(
|
||||||
R.string.webview_disabled_for_user, userPackage.getUserInfo().name);
|
R.string.webview_disabled_for_user,
|
||||||
|
userManager.getUserInfo(userPackage.getUser().getIdentifier()).name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@@ -21,33 +21,30 @@ import android.util.AttributeSet;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
import androidx.preference.Preference;
|
|
||||||
import androidx.preference.PreferenceViewHolder;
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settingslib.widget.TwoTargetPreference;
|
||||||
|
|
||||||
/** A preference with tick icon. */
|
/** A preference with tick icon. */
|
||||||
public class TickButtonPreference extends Preference {
|
public class TickButtonPreference extends TwoTargetPreference {
|
||||||
private ImageView mCheckIcon;
|
private ImageView mCheckIcon;
|
||||||
private boolean mIsSelected = false;
|
private boolean mIsSelected = false;
|
||||||
|
|
||||||
public TickButtonPreference(Context context) {
|
public TickButtonPreference(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
init(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TickButtonPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
init(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(Context context, AttributeSet attrs) {
|
|
||||||
setWidgetLayoutResource(R.layout.preference_check_icon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||||
super.onBindViewHolder(holder);
|
super.onBindViewHolder(holder);
|
||||||
|
View divider =
|
||||||
|
holder.findViewById(
|
||||||
|
com.android.settingslib.widget.preference.twotarget.R.id
|
||||||
|
.two_target_divider);
|
||||||
|
if (divider != null) {
|
||||||
|
divider.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
mCheckIcon = (ImageView) holder.findViewById(R.id.check_icon);
|
mCheckIcon = (ImageView) holder.findViewById(R.id.check_icon);
|
||||||
setSelected(mIsSelected);
|
setSelected(mIsSelected);
|
||||||
}
|
}
|
||||||
@@ -64,4 +61,10 @@ public class TickButtonPreference extends Preference {
|
|||||||
public boolean isSelected() {
|
public boolean isSelected() {
|
||||||
return mIsSelected;
|
return mIsSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getSecondTargetResId() {
|
||||||
|
return R.layout.preference_check_icon;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* 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.connecteddevice.audiosharing;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothStatusCodes;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
|
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||||
|
import com.android.settingslib.flags.Flags;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadow.api.Shadow;
|
||||||
|
import org.robolectric.shadows.androidx.fragment.FragmentController;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(
|
||||||
|
shadows = {
|
||||||
|
ShadowAlertDialogCompat.class,
|
||||||
|
ShadowBluetoothAdapter.class,
|
||||||
|
})
|
||||||
|
public class AudioSharingRetryDialogFragmentTest {
|
||||||
|
@Rule
|
||||||
|
public final MockitoRule mocks = MockitoJUnit.rule();
|
||||||
|
@Rule
|
||||||
|
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
|
||||||
|
private Fragment mParent;
|
||||||
|
private AudioSharingRetryDialogFragment mFragment;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
ShadowAlertDialogCompat.reset();
|
||||||
|
ShadowBluetoothAdapter shadowBluetoothAdapter =
|
||||||
|
Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||||
|
shadowBluetoothAdapter.setEnabled(true);
|
||||||
|
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
mFragment = new AudioSharingRetryDialogFragment();
|
||||||
|
mParent = new Fragment();
|
||||||
|
FragmentController.setupFragment(
|
||||||
|
mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
ShadowAlertDialogCompat.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getMetricsCategory_correctValue() {
|
||||||
|
// TODO: update metrics id
|
||||||
|
assertThat(mFragment.getMetricsCategory())
|
||||||
|
.isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreateDialog_flagOff_dialogNotExist() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
AudioSharingRetryDialogFragment.show(mParent);
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||||
|
assertThat(dialog).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreateDialog_unattachedFragment_dialogNotExist() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
AudioSharingRetryDialogFragment.show(new Fragment());
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||||
|
assertThat(dialog).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreateDialog_flagOn_showDialog() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
AudioSharingRetryDialogFragment.show(mParent);
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||||
|
assertThat(dialog).isNotNull();
|
||||||
|
assertThat(dialog.isShowing()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onCreateDialog_clickOk_dialogDismiss() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
AudioSharingRetryDialogFragment.show(mParent);
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||||
|
assertThat(dialog).isNotNull();
|
||||||
|
View btnView = dialog.findViewById(android.R.id.button1);
|
||||||
|
assertThat(btnView).isNotNull();
|
||||||
|
btnView.performClick();
|
||||||
|
shadowMainLooper().idle();
|
||||||
|
assertThat(dialog.isShowing()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
@@ -123,12 +123,13 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
private static final String TEST_DEVICE_ANONYMIZED_ADDR2 = "XX:XX:02";
|
private static final String TEST_DEVICE_ANONYMIZED_ADDR2 = "XX:XX:02";
|
||||||
private static final int TEST_DEVICE_GROUP_ID1 = 1;
|
private static final int TEST_DEVICE_GROUP_ID1 = 1;
|
||||||
private static final int TEST_DEVICE_GROUP_ID2 = 2;
|
private static final int TEST_DEVICE_GROUP_ID2 = 2;
|
||||||
private static final Correspondence<Fragment, String> TAG_EQUALS =
|
private static final Correspondence<Fragment, String> CLAZZNAME_EQUALS =
|
||||||
Correspondence.from(
|
Correspondence.from(
|
||||||
(Fragment fragment, String tag) ->
|
(Fragment fragment, String clazzName) ->
|
||||||
fragment instanceof DialogFragment
|
fragment instanceof DialogFragment
|
||||||
&& ((DialogFragment) fragment).getTag() != null
|
&& ((DialogFragment) fragment).getClass().getName() != null
|
||||||
&& ((DialogFragment) fragment).getTag().equals(tag),
|
&& ((DialogFragment) fragment).getClass().getName().equals(
|
||||||
|
clazzName),
|
||||||
"is equal to");
|
"is equal to");
|
||||||
|
|
||||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
@@ -343,6 +344,18 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
assertThat(mSwitchBar.isEnabled()).isTrue();
|
assertThat(mSwitchBar.isEnabled()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStart_flagOn_updateSwitch() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
when(mBroadcast.isEnabled(null)).thenReturn(false);
|
||||||
|
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
|
assertThat(mSwitchBar.isChecked()).isFalse();
|
||||||
|
assertThat(mSwitchBar.isEnabled()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onStop_flagOff_doNothing() {
|
public void onStop_flagOff_doNothing() {
|
||||||
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
@@ -398,15 +411,21 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onCheckedChangedToChecked_noConnectedLeaDevices_flagOn_notStartAudioSharing() {
|
public void onCheckedChangedToChecked_noConnectedLeaDevices_flagOn_showDialog() {
|
||||||
FeatureFlagUtils.setEnabled(
|
FeatureFlagUtils.setEnabled(
|
||||||
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
|
||||||
when(mBtnView.isEnabled()).thenReturn(true);
|
when(mBtnView.isEnabled()).thenReturn(true);
|
||||||
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
|
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
|
||||||
doNothing().when(mBroadcast).startPrivateBroadcast();
|
doNothing().when(mBroadcast).startPrivateBroadcast();
|
||||||
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
assertThat(mSwitchBar.isChecked()).isFalse();
|
assertThat(mSwitchBar.isChecked()).isFalse();
|
||||||
verify(mBroadcast, never()).startPrivateBroadcast();
|
verify(mBroadcast, never()).startPrivateBroadcast();
|
||||||
|
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||||
|
assertThat(childFragments)
|
||||||
|
.comparingElementsUsing(CLAZZNAME_EQUALS)
|
||||||
|
.containsExactly(AudioSharingConfirmDialogFragment.class.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -526,8 +545,8 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
|
|
||||||
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
|
||||||
assertThat(childFragments)
|
assertThat(childFragments)
|
||||||
.comparingElementsUsing(TAG_EQUALS)
|
.comparingElementsUsing(CLAZZNAME_EQUALS)
|
||||||
.containsExactly(AudioSharingDialogFragment.tag());
|
.containsExactly(AudioSharingDialogFragment.class.getName());
|
||||||
|
|
||||||
AudioSharingDialogFragment fragment =
|
AudioSharingDialogFragment fragment =
|
||||||
(AudioSharingDialogFragment) Iterables.getOnlyElement(childFragments);
|
(AudioSharingDialogFragment) Iterables.getOnlyElement(childFragments);
|
||||||
@@ -613,6 +632,8 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
mSwitchBar.setChecked(false);
|
mSwitchBar.setChecked(false);
|
||||||
when(mBroadcast.isEnabled(any())).thenReturn(false);
|
when(mBroadcast.isEnabled(any())).thenReturn(false);
|
||||||
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1, mDevice2));
|
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1, mDevice2));
|
||||||
|
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
|
||||||
|
ImmutableList.of(mCachedDevice1, mCachedDevice2));
|
||||||
mController.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1);
|
mController.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1);
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
assertThat(mSwitchBar.isChecked()).isFalse();
|
assertThat(mSwitchBar.isChecked()).isFalse();
|
||||||
@@ -706,12 +727,30 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
mSwitchBar.setEnabled(false);
|
mSwitchBar.setEnabled(false);
|
||||||
when(mBroadcast.isEnabled(null)).thenReturn(false);
|
when(mBroadcast.isEnabled(null)).thenReturn(false);
|
||||||
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
|
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
|
||||||
|
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
|
||||||
|
ImmutableList.of(mCachedDevice2, mCachedDevice1));
|
||||||
mController.onActiveDeviceChanged(mCachedDevice2, BluetoothProfile.LE_AUDIO);
|
mController.onActiveDeviceChanged(mCachedDevice2, BluetoothProfile.LE_AUDIO);
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
assertThat(mSwitchBar.isChecked()).isFalse();
|
assertThat(mSwitchBar.isChecked()).isFalse();
|
||||||
verify(mSwitchBar).setEnabled(true);
|
verify(mSwitchBar).setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onActiveDeviceChanged_a2dpProfile_updateSwitch() {
|
||||||
|
mSwitchBar.setChecked(true);
|
||||||
|
mSwitchBar.setEnabled(false);
|
||||||
|
when(mBroadcast.isEnabled(null)).thenReturn(false);
|
||||||
|
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1, mDevice2));
|
||||||
|
when(mCachedDevice2.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(false);
|
||||||
|
when(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
|
||||||
|
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
|
||||||
|
ImmutableList.of(mCachedDevice1, mCachedDevice2));
|
||||||
|
mController.onActiveDeviceChanged(mCachedDevice2, BluetoothProfile.A2DP);
|
||||||
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
assertThat(mSwitchBar.isChecked()).isFalse();
|
||||||
|
verify(mSwitchBar).setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onActiveDeviceChanged_nullActiveDevice_doNothing() {
|
public void onActiveDeviceChanged_nullActiveDevice_doNothing() {
|
||||||
mController.onActiveDeviceChanged(/* activeDevice= */ null, BluetoothProfile.LE_AUDIO);
|
mController.onActiveDeviceChanged(/* activeDevice= */ null, BluetoothProfile.LE_AUDIO);
|
||||||
@@ -720,14 +759,6 @@ public class AudioSharingSwitchBarControllerTest {
|
|||||||
verify(mSwitchBar, never()).setChecked(anyBoolean());
|
verify(mSwitchBar, never()).setChecked(anyBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onActiveDeviceChanged_notLeaProfile_doNothing() {
|
|
||||||
mController.onActiveDeviceChanged(mCachedDevice2, BluetoothProfile.HEADSET);
|
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
|
||||||
verify(mSwitchBar, never()).setEnabled(anyBoolean());
|
|
||||||
verify(mSwitchBar, never()).setChecked(anyBoolean());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAccessibilityDelegate() {
|
public void testAccessibilityDelegate() {
|
||||||
View view = new View(mContext);
|
View view = new View(mContext);
|
||||||
|
@@ -35,8 +35,8 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.UserInfo;
|
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.webkit.UserPackage;
|
import android.webkit.UserPackage;
|
||||||
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
@@ -71,15 +71,17 @@ import java.util.Collections;
|
|||||||
})
|
})
|
||||||
public class WebViewAppPickerTest {
|
public class WebViewAppPickerTest {
|
||||||
|
|
||||||
private final static String PACKAGE_NAME = "com.example.test";
|
private static final String PACKAGE_NAME = "com.example.test";
|
||||||
private final static String PACKAGE_VERSION = "1.0.0";
|
private static final String PACKAGE_VERSION = "1.0.0";
|
||||||
|
private static final String FIRST_USER_NAME = "FIRST_USER";
|
||||||
|
private static final String SECOND_USER_NAME = "SECOND_USER";
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private FragmentActivity mActivity;
|
private FragmentActivity mActivity;
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private UserInfo mFirstUser;
|
private UserHandle mFirstUser;
|
||||||
private UserInfo mSecondUser;
|
private UserHandle mSecondUser;
|
||||||
private ShadowPackageManager mPackageManager;
|
private ShadowPackageManager mPackageManager;
|
||||||
private WebViewAppPicker mPicker;
|
private WebViewAppPicker mPicker;
|
||||||
private WebViewUpdateServiceWrapper mWvusWrapper;
|
private WebViewUpdateServiceWrapper mWvusWrapper;
|
||||||
@@ -105,8 +107,8 @@ public class WebViewAppPickerTest {
|
|||||||
mPackageManager.addPackage(packageInfo);
|
mPackageManager.addPackage(packageInfo);
|
||||||
mPackageManager.setUnbadgedApplicationIcon(PACKAGE_NAME, new ColorDrawable());
|
mPackageManager.setUnbadgedApplicationIcon(PACKAGE_NAME, new ColorDrawable());
|
||||||
|
|
||||||
mFirstUser = new UserInfo(0, "FIRST_USER", 0);
|
mFirstUser = mUserManager.addUser(0, FIRST_USER_NAME, 0);
|
||||||
mSecondUser = new UserInfo(0, "SECOND_USER", 0);
|
mSecondUser = mUserManager.addUser(1, SECOND_USER_NAME, 0);
|
||||||
mPicker = new WebViewAppPicker();
|
mPicker = new WebViewAppPicker();
|
||||||
mPicker = spy(mPicker);
|
mPicker = spy(mPicker);
|
||||||
doNothing().when(mPicker).updateCandidates();
|
doNothing().when(mPicker).updateCandidates();
|
||||||
@@ -238,13 +240,13 @@ public class WebViewAppPickerTest {
|
|||||||
UserPackage packageForFirstUser = mock(UserPackage.class);
|
UserPackage packageForFirstUser = mock(UserPackage.class);
|
||||||
when(packageForFirstUser.isEnabledPackage()).thenReturn(false);
|
when(packageForFirstUser.isEnabledPackage()).thenReturn(false);
|
||||||
when(packageForFirstUser.isInstalledPackage()).thenReturn(true);
|
when(packageForFirstUser.isInstalledPackage()).thenReturn(true);
|
||||||
when(packageForFirstUser.getUserInfo()).thenReturn(mFirstUser);
|
when(packageForFirstUser.getUser()).thenReturn(mFirstUser);
|
||||||
|
|
||||||
WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
|
WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
|
||||||
when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
|
when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
|
||||||
.thenReturn(Collections.singletonList(packageForFirstUser));
|
.thenReturn(Collections.singletonList(packageForFirstUser));
|
||||||
|
|
||||||
final String expectedReason = String.format("(disabled for user %s)", mFirstUser.name);
|
final String expectedReason = String.format("(disabled for user %s)", FIRST_USER_NAME);
|
||||||
assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
|
assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
|
||||||
.isEqualTo(expectedReason);
|
.isEqualTo(expectedReason);
|
||||||
}
|
}
|
||||||
@@ -254,13 +256,13 @@ public class WebViewAppPickerTest {
|
|||||||
UserPackage packageForFirstUser = mock(UserPackage.class);
|
UserPackage packageForFirstUser = mock(UserPackage.class);
|
||||||
when(packageForFirstUser.isEnabledPackage()).thenReturn(true);
|
when(packageForFirstUser.isEnabledPackage()).thenReturn(true);
|
||||||
when(packageForFirstUser.isInstalledPackage()).thenReturn(false);
|
when(packageForFirstUser.isInstalledPackage()).thenReturn(false);
|
||||||
when(packageForFirstUser.getUserInfo()).thenReturn(mFirstUser);
|
when(packageForFirstUser.getUser()).thenReturn(mFirstUser);
|
||||||
|
|
||||||
WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
|
WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
|
||||||
when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
|
when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
|
||||||
.thenReturn(Collections.singletonList(packageForFirstUser));
|
.thenReturn(Collections.singletonList(packageForFirstUser));
|
||||||
|
|
||||||
final String expectedReason = String.format("(uninstalled for user %s)", mFirstUser.name);
|
final String expectedReason = String.format("(uninstalled for user %s)", FIRST_USER_NAME);
|
||||||
assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
|
assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
|
||||||
.isEqualTo(expectedReason);
|
.isEqualTo(expectedReason);
|
||||||
}
|
}
|
||||||
@@ -270,18 +272,18 @@ public class WebViewAppPickerTest {
|
|||||||
UserPackage packageForFirstUser = mock(UserPackage.class);
|
UserPackage packageForFirstUser = mock(UserPackage.class);
|
||||||
when(packageForFirstUser.isEnabledPackage()).thenReturn(false);
|
when(packageForFirstUser.isEnabledPackage()).thenReturn(false);
|
||||||
when(packageForFirstUser.isInstalledPackage()).thenReturn(true);
|
when(packageForFirstUser.isInstalledPackage()).thenReturn(true);
|
||||||
when(packageForFirstUser.getUserInfo()).thenReturn(mFirstUser);
|
when(packageForFirstUser.getUser()).thenReturn(mFirstUser);
|
||||||
|
|
||||||
UserPackage packageForSecondUser = mock(UserPackage.class);
|
UserPackage packageForSecondUser = mock(UserPackage.class);
|
||||||
when(packageForSecondUser.isEnabledPackage()).thenReturn(true);
|
when(packageForSecondUser.isEnabledPackage()).thenReturn(true);
|
||||||
when(packageForSecondUser.isInstalledPackage()).thenReturn(false);
|
when(packageForSecondUser.isInstalledPackage()).thenReturn(false);
|
||||||
when(packageForSecondUser.getUserInfo()).thenReturn(mSecondUser);
|
when(packageForSecondUser.getUser()).thenReturn(mSecondUser);
|
||||||
|
|
||||||
WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
|
WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
|
||||||
when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
|
when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
|
||||||
.thenReturn(Arrays.asList(packageForFirstUser, packageForSecondUser));
|
.thenReturn(Arrays.asList(packageForFirstUser, packageForSecondUser));
|
||||||
|
|
||||||
final String expectedReason = String.format("(disabled for user %s)", mFirstUser.name);
|
final String expectedReason = String.format("(disabled for user %s)", FIRST_USER_NAME);
|
||||||
assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
|
assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
|
||||||
.isEqualTo(expectedReason);
|
.isEqualTo(expectedReason);
|
||||||
}
|
}
|
||||||
@@ -295,18 +297,18 @@ public class WebViewAppPickerTest {
|
|||||||
UserPackage packageForFirstUser = mock(UserPackage.class);
|
UserPackage packageForFirstUser = mock(UserPackage.class);
|
||||||
when(packageForFirstUser.isEnabledPackage()).thenReturn(false);
|
when(packageForFirstUser.isEnabledPackage()).thenReturn(false);
|
||||||
when(packageForFirstUser.isInstalledPackage()).thenReturn(false);
|
when(packageForFirstUser.isInstalledPackage()).thenReturn(false);
|
||||||
when(packageForFirstUser.getUserInfo()).thenReturn(mFirstUser);
|
when(packageForFirstUser.getUser()).thenReturn(mFirstUser);
|
||||||
|
|
||||||
UserPackage packageForSecondUser = mock(UserPackage.class);
|
UserPackage packageForSecondUser = mock(UserPackage.class);
|
||||||
when(packageForSecondUser.isEnabledPackage()).thenReturn(true);
|
when(packageForSecondUser.isEnabledPackage()).thenReturn(true);
|
||||||
when(packageForSecondUser.isInstalledPackage()).thenReturn(true);
|
when(packageForSecondUser.isInstalledPackage()).thenReturn(true);
|
||||||
when(packageForSecondUser.getUserInfo()).thenReturn(mSecondUser);
|
when(packageForSecondUser.getUser()).thenReturn(mSecondUser);
|
||||||
|
|
||||||
WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
|
WebViewUpdateServiceWrapper wvusWrapper = mock(WebViewUpdateServiceWrapper.class);
|
||||||
when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
|
when(wvusWrapper.getPackageInfosAllUsers(any(), eq(PACKAGE_NAME)))
|
||||||
.thenReturn(Arrays.asList(packageForFirstUser, packageForSecondUser));
|
.thenReturn(Arrays.asList(packageForFirstUser, packageForSecondUser));
|
||||||
|
|
||||||
final String expectedReason = String.format("(uninstalled for user %s)", mFirstUser.name);
|
final String expectedReason = String.format("(uninstalled for user %s)", FIRST_USER_NAME);
|
||||||
assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
|
assertThat(mPicker.getDisabledReason(wvusWrapper, mContext, PACKAGE_NAME))
|
||||||
.isEqualTo(expectedReason);
|
.isEqualTo(expectedReason);
|
||||||
}
|
}
|
||||||
|
@@ -77,6 +77,19 @@ class CarrierConfigRepositoryTest {
|
|||||||
assertThat(value).isEqualTo(99)
|
assertThat(value).isEqualTo(99)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getIntArray_returnValue() {
|
||||||
|
val key = CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY
|
||||||
|
mockCarrierConfigManager.stub {
|
||||||
|
on { getConfigForSubId(any(), eq(key)) } doReturn
|
||||||
|
persistableBundleOf(key to intArrayOf(99))
|
||||||
|
}
|
||||||
|
|
||||||
|
val value = repository.getIntArray(SUB_ID, key)!!.toList()
|
||||||
|
|
||||||
|
assertThat(value).containsExactly(99)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getString_returnValue() {
|
fun getString_returnValue() {
|
||||||
val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING
|
val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING
|
||||||
@@ -104,7 +117,8 @@ class CarrierConfigRepositoryTest {
|
|||||||
assertThat(carrierName)
|
assertThat(carrierName)
|
||||||
.isEqualTo(
|
.isEqualTo(
|
||||||
CarrierConfigManager.getDefaultConfig()
|
CarrierConfigManager.getDefaultConfig()
|
||||||
.getInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT))
|
.getInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -41,91 +41,77 @@ import org.mockito.kotlin.verify
|
|||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class NrAdvancedCallingPreferenceControllerTest {
|
class NrAdvancedCallingPreferenceControllerTest {
|
||||||
@get:Rule
|
@get:Rule val composeTestRule = createComposeRule()
|
||||||
val composeTestRule = createComposeRule()
|
|
||||||
|
|
||||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {}
|
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {}
|
||||||
|
|
||||||
private val callStateRepository = mock<CallStateRepository> {
|
private val callStateRepository =
|
||||||
on { isInCallFlow() } doReturn flowOf(false)
|
mock<CallStateRepository> { on { isInCallFlow() } doReturn flowOf(false) }
|
||||||
}
|
|
||||||
|
|
||||||
private val voNrRepository = mock<VoNrRepository>()
|
private val voNrRepository =
|
||||||
|
mock<VoNrRepository> { on { isVoNrEnabledFlow(SUB_ID) } doReturn flowOf(true) }
|
||||||
|
|
||||||
private val controller = NrAdvancedCallingPreferenceController(
|
private val controller =
|
||||||
|
NrAdvancedCallingPreferenceController(
|
||||||
context = context,
|
context = context,
|
||||||
key = TEST_KEY,
|
key = TEST_KEY,
|
||||||
|
voNrRepository = voNrRepository,
|
||||||
callStateRepository = callStateRepository,
|
callStateRepository = callStateRepository,
|
||||||
).apply { init(SUB_ID, voNrRepository) }
|
)
|
||||||
|
.apply { init(SUB_ID) }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isChecked_voNrEnabled_on() {
|
fun isChecked_voNrEnabled_on() {
|
||||||
voNrRepository.stub {
|
voNrRepository.stub { on { isVoNrEnabledFlow(SUB_ID) } doReturn flowOf(true) }
|
||||||
on { isVoNrEnabledFlow() } doReturn flowOf(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent { controller.Content() }
|
||||||
controller.Content()
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(context.getString(R.string.nr_advanced_calling_title))
|
composeTestRule
|
||||||
|
.onNodeWithText(context.getString(R.string.nr_advanced_calling_title))
|
||||||
.assertIsOn()
|
.assertIsOn()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isChecked_voNrDisabled_off() {
|
fun isChecked_voNrDisabled_off() {
|
||||||
voNrRepository.stub {
|
voNrRepository.stub { on { isVoNrEnabledFlow(SUB_ID) } doReturn flowOf(false) }
|
||||||
on { isVoNrEnabledFlow() } doReturn flowOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent { controller.Content() }
|
||||||
controller.Content()
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(context.getString(R.string.nr_advanced_calling_title))
|
composeTestRule
|
||||||
|
.onNodeWithText(context.getString(R.string.nr_advanced_calling_title))
|
||||||
.assertIsOff()
|
.assertIsOff()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isEnabled_notInCall_enabled() {
|
fun isChangeable_notInCall_changeable() {
|
||||||
callStateRepository.stub {
|
callStateRepository.stub { on { isInCallFlow() } doReturn flowOf(false) }
|
||||||
on { isInCallFlow() } doReturn flowOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent { controller.Content() }
|
||||||
controller.Content()
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(context.getString(R.string.nr_advanced_calling_title))
|
composeTestRule
|
||||||
|
.onNodeWithText(context.getString(R.string.nr_advanced_calling_title))
|
||||||
.assertIsEnabled()
|
.assertIsEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isEnabled_inCall_notEnabled() {
|
fun isChangeable_inCall_notChangeable() {
|
||||||
callStateRepository.stub {
|
callStateRepository.stub { on { isInCallFlow() } doReturn flowOf(true) }
|
||||||
on { isInCallFlow() } doReturn flowOf(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent { controller.Content() }
|
||||||
controller.Content()
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(context.getString(R.string.nr_advanced_calling_title))
|
composeTestRule
|
||||||
|
.onNodeWithText(context.getString(R.string.nr_advanced_calling_title))
|
||||||
.assertIsNotEnabled()
|
.assertIsNotEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onClick_setVoNrEnabled(): Unit = runBlocking {
|
fun onClick_setVoNrEnabled(): Unit = runBlocking {
|
||||||
voNrRepository.stub {
|
voNrRepository.stub { on { isVoNrEnabledFlow(SUB_ID) } doReturn flowOf(false) }
|
||||||
on { isVoNrEnabledFlow() } doReturn flowOf(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent { controller.Content() }
|
||||||
controller.Content()
|
|
||||||
}
|
|
||||||
composeTestRule.onRoot().performClick()
|
composeTestRule.onRoot().performClick()
|
||||||
|
|
||||||
verify(voNrRepository).setVoNrEnabled(true)
|
verify(voNrRepository).setVoNrEnabled(SUB_ID, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* 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.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.telephony.CarrierConfigManager
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
|
import org.mockito.kotlin.stub
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class NrRepositoryTest {
|
||||||
|
private val mockTelephonyManager =
|
||||||
|
mock<TelephonyManager> {
|
||||||
|
on { createForSubscriptionId(SUB_ID) } doReturn mock
|
||||||
|
on { supportedRadioAccessFamily } doReturn TelephonyManager.NETWORK_TYPE_BITMASK_NR
|
||||||
|
}
|
||||||
|
|
||||||
|
private val context: Context =
|
||||||
|
spy(ApplicationProvider.getApplicationContext()) {
|
||||||
|
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
|
||||||
|
}
|
||||||
|
|
||||||
|
private val repository = NrRepository(context)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
CarrierConfigRepository.resetForTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isNrAvailable_deviceNoNr_returnFalse() {
|
||||||
|
mockTelephonyManager.stub {
|
||||||
|
on { supportedRadioAccessFamily } doReturn TelephonyManager.NETWORK_TYPE_BITMASK_LTE
|
||||||
|
}
|
||||||
|
|
||||||
|
val available = repository.isNrAvailable(SUB_ID)
|
||||||
|
|
||||||
|
assertThat(available).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isNrAvailable_carrierConfigNrIsEmpty_returnFalse() {
|
||||||
|
CarrierConfigRepository.setIntArrayForTest(
|
||||||
|
subId = SUB_ID,
|
||||||
|
key = CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
|
||||||
|
value = intArrayOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val available = repository.isNrAvailable(SUB_ID)
|
||||||
|
|
||||||
|
assertThat(available).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isNrAvailable_carrierConfigNrIsNull_returnFalse() {
|
||||||
|
CarrierConfigRepository.setIntArrayForTest(
|
||||||
|
subId = SUB_ID,
|
||||||
|
key = CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
|
||||||
|
value = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
val available = repository.isNrAvailable(SUB_ID)
|
||||||
|
|
||||||
|
assertThat(available).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isNrAvailable_allEnabled_returnTrue() {
|
||||||
|
mockTelephonyManager.stub {
|
||||||
|
on { supportedRadioAccessFamily } doReturn TelephonyManager.NETWORK_TYPE_BITMASK_NR
|
||||||
|
}
|
||||||
|
CarrierConfigRepository.setIntArrayForTest(
|
||||||
|
subId = SUB_ID,
|
||||||
|
key = CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
|
||||||
|
value = intArrayOf(1, 2),
|
||||||
|
)
|
||||||
|
|
||||||
|
val available = repository.isNrAvailable(SUB_ID)
|
||||||
|
|
||||||
|
assertThat(available).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val SUB_ID = 10
|
||||||
|
}
|
||||||
|
}
|
@@ -19,17 +19,15 @@ package com.android.settings.network.telephony
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.telephony.CarrierConfigManager
|
import android.telephony.CarrierConfigManager
|
||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
import androidx.core.os.persistableBundleOf
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.kotlin.anyVararg
|
|
||||||
import org.mockito.kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import org.mockito.kotlin.eq
|
|
||||||
import org.mockito.kotlin.mock
|
import org.mockito.kotlin.mock
|
||||||
import org.mockito.kotlin.spy
|
import org.mockito.kotlin.spy
|
||||||
import org.mockito.kotlin.stub
|
import org.mockito.kotlin.stub
|
||||||
@@ -38,127 +36,107 @@ import org.mockito.kotlin.verify
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class VoNrRepositoryTest {
|
class VoNrRepositoryTest {
|
||||||
|
|
||||||
private val mockTelephonyManager = mock<TelephonyManager> {
|
private val mockTelephonyManager =
|
||||||
on { createForSubscriptionId(SUB_ID) } doReturn mock
|
mock<TelephonyManager> { on { createForSubscriptionId(SUB_ID) } doReturn mock }
|
||||||
on { supportedRadioAccessFamily } doReturn TelephonyManager.NETWORK_TYPE_BITMASK_NR
|
|
||||||
}
|
|
||||||
|
|
||||||
private val carrierConfig = persistableBundleOf(
|
private val context: Context =
|
||||||
CarrierConfigManager.KEY_VONR_ENABLED_BOOL to true,
|
spy(ApplicationProvider.getApplicationContext()) {
|
||||||
CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL to true,
|
|
||||||
CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY to intArrayOf(1, 2),
|
|
||||||
)
|
|
||||||
|
|
||||||
private val mockCarrierConfigManager = mock<CarrierConfigManager> {
|
|
||||||
on { getConfigForSubId(eq(SUB_ID), anyVararg()) } doReturn carrierConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
|
||||||
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
|
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
|
||||||
on { getSystemService(CarrierConfigManager::class.java) } doReturn mockCarrierConfigManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val repository = VoNrRepository(context, SUB_ID)
|
private val mockNrRepository = mock<NrRepository> { on { isNrAvailable(SUB_ID) } doReturn true }
|
||||||
|
|
||||||
|
private val repository = VoNrRepository(context, mockNrRepository)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
CarrierConfigRepository.resetForTest()
|
||||||
|
CarrierConfigRepository.setBooleanForTest(
|
||||||
|
subId = SUB_ID,
|
||||||
|
key = CarrierConfigManager.KEY_VONR_ENABLED_BOOL,
|
||||||
|
value = true,
|
||||||
|
)
|
||||||
|
CarrierConfigRepository.setBooleanForTest(
|
||||||
|
subId = SUB_ID,
|
||||||
|
key = CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL,
|
||||||
|
value = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isVoNrAvailable_visibleDisable_returnFalse() {
|
fun isVoNrAvailable_visibleDisable_returnFalse() {
|
||||||
carrierConfig.apply {
|
CarrierConfigRepository.setBooleanForTest(
|
||||||
putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, false)
|
subId = SUB_ID,
|
||||||
}
|
key = CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL,
|
||||||
|
value = false,
|
||||||
|
)
|
||||||
|
|
||||||
val available = repository.isVoNrAvailable()
|
val available = repository.isVoNrAvailable(SUB_ID)
|
||||||
|
|
||||||
assertThat(available).isFalse()
|
assertThat(available).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isVoNrAvailable_voNrDisabled_returnFalse() {
|
fun isVoNrAvailable_voNrDisabled_returnFalse() {
|
||||||
carrierConfig.apply {
|
CarrierConfigRepository.setBooleanForTest(
|
||||||
putBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL, false)
|
subId = SUB_ID,
|
||||||
}
|
key = CarrierConfigManager.KEY_VONR_ENABLED_BOOL,
|
||||||
|
value = false,
|
||||||
|
)
|
||||||
|
|
||||||
val available = repository.isVoNrAvailable()
|
val available = repository.isVoNrAvailable(SUB_ID)
|
||||||
|
|
||||||
assertThat(available).isFalse()
|
assertThat(available).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isVoNrAvailable_allEnabled_returnTrue() {
|
fun isVoNrAvailable_allEnabled_returnTrue() {
|
||||||
mockTelephonyManager.stub {
|
CarrierConfigRepository.setBooleanForTest(
|
||||||
on { supportedRadioAccessFamily } doReturn TelephonyManager.NETWORK_TYPE_BITMASK_NR
|
subId = SUB_ID,
|
||||||
}
|
key = CarrierConfigManager.KEY_VONR_ENABLED_BOOL,
|
||||||
carrierConfig.apply {
|
value = true,
|
||||||
putBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL, true)
|
)
|
||||||
putBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, true)
|
CarrierConfigRepository.setBooleanForTest(
|
||||||
putIntArray(
|
subId = SUB_ID,
|
||||||
CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY,
|
key = CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL,
|
||||||
intArrayOf(1, 2),
|
value = true,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
val available = repository.isVoNrAvailable()
|
val available = repository.isVoNrAvailable(SUB_ID)
|
||||||
|
|
||||||
assertThat(available).isTrue()
|
assertThat(available).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isVoNrAvailable_deviceNoNr_returnFalse() {
|
fun isVoNrAvailable_noNr_returnFalse() {
|
||||||
mockTelephonyManager.stub {
|
mockNrRepository.stub { on { isNrAvailable(SUB_ID) } doReturn false }
|
||||||
on { supportedRadioAccessFamily } doReturn TelephonyManager.NETWORK_TYPE_BITMASK_LTE
|
|
||||||
}
|
|
||||||
|
|
||||||
val available = repository.isVoNrAvailable()
|
val available = repository.isVoNrAvailable(SUB_ID)
|
||||||
|
|
||||||
assertThat(available).isFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun isVoNrAvailable_carrierNoNr_returnFalse() {
|
|
||||||
carrierConfig.apply {
|
|
||||||
putIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, intArrayOf())
|
|
||||||
}
|
|
||||||
|
|
||||||
val available = repository.isVoNrAvailable()
|
|
||||||
|
|
||||||
assertThat(available).isFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun isVoNrAvailable_carrierConfigNrIsNull_returnFalse() {
|
|
||||||
carrierConfig.apply {
|
|
||||||
putIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
val available = repository.isVoNrAvailable()
|
|
||||||
|
|
||||||
assertThat(available).isFalse()
|
assertThat(available).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isVoNrEnabledFlow_voNrDisabled() = runBlocking {
|
fun isVoNrEnabledFlow_voNrDisabled() = runBlocking {
|
||||||
mockTelephonyManager.stub {
|
mockTelephonyManager.stub { on { isVoNrEnabled } doReturn false }
|
||||||
on { isVoNrEnabled } doReturn false
|
|
||||||
}
|
|
||||||
|
|
||||||
val isVoNrEnabled = repository.isVoNrEnabledFlow().firstWithTimeoutOrNull()
|
val isVoNrEnabled = repository.isVoNrEnabledFlow(SUB_ID).firstWithTimeoutOrNull()
|
||||||
|
|
||||||
assertThat(isVoNrEnabled).isFalse()
|
assertThat(isVoNrEnabled).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun isVoNrEnabledFlow_voNrEnabled() = runBlocking {
|
fun isVoNrEnabledFlow_voNrEnabled() = runBlocking {
|
||||||
mockTelephonyManager.stub {
|
mockTelephonyManager.stub { on { isVoNrEnabled } doReturn true }
|
||||||
on { isVoNrEnabled } doReturn true
|
|
||||||
}
|
|
||||||
|
|
||||||
val isVoNrEnabled = repository.isVoNrEnabledFlow().firstWithTimeoutOrNull()
|
val isVoNrEnabled = repository.isVoNrEnabledFlow(SUB_ID).firstWithTimeoutOrNull()
|
||||||
|
|
||||||
assertThat(isVoNrEnabled).isTrue()
|
assertThat(isVoNrEnabled).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun setVoNrEnabled(): Unit = runBlocking {
|
fun setVoNrEnabled(): Unit = runBlocking {
|
||||||
repository.setVoNrEnabled(true)
|
repository.setVoNrEnabled(SUB_ID, true)
|
||||||
|
|
||||||
verify(mockTelephonyManager).setVoNrEnabled(true)
|
verify(mockTelephonyManager).setVoNrEnabled(true)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user