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() {