diff --git a/aconfig/settings_experience_flag_declarations.aconfig b/aconfig/settings_experience_flag_declarations.aconfig
index 9fe3f327383..d5caccfbb5c 100644
--- a/aconfig/settings_experience_flag_declarations.aconfig
+++ b/aconfig/settings_experience_flag_declarations.aconfig
@@ -7,3 +7,13 @@ flag {
description: "Change to the new APN page."
bug: "298906796"
}
+
+flag {
+ name: "refactor_print_settings"
+ namespace: "settings_experience"
+ description: "Refactor the PrintSettings page."
+ bug: "320076351"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 866a529dd8b..227f72dc720 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -675,12 +675,6 @@
android:title="@string/immediately_destroy_activities"
android:summary="@string/immediately_destroy_activities_summary" />
-
-
= limit) {
- index = i;
- break;
- }
- }
- final ListPreference listPreference = (ListPreference) mPreference;
- listPreference.setValue(mListValues[index]);
- listPreference.setSummary(mListSummaries[index]);
- } catch (RemoteException e) {
- // intentional no-op
- }
- }
-
- private void writeAppProcessLimitOptions(Object newValue) {
- try {
- final int limit = newValue != null ? Integer.parseInt(newValue.toString()) : -1;
- getActivityManagerService().setProcessLimit(limit);
- updateAppProcessLimitOptions();
- } catch (RemoteException e) {
- // intentional no-op
- }
- }
-
- @VisibleForTesting
- IActivityManager getActivityManagerService() {
- return ActivityManager.getService();
- }
-}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 65a23eb399f..dd9a1f05f25 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -740,7 +740,6 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
controllers.add(new StrictModePreferenceController(context));
controllers.add(new ProfileGpuRenderingPreferenceController(context));
controllers.add(new KeepActivitiesPreferenceController(context));
- controllers.add(new BackgroundProcessLimitPreferenceController(context));
controllers.add(new CachedAppsFreezerPreferenceController(context));
controllers.add(new ShowFirstCrashDialogPreferenceController(context));
controllers.add(new AppsNotRespondingPreferenceController(context));
diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
index 56ce9e7a301..e92d9995f99 100644
--- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
+++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
@@ -507,7 +507,9 @@ public class EnabledNetworkModePreferenceController extends
}
}
} else if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
- if (MobileNetworkUtils.isTdscdmaSupported(mContext, mSubId)) {
+ if (mIsGlobalCdma) {
+ enabledNetworkType = EnabledNetworks.ENABLED_NETWORKS_CDMA_CHOICES;
+ } else if (MobileNetworkUtils.isTdscdmaSupported(mContext, mSubId)) {
enabledNetworkType = EnabledNetworks.ENABLED_NETWORKS_TDSCDMA_CHOICES;
} else if (!mDisplay2gOptions && !mDisplay3gOptions) {
enabledNetworkType = mShow4gForLTE
@@ -521,8 +523,6 @@ public class EnabledNetworkModePreferenceController extends
: EnabledNetworks.ENABLED_NETWORKS_EXCEPT_GSM_CHOICES;
} else if (!mLteEnabled) {
enabledNetworkType = EnabledNetworks.ENABLED_NETWORKS_EXCEPT_LTE_CHOICES;
- } else if (mIsGlobalCdma) {
- enabledNetworkType = EnabledNetworks.ENABLED_NETWORKS_CDMA_CHOICES;
} else {
enabledNetworkType = mShow4gForLTE ? EnabledNetworks.ENABLED_NETWORKS_4G_CHOICES
: EnabledNetworks.ENABLED_NETWORKS_CHOICES;
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index 1c3c78d9706..8b927a9f1ca 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -68,7 +68,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.function.Consumer;
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class MobileNetworkSettings extends AbstractMobileNetworkSettings implements
@@ -359,23 +358,17 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
}
private void onSubscriptionDetailChanged() {
- if (mSubscriptionInfoEntity != null) {
- /**
- * Update the title when SIM stats got changed
- */
- final Consumer renameTitle = activity -> {
- if (activity != null && !activity.isFinishing()) {
- if (activity instanceof SettingsActivity) {
- ((SettingsActivity) activity).setTitle(mSubscriptionInfoEntity.uniqueName);
- }
- }
- };
-
- ThreadUtils.postOnMainThread(() -> {
- renameTitle.accept(getActivity());
- redrawPreferenceControllers();
- });
+ final SubscriptionInfoEntity subscriptionInfoEntity = mSubscriptionInfoEntity;
+ if (subscriptionInfoEntity == null) {
+ return;
}
+ ThreadUtils.postOnMainThread(() -> {
+ if (getActivity() instanceof SettingsActivity activity && !activity.isFinishing()) {
+ // Update the title when SIM stats got changed
+ activity.setTitle(subscriptionInfoEntity.uniqueName);
+ }
+ redrawPreferenceControllers();
+ });
}
@Override
diff --git a/src/com/android/settings/password/BiometricFragment.java b/src/com/android/settings/password/BiometricFragment.java
index aeef4824f28..02f5b861ea3 100644
--- a/src/com/android/settings/password/BiometricFragment.java
+++ b/src/com/android/settings/password/BiometricFragment.java
@@ -25,6 +25,7 @@ import android.hardware.biometrics.PromptInfo;
import android.multiuser.Flags;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.text.TextUtils;
import androidx.annotation.NonNull;
@@ -149,6 +150,13 @@ public class BiometricFragment extends InstrumentedFragment {
.setShowEmergencyCallButton(promptInfo.isShowEmergencyCallButton())
.setReceiveSystemEvents(true)
.setComponentNameForConfirmDeviceCredentialActivity(callingActivity);
+ if (promptInfo.getLogoRes() != 0){
+ promptBuilder.setLogoRes(promptInfo.getLogoRes());
+ }
+ String logoDescription = promptInfo.getLogoDescription();
+ if (!TextUtils.isEmpty(logoDescription)) {
+ promptBuilder.setLogoDescription(logoDescription);
+ }
if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpaceFeatures()
&& Flags.enableBiometricsToUnlockPrivateSpace()) {
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 069f910adc0..30fd619d9df 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -20,6 +20,7 @@ package com.android.settings.password;
import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PASSWORD_HEADER;
import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PATTERN_HEADER;
import static android.app.admin.DevicePolicyResources.Strings.Settings.CONFIRM_WORK_PROFILE_PIN_HEADER;
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import android.app.Activity;
@@ -31,6 +32,7 @@ import android.app.trust.TrustManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.UserProperties;
import android.content.res.Configuration;
import android.graphics.Color;
@@ -44,6 +46,7 @@ import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
+import android.text.TextUtils;
import android.util.Log;
import android.view.WindowManager;
@@ -65,6 +68,12 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
private static final String TAG_BIOMETRIC_FRAGMENT = "fragment";
+ /** Use this extra value to provide a custom logo for the biometric prompt. **/
+ public static final String CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY = "custom_logo_res_id";
+ /** Use this extra value to provide a custom logo description for the biometric prompt. **/
+ public static final String CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY =
+ "custom_logo_description";
+
public static class InternalActivity extends ConfirmDeviceCredentialActivity {
}
@@ -202,6 +211,21 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
promptInfo.setDescription(mDetails);
promptInfo.setDisallowBiometricsIfPolicyExists(mCheckDevicePolicyManager);
+ if (android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && android.multiuser.Flags.usePrivateSpaceIconInBiometricPrompt()
+ && hasSetBiometricDialogAdvanced(mContext, getLaunchedFromUid())
+ ) {
+ int iconResId = intent.getIntExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY, 0);
+ if (iconResId != 0) {
+ promptInfo.setLogoRes(iconResId);
+ }
+ String logoDescription = intent.getStringExtra(
+ CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY);
+ if (!TextUtils.isEmpty(logoDescription)) {
+ promptInfo.setLogoDescription(logoDescription);
+ }
+ }
+
final int policyType = mDevicePolicyManager.getManagedSubscriptionsPolicy().getPolicyType();
if (isEffectiveUserManagedProfile
@@ -409,6 +433,14 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
}
}
+ /**
+ * Checks if the calling uid has the permission to set biometric dialog icon and description.
+ */
+ private static boolean hasSetBiometricDialogAdvanced(@NonNull Context context, int callingUid) {
+ return context.checkPermission(SET_BIOMETRIC_DIALOG_ADVANCED, /* pid */ -1, callingUid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
// User could be locked while Effective user is unlocked even though the effective owns the
// credential. Otherwise, biometric can't unlock fbe/keystore through
// verifyTiedProfileChallenge. In such case, we also wanna show the user message that
diff --git a/src/com/android/settings/print/PrintRepository.kt b/src/com/android/settings/print/PrintRepository.kt
new file mode 100644
index 00000000000..8a9182a952d
--- /dev/null
+++ b/src/com/android/settings/print/PrintRepository.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.print
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.print.PrintManager
+import android.printservice.PrintServiceInfo
+import com.android.settings.R
+import com.android.settingslib.spa.framework.util.mapItem
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+class PrintRepository(private val context: Context) {
+
+ private val printManager = context.getSystemService(PrintManager::class.java)!!
+ private val packageManager = context.packageManager
+
+ data class PrintServiceDisplayInfo(
+ val title: String,
+ val isEnabled: Boolean,
+ val summary: String,
+ val icon: Drawable,
+ val componentName: String,
+ )
+
+ fun printServiceDisplayInfosFlow(): Flow> =
+ printServicesFlow()
+ .mapItem { printService -> printService.toPrintServiceDisplayInfo() }
+ .conflate()
+ .flowOn(Dispatchers.Default)
+
+ private fun PrintServiceInfo.toPrintServiceDisplayInfo() = PrintServiceDisplayInfo(
+ title = resolveInfo.loadLabel(packageManager).toString(),
+ isEnabled = isEnabled,
+ summary = context.getString(
+ if (isEnabled) R.string.print_feature_state_on else R.string.print_feature_state_off
+ ),
+ icon = resolveInfo.loadIcon(packageManager),
+ componentName = componentName.flattenToString(),
+ )
+
+ private fun printServicesFlow(): Flow> =
+ printManager.printServicesChangeFlow()
+ .map { printManager.getPrintServices(PrintManager.ALL_SERVICES) }
+ .conflate()
+ .flowOn(Dispatchers.Default)
+
+ private companion object {
+ fun PrintManager.printServicesChangeFlow(): Flow = callbackFlow {
+ val listener = PrintManager.PrintServicesChangeListener { trySend(Unit) }
+ addPrintServicesChangeListener(listener, null)
+ trySend(Unit)
+ awaitClose { removePrintServicesChangeListener(listener) }
+ }.conflate().flowOn(Dispatchers.Default)
+ }
+}
diff --git a/src/com/android/settings/print/PrintSettingsFragment.java b/src/com/android/settings/print/PrintSettingsFragment.java
index cd80998170c..ee94683b012 100644
--- a/src/com/android/settings/print/PrintSettingsFragment.java
+++ b/src/com/android/settings/print/PrintSettingsFragment.java
@@ -46,6 +46,7 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.AsyncTaskLoader;
@@ -54,7 +55,9 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.settings.R;
+import com.android.settings.flags.Flags;
import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.spa.SpaActivity;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.AppPreference;
@@ -101,6 +104,15 @@ public class PrintSettingsFragment extends ProfileSettingsPreferenceFragment
super(UserManager.DISALLOW_PRINTING);
}
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ if (Flags.refactorPrintSettings()) {
+ SpaActivity.startSpaActivity(context, PrintSettingsPageProvider.INSTANCE.getName());
+ finish();
+ }
+ }
+
@Override
protected String getLogTag() {
return TAG;
diff --git a/src/com/android/settings/print/PrintSettingsPageProvider.kt b/src/com/android/settings/print/PrintSettingsPageProvider.kt
new file mode 100644
index 00000000000..aac0a5d0cf4
--- /dev/null
+++ b/src/com/android/settings/print/PrintSettingsPageProvider.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.print
+
+import android.app.settings.SettingsEnums
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.core.os.bundleOf
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.settings.R
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.print.PrintRepository.PrintServiceDisplayInfo
+import com.android.settings.print.PrintSettingsFragment.EXTRA_CHECKED
+import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
+import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import com.android.settingslib.spa.widget.ui.Category
+import com.android.settingslib.spaprivileged.template.common.UserProfilePager
+
+object PrintSettingsPageProvider : SettingsPageProvider {
+ override val name = "PrintSettings"
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ RegularScaffold(title = stringResource(R.string.print_settings)) {
+ val context = LocalContext.current
+ val printRepository = remember(context) { PrintRepository(context) }
+ UserProfilePager {
+ PrintServices(printRepository)
+ }
+ }
+ }
+
+ @Composable
+ private fun PrintServices(printRepository: PrintRepository) {
+ val printServiceDisplayInfos by remember {
+ printRepository.printServiceDisplayInfosFlow()
+ }.collectAsStateWithLifecycle(initialValue = emptyList())
+ Category(title = stringResource(R.string.print_settings_title)) {
+ for (printServiceDisplayInfo in printServiceDisplayInfos) {
+ PrintService(printServiceDisplayInfo)
+ }
+ }
+ }
+
+ @VisibleForTesting
+ @Composable
+ fun PrintService(displayInfo: PrintServiceDisplayInfo) {
+ val context = LocalContext.current
+ Preference(model = object : PreferenceModel {
+ override val title = displayInfo.title
+ override val summary = { displayInfo.summary }
+ override val icon: @Composable () -> Unit = {
+ Image(
+ painter = rememberDrawablePainter(displayInfo.icon),
+ contentDescription = null,
+ modifier = Modifier.size(SettingsDimension.appIconItemSize),
+ )
+ }
+ override val onClick = {
+ SubSettingLauncher(context).apply {
+ setDestination(PrintServiceSettingsFragment::class.qualifiedName)
+ setArguments(
+ bundleOf(
+ EXTRA_CHECKED to displayInfo.isEnabled,
+ EXTRA_TITLE to displayInfo.title,
+ EXTRA_SERVICE_COMPONENT_NAME to displayInfo.componentName
+ )
+ )
+ setSourceMetricsCategory(SettingsEnums.PRINT_SETTINGS)
+ }.launch()
+ }
+ })
+ }
+}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
index 00dcc4629ee..08b5fb93aa6 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
@@ -20,6 +20,8 @@ import static android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD;
import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS;
import static com.android.settings.activityembedding.EmbeddedDeepLinkUtils.tryStartMultiPaneDeepLink;
+import static com.android.settings.password.ConfirmDeviceCredentialActivity.CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY;
+import static com.android.settings.password.ConfirmDeviceCredentialActivity.CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY;
import android.app.ActivityOptions;
import android.app.KeyguardManager;
@@ -228,6 +230,14 @@ public class PrivateSpaceAuthenticationActivity extends FragmentActivity {
private void authenticatePrivateSpaceEntry() {
Intent credentialIntent = mPrivateSpaceMaintainer.getPrivateProfileLockCredentialIntent();
if (credentialIntent != null) {
+ if (android.multiuser.Flags.usePrivateSpaceIconInBiometricPrompt()) {
+ credentialIntent.putExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY,
+ com.android.internal.R.drawable.stat_sys_private_profile_status);
+ credentialIntent.putExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY,
+ getApplicationContext().getString(
+ com.android.internal.R.string.private_space_biometric_prompt_title
+ ));
+ }
mVerifyDeviceLock.launch(credentialIntent);
} else {
Log.e(TAG, "verifyCredentialIntent is null even though device lock is set");
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 5dbc8dc7099..45d238a8771 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -19,6 +19,7 @@ package com.android.settings.spa
import android.content.Context
import android.util.FeatureFlagUtils
import com.android.settings.network.apn.ApnEditPageProvider
+import com.android.settings.print.PrintSettingsPageProvider
import com.android.settings.spa.about.AboutPhonePageProvider
import com.android.settings.spa.app.AllAppListPageProvider
import com.android.settings.spa.app.AppsMainPageProvider
@@ -120,6 +121,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
BatteryOptimizationModeAppListPageProvider,
NetworkCellularGroupProvider(),
WifiPrivacyPageProvider,
+ PrintSettingsPageProvider,
)
override val logger = if (FeatureFlagUtils.isEnabled(
diff --git a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
index 6c8ce6e3bdf..64667318d5a 100644
--- a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
@@ -87,12 +87,17 @@ private fun LabelSimPreference(
}
val phoneNumber = phoneNumber(subInfo)
val alertDialogPresenter = rememberAlertDialogPresenter(
- confirmButton = AlertDialogButton(stringResource(R.string.mobile_network_sim_name_rename)) {
+ confirmButton = AlertDialogButton(
+ stringResource(R.string.mobile_network_sim_name_rename),
+ titleSimName.isNotBlank()
+ ) {
onboardingService.addItemForRenaming(
subInfo, if (titleSimName.isEmpty()) originalSimCarrierName else titleSimName
)
},
- dismissButton = AlertDialogButton(stringResource(R.string.cancel)) {
+ dismissButton = AlertDialogButton(
+ stringResource(R.string.cancel),
+ ) {
titleSimName = onboardingService.getSubscriptionInfoDisplayName(subInfo)
},
title = stringResource(R.string.sim_onboarding_label_sim_dialog_title),
@@ -107,7 +112,7 @@ private fun LabelSimPreference(
placeholder = {Text(text = originalSimCarrierName)},
modifier = Modifier.fillMaxWidth().testTag("contentInput")
) {
- titleSimName = if (it.matches(Regex("^\\s*$"))) originalSimCarrierName else it
+ titleSimName = it
}
},
)
diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java
index 1e405686e19..70e08eb9033 100644
--- a/src/com/android/settings/wifi/WifiConfigController2.java
+++ b/src/com/android/settings/wifi/WifiConfigController2.java
@@ -311,7 +311,9 @@ public class WifiConfigController2 implements TextWatcher,
mHiddenSettingsSpinner = mView.findViewById(R.id.hidden_settings);
if (!mHideMeteredAndPrivacy && mWifiManager.isConnectedMacRandomizationSupported()) {
mPrivacySettingsSpinner = mView.findViewById(R.id.privacy_settings);
- mDhcpSettingsSpinner = mView.findViewById(R.id.dhcp_settings);
+ if (Flags.androidVWifiApi()) {
+ mDhcpSettingsSpinner = mView.findViewById(R.id.dhcp_settings);
+ }
mView.findViewById(R.id.privacy_settings_fields).setVisibility(View.VISIBLE);
}
mHiddenSettingsSpinner.setOnItemSelectedListener(this);
diff --git a/tests/Enable16KbTests/Android.bp b/tests/Enable16KbTests/Android.bp
index 57c6ef6503c..ecb0357ea9e 100644
--- a/tests/Enable16KbTests/Android.bp
+++ b/tests/Enable16KbTests/Android.bp
@@ -17,7 +17,7 @@ package {
default_team: "trendy_team_android_kernel",
}
-android_test_helper_app {
+android_test {
name: "test_16kb_app",
srcs: ["test_16kb_app/src/**/*.java"],
manifest: "test_16kb_app/test_16kb_app.xml",
diff --git a/tests/Enable16KbTests/TEST_MAPPING b/tests/Enable16KbTests/TEST_MAPPING
new file mode 100644
index 00000000000..fbe6fe15b63
--- /dev/null
+++ b/tests/Enable16KbTests/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "postsubmit": [
+ {
+ "name": "Enable16KbTest"
+ }
+ ]
+}
+
diff --git a/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml b/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml
index 8fe9ad52fed..3ca786afffa 100644
--- a/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml
+++ b/tests/Enable16KbTests/test_16kb_app/test_16kb_app.xml
@@ -16,8 +16,8 @@
-->
+ android:installLocation="internalOnly"
+ package="com.android.settings.development.test">
diff --git a/tests/robotests/src/com/android/settings/development/BackgroundProcessLimitPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BackgroundProcessLimitPreferenceControllerTest.java
deleted file mode 100644
index d51547ebaac..00000000000
--- a/tests/robotests/src/com/android/settings/development/BackgroundProcessLimitPreferenceControllerTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2017 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.development;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.IActivityManager;
-import android.content.Context;
-import android.os.RemoteException;
-
-import androidx.preference.ListPreference;
-import androidx.preference.PreferenceScreen;
-
-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;
-
-@RunWith(RobolectricTestRunner.class)
-public class BackgroundProcessLimitPreferenceControllerTest {
-
- @Mock
- private IActivityManager mActivityManager;
- @Mock
- private ListPreference mPreference;
- @Mock
- private PreferenceScreen mScreen;
-
- /**
- * 0: Standard limit
- * 1: No Background processes
- * 2: At most 1 process
- * 3: At most 2 processes
- * 4: At most 3 processes
- * 5: At most 4 processes
- */
- private String[] mListValues;
- private String[] mListSummaries;
- private Context mContext;
- private BackgroundProcessLimitPreferenceController mController;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mListValues = mContext.getResources()
- .getStringArray(com.android.settingslib.R.array.app_process_limit_values);
- mListSummaries = mContext.getResources()
- .getStringArray(com.android.settingslib.R.array.app_process_limit_entries);
- mController = spy(new BackgroundProcessLimitPreferenceController(mContext));
- doReturn(mActivityManager).when(mController).getActivityManagerService();
- when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
- mController.displayPreference(mScreen);
- }
-
- @Test
- public void onPreferenceChange_noBackgroundProcessSet_shouldSetToNoBackgroundProcess()
- throws RemoteException {
- mController.onPreferenceChange(mPreference, mListValues[1]);
-
- verify(mActivityManager).setProcessLimit(Integer.valueOf(mListValues[1]));
- }
-
- @Test
- public void onPreferenceChange_1ProcessSet_shouldSetTo1BackgroundProcess()
- throws RemoteException {
- mController.onPreferenceChange(mPreference, mListValues[2]);
-
- verify(mActivityManager).setProcessLimit(Integer.valueOf(mListValues[2]));
- }
-
- @Test
- public void updateState_noBackgroundProcessSet_shouldSetPreferenceToNoBackgroundProcess()
- throws RemoteException {
- when(mActivityManager.getProcessLimit()).thenReturn(Integer.valueOf(mListValues[1]));
-
- mController.updateState(mPreference);
-
- verify(mPreference).setValue(mListValues[1]);
- verify(mPreference).setSummary(mListSummaries[1]);
- }
-
- @Test
- public void updateState_1ProcessSet_shouldSetPreference1BackgroundProcess()
- throws RemoteException {
- when(mActivityManager.getProcessLimit()).thenReturn(Integer.valueOf(mListValues[2]));
-
- mController.updateState(mPreference);
-
- verify(mPreference).setValue(mListValues[2]);
- verify(mPreference).setSummary(mListSummaries[2]);
- }
-
- @Test
- public void updateState_veryHighLimit_shouldDefaultToStandardLimit() throws RemoteException {
- when(mActivityManager.getProcessLimit()).thenReturn(Integer.MAX_VALUE);
-
- mController.updateState(mPreference);
-
- verify(mPreference).setValue(mListValues[0]);
- verify(mPreference).setSummary(mListSummaries[0]);
- }
-
- @Test
- public void onDeveloperOptionsSwitchDisabled_shouldDisableAndResetPreference()
- throws RemoteException {
- mController.onDeveloperOptionsSwitchDisabled();
-
- verify(mPreference).setEnabled(false);
- verify(mActivityManager).setProcessLimit(-1);
- }
-}
diff --git a/tests/spa_unit/src/com/android/settings/print/PrintRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/print/PrintRepositoryTest.kt
new file mode 100644
index 00000000000..79d86da1a3a
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/print/PrintRepositoryTest.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.print
+
+import android.content.Context
+import android.content.pm.ResolveInfo
+import android.content.pm.ServiceInfo
+import android.graphics.drawable.Drawable
+import android.print.PrintManager
+import android.printservice.PrintServiceInfo
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+
+@RunWith(AndroidJUnit4::class)
+class PrintRepositoryTest {
+
+ private val printServiceInfo = PrintServiceInfo(
+ /* resolveInfo = */ ResolveInfo().apply { serviceInfo = MockServiceInfo },
+ /* settingsActivityName = */ "",
+ /* addPrintersActivityName = */ "",
+ /* advancedPrintOptionsActivityName = */ "",
+ )
+
+ private val mockPrintManager = mock {
+ on { getPrintServices(PrintManager.ALL_SERVICES) } doReturn listOf(printServiceInfo)
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(PrintManager::class.java) } doReturn mockPrintManager
+ }
+
+ private val repository = PrintRepository(context)
+
+ @Test
+ fun printServiceDisplayInfosFlow_title() = runBlocking {
+ val displayInfo = repository.printServiceDisplayInfosFlow().firstWithTimeoutOrNull()!!
+ .single()
+
+ assertThat(displayInfo.title).isEqualTo(LABEL)
+ }
+
+ @Test
+ fun printServiceDisplayInfosFlow_isEnabled() = runBlocking {
+ printServiceInfo.setIsEnabled(true)
+
+ val displayInfo = repository.printServiceDisplayInfosFlow().firstWithTimeoutOrNull()!!
+ .single()
+
+ assertThat(displayInfo.isEnabled).isTrue()
+ assertThat(displayInfo.summary)
+ .isEqualTo(context.getString(R.string.print_feature_state_on))
+ }
+
+ @Test
+ fun printServiceDisplayInfosFlow_notEnabled() = runBlocking {
+ printServiceInfo.setIsEnabled(false)
+
+ val displayInfo = repository.printServiceDisplayInfosFlow().firstWithTimeoutOrNull()!!
+ .single()
+
+ assertThat(displayInfo.isEnabled).isFalse()
+ assertThat(displayInfo.summary)
+ .isEqualTo(context.getString(R.string.print_feature_state_off))
+ }
+
+ @Test
+ fun printServiceDisplayInfosFlow_componentName() = runBlocking {
+ val displayInfo = repository.printServiceDisplayInfosFlow().firstWithTimeoutOrNull()!!
+ .single()
+
+ assertThat(displayInfo.componentName).isEqualTo("$PACKAGE_NAME/$SERVICE_NAME")
+ }
+
+ private companion object {
+ const val PACKAGE_NAME = "package.name"
+ const val SERVICE_NAME = "ServiceName"
+ const val LABEL = "Label"
+ val MockServiceInfo = mock {
+ on { loadLabel(any()) } doReturn LABEL
+ on { loadIcon(any()) } doReturn mock()
+ }.apply {
+ packageName = PACKAGE_NAME
+ name = SERVICE_NAME
+ }
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/print/PrintSettingsPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/print/PrintSettingsPageProviderTest.kt
new file mode 100644
index 00000000000..746816b52c8
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/print/PrintSettingsPageProviderTest.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.print
+
+import android.content.Context
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.isDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.SettingsActivity
+import com.android.settings.print.PrintRepository.PrintServiceDisplayInfo
+import com.android.settings.print.PrintSettingsFragment.EXTRA_CHECKED
+import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
+import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
+import com.android.settings.print.PrintSettingsPageProvider.PrintService
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argThat
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class PrintSettingsPageProviderTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ doNothing().whenever(mock).startActivity(any())
+ }
+
+ private val displayInfo = PrintServiceDisplayInfo(
+ title = TITLE,
+ isEnabled = true,
+ summary = SUMMARY,
+ icon = context.getDrawable(R.drawable.ic_settings_print)!!,
+ componentName = "ComponentName",
+ )
+
+ @Test
+ fun printService_titleDisplayed() {
+ composeTestRule.setContent {
+ PrintService(displayInfo)
+ }
+
+ composeTestRule.onNodeWithText(TITLE).isDisplayed()
+ }
+
+ @Test
+ fun printService_summaryDisplayed() {
+ composeTestRule.setContent {
+ PrintService(displayInfo)
+ }
+
+ composeTestRule.onNodeWithText(SUMMARY).isDisplayed()
+ }
+
+ @Test
+ fun printService_onClick() {
+ composeTestRule.setContent {
+ CompositionLocalProvider(LocalContext provides context) {
+ PrintService(displayInfo)
+ }
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ verify(context).startActivity(argThat {
+ val fragment = getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)
+ val arguments = getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
+ fragment == PrintServiceSettingsFragment::class.qualifiedName &&
+ arguments.getBoolean(EXTRA_CHECKED) == displayInfo.isEnabled &&
+ arguments.getString(EXTRA_TITLE) == displayInfo.title &&
+ arguments.getString(EXTRA_SERVICE_COMPONENT_NAME) == displayInfo.componentName
+ })
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val SUMMARY = "Summary"
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
index 79f53cab4ad..ad2ba555d86 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
@@ -28,6 +28,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
@@ -186,7 +188,7 @@ class SimOnboardingLabelSimTest {
}
@Test
- fun showDialog_clearContent_showOriginalDisplayName() {
+ fun showDialog_clearContent_saveBtnIsDisabled() {
preSetupContent()
composeTestRule.setContent {
@@ -196,15 +198,12 @@ class SimOnboardingLabelSimTest {
composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT).performTextClearance()
- assertEquals(
- composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
- .fetchSemanticsNode()
- .config[SemanticsProperties.EditableText].text, DISPLAY_NAME_1
- )
+ composeTestRule.onNodeWithText(context.getString(R.string.mobile_network_sim_name_rename))
+ .assertIsNotEnabled()
}
@Test
- fun showDialog_modifyContent_showModifiedDisplayName() {
+ fun showDialog_modifyContent_showAndSaveModifiedDisplayName() {
val inputData = "input_data"
preSetupContent()
@@ -232,7 +231,7 @@ class SimOnboardingLabelSimTest {
}
@Test
- fun showDialog_onlySpaceCharContent_showAndSaveOriginalDisplayName() {
+ fun showDialog_onlySpaceCharContent_saveBtnIsDisabled() {
val spaceChars = " ";
preSetupContent()
@@ -241,30 +240,12 @@ class SimOnboardingLabelSimTest {
}
// Simulate real operation,
- // 1. Click preference of DISPLAY_NAME_1
composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
- // 2. Input space chars to EditText view
+ composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT).performTextClearance()
composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT).performTextInput(spaceChars)
- // 3. Remove the string of DISPLAY_NAME_1 from EditText view
- repeat(DISPLAY_NAME_1.length) {
- composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
- .performKeyPress(KeyEvent(NativeKeyEvent(ACTION_DOWN, KEYCODE_FORWARD_DEL)))
- }
- // Due to this TextField with Text and EditText, it need fetch correct node to get correct
- // content.
- assertEquals(
- DISPLAY_NAME_1, composeTestRule.onNodeWithTag(TEXT_FIELD_INPUT)
- .fetchSemanticsNode()
- .config[SemanticsProperties.EditableText].text
- )
-
- // Click save button
composeTestRule.onNodeWithText(context.getString(R.string.mobile_network_sim_name_rename))
- .performClick()
-
- // Check preference's name is still DISPLAY_NAME_1
- composeTestRule.onNodeWithText(DISPLAY_NAME_1).assertExists()
+ .assertIsNotEnabled()
}
fun preSetupContent() {