Snap for 11420186 from ca4565083f to 24Q2-release
Change-Id: I8313be7be164bd6b6f9de9e4290a5765681d1f54
This commit is contained in:
@@ -802,6 +802,11 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".network.SimOnboardingActivity"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
|
||||
android:theme="@style/Theme.SpaLib.BottomSheetDialog"/>
|
||||
|
||||
<activity android:name=".network.telephony.ToggleSubscriptionDialogActivity"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
|
||||
@@ -4404,9 +4409,12 @@
|
||||
<activity android:name=".applications.credentials.CredentialsPickerActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:exported="false">
|
||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||
android:value="com.android.settings.applications.credentials.DefaultCombinedPicker" />
|
||||
android:exported="true">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="android.settings.REQUEST_SET_AUTOFILL_SERVICE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
@@ -4551,19 +4559,6 @@
|
||||
android:exported="false">
|
||||
</activity>
|
||||
|
||||
<activity android:name=".applications.autofill.AutofillPickerTrampolineActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:exported="true"
|
||||
android:label="@string/autofill_app">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="android.settings.REQUEST_SET_AUTOFILL_SERVICE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="Settings$AdvancedConnectedDeviceActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/connected_device_connections_title">
|
||||
|
||||
@@ -709,7 +709,7 @@
|
||||
<bool name="default_allow_sensitive_lockscreen_content">true</bool>
|
||||
|
||||
<!-- Whether to enable the app battery usage list page feature. -->
|
||||
<bool name="config_app_battery_usage_list_enabled">true</bool>
|
||||
<bool name="config_app_battery_usage_list_enabled">false</bool>
|
||||
|
||||
<!-- Whether sim related information is visible to the end user. -->
|
||||
<bool name="config_show_sim_info">true</bool>
|
||||
|
||||
@@ -1269,7 +1269,7 @@
|
||||
<!-- Header in hide Private Space settings page to access Private Space when hidden. [CHAR LIMIT=60] -->
|
||||
<string name="privatespace_access_header">Access private space when hidden</string>
|
||||
<!-- Text in hide Private Space settings page on how to search Private Space when hidden. [CHAR LIMIT=60] -->
|
||||
<string name="privatespace_search_description">From your apps list, enter \'private space\' in the search bar</string>
|
||||
<string name="privatespace_search_description">From your apps list, enter \"private space\" in the search bar</string>
|
||||
<!-- Text in hide Private Space settings page to tap on Private Space tile. [CHAR LIMIT=60] -->
|
||||
<string name="privatespace_tap_tile_description">Tap the private space tile</string>
|
||||
<!-- Text in hide Private Space settings page to Unlock Private Space. [CHAR LIMIT=60] -->
|
||||
@@ -1373,18 +1373,6 @@
|
||||
<!-- Header for private space choose your pattern screen [CHAR LIMIT=40] -->
|
||||
<string name="private_space_choose_your_pattern_header">Set a pattern for your private space</string>
|
||||
|
||||
<!-- TODO(b/309950257): Remove below strings once QSTIle fulfillment is complete. -->
|
||||
<!-- Header in hide Private Space settings page to unhide Private Space. [CHAR LIMIT=90] -->
|
||||
<string name="privatespace_unhide_header">To show private space (Not final UX)</string>
|
||||
<!-- Text in hide Private Space settings page on how to open Private Space setting. [CHAR LIMIT=NONE] -->
|
||||
<string name="privatespace_open_settings">Open the Settings App</string>
|
||||
<!-- Text in hide Private Space settings page on how to open Private Space setting. [CHAR LIMIT=NONE] -->
|
||||
<string name="privatespace_tap_settings">Tap on Security & privacy > private space > Hide private space when locked</string>
|
||||
<!-- Text in hide Private Space settings page to off hide toggle. [CHAR LIMIT=90] -->
|
||||
<string name="privatespace_turnoff_hide">Turn off \‘Hide private space when locked\’ toggle</string>
|
||||
<!-- Note in hide Private Space settings page to inform that this is a development feature. [CHAR LIMIT=NONE] -->
|
||||
<string name="privatespace_development_note">Note to Googlers: The development of this feature is still in progress</string>
|
||||
|
||||
<!-- Text shown when "Add fingerprint" button is disabled -->
|
||||
<string name="fingerprint_add_max">You can add up to <xliff:g id="count" example="5">%d</xliff:g> fingerprints</string>
|
||||
<!-- Text shown when users has enrolled a maximum number of fingerprints [CHAR LIMIT=NONE] -->
|
||||
@@ -7318,7 +7306,7 @@
|
||||
<!-- Title for setting tile leading to saved autofill passwords, autofill, and account settings [CHAR LIMIT=40]-->
|
||||
<string name="account_dashboard_title">Passwords & accounts</string>
|
||||
<!-- Summary for setting tile leading to saved autofill passwords, autofill, and account settings [CHAR LIMIT=NONE]-->
|
||||
<string name="account_dashboard_default_summary">Saved passwords, autofill, synced accounts</string>
|
||||
<string name="account_dashboard_default_summary">Suggestions for sign-in & autofill</string>
|
||||
<!-- Title for setting tile leading to setting UI which allows user set default app to
|
||||
handle actions such as open web page, making phone calls, default SMS apps [CHAR LIMIT=40]-->
|
||||
<string name="app_default_dashboard_title">Default apps</string>
|
||||
@@ -10785,8 +10773,8 @@
|
||||
<string name="autofill_passwords">Passwords</string>
|
||||
<!-- Preference category for showing autofill and credman services with saved credentials. [CHAR LIMIT=60] -->
|
||||
<string name="credman_chosen_app_title">Preferred service</string>
|
||||
<!-- Preference category for showing additional credential providers. [CHAR LIMIT=60] -->
|
||||
<string name="credman_credentials">Additional providers</string>
|
||||
<!-- Preference category for showing additional credential services. [CHAR LIMIT=60] -->
|
||||
<string name="credman_credentials">Additional services</string>
|
||||
<!-- Summary for passwords settings that shows how many passwords are saved for each autofill
|
||||
service. [CHAR LIMIT=NONE] -->
|
||||
<string name="autofill_passwords_count">{count, plural,
|
||||
@@ -10831,7 +10819,7 @@
|
||||
</string>
|
||||
|
||||
<!-- Title of the screen where the user picks a provider. [CHAR_LIMIT=NONE] -->
|
||||
<string name="credman_picker_title">Passwords, passkeys, and data services</string>
|
||||
<string name="credman_picker_title">Preferred service for passwords, passkeys & autofill</string>
|
||||
|
||||
<!-- Title of the warning dialog for disabling the credential provider. [CHAR_LIMIT=NONE] -->
|
||||
<string name="credman_confirmation_message_title">Turn off %1$s\?</string>
|
||||
@@ -10848,6 +10836,9 @@
|
||||
]]>
|
||||
</string>
|
||||
|
||||
<!-- Title for setting tile leading to saved autofill passwords, passkeys, autofill, and account settings [CHAR LIMIT=40]-->
|
||||
<string name="account_dashboard_title_with_passkeys">Passwords, passkeys & autofill</string>
|
||||
|
||||
<!-- Message of the warning dialog for disabling the credential provider (new strings for 24Q3). [CHAR_LIMIT=NONE] -->
|
||||
<string name="credman_confirmation_message_new_ui">
|
||||
<![CDATA[
|
||||
@@ -12891,4 +12882,4 @@
|
||||
|
||||
<!-- Authority of the content provider that support methods restartPhoneProcess and restartRild. Will be overlaid by OEM.-->
|
||||
<string name="reset_telephony_stack_content_provider_authority" translatable="false"></string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="user_and_account_settings_screen"
|
||||
android:title="@string/account_dashboard_title"
|
||||
android:title="@string/account_dashboard_title_with_passkeys"
|
||||
settings:keywords="@string/keywords_accounts">
|
||||
|
||||
<PreferenceCategory
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="user_and_account_settings_screen"
|
||||
android:title="@string/account_dashboard_title"
|
||||
android:title="@string/account_dashboard_title_with_passkeys"
|
||||
settings:keywords="@string/keywords_accounts">
|
||||
|
||||
<PreferenceCategory
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="user_and_account_settings_screen"
|
||||
android:title="@string/account_dashboard_title"
|
||||
android:title="@string/account_dashboard_title_with_passkeys"
|
||||
settings:keywords="@string/keywords_accounts">
|
||||
|
||||
<PreferenceCategory
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="user_and_account_settings_screen"
|
||||
android:title="@string/account_dashboard_title"
|
||||
android:title="@string/account_dashboard_title_with_passkeys"
|
||||
settings:keywords="@string/keywords_accounts">
|
||||
|
||||
<PreferenceCategory
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2023 The Android Open Source Project
|
||||
<!-- 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.
|
||||
@@ -51,6 +51,13 @@
|
||||
settings:keywords="@string/keywords_battery_saver"
|
||||
settings:controller="com.android.settings.fuelgauge.BatterySaverController" />
|
||||
|
||||
<Preference
|
||||
android:fragment="com.android.settings.fuelgauge.SmartBatterySettings"
|
||||
android:key="smart_battery_manager"
|
||||
android:title="@string/smart_battery_manager_title"
|
||||
settings:controller="com.android.settings.fuelgauge.batterytip.BatteryManagerPreferenceController"
|
||||
settings:keywords="@string/keywords_battery_adaptive_preferences" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="battery_percentage"
|
||||
android:title="@string/battery_percentage"
|
||||
|
||||
@@ -34,33 +34,27 @@
|
||||
android:selectable="false"
|
||||
settings:searchable="false" />
|
||||
|
||||
<Preference
|
||||
android:key="private_space_note"
|
||||
android:summary="@string/privatespace_development_note"
|
||||
android:selectable="false"
|
||||
settings:searchable="false" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/privatespace_unhide_header">
|
||||
android:title="@string/privatespace_access_header">
|
||||
|
||||
<Preference
|
||||
android:key="search_when_locked_footer"
|
||||
android:icon="@drawable/counter_1_24dp"
|
||||
android:title="@string/privatespace_open_settings"
|
||||
android:title="@string/privatespace_search_description"
|
||||
android:selectable="false"
|
||||
settings:searchable="false" />
|
||||
|
||||
<Preference
|
||||
android:key="tap_tile_footer"
|
||||
android:icon="@drawable/counter_2_24dp"
|
||||
android:title="@string/privatespace_tap_settings"
|
||||
android:title="@string/privatespace_tap_tile_description"
|
||||
android:selectable="false"
|
||||
settings:searchable="false" />
|
||||
|
||||
<Preference
|
||||
android:key="turn_off_footer"
|
||||
android:key="unlock_profile_footer"
|
||||
android:icon="@drawable/counter_3_24dp"
|
||||
android:title="@string/privatespace_turnoff_hide"
|
||||
android:title="@string/privatespace_unlock_description"
|
||||
android:selectable="false"
|
||||
settings:searchable="false" />
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
android:icon="@drawable/ic_settings_accounts"
|
||||
android:key="top_level_accounts"
|
||||
android:order="-10"
|
||||
android:title="@string/account_dashboard_title"
|
||||
android:title="@string/account_dashboard_title_with_passkeys"
|
||||
android:summary="@string/summary_placeholder"
|
||||
settings:highlightableMenuKey="@string/menu_key_accounts"
|
||||
settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static android.bluetooth.BluetoothGatt.GATT_SUCCESS;
|
||||
import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
@@ -191,7 +192,7 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im
|
||||
public void onDeviceBondStateChanged(@NonNull CachedBluetoothDevice cachedDevice,
|
||||
int bondState) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onDeviceBondStateChanged: " + cachedDevice.getName() + ", state = "
|
||||
Log.d(TAG, "onDeviceBondStateChanged: " + cachedDevice.getDevice() + ", state = "
|
||||
+ bondState);
|
||||
}
|
||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
||||
@@ -275,13 +276,13 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im
|
||||
}
|
||||
mDevicePreferenceMap.put(cachedDevice, preference);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Add device. device: " + cachedDevice);
|
||||
Log.d(TAG, "Add device. device: " + cachedDevice.getDevice());
|
||||
}
|
||||
}
|
||||
|
||||
void removeDevice(CachedBluetoothDevice cachedDevice) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "removeDevice: " + cachedDevice);
|
||||
Log.d(TAG, "removeDevice: " + cachedDevice.getDevice());
|
||||
}
|
||||
BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
|
||||
if (mAvailableHearingDeviceGroup != null && preference != null) {
|
||||
@@ -328,10 +329,15 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im
|
||||
CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(device);
|
||||
if (cachedDevice == null) {
|
||||
cachedDevice = mCachedDeviceManager.addDevice(device);
|
||||
} else if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Skip this device, already bonded: " + cachedDevice.getDevice());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (cachedDevice.getHearingAidInfo() == null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Set hearing aid info on device: " + cachedDevice);
|
||||
Log.d(TAG, "Set hearing aid info on device: " + cachedDevice.getDevice());
|
||||
}
|
||||
cachedDevice.setHearingAidInfo(new HearingAidInfo.Builder().build());
|
||||
}
|
||||
@@ -449,7 +455,7 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im
|
||||
|
||||
void discoverServices(CachedBluetoothDevice cachedDevice) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "connectGattToCheckCompatibility, device: " + cachedDevice);
|
||||
Log.d(TAG, "connectGattToCheckCompatibility, device: " + cachedDevice.getDevice());
|
||||
}
|
||||
BluetoothGatt gatt = cachedDevice.getDevice().connectGatt(getContext(), false,
|
||||
new BluetoothGattCallback() {
|
||||
@@ -459,26 +465,36 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im
|
||||
super.onConnectionStateChange(gatt, status, newState);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onConnectionStateChange, status: " + status + ", newState: "
|
||||
+ newState + ", device: " + cachedDevice);
|
||||
+ newState + ", device: " + cachedDevice.getDevice());
|
||||
}
|
||||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
||||
if (status == GATT_SUCCESS
|
||||
&& newState == BluetoothProfile.STATE_CONNECTED) {
|
||||
gatt.discoverServices();
|
||||
} else {
|
||||
gatt.disconnect();
|
||||
mConnectingGattList.remove(gatt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
super.onServicesDiscovered(gatt, status);
|
||||
boolean isCompatible = gatt.getService(BluetoothUuid.HEARING_AID.getUuid())
|
||||
!= null
|
||||
|| gatt.getService(BluetoothUuid.HAS.getUuid()) != null;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG,
|
||||
"onServicesDiscovered, compatible with Android: " + isCompatible
|
||||
+ ", device: " + cachedDevice);
|
||||
Log.d(TAG, "onServicesDiscovered, status: " + status + ", device: "
|
||||
+ cachedDevice.getDevice());
|
||||
}
|
||||
if (isCompatible) {
|
||||
addDevice(cachedDevice);
|
||||
if (status == GATT_SUCCESS) {
|
||||
if (gatt.getService(BluetoothUuid.HEARING_AID.getUuid()) != null
|
||||
|| gatt.getService(BluetoothUuid.HAS.getUuid()) != null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "compatible with Android, device: "
|
||||
+ cachedDevice.getDevice());
|
||||
}
|
||||
addDevice(cachedDevice);
|
||||
}
|
||||
} else {
|
||||
gatt.disconnect();
|
||||
mConnectingGattList.remove(gatt);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.applications.credentials;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -24,7 +25,6 @@ import android.content.pm.ServiceInfo;
|
||||
import android.credentials.CredentialProviderInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.service.autofill.AutofillServiceInfo;
|
||||
import android.text.TextUtils;
|
||||
import android.util.IconDrawableFactory;
|
||||
@@ -49,7 +49,7 @@ public final class CombinedProviderInfo {
|
||||
private static final String TAG = "CombinedProviderInfo";
|
||||
private static final String SETTINGS_ACTIVITY_INTENT_ACTION = "android.intent.action.MAIN";
|
||||
private static final String SETTINGS_ACTIVITY_INTENT_CATEGORY =
|
||||
"android.intent.category.LAUNCHER";
|
||||
"android.intent.category.DEFAULT";
|
||||
|
||||
private final List<CredentialProviderInfo> mCredentialProviderInfos;
|
||||
private final @Nullable AutofillServiceInfo mAutofillServiceInfo;
|
||||
@@ -327,10 +327,8 @@ public final class CombinedProviderInfo {
|
||||
}
|
||||
|
||||
public static @Nullable Intent createSettingsActivityIntent(
|
||||
@NonNull Context context,
|
||||
@Nullable CharSequence packageName,
|
||||
@Nullable CharSequence settingsActivity,
|
||||
int currentUserId) {
|
||||
@Nullable CharSequence settingsActivity) {
|
||||
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(settingsActivity)) {
|
||||
return null;
|
||||
}
|
||||
@@ -350,19 +348,25 @@ public final class CombinedProviderInfo {
|
||||
Intent intent = new Intent(SETTINGS_ACTIVITY_INTENT_ACTION);
|
||||
intent.addCategory(SETTINGS_ACTIVITY_INTENT_CATEGORY);
|
||||
intent.setComponent(cn);
|
||||
|
||||
int contextUserId = context.getUser().getIdentifier();
|
||||
if (currentUserId != contextUserId && UserManager.isHeadlessSystemUserMode()) {
|
||||
Log.w(
|
||||
TAG,
|
||||
"onLeftSideClicked(): using context for current user ("
|
||||
+ currentUserId
|
||||
+ ") instead of user "
|
||||
+ contextUserId
|
||||
+ " on headless system user mode");
|
||||
context = context.createContextAsUser(UserHandle.of(currentUserId), /* flags= */ 0);
|
||||
}
|
||||
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/** Launches the settings activity intent. */
|
||||
public static void launchSettingsActivityIntent(
|
||||
@NonNull Context context,
|
||||
@Nullable CharSequence packageName,
|
||||
@Nullable CharSequence settingsActivity,
|
||||
int userId) {
|
||||
Intent settingsIntent = createSettingsActivityIntent(packageName, settingsActivity);
|
||||
if (settingsIntent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
context.startActivityAsUser(settingsIntent, UserHandle.of(userId));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(TAG, "Failed to open settings activity", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@@ -677,16 +676,8 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
|
||||
|
||||
@Override
|
||||
public void onLeftSideClicked() {
|
||||
Intent settingsIntent =
|
||||
CombinedProviderInfo.createSettingsActivityIntent(
|
||||
mContext, packageName, settingsActivity, getUser());
|
||||
if (settingsIntent != null) {
|
||||
try {
|
||||
mContext.startActivity(settingsIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(TAG, "Failed to open settings activity", e);
|
||||
}
|
||||
}
|
||||
CombinedProviderInfo.launchSettingsActivityIntent(
|
||||
mContext, packageName, settingsActivity, getUser());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -16,15 +16,53 @@
|
||||
|
||||
package com.android.settings.applications.credentials;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.SettingsActivity;
|
||||
|
||||
/** Standalone activity used to launch a {@link DefaultCombinedPicker} fragment. */
|
||||
/**
|
||||
* Standalone activity used to launch a {@link DefaultCombinedPicker} fragment if the user is a
|
||||
* normal user, a {@link DefaultCombinedPickerWork} fragment if the user is a work profile or {@link
|
||||
* DefaultCombinedPickerPrivate} fragment if the user is a private profile.
|
||||
*/
|
||||
public class CredentialsPickerActivity extends SettingsActivity {
|
||||
private static final String TAG = "CredentialsPickerActivity";
|
||||
|
||||
/** Injects the fragment name into the intent so the correct fragment is opened. */
|
||||
@VisibleForTesting
|
||||
public static void injectFragmentIntoIntent(Context context, Intent intent) {
|
||||
final int userId = UserHandle.myUserId();
|
||||
final UserManager userManager = UserManager.get(context);
|
||||
|
||||
if (DefaultCombinedPickerWork.isUserHandledByFragment(userManager, userId)) {
|
||||
Slog.d(TAG, "Creating picker fragment using work profile");
|
||||
intent.putExtra(EXTRA_SHOW_FRAGMENT, DefaultCombinedPickerWork.class.getName());
|
||||
} else if (DefaultCombinedPickerPrivate.isUserHandledByFragment(userManager)) {
|
||||
Slog.d(TAG, "Creating picker fragment using private profile");
|
||||
intent.putExtra(EXTRA_SHOW_FRAGMENT, DefaultCombinedPickerPrivate.class.getName());
|
||||
} else {
|
||||
Slog.d(TAG, "Creating picker fragment using normal profile");
|
||||
intent.putExtra(EXTRA_SHOW_FRAGMENT, DefaultCombinedPicker.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
injectFragmentIntoIntent(this, getIntent());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
return super.isValidFragment(fragmentName)
|
||||
|| DefaultCombinedPicker.class.getName().equals(fragmentName);
|
||||
|| DefaultCombinedPicker.class.getName().equals(fragmentName)
|
||||
|| DefaultCombinedPickerWork.class.getName().equals(fragmentName)
|
||||
|| DefaultCombinedPickerPrivate.class.getName().equals(fragmentName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,29 @@
|
||||
package com.android.settings.applications.credentials;
|
||||
|
||||
import android.os.UserManager;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.dashboard.profileselector.ProfileSelectFragment.ProfileType;
|
||||
|
||||
public class DefaultCombinedPickerPrivate extends DefaultCombinedPicker {
|
||||
private static final String TAG = "DefaultCombinedPickerPrivate";
|
||||
|
||||
@Override
|
||||
protected int getUser() {
|
||||
UserManager userManager = getContext().getSystemService(UserManager.class);
|
||||
return Utils.getCurrentUserIdOfType(userManager, ProfileType.PRIVATE);
|
||||
}
|
||||
|
||||
/** Returns whether the user is handled by this fragment. */
|
||||
public static boolean isUserHandledByFragment(UserManager userManager) {
|
||||
try {
|
||||
// If there is no private profile then this will throw an exception.
|
||||
Utils.getCurrentUserIdOfType(userManager, ProfileType.PRIVATE);
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
Slog.e(TAG, "Failed to get private profile user id", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,16 @@ package com.android.settings.applications.credentials;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
|
||||
public class DefaultCombinedPickerWork extends DefaultCombinedPicker {
|
||||
private static final String TAG = "DefaultCombinedPickerWork";
|
||||
|
||||
@Override
|
||||
protected int getUser() {
|
||||
UserHandle workProfile = Utils.getManagedProfile(UserManager.get(getContext()));
|
||||
return workProfile.getIdentifier();
|
||||
return UserHandle.myUserId();
|
||||
}
|
||||
|
||||
/** Returns whether the user is handled by this fragment. */
|
||||
public static boolean isUserHandledByFragment(UserManager userManager, int userId) {
|
||||
return userManager.isManagedProfile(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.settings.applications.credentials;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.credentials.CredentialManager;
|
||||
@@ -26,11 +25,11 @@ import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.service.autofill.AutofillService;
|
||||
import android.service.autofill.AutofillServiceInfo;
|
||||
import android.text.TextUtils;
|
||||
import android.view.autofill.AutofillManager;
|
||||
import android.util.Slog;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -83,7 +82,7 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
|
||||
// hand side presses to align the UX.
|
||||
if (PrimaryProviderPreference.shouldUseNewSettingsUi()) {
|
||||
// We need to return an empty intent here since the class we inherit
|
||||
// from will throw an NPE if we return null and we don't want it to
|
||||
// from will throw an NPE if we return null and we don't want it to
|
||||
// open anything since we added the buttons.
|
||||
return new Intent();
|
||||
}
|
||||
@@ -99,10 +98,10 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
|
||||
topProvider.getAppName(mContext),
|
||||
topProvider.getSettingsSubtitle(),
|
||||
topProvider.getAppIcon(mContext, getUser()),
|
||||
createSettingsActivityIntent(
|
||||
topProvider.getPackageName(), topProvider.getSettingsActivity()));
|
||||
topProvider.getPackageName(),
|
||||
topProvider.getSettingsActivity());
|
||||
} else {
|
||||
updatePreferenceForProvider(preference, null, null, null, null);
|
||||
updatePreferenceForProvider(preference, null, null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +111,8 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
|
||||
@Nullable CharSequence appName,
|
||||
@Nullable String appSubtitle,
|
||||
@Nullable Drawable appIcon,
|
||||
@Nullable Intent settingsActivityIntent) {
|
||||
@Nullable CharSequence packageName,
|
||||
@Nullable CharSequence settingsActivity) {
|
||||
if (appName == null) {
|
||||
preference.setTitle(R.string.app_list_preference_none);
|
||||
} else {
|
||||
@@ -133,13 +133,8 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
|
||||
primaryPref.setDelegate(
|
||||
new PrimaryProviderPreference.Delegate() {
|
||||
public void onOpenButtonClicked() {
|
||||
if (settingsActivityIntent != null) {
|
||||
try {
|
||||
startActivity(settingsActivityIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Slog.e(TAG, "Failed to open settings activity", e);
|
||||
}
|
||||
}
|
||||
CombinedProviderInfo.launchSettingsActivityIntent(
|
||||
mContext, packageName, settingsActivity, getUser());
|
||||
}
|
||||
|
||||
public void onChangeButtonClicked() {
|
||||
@@ -148,7 +143,7 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
|
||||
});
|
||||
|
||||
// Hide the open button if there is no defined settings activity.
|
||||
primaryPref.setOpenButtonVisible(settingsActivityIntent != null);
|
||||
primaryPref.setOpenButtonVisible(!TextUtils.isEmpty(settingsActivity));
|
||||
primaryPref.setButtonsVisible(appName != null);
|
||||
}
|
||||
}
|
||||
@@ -198,13 +193,8 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon
|
||||
|
||||
/** Creates an intent to open the credential picker. */
|
||||
private Intent createIntentToOpenPicker() {
|
||||
return new Intent(mContext, CredentialsPickerActivity.class);
|
||||
}
|
||||
|
||||
/** Creates an intent to open the settings activity of the primary provider (if available). */
|
||||
public @Nullable Intent createSettingsActivityIntent(
|
||||
@Nullable String packageName, @Nullable String settingsActivity) {
|
||||
return CombinedProviderInfo.createSettingsActivityIntent(
|
||||
mContext, packageName, settingsActivity, getUser());
|
||||
final Context context =
|
||||
mContext.createContextAsUser(UserHandle.of(getUser()), /* flags= */ 0);
|
||||
return new Intent(context, CredentialsPickerActivity.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,12 @@ public interface PowerUsageFeatureProvider {
|
||||
/** Checks whether we should show usage information by slots or not */
|
||||
boolean isChartGraphSlotsEnabled(Context context);
|
||||
|
||||
/** Checks whether adaptive charging feature is supported in this device */
|
||||
boolean isAdaptiveChargingSupported();
|
||||
|
||||
/** Checks whether battery manager feature is supported in this device */
|
||||
boolean isBatteryManagerSupported();
|
||||
|
||||
/** Returns {@code true} if current defender mode is extra defend */
|
||||
boolean isExtraDefend();
|
||||
|
||||
|
||||
@@ -148,6 +148,16 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdaptiveChargingSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBatteryManagerSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getResumeChargeIntent(boolean isDockDefender) {
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.fuelgauge.batterytip;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.utils.StringUtil;
|
||||
|
||||
/** Preference controller to control the battery manager */
|
||||
public class BatteryManagerPreferenceController extends BasePreferenceController {
|
||||
private static final String KEY_BATTERY_MANAGER = "smart_battery_manager";
|
||||
|
||||
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
||||
private AppOpsManager mAppOpsManager;
|
||||
private UserManager mUserManager;
|
||||
private boolean mEnableAppBatteryUsagePage;
|
||||
|
||||
public BatteryManagerPreferenceController(Context context) {
|
||||
super(context, KEY_BATTERY_MANAGER);
|
||||
mPowerUsageFeatureProvider = FeatureFactory.getFeatureFactory()
|
||||
.getPowerUsageFeatureProvider();
|
||||
mAppOpsManager = context.getSystemService(AppOpsManager.class);
|
||||
mUserManager = context.getSystemService(UserManager.class);
|
||||
mEnableAppBatteryUsagePage =
|
||||
mContext.getResources().getBoolean(R.bool.config_app_battery_usage_list_enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!mPowerUsageFeatureProvider.isBatteryManagerSupported()) {
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
if (!mContext.getResources().getBoolean(R.bool.config_battery_manager_consider_ac)) {
|
||||
return AVAILABLE_UNSEARCHABLE;
|
||||
}
|
||||
return mPowerUsageFeatureProvider.isAdaptiveChargingSupported()
|
||||
? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
if (!mEnableAppBatteryUsagePage) {
|
||||
final int num = BatteryTipUtils.getRestrictedAppsList(mAppOpsManager,
|
||||
mUserManager).size();
|
||||
updateSummary(preference, num);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateSummary(Preference preference, int num) {
|
||||
if (num > 0) {
|
||||
preference.setSummary(StringUtil.getIcuPluralsString(mContext, num,
|
||||
R.string.battery_manager_app_restricted));
|
||||
} else {
|
||||
preference.setSummary(
|
||||
mPowerUsageFeatureProvider.isAdaptiveChargingSupported()
|
||||
? R.string.battery_manager_summary
|
||||
: R.string.battery_manager_summary_unsupported);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ public class TermsOfAddressFeminineController extends TermsOfAddressBaseControll
|
||||
|
||||
@Override
|
||||
protected int getMetricsActionKey() {
|
||||
return SettingsEnums.ACTION_TERMS_OF_ADDRESS_FEMININE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,7 @@ public class TermsOfAddressMasculineController extends TermsOfAddressBaseControl
|
||||
|
||||
@Override
|
||||
protected int getMetricsActionKey() {
|
||||
return SettingsEnums.ACTION_TERMS_OF_ADDRESS_MASCULINE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,7 @@ public class TermsOfAddressNeutralController extends TermsOfAddressBaseControlle
|
||||
|
||||
@Override
|
||||
protected int getMetricsActionKey() {
|
||||
return SettingsEnums.ACTION_TERMS_OF_ADDRESS_NEUTRAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
430
src/com/android/settings/network/SimOnboardingActivity.kt
Normal file
430
src/com/android/settings/network/SimOnboardingActivity.kt
Normal file
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.telephony.SubscriptionManager
|
||||
import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.SignalCellularAlt
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.android.settings.R
|
||||
import com.android.settings.SidecarFragment
|
||||
import com.android.settings.network.telephony.SubscriptionActionDialogActivity
|
||||
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
|
||||
import com.android.settings.spa.network.SimOnboardingPageProvider.getRoute
|
||||
import com.android.settingslib.spa.SpaBaseDialogActivity
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||
import com.android.settingslib.spa.widget.ui.SettingsTitle
|
||||
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SimOnboardingActivity : SpaBaseDialogActivity() {
|
||||
lateinit var scope: CoroutineScope
|
||||
lateinit var showBottomSheet: MutableState<Boolean>
|
||||
lateinit var showError: MutableState<Boolean>
|
||||
lateinit var showDialog: MutableState<Boolean>
|
||||
|
||||
private var switchToEuiccSubscriptionSidecar: SwitchToEuiccSubscriptionSidecar? = null
|
||||
private var switchToRemovableSlotSidecar: SwitchToRemovableSlotSidecar? = null
|
||||
private var enableMultiSimSidecar: EnableMultiSimSidecar? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (!this.userManager.isAdminUser) {
|
||||
Log.e(TAG, "It is not the admin user. Unable to toggle subscription.")
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
var targetSubId = intent.getIntExtra(SUB_ID,SubscriptionManager.INVALID_SUBSCRIPTION_ID)
|
||||
initServiceData(this, targetSubId, callbackListener)
|
||||
if (!onboardingService.isUsableTargetSubscriptionId) {
|
||||
Log.e(TAG, "The subscription id is not usable.")
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
switchToEuiccSubscriptionSidecar = SwitchToEuiccSubscriptionSidecar.get(fragmentManager)
|
||||
switchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(fragmentManager)
|
||||
enableMultiSimSidecar = EnableMultiSimSidecar.get(fragmentManager)
|
||||
|
||||
setContent {
|
||||
Content()
|
||||
}
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
setProgressDialog(false)
|
||||
onboardingService.clear()
|
||||
super.finish()
|
||||
}
|
||||
|
||||
var callbackListener: (Int) -> Unit = {
|
||||
Log.d(TAG, "Receive the CALLBACK: $it")
|
||||
when (it) {
|
||||
CALLBACK_ERROR -> {
|
||||
setProgressDialog(false)
|
||||
showError.value = true
|
||||
}
|
||||
|
||||
CALLBACK_ONBOARDING_COMPLETE -> {
|
||||
showBottomSheet.value = false
|
||||
setProgressDialog(true)
|
||||
scope.launch {
|
||||
// TODO: refactor the Sidecar
|
||||
// start to activate the sim
|
||||
startSimSwitching()
|
||||
}
|
||||
}
|
||||
|
||||
CALLBACK_SETUP_NAME -> {
|
||||
scope.launch {
|
||||
onboardingService.startSetupName()
|
||||
}
|
||||
}
|
||||
|
||||
CALLBACK_SETUP_PRIMARY_SIM -> {
|
||||
scope.launch {
|
||||
onboardingService.startSetupPrimarySim(this@SimOnboardingActivity)
|
||||
}
|
||||
}
|
||||
|
||||
CALLBACK_FINISH -> {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setProgressDialog(enable: Boolean) {
|
||||
showDialog.value = enable
|
||||
val progressState = if (enable) {
|
||||
SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING
|
||||
} else {
|
||||
SubscriptionActionDialogActivity.PROGRESS_IS_NOT_SHOWING
|
||||
}
|
||||
setProgressState(progressState)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
showBottomSheet = remember { mutableStateOf(true) }
|
||||
showError = remember { mutableStateOf(false) }
|
||||
showDialog = remember { mutableStateOf(false) }
|
||||
scope = rememberCoroutineScope()
|
||||
|
||||
registerSidecarReceiverFlow()
|
||||
|
||||
if(showError.value){
|
||||
// show error
|
||||
return
|
||||
}
|
||||
|
||||
if (showBottomSheet.value) {
|
||||
var sheetState = rememberModalBottomSheetState()
|
||||
BottomSheetImpl(
|
||||
sheetState = sheetState,
|
||||
nextAction = {
|
||||
// TODO: if the phone is SS mode and the isDsdsConditionSatisfied is true, then
|
||||
// enable the DSDS mode.
|
||||
// case#1: the device need the reboot after enabling DSDS. Showing the confirm
|
||||
// dialog to user whether reboot device or not.
|
||||
// case#2: The device don't need the reboot. Enabling DSDS and then showing
|
||||
// the SIM onboarding UI.
|
||||
|
||||
// case#2
|
||||
val route = getRoute(onboardingService.targetSubId)
|
||||
startSpaActivity(route)
|
||||
},
|
||||
cancelAction = { finish() },
|
||||
)
|
||||
} else {
|
||||
ProgressDialogImpl()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ProgressDialogImpl() {
|
||||
// TODO: 1. Create the SPA's ProgressDialog and using SPA's widget
|
||||
val dialog: ProgressDialog = object : ProgressDialog(this) {
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
dialog.setMessage(
|
||||
stringResource(
|
||||
R.string.sim_onboarding_progressbar_turning_sim_on,
|
||||
onboardingService.targetSubInfo?.displayName ?: ""
|
||||
)
|
||||
)
|
||||
dialog.setCancelable(false)
|
||||
|
||||
if(showDialog.value) {
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
fun registerSidecarReceiverFlow(){
|
||||
switchToEuiccSubscriptionSidecar?.sidecarReceiverFlow()
|
||||
?.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
|
||||
onStateChange(it)
|
||||
}
|
||||
switchToRemovableSlotSidecar?.sidecarReceiverFlow()
|
||||
?.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
|
||||
onStateChange(it)
|
||||
}
|
||||
enableMultiSimSidecar?.sidecarReceiverFlow()
|
||||
?.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
|
||||
onStateChange(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun SidecarFragment.sidecarReceiverFlow(): Flow<SidecarFragment> = callbackFlow {
|
||||
val broadcastReceiver = SidecarFragment.Listener {
|
||||
Log.d(TAG, "onReceive: $it")
|
||||
trySend(it)
|
||||
}
|
||||
addListener(broadcastReceiver)
|
||||
|
||||
awaitClose { removeListener(broadcastReceiver) }
|
||||
}.catch { e ->
|
||||
Log.e(TAG, "Error while sidecarReceiverFlow", e)
|
||||
}.conflate()
|
||||
|
||||
fun startSimSwitching(){
|
||||
Log.d(TAG, "startSimSwitching:")
|
||||
|
||||
var targetSubInfo = onboardingService.targetSubInfo
|
||||
targetSubInfo?.let {
|
||||
var removedSubInfo = onboardingService.getRemovedSim()
|
||||
if (targetSubInfo.isEmbedded) {
|
||||
switchToEuiccSubscriptionSidecar!!.run(
|
||||
targetSubInfo.subscriptionId,
|
||||
UiccSlotUtil.INVALID_PORT_ID,
|
||||
removedSubInfo
|
||||
)
|
||||
return@let
|
||||
}
|
||||
switchToRemovableSlotSidecar!!.run(
|
||||
UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID,
|
||||
removedSubInfo
|
||||
)
|
||||
} ?: run {
|
||||
Log.e(TAG, "no target subInfo in onboardingService")
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun onStateChange(fragment: SidecarFragment?) {
|
||||
if (fragment === switchToEuiccSubscriptionSidecar) {
|
||||
handleSwitchToEuiccSubscriptionSidecarStateChange()
|
||||
} else if (fragment === switchToRemovableSlotSidecar) {
|
||||
handleSwitchToRemovableSlotSidecarStateChange()
|
||||
} else if (fragment === enableMultiSimSidecar) {
|
||||
handleEnableMultiSimSidecarStateChange()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSwitchToEuiccSubscriptionSidecarStateChange() {
|
||||
when (switchToEuiccSubscriptionSidecar!!.state) {
|
||||
SidecarFragment.State.SUCCESS -> {
|
||||
Log.i(TAG, "Successfully enable the eSIM profile.")
|
||||
switchToEuiccSubscriptionSidecar!!.reset()
|
||||
callbackListener(CALLBACK_SETUP_NAME)
|
||||
}
|
||||
|
||||
SidecarFragment.State.ERROR -> {
|
||||
Log.i(TAG, "Failed to enable the eSIM profile.")
|
||||
switchToEuiccSubscriptionSidecar!!.reset()
|
||||
callbackListener(CALLBACK_ERROR)
|
||||
// TODO: showErrorDialog and using privileged_action_disable_fail_title and
|
||||
// privileged_action_disable_fail_text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSwitchToRemovableSlotSidecarStateChange() {
|
||||
when (switchToRemovableSlotSidecar!!.state) {
|
||||
SidecarFragment.State.SUCCESS -> {
|
||||
Log.i(TAG, "Successfully switched to removable slot.")
|
||||
switchToRemovableSlotSidecar!!.reset()
|
||||
onboardingService.handleTogglePsimAction()
|
||||
callbackListener(CALLBACK_SETUP_NAME)
|
||||
}
|
||||
|
||||
SidecarFragment.State.ERROR -> {
|
||||
Log.e(TAG, "Failed switching to removable slot.")
|
||||
switchToRemovableSlotSidecar!!.reset()
|
||||
callbackListener(CALLBACK_ERROR)
|
||||
// TODO: showErrorDialog and using sim_action_enable_sim_fail_title and
|
||||
// sim_action_enable_sim_fail_text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleEnableMultiSimSidecarStateChange() {
|
||||
when (enableMultiSimSidecar!!.state) {
|
||||
SidecarFragment.State.SUCCESS -> {
|
||||
enableMultiSimSidecar!!.reset()
|
||||
Log.i(TAG, "Successfully switched to DSDS without reboot.")
|
||||
handleEnableSubscriptionAfterEnablingDsds()
|
||||
}
|
||||
|
||||
SidecarFragment.State.ERROR -> {
|
||||
enableMultiSimSidecar!!.reset()
|
||||
Log.i(TAG, "Failed to switch to DSDS without rebooting.")
|
||||
callbackListener(CALLBACK_ERROR)
|
||||
// TODO: showErrorDialog and using dsds_activation_failure_title and
|
||||
// dsds_activation_failure_body_msg2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun handleEnableSubscriptionAfterEnablingDsds() {
|
||||
var targetSubInfo = onboardingService.targetSubInfo
|
||||
if (targetSubInfo?.isEmbedded == true) {
|
||||
Log.i(TAG,
|
||||
"DSDS enabled, start to enable profile: " + targetSubInfo.getSubscriptionId()
|
||||
)
|
||||
// For eSIM operations, we simply switch to the selected eSIM profile.
|
||||
switchToEuiccSubscriptionSidecar!!.run(
|
||||
targetSubInfo.subscriptionId,
|
||||
UiccSlotUtil.INVALID_PORT_ID,
|
||||
null
|
||||
)
|
||||
return
|
||||
}
|
||||
Log.i(TAG, "DSDS enabled, start to enable pSIM profile.")
|
||||
onboardingService.handleTogglePsimAction()
|
||||
callbackListener(CALLBACK_FINISH)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BottomSheetBody(nextAction: () -> Unit) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(bottom = SettingsDimension.itemPaddingVertical)) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.SignalCellularAlt,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(SettingsDimension.iconLarge),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
SettingsTitle(stringResource(R.string.sim_onboarding_bottomsheets_title))
|
||||
Column(Modifier.padding(SettingsDimension.itemPadding)) {
|
||||
Text(
|
||||
text = stringResource(R.string.sim_onboarding_bottomsheets_msg),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
Button(onClick = nextAction) {
|
||||
Text(stringResource(R.string.sim_onboarding_setup))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun BottomSheetImpl(
|
||||
sheetState: SheetState,
|
||||
nextAction: () -> Unit,
|
||||
cancelAction: () -> Unit,
|
||||
) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = cancelAction,
|
||||
sheetState = sheetState,
|
||||
) {
|
||||
BottomSheetBody(nextAction = nextAction)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
sheetState.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun setProgressState(state: Int) {
|
||||
val prefs = getSharedPreferences(
|
||||
SubscriptionActionDialogActivity.SIM_ACTION_DIALOG_PREFS,
|
||||
MODE_PRIVATE
|
||||
)
|
||||
prefs.edit().putInt(SubscriptionActionDialogActivity.KEY_PROGRESS_STATE, state).apply()
|
||||
Log.i(TAG, "setProgressState:$state")
|
||||
}
|
||||
|
||||
fun initServiceData(context: Context,targetSubId: Int, callback:(Int)->Unit) {
|
||||
onboardingService.initData(targetSubId, context,callback)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun startSimOnboardingActivity(
|
||||
context: Context,
|
||||
subId: Int,
|
||||
) {
|
||||
val intent = Intent(context, SimOnboardingActivity::class.java).apply {
|
||||
putExtra(SUB_ID, subId)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
var onboardingService:SimOnboardingService = SimOnboardingService()
|
||||
const val TAG = "SimOnboardingActivity"
|
||||
const val SUB_ID = "sub_id"
|
||||
const val CALLBACK_ERROR = -1
|
||||
const val CALLBACK_ONBOARDING_COMPLETE = 1
|
||||
const val CALLBACK_SETUP_NAME = 2
|
||||
const val CALLBACK_SETUP_PRIMARY_SIM = 3
|
||||
const val CALLBACK_FINISH = 4
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,17 @@ import android.telephony.TelephonyManager
|
||||
import android.telephony.UiccCardInfo
|
||||
import android.telephony.UiccSlotInfo
|
||||
import android.util.Log
|
||||
import com.android.settings.spa.network.setAutomaticData
|
||||
import com.android.settings.spa.network.setDefaultData
|
||||
import com.android.settings.spa.network.setDefaultSms
|
||||
import com.android.settings.spa.network.setDefaultVoice
|
||||
import com.android.settingslib.utils.ThreadUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
private const val TAG = "SimOnboardingService"
|
||||
private const val INVALID = -1
|
||||
private const val INVALID = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
|
||||
class SimOnboardingService {
|
||||
var subscriptionManager:SubscriptionManager? = null
|
||||
@@ -40,19 +46,72 @@ class SimOnboardingService {
|
||||
var slotInfoList: List<UiccSlotInfo> = listOf()
|
||||
var uiccCardInfoList: List<UiccCardInfo> = listOf()
|
||||
var selectedSubInfoList: MutableList<SubscriptionInfo> = mutableListOf()
|
||||
var targetPrimarySimCalls: Int = -1
|
||||
var targetPrimarySimTexts: Int = -1
|
||||
var targetPrimarySimMobileData: Int = -1
|
||||
var targetPrimarySimCalls: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
var targetPrimarySimTexts: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
var targetPrimarySimMobileData: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
var targetPrimarySimAutoDataSwitch: Boolean = false
|
||||
var targetNonDds: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
get() {
|
||||
if(targetPrimarySimMobileData == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
Log.w(TAG, "No DDS")
|
||||
return SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
}
|
||||
return selectedSubInfoList
|
||||
.filter { info ->
|
||||
(info.simSlotIndex != -1) && (info.subscriptionId != targetPrimarySimMobileData)
|
||||
}
|
||||
.map { it.subscriptionId }
|
||||
.firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
}
|
||||
var callback: (Int) -> Unit = {}
|
||||
|
||||
var isMultipleEnabledProfilesSupported: Boolean = false
|
||||
get() {
|
||||
if (uiccCardInfoList.isEmpty()) {
|
||||
Log.w(TAG, "UICC cards info list is empty.")
|
||||
return false
|
||||
}
|
||||
return uiccCardInfoList.stream()
|
||||
.anyMatch { cardInfo: UiccCardInfo -> cardInfo.isMultipleEnabledProfilesSupported }
|
||||
return uiccCardInfoList.any { it.isMultipleEnabledProfilesSupported }
|
||||
}
|
||||
var isRemovableSimEnabled: Boolean = false
|
||||
get() {
|
||||
if(slotInfoList.isEmpty()) {
|
||||
Log.w(TAG, "UICC Slot info list is empty.")
|
||||
return false
|
||||
}
|
||||
return UiccSlotUtil.isRemovableSimEnabled(slotInfoList)
|
||||
}
|
||||
|
||||
var doesTargetSimHaveEsimOperation = false
|
||||
get() {
|
||||
return targetSubInfo?.isEmbedded ?: false
|
||||
}
|
||||
|
||||
var isUsableTargetSubscriptionId = false
|
||||
get() {
|
||||
return SubscriptionManager.isUsableSubscriptionId(targetSubId)
|
||||
}
|
||||
var getActiveModemCount = 0
|
||||
get() {
|
||||
return telephonyManager?.getActiveModemCount() ?: 0
|
||||
}
|
||||
|
||||
var renameMutableMap : MutableMap<Int, String> = mutableMapOf()
|
||||
var userSelectedSubInfoList : MutableList<SubscriptionInfo> = mutableListOf()
|
||||
|
||||
var isSimSelectionFinished = false
|
||||
get() {
|
||||
return getActiveModemCount != 0 && userSelectedSubInfoList.size == getActiveModemCount
|
||||
}
|
||||
|
||||
var isAllOfSlotAssigned = false
|
||||
get() {
|
||||
if(getActiveModemCount == 0){
|
||||
Log.e(TAG, "isAllOfSlotAssigned: getActiveModemCount is 0")
|
||||
return true
|
||||
}
|
||||
return getActiveModemCount != 0 && activeSubInfoList.size == getActiveModemCount
|
||||
}
|
||||
|
||||
fun isValid(): Boolean {
|
||||
return targetSubId != INVALID
|
||||
@@ -73,18 +132,27 @@ class SimOnboardingService {
|
||||
targetPrimarySimCalls = -1
|
||||
targetPrimarySimTexts = -1
|
||||
targetPrimarySimMobileData = -1
|
||||
renameMutableMap.clear()
|
||||
clearUserRecord()
|
||||
}
|
||||
|
||||
fun initData(inputTargetSubId:Int,context: Context) {
|
||||
fun clearUserRecord(){
|
||||
renameMutableMap.clear()
|
||||
userSelectedSubInfoList.clear()
|
||||
}
|
||||
|
||||
fun initData(inputTargetSubId:Int,context: Context, callback: (Int) -> Unit) {
|
||||
this.callback = callback
|
||||
targetSubId = inputTargetSubId
|
||||
subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
|
||||
telephonyManager = context.getSystemService(TelephonyManager::class.java)
|
||||
|
||||
Log.d(
|
||||
TAG, "startInit: targetSubId:$targetSubId"
|
||||
)
|
||||
ThreadUtils.postOnBackgroundThread {
|
||||
activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
|
||||
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
|
||||
targetSubInfo = availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
|
||||
targetSubInfo?.let { userSelectedSubInfoList.add(it) }
|
||||
Log.d(
|
||||
TAG, "targetSubId: $targetSubId" + ", targetSubInfo: $targetSubInfo" +
|
||||
". activeSubInfoList: $activeSubInfoList"
|
||||
@@ -94,11 +162,24 @@ class SimOnboardingService {
|
||||
uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
|
||||
Log.d(TAG, "uiccCardInfoList: $uiccCardInfoList")
|
||||
|
||||
Log.d(TAG, "isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported")
|
||||
targetPrimarySimCalls = SubscriptionManager.getDefaultVoiceSubscriptionId()
|
||||
targetPrimarySimTexts = SubscriptionManager.getDefaultSmsSubscriptionId()
|
||||
targetPrimarySimMobileData = SubscriptionManager.getDefaultDataSubscriptionId()
|
||||
Log.d(
|
||||
TAG,"doesTargetSimHaveEsimOperation: $doesTargetSimHaveEsimOperation" +
|
||||
", isRemovableSimEnabled: $isRemovableSimEnabled" +
|
||||
", isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported" +
|
||||
", targetPrimarySimCalls: $targetPrimarySimCalls" +
|
||||
", targetPrimarySimTexts: $targetPrimarySimTexts" +
|
||||
", targetPrimarySimMobileData: $targetPrimarySimMobileData")
|
||||
}
|
||||
}
|
||||
|
||||
fun getSelectableSubscriptionInfo(): List<SubscriptionInfo> {
|
||||
/**
|
||||
* Return the subscriptionInfo list which has
|
||||
* the target subscriptionInfo + active subscriptionInfo.
|
||||
*/
|
||||
fun getSelectableSubscriptionInfoList(): List<SubscriptionInfo> {
|
||||
var list: MutableList<SubscriptionInfo> = mutableListOf()
|
||||
list.addAll(activeSubInfoList)
|
||||
if (!list.contains(targetSubInfo)) {
|
||||
@@ -109,18 +190,102 @@ class SimOnboardingService {
|
||||
return list.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user selected SubscriptionInfo list.
|
||||
*/
|
||||
fun getSelectedSubscriptionInfoList(): List<SubscriptionInfo> {
|
||||
if (userSelectedSubInfoList.isEmpty()){
|
||||
Log.d(TAG, "userSelectedSubInfoList is empty")
|
||||
return activeSubInfoList
|
||||
}
|
||||
return userSelectedSubInfoList.toList()
|
||||
}
|
||||
|
||||
fun addItemForRenaming(subInfo: SubscriptionInfo, newName: String) {
|
||||
if (subInfo.displayName == newName) {
|
||||
return
|
||||
}
|
||||
renameMutableMap[subInfo.subscriptionId] = newName
|
||||
Log.d(TAG, "renameMutableMap add ${subInfo.subscriptionId} & $newName into: $renameMutableMap")
|
||||
}
|
||||
|
||||
fun getSubscriptionInfoDisplayName(subInfo: SubscriptionInfo): String {
|
||||
return renameMutableMap[subInfo.subscriptionId] ?: subInfo.displayName.toString()
|
||||
}
|
||||
|
||||
fun startActivatingSim(callback:() -> Unit){
|
||||
fun addCurrentItemForSelectedSim(){
|
||||
userSelectedSubInfoList.addAll(activeSubInfoList)
|
||||
}
|
||||
|
||||
fun addItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
|
||||
userSelectedSubInfoList.add(selectedSubInfo)
|
||||
}
|
||||
|
||||
fun removeItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
|
||||
if (userSelectedSubInfoList.contains(selectedSubInfo)) {
|
||||
userSelectedSubInfoList.remove(selectedSubInfo)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the subscriptionInfo which will be removed in the slot during the sim onboarding.
|
||||
* If return Null, then no subscriptionInfo will be removed in the slot.
|
||||
*/
|
||||
fun getRemovedSim():SubscriptionInfo?{
|
||||
return activeSubInfoList.find { !userSelectedSubInfoList.contains(it) }
|
||||
}
|
||||
|
||||
fun handleTogglePsimAction() {
|
||||
val canDisablePhysicalSubscription =
|
||||
subscriptionManager?.canDisablePhysicalSubscription() == true
|
||||
if (targetSubInfo != null && canDisablePhysicalSubscription) {
|
||||
// TODO: to support disable case.
|
||||
subscriptionManager?.setUiccApplicationsEnabled(
|
||||
targetSubInfo!!.subscriptionId, /*enabled=*/true)
|
||||
} else {
|
||||
Log.i(TAG, "The device does not support toggling pSIM. It is enough to just "
|
||||
+ "enable the removable slot."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun startActivatingSim(){
|
||||
// TODO: start to activate sim
|
||||
callback(SimOnboardingActivity.CALLBACK_FINISH)
|
||||
}
|
||||
|
||||
suspend fun startSetupName() {
|
||||
withContext(Dispatchers.Default) {
|
||||
renameMutableMap.forEach {
|
||||
subscriptionManager?.setDisplayName(
|
||||
it.value, it.key,
|
||||
SubscriptionManager.NAME_SOURCE_USER_INPUT
|
||||
)
|
||||
}
|
||||
// next action is SETUP_PRIMARY_SIM
|
||||
callback(SimOnboardingActivity.CALLBACK_SETUP_PRIMARY_SIM)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun startSetupPrimarySim(context: Context) {
|
||||
withContext(Dispatchers.Default) {
|
||||
setDefaultVoice(subscriptionManager,targetPrimarySimCalls)
|
||||
setDefaultSms(subscriptionManager,targetPrimarySimTexts)
|
||||
setDefaultData(
|
||||
context,
|
||||
subscriptionManager,
|
||||
null,
|
||||
targetPrimarySimMobileData
|
||||
)
|
||||
|
||||
|
||||
val telephonyManagerForNonDds: TelephonyManager? =
|
||||
context.getSystemService(TelephonyManager::class.java)
|
||||
?.createForSubscriptionId(targetNonDds)
|
||||
setAutomaticData(telephonyManagerForNonDds, targetPrimarySimAutoDataSwitch)
|
||||
|
||||
// no next action, send finish
|
||||
callback(SimOnboardingActivity.CALLBACK_FINISH)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,8 +48,6 @@ import com.android.settings.network.helper.SelectableSubscriptions;
|
||||
import com.android.settings.network.helper.SubscriptionAnnotation;
|
||||
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
|
||||
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
|
||||
import com.android.settings.spa.SpaActivity;
|
||||
import com.android.settings.spa.network.SimOnboardingPageProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -546,8 +544,7 @@ public class SubscriptionUtil {
|
||||
return;
|
||||
}
|
||||
if (enable && Flags.isDualSimOnboardingEnabled()) {
|
||||
String route = SimOnboardingPageProvider.INSTANCE.getRoute(subId);
|
||||
SpaActivity.startSpaActivity(context, route);
|
||||
SimOnboardingActivity.startSimOnboardingActivity(context, subId);
|
||||
return;
|
||||
}
|
||||
context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable));
|
||||
|
||||
@@ -463,17 +463,27 @@ public class UiccSlotUtil {
|
||||
if (telMgr == null) {
|
||||
return false;
|
||||
}
|
||||
ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
|
||||
List<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
|
||||
return isRemovableSimEnabled(slotInfos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the removable psim is enabled.
|
||||
*
|
||||
* @param slotInfos is a List of UiccSlotInfo.
|
||||
* @return whether the removable psim is enabled.
|
||||
*/
|
||||
public static boolean isRemovableSimEnabled(List<UiccSlotInfo> slotInfos) {
|
||||
boolean isRemovableSimEnabled =
|
||||
slotInfos.stream()
|
||||
.anyMatch(
|
||||
slot -> slot != null
|
||||
&& slot.isRemovable()
|
||||
&& !slot.getIsEuicc()
|
||||
&& slot.getPorts().stream().anyMatch(
|
||||
port -> port.isActive())
|
||||
&& slot.getPorts().stream()
|
||||
.anyMatch(port -> port.isActive())
|
||||
&& slot.getCardStateInfo()
|
||||
== UiccSlotInfo.CARD_STATE_INFO_PRESENT);
|
||||
== UiccSlotInfo.CARD_STATE_INFO_PRESENT);
|
||||
Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
|
||||
return isRemovableSimEnabled;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.network.CarrierConfigCache;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
import com.android.settings.network.ims.WifiCallingQueryImsState;
|
||||
@@ -134,6 +135,17 @@ public class SimDialogActivity extends FragmentActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Flags.isDualSimOnboardingEnabled()
|
||||
&& getProgressState() == SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING
|
||||
&& (dialogType == PREFERRED_PICK
|
||||
|| dialogType == DATA_PICK
|
||||
|| dialogType == CALLS_PICK
|
||||
|| dialogType == SMS_PICK)) {
|
||||
Log.d(TAG, "Finish the sim dialog since the sim onboarding is shown");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
final String tag = Integer.toString(dialogType);
|
||||
final FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
SimDialogFragment fragment = (SimDialogFragment) fragmentManager.findFragmentByTag(tag);
|
||||
|
||||
@@ -67,6 +67,7 @@ import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverF
|
||||
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
|
||||
import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -171,6 +172,8 @@ object NetworkCellularGroupProvider : SettingsPageProvider {
|
||||
.map { it.subscriptionId }
|
||||
.firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
}
|
||||
|
||||
Log.d(name, "defaultDataSubId: $defaultDataSubId, nonDds: $nonDds")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +198,6 @@ fun PageImpl(selectableSubscriptionInfoList: List<SubscriptionInfo>,
|
||||
selectableSubscriptionInfoList
|
||||
)
|
||||
PrimarySimSectionImpl(
|
||||
subscriptionManager,
|
||||
activeSubscriptionInfoList,
|
||||
defaultVoiceSubId,
|
||||
defaultSmsSubId,
|
||||
@@ -257,12 +259,49 @@ fun SimsSectionImpl(
|
||||
|
||||
@Composable
|
||||
fun PrimarySimSectionImpl(
|
||||
subscriptionManager: SubscriptionManager?,
|
||||
activeSubscriptionInfoList: List<SubscriptionInfo>,
|
||||
callsSelectedId: MutableIntState,
|
||||
textsSelectedId: MutableIntState,
|
||||
mobileDataSelectedId: MutableIntState,
|
||||
nonDds: MutableIntState
|
||||
subscriptionInfoList: List<SubscriptionInfo>,
|
||||
callsSelectedId: MutableIntState,
|
||||
textsSelectedId: MutableIntState,
|
||||
mobileDataSelectedId: MutableIntState,
|
||||
nonDds: MutableIntState,
|
||||
subscriptionManager: SubscriptionManager? =
|
||||
LocalContext.current.getSystemService(SubscriptionManager::class.java),
|
||||
coroutineScope: CoroutineScope = rememberCoroutineScope(),
|
||||
context: Context = LocalContext.current,
|
||||
actionSetCalls: (Int) -> Unit = {
|
||||
callsSelectedId.intValue = it
|
||||
coroutineScope.launch {
|
||||
setDefaultVoice(subscriptionManager, it)
|
||||
}
|
||||
},
|
||||
actionSetTexts: (Int) -> Unit = {
|
||||
textsSelectedId.intValue = it
|
||||
coroutineScope.launch {
|
||||
setDefaultSms(subscriptionManager, it)
|
||||
}
|
||||
},
|
||||
actionSetMobileData: (Int) -> Unit = {
|
||||
mobileDataSelectedId.intValue = it
|
||||
coroutineScope.launch {
|
||||
// TODO: to fix the WifiPickerTracker crash when create
|
||||
// the wifiPickerTrackerHelper
|
||||
setDefaultData(
|
||||
context,
|
||||
subscriptionManager,
|
||||
null/*wifiPickerTrackerHelper*/,
|
||||
it
|
||||
)
|
||||
}
|
||||
},
|
||||
actionSetAutoDataSwitch: (Boolean) -> Unit = { newState ->
|
||||
coroutineScope.launch {
|
||||
val telephonyManagerForNonDds: TelephonyManager? =
|
||||
context.getSystemService(TelephonyManager::class.java)
|
||||
?.createForSubscriptionId(nonDds.intValue)
|
||||
Log.d(NetworkCellularGroupProvider.name, "NonDds:${nonDds.intValue} setAutomaticData")
|
||||
setAutomaticData(telephonyManagerForNonDds, newState)
|
||||
}
|
||||
},
|
||||
) {
|
||||
var state = rememberSaveable { mutableStateOf(false) }
|
||||
var callsAndSmsList = remember {
|
||||
@@ -272,11 +311,11 @@ fun PrimarySimSectionImpl(
|
||||
mutableListOf(ListPreferenceOption(id = -1, text = "Loading"))
|
||||
}
|
||||
|
||||
if (activeSubscriptionInfoList.size >= 2) {
|
||||
if (subscriptionInfoList.size >= 2) {
|
||||
state.value = true
|
||||
callsAndSmsList.clear()
|
||||
dataList.clear()
|
||||
for (info in activeSubscriptionInfoList) {
|
||||
for (info in subscriptionInfoList) {
|
||||
var item = ListPreferenceOption(
|
||||
id = info.subscriptionId,
|
||||
text = "${info.displayName}"
|
||||
@@ -291,12 +330,10 @@ fun PrimarySimSectionImpl(
|
||||
} else {
|
||||
// hide the primary sim
|
||||
state.value = false
|
||||
Log.d("NetworkCellularGroupProvider", "Hide primary sim")
|
||||
Log.d(NetworkCellularGroupProvider.name, "Hide primary sim")
|
||||
}
|
||||
|
||||
if (state.value) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
var context = LocalContext.current
|
||||
val telephonyManagerForNonDds: TelephonyManager? =
|
||||
context.getSystemService(TelephonyManager::class.java)
|
||||
?.createForSubscriptionId(nonDds.intValue)
|
||||
@@ -305,44 +342,27 @@ fun PrimarySimSectionImpl(
|
||||
}
|
||||
|
||||
Category(title = stringResource(id = R.string.primary_sim_title)) {
|
||||
createPrimarySimListPreference(
|
||||
CreatePrimarySimListPreference(
|
||||
stringResource(id = R.string.primary_sim_calls_title),
|
||||
callsAndSmsList,
|
||||
callsSelectedId,
|
||||
ImageVector.vectorResource(R.drawable.ic_phone),
|
||||
) {
|
||||
callsSelectedId.intValue = it
|
||||
coroutineScope.launch {
|
||||
setDefaultVoice(subscriptionManager, it)
|
||||
}
|
||||
}
|
||||
createPrimarySimListPreference(
|
||||
actionSetCalls
|
||||
)
|
||||
CreatePrimarySimListPreference(
|
||||
stringResource(id = R.string.primary_sim_texts_title),
|
||||
callsAndSmsList,
|
||||
textsSelectedId,
|
||||
Icons.AutoMirrored.Outlined.Message,
|
||||
) {
|
||||
textsSelectedId.intValue = it
|
||||
coroutineScope.launch {
|
||||
setDefaultSms(subscriptionManager, it)
|
||||
}
|
||||
}
|
||||
createPrimarySimListPreference(
|
||||
actionSetTexts
|
||||
)
|
||||
CreatePrimarySimListPreference(
|
||||
stringResource(id = R.string.mobile_data_settings_title),
|
||||
dataList,
|
||||
mobileDataSelectedId,
|
||||
Icons.Outlined.DataUsage,
|
||||
) {
|
||||
mobileDataSelectedId.intValue = it
|
||||
coroutineScope.launch {
|
||||
// TODO: to fix the WifiPickerTracker crash when create
|
||||
// the wifiPickerTrackerHelper
|
||||
setDefaultData(context,
|
||||
subscriptionManager,
|
||||
null/*wifiPickerTrackerHelper*/,
|
||||
it)
|
||||
}
|
||||
}
|
||||
actionSetMobileData
|
||||
)
|
||||
}
|
||||
|
||||
val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
|
||||
@@ -351,25 +371,18 @@ fun PrimarySimSectionImpl(
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = autoDataTitle
|
||||
override val summary = { autoDataSummary }
|
||||
override val changeable: () -> Boolean = {
|
||||
nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
}
|
||||
override val checked = {
|
||||
coroutineScope.launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
automaticDataChecked.value = telephonyManagerForNonDds != null
|
||||
&& telephonyManagerForNonDds.isMobileDataPolicyEnabled(
|
||||
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
|
||||
if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
coroutineScope.launch {
|
||||
automaticDataChecked.value = getAutomaticData(telephonyManagerForNonDds)
|
||||
}
|
||||
}
|
||||
automaticDataChecked.value
|
||||
}
|
||||
override val onCheckedChange: ((Boolean) -> Unit)? =
|
||||
{ newChecked: Boolean ->
|
||||
coroutineScope.launch {
|
||||
setAutomaticData(telephonyManagerForNonDds, newChecked)
|
||||
}
|
||||
}
|
||||
override val onCheckedChange: ((Boolean) -> Unit)? = {
|
||||
automaticDataChecked.value = it
|
||||
actionSetAutoDataSwitch(it)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -428,19 +441,19 @@ private fun showEuiccSettings(context: Context): Boolean {
|
||||
return MobileNetworkUtils.showEuiccSettings(context)
|
||||
}
|
||||
|
||||
private suspend fun setDefaultVoice(
|
||||
suspend fun setDefaultVoice(
|
||||
subscriptionManager: SubscriptionManager?,
|
||||
subId: Int): Unit = withContext(Dispatchers.Default) {
|
||||
subscriptionManager?.setDefaultVoiceSubscriptionId(subId)
|
||||
}
|
||||
|
||||
private suspend fun setDefaultSms(
|
||||
suspend fun setDefaultSms(
|
||||
subscriptionManager: SubscriptionManager?,
|
||||
subId: Int): Unit = withContext(Dispatchers.Default) {
|
||||
subscriptionManager?.setDefaultSmsSubId(subId)
|
||||
}
|
||||
|
||||
private suspend fun setDefaultData(context: Context,
|
||||
suspend fun setDefaultData(context: Context,
|
||||
subscriptionManager: SubscriptionManager?,
|
||||
wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
|
||||
subId: Int): Unit = withContext(Dispatchers.Default) {
|
||||
@@ -455,11 +468,22 @@ private suspend fun setDefaultData(context: Context,
|
||||
wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
|
||||
}
|
||||
}
|
||||
suspend fun getAutomaticData(telephonyManagerForNonDds: TelephonyManager?): Boolean =
|
||||
withContext(Dispatchers.Default) {
|
||||
telephonyManagerForNonDds != null
|
||||
&& telephonyManagerForNonDds.isMobileDataPolicyEnabled(
|
||||
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
|
||||
}
|
||||
|
||||
private suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
|
||||
withContext(Dispatchers.Default) {
|
||||
telephonyManager?.setMobileDataPolicyEnabled(
|
||||
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
|
||||
newState)
|
||||
//TODO: setup backup calling
|
||||
}
|
||||
suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
|
||||
withContext(Dispatchers.Default) {
|
||||
Log.d(
|
||||
"NetworkCellularGroupProvider",
|
||||
"setAutomaticData: MOBILE_DATA_POLICY_AUTO_DATA_SWITCH as $newState"
|
||||
)
|
||||
telephonyManager?.setMobileDataPolicyEnabled(
|
||||
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
|
||||
newState
|
||||
)
|
||||
//TODO: setup backup calling
|
||||
}
|
||||
@@ -27,11 +27,9 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import com.android.settings.network.SubscriptionUtil
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
|
||||
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
|
||||
@@ -74,7 +72,7 @@ private fun labelSimBody(onboardingService: SimOnboardingService) {
|
||||
SettingsBody(stringResource(R.string.sim_onboarding_label_sim_msg))
|
||||
}
|
||||
|
||||
for (subInfo in onboardingService.getSelectableSubscriptionInfo()) {
|
||||
for (subInfo in onboardingService.getSelectableSubscriptionInfoList()) {
|
||||
var titleSimName by remember {
|
||||
mutableStateOf(
|
||||
onboardingService.getSubscriptionInfoDisplayName(subInfo)
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -31,12 +32,12 @@ import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingActivity
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
|
||||
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||
import com.android.settingslib.spa.framework.common.createSettingsPage
|
||||
import com.android.settingslib.spa.framework.compose.navigator
|
||||
|
||||
import com.android.settingslib.spa.widget.preference.Preference
|
||||
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
||||
|
||||
@@ -59,7 +60,7 @@ object SimOnboardingPageProvider : SettingsPageProvider {
|
||||
|
||||
private val owner = createSettingsPage()
|
||||
@VisibleForTesting
|
||||
var onboardingService: SimOnboardingService = SimOnboardingService()
|
||||
var onboardingService: SimOnboardingService = SimOnboardingActivity.onboardingService
|
||||
|
||||
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
|
||||
.setUiLayoutFn {
|
||||
@@ -72,18 +73,12 @@ object SimOnboardingPageProvider : SettingsPageProvider {
|
||||
|
||||
@Composable
|
||||
override fun Page(arguments: Bundle?) {
|
||||
initServiceData(arguments!!.getInt(SUB_ID))
|
||||
PageImpl(onboardingService,rememberNavController())
|
||||
}
|
||||
|
||||
fun getRoute(
|
||||
subId: Int
|
||||
): String = "${name}/$subId"
|
||||
|
||||
@Composable
|
||||
fun initServiceData(targetSubId: Int) {
|
||||
onboardingService.initData(targetSubId, LocalContext.current)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.getActivity(): Activity? = when (this) {
|
||||
@@ -95,7 +90,10 @@ private fun Context.getActivity(): Activity? = when (this) {
|
||||
@Composable
|
||||
fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostController) {
|
||||
val context = LocalContext.current
|
||||
var previousPageOfOnboarding: () -> Unit = { context.getActivity()?.finish() }
|
||||
var finishOnboarding: () -> Unit = {
|
||||
context.getActivity()?.finish()
|
||||
onboardingService.callback(SimOnboardingActivity.CALLBACK_FINISH)
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navHostController,
|
||||
@@ -103,31 +101,32 @@ fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostCo
|
||||
) {
|
||||
composable(route = SimOnboardingScreen.LabelSim.name) {
|
||||
val nextPage =
|
||||
// Adding more conditions
|
||||
if (onboardingService.isMultipleEnabledProfilesSupported) {
|
||||
if (onboardingService.isMultipleEnabledProfilesSupported && onboardingService.isAllOfSlotAssigned) {
|
||||
SimOnboardingScreen.SelectSim.name
|
||||
} else {
|
||||
onboardingService.addCurrentItemForSelectedSim()
|
||||
SimOnboardingScreen.PrimarySim.name
|
||||
}
|
||||
SimOnboardingLabelSimImpl(
|
||||
nextAction = { navHostController.navigate(nextPage) },
|
||||
cancelAction = previousPageOfOnboarding,
|
||||
cancelAction = finishOnboarding,
|
||||
onboardingService = onboardingService
|
||||
)
|
||||
}
|
||||
composable(route = SimOnboardingScreen.PrimarySim.name) {
|
||||
SimOnboardingPrimarySimImpl(
|
||||
nextAction = {
|
||||
//go back and activate sim
|
||||
onboardingService.callback(SimOnboardingActivity.CALLBACK_ONBOARDING_COMPLETE)
|
||||
context.getActivity()?.finish()
|
||||
},
|
||||
cancelAction = previousPageOfOnboarding,
|
||||
cancelAction = finishOnboarding,
|
||||
onboardingService = onboardingService
|
||||
)
|
||||
}
|
||||
composable(route = SimOnboardingScreen.SelectSim.name) {
|
||||
SimOnboardingSelectSimImpl(
|
||||
nextAction = { navHostController.navigate(SimOnboardingScreen.PrimarySim.name) },
|
||||
cancelAction = previousPageOfOnboarding,
|
||||
cancelAction = finishOnboarding,
|
||||
onboardingService = onboardingService
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,32 +16,25 @@
|
||||
|
||||
package com.android.settings.spa.network
|
||||
|
||||
import android.telephony.SubscriptionManager
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Message
|
||||
import androidx.compose.material.icons.outlined.DataUsage
|
||||
import androidx.compose.material.icons.outlined.SignalCellularAlt
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableIntState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.widget.preference.ListPreference
|
||||
import com.android.settingslib.spa.widget.preference.ListPreferenceModel
|
||||
import com.android.settingslib.spa.widget.preference.ListPreferenceOption
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreference
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
|
||||
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
|
||||
import com.android.settingslib.spa.widget.ui.SettingsBody
|
||||
@@ -68,84 +61,54 @@ fun SimOnboardingPrimarySimImpl(
|
||||
cancelAction
|
||||
),
|
||||
) {
|
||||
primarySimBody(onboardingService)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun primarySimBody(onboardingService: SimOnboardingService) {
|
||||
//TODO: Load the status from the frameworks
|
||||
var callsSelectedId = rememberSaveable { mutableIntStateOf(1) }
|
||||
var textsSelectedId = rememberSaveable { mutableIntStateOf(1) }
|
||||
var mobileDataSelectedId = rememberSaveable { mutableIntStateOf(1) }
|
||||
var automaticDataChecked by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
Column(Modifier.padding(SettingsDimension.itemPadding)) {
|
||||
SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
|
||||
}
|
||||
var selectableSubscriptionInfo = onboardingService.getSelectableSubscriptionInfo()
|
||||
var list = listOf(ListPreferenceOption(id = -1, text = "Loading"))
|
||||
if (selectableSubscriptionInfo.size >= 2) {
|
||||
list = listOf(
|
||||
ListPreferenceOption(
|
||||
id = selectableSubscriptionInfo[0].subscriptionId,
|
||||
text = "${selectableSubscriptionInfo[0].displayName}"
|
||||
),
|
||||
ListPreferenceOption(
|
||||
id = selectableSubscriptionInfo[1].subscriptionId,
|
||||
text = "${selectableSubscriptionInfo[1].displayName}"
|
||||
),
|
||||
ListPreferenceOption(
|
||||
id = -1,
|
||||
text = stringResource(id = R.string.sim_calls_ask_first_prefs_title)
|
||||
),
|
||||
)
|
||||
} else {
|
||||
// set all of primary sim items' enable as false and showing that sim.
|
||||
}
|
||||
createPrimarySimListPreference(
|
||||
stringResource(id = R.string.primary_sim_calls_title),
|
||||
list,
|
||||
callsSelectedId,
|
||||
ImageVector.vectorResource(R.drawable.ic_phone),
|
||||
onIdSelected = { callsSelectedId.intValue = it }
|
||||
)
|
||||
createPrimarySimListPreference(
|
||||
stringResource(id = R.string.primary_sim_texts_title),
|
||||
list,
|
||||
textsSelectedId,
|
||||
Icons.AutoMirrored.Outlined.Message,
|
||||
onIdSelected = { textsSelectedId.intValue = it }
|
||||
)
|
||||
|
||||
createPrimarySimListPreference(
|
||||
stringResource(id = R.string.mobile_data_settings_title),
|
||||
list,
|
||||
mobileDataSelectedId,
|
||||
Icons.Outlined.DataUsage,
|
||||
onIdSelected = { mobileDataSelectedId.intValue = it }
|
||||
)
|
||||
|
||||
val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
|
||||
val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
|
||||
SwitchPreference(remember {
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = autoDataTitle
|
||||
override val summary = { autoDataSummary }
|
||||
override val checked = { automaticDataChecked }
|
||||
override val onCheckedChange =
|
||||
{ newChecked: Boolean -> automaticDataChecked = newChecked }
|
||||
val callsSelectedId = rememberSaveable {
|
||||
mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
|
||||
}
|
||||
})
|
||||
val textsSelectedId = rememberSaveable {
|
||||
mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
|
||||
}
|
||||
val mobileDataSelectedId = rememberSaveable {
|
||||
mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
|
||||
}
|
||||
val nonDdsRemember = rememberSaveable {
|
||||
mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
|
||||
}
|
||||
|
||||
Column(Modifier.padding(SettingsDimension.itemPadding)) {
|
||||
SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
|
||||
}
|
||||
|
||||
var selectedSubscriptionInfoList = onboardingService.getSelectedSubscriptionInfoList()
|
||||
callsSelectedId.intValue = onboardingService.targetPrimarySimCalls
|
||||
textsSelectedId.intValue = onboardingService.targetPrimarySimTexts
|
||||
mobileDataSelectedId.intValue = onboardingService.targetPrimarySimMobileData
|
||||
PrimarySimSectionImpl(
|
||||
subscriptionInfoList = selectedSubscriptionInfoList,
|
||||
callsSelectedId = callsSelectedId,
|
||||
textsSelectedId = textsSelectedId,
|
||||
mobileDataSelectedId = mobileDataSelectedId,
|
||||
nonDds = nonDdsRemember,
|
||||
actionSetCalls = {
|
||||
callsSelectedId.intValue = it
|
||||
onboardingService.targetPrimarySimCalls = it},
|
||||
actionSetTexts = {
|
||||
textsSelectedId.intValue = it
|
||||
onboardingService.targetPrimarySimTexts = it},
|
||||
actionSetMobileData = {
|
||||
mobileDataSelectedId.intValue = it
|
||||
onboardingService.targetPrimarySimMobileData = it},
|
||||
actionSetAutoDataSwitch = {
|
||||
onboardingService.targetPrimarySimAutoDataSwitch = it},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun createPrimarySimListPreference(
|
||||
fun CreatePrimarySimListPreference(
|
||||
title: String,
|
||||
list: List<ListPreferenceOption>,
|
||||
selectedId: MutableIntState,
|
||||
icon: ImageVector,
|
||||
enable: Boolean = true,
|
||||
onIdSelected: (id: Int) -> Unit
|
||||
) = ListPreference(remember {
|
||||
object : ListPreferenceModel {
|
||||
@@ -156,7 +119,5 @@ fun createPrimarySimListPreference(
|
||||
override val icon = @Composable {
|
||||
SettingsIcon(icon)
|
||||
}
|
||||
override val enabled: () -> Boolean
|
||||
get() = { enable }
|
||||
}
|
||||
})
|
||||
@@ -16,24 +16,23 @@
|
||||
|
||||
package com.android.settings.spa.network
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.SignalCellularAlt
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.SimOnboardingService
|
||||
import com.android.settings.sim.SimDialogActivity
|
||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||
import com.android.settingslib.spa.widget.preference.CheckboxPreference
|
||||
import com.android.settingslib.spa.widget.preference.CheckboxPreferenceModel
|
||||
|
||||
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
|
||||
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
|
||||
import com.android.settingslib.spa.widget.ui.SettingsBody
|
||||
@@ -68,22 +67,38 @@ private fun selectSimBody(onboardingService: SimOnboardingService) {
|
||||
Column(Modifier.padding(SettingsDimension.itemPadding)) {
|
||||
SettingsBody(stringResource(id = R.string.sim_onboarding_select_sim_msg))
|
||||
}
|
||||
for (subInfo in onboardingService.getSelectableSubscriptionInfo()) {
|
||||
var isFinished = rememberSaveable { mutableStateOf(false) }
|
||||
isFinished.value = onboardingService.isSimSelectionFinished
|
||||
for (subInfo in onboardingService.getSelectableSubscriptionInfoList()) {
|
||||
var title = onboardingService.getSubscriptionInfoDisplayName(subInfo)
|
||||
var summaryNumber =
|
||||
subInfo.number // TODO using the SubscriptionUtil.getFormattedPhoneNumber
|
||||
var changeable = subInfo.isActive
|
||||
var checked by rememberSaveable { mutableStateOf(!subInfo.isActive) }
|
||||
var checked = rememberSaveable {
|
||||
mutableStateOf(
|
||||
onboardingService.getSelectedSubscriptionInfoList().contains(subInfo)
|
||||
)
|
||||
}
|
||||
|
||||
CheckboxPreference(remember {
|
||||
object : CheckboxPreferenceModel {
|
||||
override val title = title
|
||||
override val summary: () -> String
|
||||
get() = { summaryNumber }
|
||||
override val checked = { checked }
|
||||
override val changeable = { changeable }
|
||||
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
|
||||
override val checked = { checked.value }
|
||||
override val onCheckedChange = { newChecked: Boolean ->
|
||||
checked.value = newChecked
|
||||
if (newChecked) {
|
||||
onboardingService.addItemForSelectedSim(subInfo)
|
||||
} else {
|
||||
onboardingService.removeItemForSelectedSim(subInfo)
|
||||
}
|
||||
isFinished.value = onboardingService.isSimSelectionFinished
|
||||
}
|
||||
override val changeable = {
|
||||
subInfo.isActive
|
||||
&& (!isFinished.value || (isFinished.value && checked.value))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +133,11 @@ public class PowerUsageFeatureProviderImplTest {
|
||||
assertThat(mPowerFeatureProvider.isSmartBatterySupported()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAdaptiveChargingSupported_returnFalse() {
|
||||
assertThat(mPowerFeatureProvider.isAdaptiveChargingSupported()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResumeChargeIntentWithoutDockDefender_returnNull() {
|
||||
assertThat(mPowerFeatureProvider.getResumeChargeIntent(false)).isNull();
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.fuelgauge.batterytip;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = SettingsShadowResources.class)
|
||||
public class BatteryManagerPreferenceControllerTest {
|
||||
private static final int ON = 1;
|
||||
private static final int OFF = 0;
|
||||
|
||||
@Mock
|
||||
private AppOpsManager mAppOpsManager;
|
||||
|
||||
private Context mContext;
|
||||
private Preference mPreference;
|
||||
private FakeFeatureFactory mFeatureFactory;
|
||||
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
||||
private BatteryManagerPreferenceController mController;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOpsManager);
|
||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mPreference = new Preference(mContext);
|
||||
mController = new BatteryManagerPreferenceController(mContext);
|
||||
mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_smartBatteryWithRestrictApps_showSummary() {
|
||||
mController.updateSummary(mPreference, 2);
|
||||
|
||||
assertThat(mPreference.getSummary()).isEqualTo("2 apps restricted");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_smartBatteryWithoutRestriction_showSummary() {
|
||||
when(mPowerUsageFeatureProvider.isSmartBatterySupported()).thenReturn(true);
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, ON);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.getSummary()).isEqualTo("Detecting when apps drain battery");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_supportBatteryManager_showPrefPage() {
|
||||
SettingsShadowResources.overrideResource(
|
||||
R.bool.config_battery_manager_consider_ac, true);
|
||||
when(mPowerUsageFeatureProvider.isBatteryManagerSupported()).thenReturn(true);
|
||||
when(mPowerUsageFeatureProvider.isAdaptiveChargingSupported()).thenReturn(true);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BatteryManagerPreferenceController.AVAILABLE_UNSEARCHABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_notSupportBatteryManager_notShowPrefPage() {
|
||||
when(mPowerUsageFeatureProvider.isBatteryManagerSupported()).thenReturn(false);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BatteryManagerPreferenceController.UNSUPPORTED_ON_DEVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_supportBatteryManagerWithoutAC_notShowPrefPage() {
|
||||
SettingsShadowResources.overrideResource(
|
||||
R.bool.config_battery_manager_consider_ac, true);
|
||||
when(mPowerUsageFeatureProvider.isBatteryManagerSupported()).thenReturn(true);
|
||||
when(mPowerUsageFeatureProvider.isAdaptiveChargingSupported()).thenReturn(false);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BatteryManagerPreferenceController.UNSUPPORTED_ON_DEVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_ignoreBatteryManagerWithoutAC_showPrefPage() {
|
||||
SettingsShadowResources.overrideResource(
|
||||
R.bool.config_battery_manager_consider_ac, false);
|
||||
when(mPowerUsageFeatureProvider.isBatteryManagerSupported()).thenReturn(true);
|
||||
when(mPowerUsageFeatureProvider.isAdaptiveChargingSupported()).thenReturn(false);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BatteryManagerPreferenceController.AVAILABLE_UNSEARCHABLE);
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ class SimOnboardingLabelSimTest {
|
||||
on { targetSubInfo }.doReturn(SUB_INFO_1)
|
||||
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
||||
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
||||
on { getSelectableSubscriptionInfo() }.doReturn(
|
||||
on { getSelectableSubscriptionInfoList() }.doReturn(
|
||||
listOf(
|
||||
SUB_INFO_1,
|
||||
SUB_INFO_2,
|
||||
@@ -139,7 +139,7 @@ class SimOnboardingLabelSimTest {
|
||||
on { targetSubInfo }.doReturn(SUB_INFO_1)
|
||||
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
||||
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
||||
on { getSelectableSubscriptionInfo() }.doReturn(
|
||||
on { getSelectableSubscriptionInfoList() }.doReturn(
|
||||
listOf(
|
||||
SUB_INFO_1,
|
||||
SUB_INFO_2,
|
||||
|
||||
@@ -91,6 +91,7 @@ class SimOnboardingPageProviderTest {
|
||||
fun simOnboardingPage_nextAction_fromLabelSimToSelectSim() {
|
||||
mockSimOnboardingService.stub {
|
||||
on { isMultipleEnabledProfilesSupported }.thenReturn(true)
|
||||
on { isAllOfSlotAssigned }.thenReturn(true)
|
||||
}
|
||||
|
||||
composeTestRule.setContent {
|
||||
|
||||
@@ -108,7 +108,7 @@ class SimOnboardingSelectSimTest {
|
||||
on { targetSubInfo }.doReturn(SUB_INFO_1)
|
||||
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
||||
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
||||
on { getSelectableSubscriptionInfo() }.doReturn(
|
||||
on { getSelectableSubscriptionInfoList() }.doReturn(
|
||||
listOf(
|
||||
SUB_INFO_1,
|
||||
SUB_INFO_2,
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.applications.credentials;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CredentialsPickerActivityTest {
|
||||
|
||||
@Mock private UserManager mUserManager;
|
||||
|
||||
private Context mMockContext;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mMockContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mMockContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInjectFragmentIntoIntent_normalProfile() {
|
||||
Intent intent = new Intent();
|
||||
CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
|
||||
assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
|
||||
.isEqualTo(DefaultCombinedPicker.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInjectFragmentIntoIntent_workProfile() {
|
||||
Intent intent = new Intent();
|
||||
|
||||
// Simulate managed / work profile.
|
||||
when(mUserManager.isManagedProfile(anyInt())).thenReturn(true);
|
||||
assertThat(DefaultCombinedPickerWork.isUserHandledByFragment(mUserManager, 10)).isTrue();
|
||||
|
||||
CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
|
||||
assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
|
||||
.isEqualTo(DefaultCombinedPickerWork.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInjectFragmentIntoIntent_privateProfile() {
|
||||
Intent intent = new Intent();
|
||||
|
||||
// Simulate private profile.
|
||||
UserHandle privateUser = new UserHandle(100);
|
||||
when(mUserManager.getUserInfo(100))
|
||||
.thenReturn(new UserInfo(100, "", "", 0, UserManager.USER_TYPE_PROFILE_PRIVATE));
|
||||
when(mUserManager.getUserProfiles()).thenReturn(Lists.newArrayList(privateUser));
|
||||
assertThat(DefaultCombinedPickerPrivate.isUserHandledByFragment(mUserManager)).isTrue();
|
||||
|
||||
CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
|
||||
assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
|
||||
.isEqualTo(DefaultCombinedPickerPrivate.class.getName());
|
||||
}
|
||||
}
|
||||
@@ -87,16 +87,14 @@ public class DefaultCombinedPreferenceControllerTest {
|
||||
|
||||
@Test
|
||||
public void ensureSettingsActivityIntentCreatedSuccessfully() {
|
||||
DefaultCombinedPreferenceController dcpc =
|
||||
new DefaultCombinedPreferenceController(mContext);
|
||||
|
||||
// Ensure that the settings activity is only created if we haved the right combination
|
||||
// of package and class name.
|
||||
assertThat(dcpc.createSettingsActivityIntent(null, null)).isNull();
|
||||
assertThat(dcpc.createSettingsActivityIntent("", null)).isNull();
|
||||
assertThat(dcpc.createSettingsActivityIntent("", "")).isNull();
|
||||
assertThat(dcpc.createSettingsActivityIntent("com.test", "")).isNull();
|
||||
assertThat(dcpc.createSettingsActivityIntent("com.test", "ClassName")).isNotNull();
|
||||
assertThat(CombinedProviderInfo.createSettingsActivityIntent(null, null)).isNull();
|
||||
assertThat(CombinedProviderInfo.createSettingsActivityIntent("", null)).isNull();
|
||||
assertThat(CombinedProviderInfo.createSettingsActivityIntent("", "")).isNull();
|
||||
assertThat(CombinedProviderInfo.createSettingsActivityIntent("com.test", "")).isNull();
|
||||
assertThat(CombinedProviderInfo.createSettingsActivityIntent("com.test", "ClassName"))
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,13 +110,13 @@ public class DefaultCombinedPreferenceControllerTest {
|
||||
|
||||
// Update the preference to use the provider and make sure the view
|
||||
// was updated.
|
||||
dcpc.updatePreferenceForProvider(ppp, "App Name", "Subtitle", appIcon, null);
|
||||
dcpc.updatePreferenceForProvider(ppp, "App Name", "Subtitle", appIcon, null, null);
|
||||
assertThat(ppp.getTitle().toString()).isEqualTo("App Name");
|
||||
assertThat(ppp.getSummary().toString()).isEqualTo("Subtitle");
|
||||
assertThat(ppp.getIcon()).isEqualTo(appIcon);
|
||||
|
||||
// Set the preference back to none and make sure the view was updated.
|
||||
dcpc.updatePreferenceForProvider(ppp, null, null, null, null);
|
||||
dcpc.updatePreferenceForProvider(ppp, null, null, null, null, null);
|
||||
assertThat(ppp.getTitle().toString()).isEqualTo("None");
|
||||
assertThat(ppp.getSummary()).isNull();
|
||||
assertThat(ppp.getIcon()).isNull();
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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.biometrics.fingerprint.feature
|
||||
|
||||
import android.animation.Animator
|
||||
import android.content.Context
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.view.View
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_CENTER
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_FINGERTIP
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_LEFT_EDGE
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_NO_ANIMATION
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_RIGHT_EDGE
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.STAGE_UNKNOWN
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlin.math.roundToInt
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.doReturn
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SfpsEnrollmentFeatureImplTest {
|
||||
@get:Rule
|
||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Spy
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private val settingsPackageName = "com.android.settings"
|
||||
|
||||
private lateinit var settingsContext: Context
|
||||
|
||||
@Mock
|
||||
private lateinit var mockFingerprintManager: FingerprintManager
|
||||
|
||||
private val mSfpsEnrollmentFeatureImpl: SfpsEnrollmentFeatureImpl = SfpsEnrollmentFeatureImpl()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
assertThat(mSfpsEnrollmentFeatureImpl).isInstanceOf(SfpsEnrollmentFeatureImpl::class.java)
|
||||
whenever(context.getSystemService(FingerprintManager::class.java))
|
||||
.thenReturn(mockFingerprintManager)
|
||||
doReturn(0f).`when`(mockFingerprintManager).getEnrollStageThreshold(0)
|
||||
doReturn(0.36f).`when`(mockFingerprintManager).getEnrollStageThreshold(1)
|
||||
doReturn(0.52f).`when`(mockFingerprintManager).getEnrollStageThreshold(2)
|
||||
doReturn(0.76f).`when`(mockFingerprintManager).getEnrollStageThreshold(3)
|
||||
doReturn(1f).`when`(mockFingerprintManager).getEnrollStageThreshold(4)
|
||||
settingsContext = context.createPackageContext(settingsPackageName, 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetEnrollStageThreshold() {
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 0)).isEqualTo(0f)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 1)).isEqualTo(0.36f)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 2)).isEqualTo(0.52f)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 3)).isEqualTo(0.76f)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, 4)).isEqualTo(1f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetHelpAnimator() {
|
||||
val mockView: View = mock(View::class.java)
|
||||
val animator: Animator = mSfpsEnrollmentFeatureImpl.getHelpAnimator(mockView)
|
||||
assertThat(animator.duration).isEqualTo(550)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCurrentSfpsEnrollStage() {
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(0, null))
|
||||
.isEqualTo(STAGE_UNKNOWN)
|
||||
val mapper = { i: Int ->
|
||||
(25 * mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, i)).roundToInt()
|
||||
}
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(-1, mapper))
|
||||
.isEqualTo(SFPS_STAGE_NO_ANIMATION)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(0, mapper))
|
||||
.isEqualTo(SFPS_STAGE_CENTER)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(9, mapper))
|
||||
.isEqualTo(SFPS_STAGE_FINGERTIP)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(13, mapper))
|
||||
.isEqualTo(SFPS_STAGE_LEFT_EDGE)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(19, mapper))
|
||||
.isEqualTo(SFPS_STAGE_RIGHT_EDGE)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(25, mapper))
|
||||
.isEqualTo(SFPS_STAGE_RIGHT_EDGE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetFeaturedStageHeaderResource() {
|
||||
val type = "string"
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_NO_ANIMATION)
|
||||
).isEqualTo(
|
||||
settingsContext.resources.getIdentifier(
|
||||
"security_settings_fingerprint_enroll_repeat_title",
|
||||
type,
|
||||
settingsPackageName)
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_CENTER)
|
||||
).isEqualTo(
|
||||
settingsContext.resources.getIdentifier(
|
||||
"security_settings_sfps_enroll_finger_center_title",
|
||||
type,
|
||||
settingsPackageName)
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_FINGERTIP)
|
||||
).isEqualTo(
|
||||
settingsContext.resources.getIdentifier(
|
||||
"security_settings_sfps_enroll_fingertip_title",
|
||||
type,
|
||||
settingsPackageName)
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_LEFT_EDGE)
|
||||
).isEqualTo(
|
||||
settingsContext.resources.getIdentifier(
|
||||
"security_settings_sfps_enroll_left_edge_title",
|
||||
type,
|
||||
settingsPackageName)
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_RIGHT_EDGE)
|
||||
).isEqualTo(
|
||||
settingsContext.resources.getIdentifier(
|
||||
"security_settings_sfps_enroll_right_edge_title",
|
||||
type,
|
||||
settingsPackageName)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSfpsEnrollLottiePerStage() {
|
||||
val type = "raw"
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_NO_ANIMATION)
|
||||
).isEqualTo(
|
||||
settingsContext.resources.getIdentifier(
|
||||
"sfps_lottie_no_animation",
|
||||
type,
|
||||
settingsPackageName)
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_CENTER)
|
||||
).isEqualTo(
|
||||
settingsContext.resources.getIdentifier(
|
||||
"sfps_lottie_pad_center",
|
||||
type,
|
||||
settingsPackageName)
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_FINGERTIP)
|
||||
).isEqualTo(
|
||||
settingsContext.resources.getIdentifier(
|
||||
"sfps_lottie_tip",
|
||||
type,
|
||||
settingsPackageName)
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_LEFT_EDGE)
|
||||
).isEqualTo(
|
||||
settingsContext.resources.getIdentifier(
|
||||
"sfps_lottie_left_edge",
|
||||
type,
|
||||
settingsPackageName)
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_RIGHT_EDGE)
|
||||
).isEqualTo(
|
||||
settingsContext.resources.getIdentifier(
|
||||
"sfps_lottie_right_edge",
|
||||
type,
|
||||
settingsPackageName)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,6 @@ public class TermsOfAddressFeminineControllerTest {
|
||||
|
||||
selectedPreference.performClick();
|
||||
|
||||
assertThat(selectedPreference.getKey()).isEqualTo(KEY_FEMININE);
|
||||
assertThat(selectedPreference.isSelected()).isTrue();
|
||||
assertThat(pref.isSelected()).isFalse();
|
||||
}
|
||||
|
||||
@@ -97,7 +97,6 @@ public class TermsOfAddressMasculineControllerTest {
|
||||
|
||||
selectedPreference.performClick();
|
||||
|
||||
assertThat(selectedPreference.getKey()).isEqualTo(KEY_MASCULINE);
|
||||
assertThat(selectedPreference.isSelected()).isTrue();
|
||||
assertThat(pref.isSelected()).isFalse();
|
||||
}
|
||||
|
||||
@@ -97,7 +97,6 @@ public class TermsOfAddressNeutralControllerTest {
|
||||
|
||||
selectedPreference.performClick();
|
||||
|
||||
assertThat(selectedPreference.getKey()).isEqualTo(KEY_NEUTRAL);
|
||||
assertThat(selectedPreference.isSelected()).isTrue();
|
||||
assertThat(pref.isSelected()).isFalse();
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -275,7 +275,7 @@ public class ChannelListPreferenceControllerTest {
|
||||
PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0);
|
||||
assertEquals("group", group.getKey());
|
||||
assertEquals(3, group.getPreferenceCount());
|
||||
SwitchPreference groupBlockPref = (SwitchPreference) group.getPreference(0);
|
||||
SwitchPreferenceCompat groupBlockPref = (SwitchPreferenceCompat) group.getPreference(0);
|
||||
assertNull(groupBlockPref.getKey());
|
||||
assertEquals("All \"My Group\" notifications", groupBlockPref.getTitle().toString());
|
||||
assertTrue(groupBlockPref.isChecked());
|
||||
@@ -297,7 +297,7 @@ public class ChannelListPreferenceControllerTest {
|
||||
PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0);
|
||||
assertEquals("group", group.getKey());
|
||||
assertEquals(1, group.getPreferenceCount());
|
||||
SwitchPreference groupBlockPref = (SwitchPreference) group.getPreference(0);
|
||||
SwitchPreferenceCompat groupBlockPref = (SwitchPreferenceCompat) group.getPreference(0);
|
||||
assertNull(groupBlockPref.getKey());
|
||||
assertEquals("All \"My Group\" notifications", groupBlockPref.getTitle().toString());
|
||||
assertFalse(groupBlockPref.isChecked());
|
||||
@@ -311,7 +311,7 @@ public class ChannelListPreferenceControllerTest {
|
||||
PreferenceGroup group = (PreferenceGroup) mGroupList.getPreference(0);
|
||||
assertEquals("group", group.getKey());
|
||||
assertEquals(3, group.getPreferenceCount());
|
||||
SwitchPreference groupBlockPref = (SwitchPreference) group.getPreference(0);
|
||||
SwitchPreferenceCompat groupBlockPref = (SwitchPreferenceCompat) group.getPreference(0);
|
||||
assertNull(groupBlockPref.getKey());
|
||||
assertEquals("All \"My Group\" notifications", groupBlockPref.getTitle().toString());
|
||||
assertTrue(groupBlockPref.isChecked());
|
||||
|
||||
Reference in New Issue
Block a user