From 98e3bfdebb99cb3f47b16cfde2181fcb553db8c8 Mon Sep 17 00:00:00 2001 From: Milton Wu Date: Mon, 8 Aug 2022 09:05:00 +0000 Subject: [PATCH 1/8] [DO NOT MERGE] Add FLAG_SECURE for ChooseLockPassword and Pattern Prevent ChooseLockPassword and ChooseLockPatten being projected to remote views, add FLAG_SECURE for these screens. Bug: 179725730 Test: Check these 2 screens not projected to chromecast Test: robo test for SetupChooseLockPatternTest ChooseLockPatternTest SetupChooseLockPasswordTest ChooseLockPasswordTest Change-Id: I7449a24427c966c1aa4280a7b7e7e70b60997cca Merged-In: I7449a24427c966c1aa4280a7b7e7e70b60997cca (cherry picked from commit 98239c0da68917a0622c24e9af16ce06768a68f2) --- .../settings/password/ChooseLockPassword.java | 2 ++ .../settings/password/ChooseLockPattern.java | 2 ++ .../password/ChooseLockPasswordTest.java | 16 ++++++++++++++++ .../settings/password/ChooseLockPatternTest.java | 10 ++++++++++ 4 files changed, 30 insertions(+) diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java index c03362510d0..24bfe46bf02 100644 --- a/src/com/android/settings/password/ChooseLockPassword.java +++ b/src/com/android/settings/password/ChooseLockPassword.java @@ -61,6 +61,7 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.widget.ImeAwareEditText; import android.widget.TextView; @@ -205,6 +206,7 @@ public class ChooseLockPassword extends SettingsActivity { ThemeHelper.trySetDynamicColor(this); super.onCreate(savedInstanceState); findViewById(R.id.content_parent).setFitsSystemWindows(false); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } public static class ChooseLockPasswordFragment extends InstrumentedFragment diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java index 8464a2ffd7c..d45bb2e21e8 100644 --- a/src/com/android/settings/password/ChooseLockPattern.java +++ b/src/com/android/settings/password/ChooseLockPattern.java @@ -37,6 +37,7 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.widget.TextView; import androidx.fragment.app.Fragment; @@ -171,6 +172,7 @@ public class ChooseLockPattern extends SettingsActivity { ThemeHelper.trySetDynamicColor(this); super.onCreate(savedInstanceState); findViewById(R.id.content_parent).setFitsSystemWindows(false); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } @Override diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java index e789b61f188..5299190051f 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockPasswordTest.java @@ -27,6 +27,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static com.android.internal.widget.LockPatternUtils.PASSWORD_TYPE_KEY; import static com.android.settings.password.ChooseLockGeneric.CONFIRM_CREDENTIALS; @@ -162,6 +163,21 @@ public class ChooseLockPasswordTest { .isNotNull(); } + @Test + public void activity_shouldHaveSecureFlag() { + PasswordPolicy policy = new PasswordPolicy(); + policy.quality = PASSWORD_QUALITY_ALPHABETIC; + policy.length = 10; + + Intent intent = createIntentForPasswordValidation( + /* minMetrics */ policy.getMinMetrics(), + /* minComplexity= */ PASSWORD_COMPLEXITY_NONE, + /* passwordType= */ PASSWORD_QUALITY_ALPHABETIC); + ChooseLockPassword activity = buildChooseLockPasswordActivity(intent); + final int flags = activity.getWindow().getAttributes().flags; + assertThat(flags & FLAG_SECURE).isEqualTo(FLAG_SECURE); + } + @Test public void processAndValidatePasswordRequirements_noMinPasswordComplexity() { PasswordPolicy policy = new PasswordPolicy(); diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java index f5cc39435ad..1fc10fc75f2 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockPatternTest.java @@ -16,6 +16,8 @@ package com.android.settings.password; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; + import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -107,6 +109,14 @@ public class ChooseLockPatternTest { assertThat(iconView.getVisibility()).isEqualTo(View.GONE); } + @Test + public void activity_shouldHaveSecureFlag() { + final ChooseLockPattern activity = Robolectric.buildActivity( + ChooseLockPattern.class, new IntentBuilder(application).build()).setup().get(); + final int flags = activity.getWindow().getAttributes().flags; + assertThat(flags & FLAG_SECURE).isEqualTo(FLAG_SECURE); + } + private ChooseLockPattern createActivity(boolean addFingerprintExtra) { return Robolectric.buildActivity( ChooseLockPattern.class, From 99fd7e49e6525cea3219617b4a4382b21e3d25f1 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 28 Sep 2022 13:47:33 +0800 Subject: [PATCH 2/8] Fix app launch button not shown This is caused by using getLaunchIntentForPackage() without specific which user. Use packageManagerAsUser to fix this issue. Bug: 236346018 Test: Manual with Settings App Change-Id: I7d71f9f46b18299245e654c9eef78232cb5e0392 --- .../android/settings/spa/app/appsettings/AppButtons.kt | 2 +- .../settings/spa/app/appsettings/AppLaunchButton.kt | 8 ++++---- .../settings/spa/app/appsettings/PackageInfoPresenter.kt | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/com/android/settings/spa/app/appsettings/AppButtons.kt b/src/com/android/settings/spa/app/appsettings/AppButtons.kt index 89f3b138df1..5082bea22e8 100644 --- a/src/com/android/settings/spa/app/appsettings/AppButtons.kt +++ b/src/com/android/settings/spa/app/appsettings/AppButtons.kt @@ -32,7 +32,7 @@ fun AppButtons(packageInfoPresenter: PackageInfoPresenter) { } private class AppButtonsHolder(private val packageInfoPresenter: PackageInfoPresenter) { - private val appLaunchButton = AppLaunchButton(context = packageInfoPresenter.context) + private val appLaunchButton = AppLaunchButton(packageInfoPresenter) private val appDisableButton = AppDisableButton(packageInfoPresenter) private val appUninstallButton = AppUninstallButton(packageInfoPresenter) private val appForceStopButton = AppForceStopButton(packageInfoPresenter) diff --git a/src/com/android/settings/spa/app/appsettings/AppLaunchButton.kt b/src/com/android/settings/spa/app/appsettings/AppLaunchButton.kt index a983b675938..9f81661b03b 100644 --- a/src/com/android/settings/spa/app/appsettings/AppLaunchButton.kt +++ b/src/com/android/settings/spa/app/appsettings/AppLaunchButton.kt @@ -16,7 +16,6 @@ package com.android.settings.spa.app.appsettings -import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo @@ -26,11 +25,12 @@ import com.android.settings.R import com.android.settingslib.spa.widget.button.ActionButton import com.android.settingslib.spaprivileged.model.app.userHandle -class AppLaunchButton(private val context: Context) { - private val packageManager = context.packageManager +class AppLaunchButton(packageInfoPresenter: PackageInfoPresenter) { + private val context = packageInfoPresenter.context + private val packageManagerAsUser = packageInfoPresenter.packageManagerAsUser fun getActionButton(packageInfo: PackageInfo): ActionButton? = - packageManager.getLaunchIntentForPackage(packageInfo.packageName)?.let { intent -> + packageManagerAsUser.getLaunchIntentForPackage(packageInfo.packageName)?.let { intent -> launchButton(intent, packageInfo.applicationInfo) } diff --git a/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt index eb39c4ff636..cd672d3d7d1 100644 --- a/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt +++ b/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt @@ -48,7 +48,7 @@ class PackageInfoPresenter( private val coroutineScope: CoroutineScope, ) { private val metricsFeatureProvider = FeatureFactory.getFactory(context).metricsFeatureProvider - private val packageManager by lazy { + val packageManagerAsUser: PackageManager by lazy { context.createContextAsUser(UserHandle.of(userId), 0).packageManager } private val _flow: MutableStateFlow = MutableStateFlow(null) @@ -85,7 +85,7 @@ class PackageInfoPresenter( fun enable() { logAction(SettingsEnums.ACTION_SETTINGS_ENABLE_APP) coroutineScope.launch(Dispatchers.IO) { - packageManager.setApplicationEnabledSetting( + packageManagerAsUser.setApplicationEnabledSetting( packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0 ) notifyChange() @@ -96,7 +96,7 @@ class PackageInfoPresenter( fun disable() { logAction(SettingsEnums.ACTION_SETTINGS_DISABLE_APP) coroutineScope.launch(Dispatchers.IO) { - packageManager.setApplicationEnabledSetting( + packageManagerAsUser.setApplicationEnabledSetting( packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0 ) notifyChange() From 9bad2fd7acac3c8482c5a3b579ca2bc9a6d6cc26 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 28 Sep 2022 20:28:41 +0800 Subject: [PATCH 3/8] Add install & clear app button for instant app These 2 buttons are only for instant apps. Bug: 236346018 Test: App Settings page with Instant App Change-Id: Ibdb27ff78172a59267c16bdfe5a256353fc91b26 --- .../spa/app/appsettings/AppButtons.kt | 5 ++ .../spa/app/appsettings/AppClearButton.kt | 80 +++++++++++++++++++ .../spa/app/appsettings/AppDisableButton.kt | 2 +- .../spa/app/appsettings/AppInstallButton.kt | 46 +++++++++++ .../spa/app/appsettings/AppUninstallButton.kt | 2 +- .../app/appsettings/PackageInfoPresenter.kt | 14 +++- 6 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 src/com/android/settings/spa/app/appsettings/AppClearButton.kt create mode 100644 src/com/android/settings/spa/app/appsettings/AppInstallButton.kt diff --git a/src/com/android/settings/spa/app/appsettings/AppButtons.kt b/src/com/android/settings/spa/app/appsettings/AppButtons.kt index 5082bea22e8..30fcdb77515 100644 --- a/src/com/android/settings/spa/app/appsettings/AppButtons.kt +++ b/src/com/android/settings/spa/app/appsettings/AppButtons.kt @@ -33,8 +33,10 @@ fun AppButtons(packageInfoPresenter: PackageInfoPresenter) { private class AppButtonsHolder(private val packageInfoPresenter: PackageInfoPresenter) { private val appLaunchButton = AppLaunchButton(packageInfoPresenter) + private val appInstallButton = AppInstallButton(packageInfoPresenter) private val appDisableButton = AppDisableButton(packageInfoPresenter) private val appUninstallButton = AppUninstallButton(packageInfoPresenter) + private val appClearButton = AppClearButton(packageInfoPresenter) private val appForceStopButton = AppForceStopButton(packageInfoPresenter) @Composable @@ -46,14 +48,17 @@ private class AppButtonsHolder(private val packageInfoPresenter: PackageInfoPres private fun getActionButtons(packageInfo: PackageInfo): List = listOfNotNull( appLaunchButton.getActionButton(packageInfo), + appInstallButton.getActionButton(packageInfo), appDisableButton.getActionButton(packageInfo), appUninstallButton.getActionButton(packageInfo), + appClearButton.getActionButton(packageInfo), appForceStopButton.getActionButton(packageInfo), ) @Composable fun Dialogs() { appDisableButton.DisableConfirmDialog() + appClearButton.ClearConfirmDialog() appForceStopButton.ForceStopConfirmDialog() } } diff --git a/src/com/android/settings/spa/app/appsettings/AppClearButton.kt b/src/com/android/settings/spa/app/appsettings/AppClearButton.kt new file mode 100644 index 00000000000..e16e09168d3 --- /dev/null +++ b/src/com/android/settings/spa/app/appsettings/AppClearButton.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 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.spa.app.appsettings + +import android.content.pm.PackageInfo +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.res.stringResource +import com.android.settings.R +import com.android.settingslib.spa.widget.button.ActionButton + +class AppClearButton( + private val packageInfoPresenter: PackageInfoPresenter, +) { + private val context = packageInfoPresenter.context + + private var openConfirmDialog by mutableStateOf(false) + + fun getActionButton(packageInfo: PackageInfo): ActionButton? { + val app = packageInfo.applicationInfo + if (!app.isInstantApp) return null + + return clearButton() + } + + private fun clearButton() = ActionButton( + text = context.getString(R.string.clear_instant_app_data), + imageVector = Icons.Outlined.Delete, + ) { openConfirmDialog = true } + + @Composable + fun ClearConfirmDialog() { + if (!openConfirmDialog) return + AlertDialog( + onDismissRequest = { openConfirmDialog = false }, + confirmButton = { + TextButton( + onClick = { + openConfirmDialog = false + packageInfoPresenter.clearInstantApp() + }, + ) { + Text(stringResource(R.string.clear_instant_app_data)) + } + }, + dismissButton = { + TextButton(onClick = { openConfirmDialog = false }) { + Text(stringResource(R.string.cancel)) + } + }, + title = { + Text(stringResource(R.string.clear_instant_app_data)) + }, + text = { + Text(stringResource(R.string.clear_instant_app_confirmation)) + }, + ) + } +} diff --git a/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt b/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt index ff3fbe3c10e..365da7c0036 100644 --- a/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt +++ b/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt @@ -55,7 +55,7 @@ class AppDisableButton( fun getActionButton(packageInfo: PackageInfo): ActionButton? { val app = packageInfo.applicationInfo - if (!app.hasFlag(ApplicationInfo.FLAG_SYSTEM)) return null + if (!app.isSystemApp) return null return when { app.enabled && !app.isDisabledUntilUsed -> { diff --git a/src/com/android/settings/spa/app/appsettings/AppInstallButton.kt b/src/com/android/settings/spa/app/appsettings/AppInstallButton.kt new file mode 100644 index 00000000000..94290e6a2ad --- /dev/null +++ b/src/com/android/settings/spa/app/appsettings/AppInstallButton.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 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.spa.app.appsettings + +import android.content.Intent +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.FileDownload +import com.android.settings.R +import com.android.settings.applications.AppStoreUtil +import com.android.settingslib.spa.widget.button.ActionButton +import com.android.settingslib.spaprivileged.model.app.userHandle + +class AppInstallButton(private val packageInfoPresenter: PackageInfoPresenter) { + private val context = packageInfoPresenter.context + + fun getActionButton(packageInfo: PackageInfo): ActionButton? { + val app = packageInfo.applicationInfo + if (!app.isInstantApp) return null + + return AppStoreUtil.getAppStoreLink(packageInfoPresenter.contextAsUser, app.packageName) + ?.let { intent -> installButton(intent, app) } + } + + private fun installButton(intent: Intent, app: ApplicationInfo) = ActionButton( + text = context.getString(R.string.install_text), + imageVector = Icons.Outlined.FileDownload, + ) { + context.startActivityAsUser(intent, app.userHandle) + } +} diff --git a/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt b/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt index 53b36b99d11..3458f1ddc5e 100644 --- a/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt +++ b/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt @@ -46,7 +46,7 @@ class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) fun getActionButton(packageInfo: PackageInfo): ActionButton? { val app = packageInfo.applicationInfo - if (app.hasFlag(ApplicationInfo.FLAG_SYSTEM)) return null + if (app.isSystemApp || app.isInstantApp) return null return uninstallButton(app = app, enabled = isUninstallButtonEnabled(app)) } diff --git a/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt index cd672d3d7d1..b164d7c5064 100644 --- a/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt +++ b/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt @@ -48,9 +48,8 @@ class PackageInfoPresenter( private val coroutineScope: CoroutineScope, ) { private val metricsFeatureProvider = FeatureFactory.getFactory(context).metricsFeatureProvider - val packageManagerAsUser: PackageManager by lazy { - context.createContextAsUser(UserHandle.of(userId), 0).packageManager - } + val contextAsUser by lazy { context.createContextAsUser(UserHandle.of(userId), 0) } + val packageManagerAsUser: PackageManager by lazy { contextAsUser.packageManager } private val _flow: MutableStateFlow = MutableStateFlow(null) val flow: StateFlow = _flow @@ -103,6 +102,15 @@ class PackageInfoPresenter( } } + /** Clears this instant app. */ + fun clearInstantApp() { + logAction(SettingsEnums.ACTION_SETTINGS_CLEAR_INSTANT_APP) + coroutineScope.launch(Dispatchers.IO) { + packageManagerAsUser.deletePackageAsUser(packageName, null, 0, userId) + notifyChange() + } + } + /** Force stops this package. */ fun forceStop() { logAction(SettingsEnums.ACTION_APP_FORCE_STOP) From 2f753c7ba7afc880b841ef4677db317e340d1fed Mon Sep 17 00:00:00 2001 From: Tom Hsu Date: Wed, 28 Sep 2022 13:37:36 +0000 Subject: [PATCH 4/8] Revert "[Languages] Add new information to system language page." This reverts commit 3cbb6f86802ade7aac51234a070b6f623d829081. Reason for revert: b/248456615 Bug: b/248456615 Test: Manual test pass Change-Id: I3bc5088784ed97f69ffadb0ea69078bfcf7dc39d --- res/layout/locale_drag_cell.xml | 11 --- res/values/strings.xml | 24 +----- res/xml/languages.xml | 42 ----------- .../LocaleDragAndDropAdapter.java | 3 +- .../settings/localepicker/LocaleDragCell.java | 10 --- .../LocaleHelperPreferenceController.java | 74 ------------------- .../localepicker/LocaleListEditor.java | 27 ++----- .../LocaleHelperPreferenceControllerTest.java | 59 --------------- 8 files changed, 12 insertions(+), 238 deletions(-) delete mode 100644 res/xml/languages.xml delete mode 100644 src/com/android/settings/localepicker/LocaleHelperPreferenceController.java delete mode 100644 tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java diff --git a/res/layout/locale_drag_cell.xml b/res/layout/locale_drag_cell.xml index 47bf70a473e..7b932f39a02 100644 --- a/res/layout/locale_drag_cell.xml +++ b/res/layout/locale_drag_cell.xml @@ -56,17 +56,6 @@ android:layout_toStartOf="@+id/dragHandle" android:layout_below="@id/label"/> - - Languages - - Preferred language order - - - System language - Remove @@ -350,13 +344,13 @@ Preferred Language - App languages + App Languages Set the language for each app - App language + App Language Suggested languages @@ -379,18 +373,6 @@ Only apps that support language selection are shown here. - - Your system, apps, and websites use the first supported language from your preferred languages. - - - To select a language for each app, go to app language settings. - - - Learn more about languages - - - https://support.google.com/android?p=per_language_app_settings - Remove selected language? @@ -406,7 +388,7 @@ Keep at least one preferred language - Not available as system language + May not be available in some apps Move up diff --git a/res/xml/languages.xml b/res/xml/languages.xml deleted file mode 100644 index ed65df099d0..00000000000 --- a/res/xml/languages.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java index b3c2e3071af..ab9110d01ab 100644 --- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java +++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; + class LocaleDragAndDropAdapter extends RecyclerView.Adapter { @@ -153,10 +154,8 @@ class LocaleDragAndDropAdapter final LocaleDragCell dragCell = holder.getLocaleDragCell(); final String label = feedItem.getFullNameNative(); final String description = feedItem.getFullNameInUiLanguage(); - dragCell.setLabelAndDescription(label, description); dragCell.setLocalized(feedItem.isTranslated()); - dragCell.setCurrentDefault(feedItem.getLocale().equals(Locale.getDefault())); dragCell.setMiniLabel(mNumberFormatter.format(i + 1)); dragCell.setShowCheckbox(mRemoveMode); dragCell.setShowMiniLabel(!mRemoveMode); diff --git a/src/com/android/settings/localepicker/LocaleDragCell.java b/src/com/android/settings/localepicker/LocaleDragCell.java index 2f4cfefefb1..ea86189b018 100644 --- a/src/com/android/settings/localepicker/LocaleDragCell.java +++ b/src/com/android/settings/localepicker/LocaleDragCell.java @@ -33,7 +33,6 @@ class LocaleDragCell extends RelativeLayout { private CheckBox mCheckbox; private TextView mMiniLabel; private TextView mLocalized; - private TextView mCurrentDefault; private ImageView mDragHandle; public LocaleDragCell(Context context, AttributeSet attrs) { @@ -45,7 +44,6 @@ class LocaleDragCell extends RelativeLayout { super.onFinishInflate(); mLabel = (TextView) findViewById(R.id.label); mLocalized = (TextView) findViewById(R.id.l10nWarn); - mCurrentDefault = (TextView) findViewById(R.id.default_locale); mMiniLabel = (TextView) findViewById(R.id.miniLabel); mCheckbox = (CheckBox) findViewById(R.id.checkbox); mDragHandle = (ImageView) findViewById(R.id.dragHandle); @@ -102,14 +100,6 @@ class LocaleDragCell extends RelativeLayout { invalidate(); } - /** - * Indicate current locale is system default. - */ - public void setCurrentDefault(boolean isCurrentDefault) { - mCurrentDefault.setVisibility(isCurrentDefault ? VISIBLE : GONE); - invalidate(); - } - public ImageView getDragHandle() { return mDragHandle; } diff --git a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java deleted file mode 100644 index 05c740139cc..00000000000 --- a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2022 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.localepicker; - -import android.content.Context; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.PreferenceScreen; - -import com.android.settings.R; -import com.android.settingslib.HelpUtils; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.widget.FooterPreference; - -/** - * A controller to update current locale information of application. - */ -public class LocaleHelperPreferenceController extends AbstractPreferenceController { - private static final String TAG = LocaleHelperPreferenceController.class.getSimpleName(); - - private static final String KEY_FOOTER_LANGUAGE_PICKER = "footer_languages_picker"; - - public LocaleHelperPreferenceController(Context context) { - super(context); - } - - @Override - public boolean isAvailable() { - return true; - } - - @Override - public String getPreferenceKey() { - return KEY_FOOTER_LANGUAGE_PICKER; - } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - FooterPreference footerPreference = screen.findPreference(getPreferenceKey()); - updateFooterPreference(footerPreference); - } - - @VisibleForTesting - void updateFooterPreference(FooterPreference footerPreference) { - if (footerPreference != null) { - footerPreference.setLearnMoreAction(v -> openLocaleLearnMoreLink()); - footerPreference.setLearnMoreText(mContext.getString( - R.string.desc_locale_helper_footer_general)); - } - } - - private void openLocaleLearnMoreLink() { - mContext.startActivity( - HelpUtils.getHelpIntent( - mContext, - mContext.getString(R.string.link_locale_picker_footer_learn_more), - /*backupContext=*/"")); - } -} diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java index 9db3468c9e1..eac2dd1df3c 100644 --- a/src/com/android/settings/localepicker/LocaleListEditor.java +++ b/src/com/android/settings/localepicker/LocaleListEditor.java @@ -36,7 +36,6 @@ import android.widget.TextView; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; -import androidx.preference.PreferenceScreen; import androidx.recyclerview.widget.RecyclerView; import com.android.internal.app.LocalePicker; @@ -47,7 +46,6 @@ import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexableRaw; -import com.android.settingslib.widget.LayoutPreference; import java.util.ArrayList; import java.util.List; @@ -66,7 +64,6 @@ public class LocaleListEditor extends RestrictedSettingsFragment { private static final int REQUEST_LOCALE_PICKER = 0; private static final String INDEX_KEY_ADD_LANGUAGE = "add_language"; - private static final String KEY_LANGUAGES_PICKER = "languages_picker"; private LocaleDragAndDropAdapter mAdapter; private Menu mMenu; @@ -75,9 +72,6 @@ public class LocaleListEditor extends RestrictedSettingsFragment { private boolean mShowingRemoveDialog; private boolean mIsUiRestricted; - private LayoutPreference mLocalePickerPreference; - private LocaleHelperPreferenceController mLocaleHelperPreferenceController; - public LocaleListEditor() { super(DISALLOW_CONFIG_LOCALE); } @@ -92,14 +86,6 @@ public class LocaleListEditor extends RestrictedSettingsFragment { super.onCreate(savedInstanceState); setHasOptionsMenu(true); - addPreferencesFromResource(R.xml.languages); - final Activity activity = getActivity(); - activity.setTitle(R.string.language_picker_title); - mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(activity); - final PreferenceScreen screen = getPreferenceScreen(); - mLocalePickerPreference = screen.findPreference(KEY_LANGUAGES_PICKER); - mLocaleHelperPreferenceController.displayPreference(screen); - LocaleStore.fillCache(this.getContext()); final List feedsList = getUserLocaleList(); mAdapter = new LocaleDragAndDropAdapter(this.getContext(), feedsList); @@ -107,8 +93,11 @@ public class LocaleListEditor extends RestrictedSettingsFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstState) { - configureDragAndDrop(mLocalePickerPreference); - return super.onCreateView(inflater, container, savedInstState); + final View result = super.onCreateView(inflater, container, savedInstState); + final View myLayout = inflater.inflate(R.layout.locale_order_list, (ViewGroup) result); + + configureDragAndDrop(myLayout); + return result; } @Override @@ -298,8 +287,8 @@ public class LocaleListEditor extends RestrictedSettingsFragment { return result; } - private void configureDragAndDrop(LayoutPreference layout) { - final RecyclerView list = layout.findViewById(R.id.dragList); + private void configureDragAndDrop(View view) { + final RecyclerView list = view.findViewById(R.id.dragList); final LocaleLinearLayoutManager llm = new LocaleLinearLayoutManager(getContext(), mAdapter); llm.setAutoMeasureEnabled(true); list.setLayoutManager(llm); @@ -308,7 +297,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment { mAdapter.setRecyclerView(list); list.setAdapter(mAdapter); - mAddLanguage = layout.findViewById(R.id.add_language); + mAddLanguage = view.findViewById(R.id.add_language); mAddLanguage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java b/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java deleted file mode 100644 index 31b8e794480..00000000000 --- a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2022 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.localepicker; - -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.os.Looper; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.settingslib.widget.FooterPreference; - -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 LocaleHelperPreferenceControllerTest { - private Context mContext; - private LocaleHelperPreferenceController mLocaleHelperPreferenceController; - - @Mock - private FooterPreference mMockFooterPreference; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - if (Looper.myLooper() == null) { - Looper.prepare(); - } - mContext = ApplicationProvider.getApplicationContext(); - mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(mContext); - } - - @Test - public void updateFooterPreference_setFooterPreference_hasClickAction() { - mLocaleHelperPreferenceController.updateFooterPreference(mMockFooterPreference); - verify(mMockFooterPreference).setLearnMoreText(anyString()); - } -} From a4c78e3820c9bc956fe72a293780a01708bac77d Mon Sep 17 00:00:00 2001 From: Tom Hsu Date: Wed, 28 Sep 2022 13:37:36 +0000 Subject: [PATCH 5/8] Revert "[Languages] Add new information to system language page." This reverts commit 3cbb6f86802ade7aac51234a070b6f623d829081. Reason for revert: b/248456615 Change-Id: I3bc5088784ed97f69ffadb0ea69078bfcf7dc39d Merged-In: I3bc5088784ed97f69ffadb0ea69078bfcf7dc39d --- res/layout/locale_drag_cell.xml | 11 --- res/values/strings.xml | 24 +----- res/xml/languages.xml | 42 ----------- .../LocaleDragAndDropAdapter.java | 3 +- .../settings/localepicker/LocaleDragCell.java | 10 --- .../LocaleHelperPreferenceController.java | 74 ------------------- .../localepicker/LocaleListEditor.java | 27 ++----- .../LocaleHelperPreferenceControllerTest.java | 59 --------------- 8 files changed, 12 insertions(+), 238 deletions(-) delete mode 100644 res/xml/languages.xml delete mode 100644 src/com/android/settings/localepicker/LocaleHelperPreferenceController.java delete mode 100644 tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java diff --git a/res/layout/locale_drag_cell.xml b/res/layout/locale_drag_cell.xml index 47bf70a473e..7b932f39a02 100644 --- a/res/layout/locale_drag_cell.xml +++ b/res/layout/locale_drag_cell.xml @@ -56,17 +56,6 @@ android:layout_toStartOf="@+id/dragHandle" android:layout_below="@id/label"/> - - Languages - - Preferred language order - - - System language - Remove @@ -503,13 +497,13 @@ Preferred Language - App languages + App Languages Set the language for each app - App language + App Language Suggested languages @@ -532,18 +526,6 @@ Only apps that support language selection are shown here. - - Your system, apps, and websites use the first supported language from your preferred languages. - - - To select a language for each app, go to app language settings. - - - Learn more about languages - - - https://support.google.com/android?p=per_language_app_settings - Remove selected language? @@ -559,7 +541,7 @@ Keep at least one preferred language - Not available as system language + May not be available in some apps Move up diff --git a/res/xml/languages.xml b/res/xml/languages.xml deleted file mode 100644 index 0f455407645..00000000000 --- a/res/xml/languages.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java index b3c2e3071af..ab9110d01ab 100644 --- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java +++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; + class LocaleDragAndDropAdapter extends RecyclerView.Adapter { @@ -153,10 +154,8 @@ class LocaleDragAndDropAdapter final LocaleDragCell dragCell = holder.getLocaleDragCell(); final String label = feedItem.getFullNameNative(); final String description = feedItem.getFullNameInUiLanguage(); - dragCell.setLabelAndDescription(label, description); dragCell.setLocalized(feedItem.isTranslated()); - dragCell.setCurrentDefault(feedItem.getLocale().equals(Locale.getDefault())); dragCell.setMiniLabel(mNumberFormatter.format(i + 1)); dragCell.setShowCheckbox(mRemoveMode); dragCell.setShowMiniLabel(!mRemoveMode); diff --git a/src/com/android/settings/localepicker/LocaleDragCell.java b/src/com/android/settings/localepicker/LocaleDragCell.java index 2f4cfefefb1..ea86189b018 100644 --- a/src/com/android/settings/localepicker/LocaleDragCell.java +++ b/src/com/android/settings/localepicker/LocaleDragCell.java @@ -33,7 +33,6 @@ class LocaleDragCell extends RelativeLayout { private CheckBox mCheckbox; private TextView mMiniLabel; private TextView mLocalized; - private TextView mCurrentDefault; private ImageView mDragHandle; public LocaleDragCell(Context context, AttributeSet attrs) { @@ -45,7 +44,6 @@ class LocaleDragCell extends RelativeLayout { super.onFinishInflate(); mLabel = (TextView) findViewById(R.id.label); mLocalized = (TextView) findViewById(R.id.l10nWarn); - mCurrentDefault = (TextView) findViewById(R.id.default_locale); mMiniLabel = (TextView) findViewById(R.id.miniLabel); mCheckbox = (CheckBox) findViewById(R.id.checkbox); mDragHandle = (ImageView) findViewById(R.id.dragHandle); @@ -102,14 +100,6 @@ class LocaleDragCell extends RelativeLayout { invalidate(); } - /** - * Indicate current locale is system default. - */ - public void setCurrentDefault(boolean isCurrentDefault) { - mCurrentDefault.setVisibility(isCurrentDefault ? VISIBLE : GONE); - invalidate(); - } - public ImageView getDragHandle() { return mDragHandle; } diff --git a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java deleted file mode 100644 index 05c740139cc..00000000000 --- a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2022 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.localepicker; - -import android.content.Context; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.PreferenceScreen; - -import com.android.settings.R; -import com.android.settingslib.HelpUtils; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.widget.FooterPreference; - -/** - * A controller to update current locale information of application. - */ -public class LocaleHelperPreferenceController extends AbstractPreferenceController { - private static final String TAG = LocaleHelperPreferenceController.class.getSimpleName(); - - private static final String KEY_FOOTER_LANGUAGE_PICKER = "footer_languages_picker"; - - public LocaleHelperPreferenceController(Context context) { - super(context); - } - - @Override - public boolean isAvailable() { - return true; - } - - @Override - public String getPreferenceKey() { - return KEY_FOOTER_LANGUAGE_PICKER; - } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - FooterPreference footerPreference = screen.findPreference(getPreferenceKey()); - updateFooterPreference(footerPreference); - } - - @VisibleForTesting - void updateFooterPreference(FooterPreference footerPreference) { - if (footerPreference != null) { - footerPreference.setLearnMoreAction(v -> openLocaleLearnMoreLink()); - footerPreference.setLearnMoreText(mContext.getString( - R.string.desc_locale_helper_footer_general)); - } - } - - private void openLocaleLearnMoreLink() { - mContext.startActivity( - HelpUtils.getHelpIntent( - mContext, - mContext.getString(R.string.link_locale_picker_footer_learn_more), - /*backupContext=*/"")); - } -} diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java index 9db3468c9e1..eac2dd1df3c 100644 --- a/src/com/android/settings/localepicker/LocaleListEditor.java +++ b/src/com/android/settings/localepicker/LocaleListEditor.java @@ -36,7 +36,6 @@ import android.widget.TextView; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; -import androidx.preference.PreferenceScreen; import androidx.recyclerview.widget.RecyclerView; import com.android.internal.app.LocalePicker; @@ -47,7 +46,6 @@ import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexableRaw; -import com.android.settingslib.widget.LayoutPreference; import java.util.ArrayList; import java.util.List; @@ -66,7 +64,6 @@ public class LocaleListEditor extends RestrictedSettingsFragment { private static final int REQUEST_LOCALE_PICKER = 0; private static final String INDEX_KEY_ADD_LANGUAGE = "add_language"; - private static final String KEY_LANGUAGES_PICKER = "languages_picker"; private LocaleDragAndDropAdapter mAdapter; private Menu mMenu; @@ -75,9 +72,6 @@ public class LocaleListEditor extends RestrictedSettingsFragment { private boolean mShowingRemoveDialog; private boolean mIsUiRestricted; - private LayoutPreference mLocalePickerPreference; - private LocaleHelperPreferenceController mLocaleHelperPreferenceController; - public LocaleListEditor() { super(DISALLOW_CONFIG_LOCALE); } @@ -92,14 +86,6 @@ public class LocaleListEditor extends RestrictedSettingsFragment { super.onCreate(savedInstanceState); setHasOptionsMenu(true); - addPreferencesFromResource(R.xml.languages); - final Activity activity = getActivity(); - activity.setTitle(R.string.language_picker_title); - mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(activity); - final PreferenceScreen screen = getPreferenceScreen(); - mLocalePickerPreference = screen.findPreference(KEY_LANGUAGES_PICKER); - mLocaleHelperPreferenceController.displayPreference(screen); - LocaleStore.fillCache(this.getContext()); final List feedsList = getUserLocaleList(); mAdapter = new LocaleDragAndDropAdapter(this.getContext(), feedsList); @@ -107,8 +93,11 @@ public class LocaleListEditor extends RestrictedSettingsFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstState) { - configureDragAndDrop(mLocalePickerPreference); - return super.onCreateView(inflater, container, savedInstState); + final View result = super.onCreateView(inflater, container, savedInstState); + final View myLayout = inflater.inflate(R.layout.locale_order_list, (ViewGroup) result); + + configureDragAndDrop(myLayout); + return result; } @Override @@ -298,8 +287,8 @@ public class LocaleListEditor extends RestrictedSettingsFragment { return result; } - private void configureDragAndDrop(LayoutPreference layout) { - final RecyclerView list = layout.findViewById(R.id.dragList); + private void configureDragAndDrop(View view) { + final RecyclerView list = view.findViewById(R.id.dragList); final LocaleLinearLayoutManager llm = new LocaleLinearLayoutManager(getContext(), mAdapter); llm.setAutoMeasureEnabled(true); list.setLayoutManager(llm); @@ -308,7 +297,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment { mAdapter.setRecyclerView(list); list.setAdapter(mAdapter); - mAddLanguage = layout.findViewById(R.id.add_language); + mAddLanguage = view.findViewById(R.id.add_language); mAddLanguage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java b/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java deleted file mode 100644 index 31b8e794480..00000000000 --- a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2022 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.localepicker; - -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.os.Looper; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.settingslib.widget.FooterPreference; - -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 LocaleHelperPreferenceControllerTest { - private Context mContext; - private LocaleHelperPreferenceController mLocaleHelperPreferenceController; - - @Mock - private FooterPreference mMockFooterPreference; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - if (Looper.myLooper() == null) { - Looper.prepare(); - } - mContext = ApplicationProvider.getApplicationContext(); - mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(mContext); - } - - @Test - public void updateFooterPreference_setFooterPreference_hasClickAction() { - mLocaleHelperPreferenceController.updateFooterPreference(mMockFooterPreference); - verify(mMockFooterPreference).setLearnMoreText(anyString()); - } -} From 9b65b1583d5588b83a6631f334bcc6daf5d718e9 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Thu, 29 Sep 2022 13:17:12 +0800 Subject: [PATCH 6/8] Add uninstall updates & uninstall for all users These are the items in the more options of App Settings page. Uninstall updates only shows up for updated system app. Uninstall for all users only shows up for primary user when a non-system app is installed on multiple users. Bug: 236346018 Test: Manual on App Settings page Change-Id: I7530ce5215ed921c0a2b767dce56cbfd9a2b0137 --- .../app/appsettings/AppButtonRepository.kt | 30 +++---- .../spa/app/appsettings/AppDisableButton.kt | 15 ++-- .../spa/app/appsettings/AppForceStopButton.kt | 3 +- .../spa/app/appsettings/AppSettings.kt | 11 ++- .../app/appsettings/AppSettingsMoreOptions.kt | 85 +++++++++++++++++++ .../spa/app/appsettings/AppUninstallButton.kt | 28 ++---- .../app/appsettings/PackageInfoPresenter.kt | 23 ++++- 7 files changed, 140 insertions(+), 55 deletions(-) create mode 100644 src/com/android/settings/spa/app/appsettings/AppSettingsMoreOptions.kt diff --git a/src/com/android/settings/spa/app/appsettings/AppButtonRepository.kt b/src/com/android/settings/spa/app/appsettings/AppButtonRepository.kt index e1ee766603f..c5e84ae886d 100644 --- a/src/com/android/settings/spa/app/appsettings/AppButtonRepository.kt +++ b/src/com/android/settings/spa/app/appsettings/AppButtonRepository.kt @@ -17,19 +17,18 @@ package com.android.settings.spa.app.appsettings import android.app.ActivityManager -import android.app.admin.DevicePolicyManager +import android.content.ComponentName import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.ResolveInfo -import android.os.UserManager -import com.android.settingslib.RestrictedLockUtilsInternal import com.android.settingslib.Utils -import com.android.settingslib.spaprivileged.model.app.userId +import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager +import com.android.settingslib.spaprivileged.model.app.isDisallowControl class AppButtonRepository(private val context: Context) { private val packageManager = context.packageManager - private val devicePolicyManager = context.getSystemService(DevicePolicyManager::class.java)!! + private val devicePolicyManager = context.devicePolicyManager /** * Checks whether the given application is disallowed from modifying. @@ -41,20 +40,10 @@ class AppButtonRepository(private val context: Context) { // If the uninstallation intent is already queued, disable the button. devicePolicyManager.isUninstallInQueue(app.packageName) -> true - RestrictedLockUtilsInternal.hasBaseUserRestriction( - context, UserManager.DISALLOW_APPS_CONTROL, app.userId - ) -> true - - else -> false + else -> app.isDisallowControl(context) } - /** - * Checks whether the given application is an active admin. - */ - fun isActiveAdmin(app: ApplicationInfo): Boolean = - devicePolicyManager.packageHasActiveAdmins(app.packageName, app.userId) - - fun getHomePackageInfo(): AppUninstallButton.HomePackages { + fun getHomePackageInfo(): HomePackages { val homePackages = mutableSetOf() val homeActivities = ArrayList() val currentDefaultHome = packageManager.getHomeActivities(homeActivities) @@ -66,7 +55,7 @@ class AppButtonRepository(private val context: Context) { homePackages.add(metaPackageName) } } - return AppUninstallButton.HomePackages(homePackages, currentDefaultHome) + return HomePackages(homePackages, currentDefaultHome) } private fun signaturesMatch(packageName1: String, packageName2: String): Boolean = try { @@ -75,4 +64,9 @@ class AppButtonRepository(private val context: Context) { // e.g. named alternate package not found during lookup; this is an expected case sometimes false } + + data class HomePackages( + val homePackages: Set, + val currentDefaultHome: ComponentName?, + ) } diff --git a/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt b/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt index 365da7c0036..cc5a76e2f99 100644 --- a/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt +++ b/src/com/android/settings/spa/app/appsettings/AppDisableButton.kt @@ -16,10 +16,7 @@ package com.android.settings.spa.app.appsettings -import android.app.admin.DevicePolicyManager -import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo -import android.os.UserManager import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowCircleDown import androidx.compose.material.icons.outlined.HideSource @@ -34,10 +31,12 @@ import androidx.compose.ui.res.stringResource import com.android.settings.R import com.android.settings.Utils import com.android.settings.overlay.FeatureFactory -import com.android.settingslib.Utils as SettingsLibUtils import com.android.settingslib.spa.widget.button.ActionButton -import com.android.settingslib.spaprivileged.model.app.hasFlag +import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager +import com.android.settingslib.spaprivileged.framework.common.userManager +import com.android.settingslib.spaprivileged.model.app.isActiveAdmin import com.android.settingslib.spaprivileged.model.app.isDisabledUntilUsed +import com.android.settingslib.Utils as SettingsLibUtils class AppDisableButton( private val packageInfoPresenter: PackageInfoPresenter, @@ -46,8 +45,8 @@ class AppDisableButton( private val appButtonRepository = AppButtonRepository(context) private val resources = context.resources private val packageManager = context.packageManager - private val userManager = context.getSystemService(UserManager::class.java)!! - private val devicePolicyManager = context.getSystemService(DevicePolicyManager::class.java)!! + private val userManager = context.userManager + private val devicePolicyManager = context.devicePolicyManager private val applicationFeatureProvider = FeatureFactory.getFactory(context).getApplicationFeatureProvider(context) @@ -84,7 +83,7 @@ class AppDisableButton( SettingsLibUtils.isSystemPackage(resources, packageManager, packageInfo) -> false // If this is a device admin, it can't be disabled. - appButtonRepository.isActiveAdmin(app) -> false + app.isActiveAdmin(context) -> false // We don't allow disabling DO/PO on *any* users if it's a system app, because // "disabling" is actually "downgrade to the system version + disable", and "downgrade" diff --git a/src/com/android/settings/spa/app/appsettings/AppForceStopButton.kt b/src/com/android/settings/spa/app/appsettings/AppForceStopButton.kt index 49191637677..c34eff027d5 100644 --- a/src/com/android/settings/spa/app/appsettings/AppForceStopButton.kt +++ b/src/com/android/settings/spa/app/appsettings/AppForceStopButton.kt @@ -36,6 +36,7 @@ import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin import com.android.settingslib.RestrictedLockUtilsInternal import com.android.settingslib.spa.widget.button.ActionButton import com.android.settingslib.spaprivileged.model.app.hasFlag +import com.android.settingslib.spaprivileged.model.app.isActiveAdmin import com.android.settingslib.spaprivileged.model.app.userId class AppForceStopButton( @@ -61,7 +62,7 @@ class AppForceStopButton( */ private fun isForceStopButtonEnable(app: ApplicationInfo): Boolean = when { // User can't force stop device admin. - appButtonRepository.isActiveAdmin(app) -> false + app.isActiveAdmin(context) -> false appButtonRepository.isDisallowControl(app) -> false diff --git a/src/com/android/settings/spa/app/appsettings/AppSettings.kt b/src/com/android/settings/spa/app/appsettings/AppSettings.kt index 4c960e5a17a..615fa75d860 100644 --- a/src/com/android/settings/spa/app/appsettings/AppSettings.kt +++ b/src/com/android/settings/spa/app/appsettings/AppSettings.kt @@ -60,7 +60,7 @@ object AppSettingsProvider : SettingsPageProvider { PackageInfoPresenter(context, packageName, userId, coroutineScope) } AppSettings(packageInfoPresenter) - packageInfoPresenter.PageCloser() + packageInfoPresenter.PackageRemoveDetector() } @Composable @@ -77,7 +77,13 @@ object AppSettingsProvider : SettingsPageProvider { @Composable private fun AppSettings(packageInfoPresenter: PackageInfoPresenter) { val packageInfo = packageInfoPresenter.flow.collectAsState().value ?: return - RegularScaffold(title = stringResource(R.string.application_info_label)) { + val app = packageInfo.applicationInfo + RegularScaffold( + title = stringResource(R.string.application_info_label), + actions = { + AppSettingsMoreOptions(packageInfoPresenter, app) + } + ) { val appInfoProvider = remember { AppInfoProvider(packageInfo) } appInfoProvider.AppInfo() @@ -85,7 +91,6 @@ private fun AppSettings(packageInfoPresenter: PackageInfoPresenter) { AppButtons(packageInfoPresenter) Category(title = stringResource(R.string.advanced_apps)) { - val app = packageInfo.applicationInfo DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app) ModifySystemSettingsAppListProvider.InfoPageEntryItem(app) PictureInPictureListProvider.InfoPageEntryItem(app) diff --git a/src/com/android/settings/spa/app/appsettings/AppSettingsMoreOptions.kt b/src/com/android/settings/spa/app/appsettings/AppSettingsMoreOptions.kt new file mode 100644 index 00000000000..d17ff33bf39 --- /dev/null +++ b/src/com/android/settings/spa/app/appsettings/AppSettingsMoreOptions.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 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.spa.app.appsettings + +import android.content.Context +import android.content.pm.ApplicationInfo +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import com.android.settings.R +import com.android.settings.Utils +import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction +import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager +import com.android.settingslib.spaprivileged.framework.common.userManager +import com.android.settingslib.spaprivileged.model.app.PackageManagers +import com.android.settingslib.spaprivileged.model.app.isActiveAdmin +import com.android.settingslib.spaprivileged.model.app.isDisallowControl +import com.android.settingslib.spaprivileged.model.app.userId + +@Composable +fun AppSettingsMoreOptions(packageInfoPresenter: PackageInfoPresenter, app: ApplicationInfo) { + val context = LocalContext.current + // We don't allow uninstalling update for DO/PO if it's a system app, because it will clear data + // on all users. We also don't allow uninstalling for all users if it's DO/PO for any user. + val isProfileOrDeviceOwner = remember(app) { + Utils.isProfileOrDeviceOwner( + context.userManager, context.devicePolicyManager, app.packageName + ) + } + if (isProfileOrDeviceOwner) return + val shownUninstallUpdates = remember(app) { isShowUninstallUpdates(context, app) } + val shownUninstallForAllUsers = remember(app) { isShowUninstallForAllUsers(context, app) } + if (!shownUninstallUpdates && !shownUninstallForAllUsers) return + MoreOptionsAction { onDismissRequest -> + if (shownUninstallUpdates) { + DropdownMenuItem( + text = { Text(stringResource(R.string.app_factory_reset)) }, + onClick = { + onDismissRequest() + packageInfoPresenter.startUninstallActivity(forAllUsers = false) + }, + ) + } + if (shownUninstallForAllUsers) { + DropdownMenuItem( + text = { Text(stringResource(R.string.uninstall_all_users_text)) }, + onClick = { + onDismissRequest() + packageInfoPresenter.startUninstallActivity(forAllUsers = true) + }, + ) + } + } +} + +private fun isShowUninstallUpdates(context: Context, app: ApplicationInfo): Boolean = + app.isUpdatedSystemApp && context.userManager.isUserAdmin(app.userId) && + !app.isDisallowControl(context) && + !context.resources.getBoolean(R.bool.config_disable_uninstall_update) + +private fun isShowUninstallForAllUsers(context: Context, app: ApplicationInfo): Boolean = + app.userId == 0 && !app.isSystemApp && !app.isInstantApp && !app.isActiveAdmin(context) && + isOtherUserHasInstallPackage(context, app) + +private fun isOtherUserHasInstallPackage(context: Context, app: ApplicationInfo): Boolean = + context.userManager.aliveUsers + .filter { it.id != app.userId } + .any { PackageManagers.isPackageInstalledAsUser(app.packageName, it.id) } diff --git a/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt b/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt index 3458f1ddc5e..cdb2cd01e8f 100644 --- a/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt +++ b/src/com/android/settings/spa/app/appsettings/AppUninstallButton.kt @@ -16,7 +16,6 @@ package com.android.settings.spa.app.appsettings -import android.app.admin.DevicePolicyManager import android.app.settings.SettingsEnums import android.content.ComponentName import android.content.Intent @@ -24,7 +23,6 @@ import android.content.om.OverlayManager import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.net.Uri -import android.os.UserManager import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Delete import com.android.settings.R @@ -33,16 +31,17 @@ import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminAd import com.android.settingslib.RestrictedLockUtils import com.android.settingslib.RestrictedLockUtilsInternal import com.android.settingslib.spa.widget.button.ActionButton +import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.model.app.hasFlag +import com.android.settingslib.spaprivileged.model.app.isActiveAdmin import com.android.settingslib.spaprivileged.model.app.userHandle import com.android.settingslib.spaprivileged.model.app.userId class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) { private val context = packageInfoPresenter.context private val appButtonRepository = AppButtonRepository(context) - private val userManager = context.getSystemService(UserManager::class.java)!! private val overlayManager = context.getSystemService(OverlayManager::class.java)!! - private val devicePolicyManager = context.getSystemService(DevicePolicyManager::class.java)!! + private val devicePolicyManager = context.devicePolicyManager fun getActionButton(packageInfo: PackageInfo): ActionButton? { val app = packageInfo.applicationInfo @@ -52,8 +51,7 @@ class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) /** Gets whether a package can be uninstalled. */ private fun isUninstallButtonEnabled(app: ApplicationInfo): Boolean = when { - // When we have multiple users, there is a separate menu to uninstall for all users. - !app.hasFlag(ApplicationInfo.FLAG_INSTALLED) && userManager.users.size >= 2 -> false + !app.hasFlag(ApplicationInfo.FLAG_INSTALLED) -> false // Not allow to uninstall DO/PO. Utils.isProfileOrDeviceOwner(devicePolicyManager, app.packageName, app.userId) -> false @@ -101,7 +99,7 @@ class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) ) { onUninstallClicked(app) } private fun onUninstallClicked(app: ApplicationInfo) { - if (appButtonRepository.isActiveAdmin(app)) { + if (app.isActiveAdmin(context)) { packageInfoPresenter.logAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN) val intent = Intent(context, DeviceAdminAdd::class.java).apply { putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME, app.packageName) @@ -115,20 +113,6 @@ class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, admin) return } - startUninstallActivity(app) - } - - data class HomePackages( - val homePackages: Set, - val currentDefaultHome: ComponentName?, - ) - - private fun startUninstallActivity(app: ApplicationInfo) { - val packageUri = Uri.parse("package:${app.packageName}") - packageInfoPresenter.logAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP) - val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri).apply { - putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, !app.hasFlag(ApplicationInfo.FLAG_INSTALLED)) - } - context.startActivityAsUser(intent, app.userHandle) + packageInfoPresenter.startUninstallActivity() } } diff --git a/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt index b164d7c5064..299c0a7dea1 100644 --- a/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt +++ b/src/com/android/settings/spa/app/appsettings/PackageInfoPresenter.kt @@ -23,6 +23,7 @@ import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageInfo import android.content.pm.PackageManager +import android.net.Uri import android.os.UserHandle import android.util.Log import androidx.compose.runtime.Composable @@ -65,17 +66,23 @@ class PackageInfoPresenter( } /** - * Closes the page when the package is uninstalled. + * Detects the package removed event. */ @Composable - fun PageCloser() { + fun PackageRemoveDetector() { val intentFilter = IntentFilter(Intent.ACTION_PACKAGE_REMOVED).apply { addDataScheme("package") } val navController = LocalNavController.current DisposableBroadcastReceiverAsUser(userId, intentFilter) { intent -> if (packageName == intent.data?.schemeSpecificPart) { - navController.navigateBack() + val packageInfo = flow.value + if (packageInfo != null && packageInfo.applicationInfo.isSystemApp) { + // System app still exists after uninstalling the updates, refresh the page. + notifyChange() + } else { + navController.navigateBack() + } } } } @@ -102,6 +109,16 @@ class PackageInfoPresenter( } } + /** Starts the uninstallation activity. */ + fun startUninstallActivity(forAllUsers: Boolean = false) { + logAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP) + val packageUri = Uri.parse("package:${packageName}") + val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri).apply { + putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, forAllUsers) + } + context.startActivityAsUser(intent, UserHandle.of(userId)) + } + /** Clears this instant app. */ fun clearInstantApp() { logAction(SettingsEnums.ACTION_SETTINGS_CLEAR_INSTANT_APP) From 7c4e04b187f2a067b2b9e4eee4cec3e0bb2e6554 Mon Sep 17 00:00:00 2001 From: timhypeng Date: Thu, 22 Sep 2022 07:10:20 +0000 Subject: [PATCH 7/8] single component HFP device has no battery info in the Device details page -get full summary when the metadata does not contain the battery information -remove unexpected "test_summary" text Bug: 237594992 Test: make -j50 RunSettingsGoogleRoboTests ROBOTEST_FILTER=AppBypassBroadcastReceiverTest Change-Id: I3e3682cf88cdd147d4b7cd68f37f5e33f01c788d --- res/layout/advanced_bt_entity_header.xml | 3 +-- .../bluetooth/AdvancedBluetoothDetailsHeaderController.java | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/res/layout/advanced_bt_entity_header.xml b/res/layout/advanced_bt_entity_header.xml index 4aadad1fc34..6dd565f41cb 100644 --- a/res/layout/advanced_bt_entity_header.xml +++ b/res/layout/advanced_bt_entity_header.xml @@ -42,8 +42,7 @@ android:layout_marginTop="2dp" android:singleLine="false" android:ellipsize="marquee" - android:textDirection="locale" - android:text="test_summary"/> + android:textDirection="locale"/> Date: Wed, 28 Sep 2022 15:06:18 +0000 Subject: [PATCH 8/8] Fix fragment class for WIFI_SAVED_NETWORK_SETTINGS. Has been broken since the old access points implementation was deleted. Change-Id: I8575e6f79644d0a79de9e041cb30b0ccbc0e57d3 Bug: 246772836 --- AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index a23eacaef2e..0be2e2efbac 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -481,7 +481,7 @@ + android:value="com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2" />