Snap for 9121979 from 95d0ec1b00 to udc-release

Change-Id: I93b0fd4dbd9ba8b8a703c17bef5f4a548d62d430
This commit is contained in:
Android Build Coastguard Worker
2022-09-30 01:26:26 +00:00
22 changed files with 310 additions and 309 deletions

View File

@@ -481,7 +481,7 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings" />
android:value="com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2" />
<meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
android:value="@string/menu_key_network"/>
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"

View File

@@ -42,8 +42,7 @@
android:layout_marginTop="2dp"
android:singleLine="false"
android:ellipsize="marquee"
android:textDirection="locale"
android:text="test_summary"/>
android:textDirection="locale"/>
<LinearLayout
android:layout_width="match_parent"

View File

@@ -56,17 +56,6 @@
android:layout_toStartOf="@+id/dragHandle"
android:layout_below="@id/label"/>
<TextView
android:id="@+id/default_locale"
style="@style/LanguageCheckboxAndLabel"
android:layout_marginTop="-28dp"
android:paddingStart="56dp"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:textColorSecondary"
android:text="@string/desc_current_default_language"
android:layout_toStartOf="@+id/dragHandle"
android:layout_below="@id/label"/>
<ImageView
android:id="@+id/dragHandle"
android:layout_width="wrap_content"

View File

@@ -333,12 +333,6 @@
<!-- Title for the locale picker activity. [CHAR LIMIT=30]-->
<string name="language_picker_title">Languages</string>
<!-- Title for category of the locale picker . [CHAR LIMIT=50]-->
<string name="language_picker_category_title">Preferred language order</string>
<!-- Description for indication of current system default language. [CHAR LIMIT=50]-->
<string name="desc_current_default_language">System language</string>
<!-- Menu item in the locale menu. Will remove the selected locales. [CHAR LIMIT=30] -->
<string name="locale_remove_menu">Remove</string>
@@ -352,13 +346,13 @@
<string name="locale_picker_category_title">Preferred Language</string>
<!-- Title for the Apps' locale menu entry [CHAR LIMIT=50]-->
<string name="app_locales_picker_menu_title">App languages</string>
<string name="app_locales_picker_menu_title">App Languages</string>
<!-- Summary for the app's locale picker activity. [CHAR LIMIT=50]-->
<string name="app_locale_picker_summary">Set the language for each app</string>
<!-- Title for the App's locale picker activity. [CHAR LIMIT=50]-->
<string name="app_locale_picker_title">App language</string>
<string name="app_locale_picker_title">App Language</string>
<!-- Category for the suggested app's locales. [CHAR LIMIT=50]-->
<string name="suggested_app_locales_title">Suggested languages</string>
@@ -381,18 +375,6 @@
<!-- Description for introduction of the locale selection supported of app list [CHAR LIMIT=NONE]-->
<string name="desc_app_locale_selection_supported">Only apps that support language selection are shown here.</string>
<!-- Description for the introduction to language picker activity. [CHAR LIMIT=NONE]-->
<string name="desc_introduction_of_language_picker">Your system, apps, and websites use the first supported language from your preferred languages.</string>
<!-- Description for the notice of language picker. [CHAR LIMIT=NONE]-->
<string name="desc_notice_of_language_picker">To select a language for each app, go to app language settings.</string>
<!-- Title for locale helper page [CHAR LIMIT=NONE] -->
<string name="desc_locale_helper_footer_general">Learn more about languages</string>
<!-- Link for Locale helper page. [CHAR LIMIT=NONE]-->
<string name="link_locale_picker_footer_learn_more" translatable="false">https://support.google.com/android?p=per_language_app_settings</string>
<!-- The title of the confirmation dialog shown when the user selects one / several languages and tries to remove them [CHAR LIMIT=60] -->
<plurals name="dlg_remove_locales_title">
<item quantity="one">Remove selected language?</item>
@@ -408,7 +390,7 @@
<string name="dlg_remove_locales_error_message">Keep at least one preferred language</string>
<!-- This text shows in the language picker when the system is not translated into that languages [CHAR LIMIT=80] -->
<string name="locale_not_translated">Not available as system language</string>
<string name="locale_not_translated">May not be available in some apps</string>
<!-- Label for an accessibility action that moves a language up in the ordered language list [CHAR LIMIT=20] -->
<string name="action_drag_label_move_up">Move up</string>

View File

@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/language_settings">
<com.android.settingslib.widget.TopIntroPreference
android:title="@string/desc_introduction_of_language_picker"
android:persistent="false"
android:selectable="false"/>
<PreferenceCategory
android:title="@string/language_picker_category_title">
<com.android.settingslib.widget.LayoutPreference
android:key="languages_picker"
android:layout="@layout/locale_order_list" />
</PreferenceCategory>
<com.android.settingslib.widget.FooterPreference
android:key="footer_languages_picker"
android:title="@string/desc_notice_of_language_picker"
android:persistent="false"
android:selectable="false"
settings:controller="com.android.settings.localepicker.LocaleHelperPreferenceController"/>
</PreferenceScreen>

View File

@@ -182,9 +182,9 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
final TextView title = mLayoutPreference.findViewById(R.id.entity_header_title);
title.setText(mCachedDevice.getName());
final TextView summary = mLayoutPreference.findViewById(R.id.entity_header_summary);
summary.setText(mCachedDevice.getConnectionSummary(true /* shortSummary */));
if (!mCachedDevice.isConnected() || mCachedDevice.isBusy()) {
summary.setText(mCachedDevice.getConnectionSummary(true /* shortSummary */));
updateDisconnectLayout();
return;
}
@@ -196,6 +196,9 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
mLayoutPreference.findViewById(R.id.layout_left).setVisibility(View.GONE);
mLayoutPreference.findViewById(R.id.layout_right).setVisibility(View.GONE);
summary.setText(mCachedDevice.getConnectionSummary(
BluetoothUtils.getIntMetaData(device, BluetoothDevice.METADATA_MAIN_BATTERY)
!= BluetoothUtils.META_INT_ERROR));
updateSubLayout(mLayoutPreference.findViewById(R.id.layout_middle),
BluetoothDevice.METADATA_MAIN_ICON,
BluetoothDevice.METADATA_MAIN_BATTERY,
@@ -207,6 +210,7 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET)
|| BluetoothUtils.getBooleanMetaData(device,
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) {
summary.setText(mCachedDevice.getConnectionSummary(true /* shortSummary */));
updateSubLayout(mLayoutPreference.findViewById(R.id.layout_left),
BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON,
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY,

View File

@@ -43,6 +43,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
class LocaleDragAndDropAdapter
extends RecyclerView.Adapter<LocaleDragAndDropAdapter.CustomViewHolder> {
@@ -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);

View File

@@ -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;
}

View File

@@ -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=*/""));
}
}

View File

@@ -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<LocaleStore.LocaleInfo> 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) {

View File

@@ -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<String>()
val homeActivities = ArrayList<ResolveInfo>()
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<String>,
val currentDefaultHome: ComponentName?,
)
}

View File

@@ -32,9 +32,11 @@ 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 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<ActionButton> = 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()
}
}

View File

@@ -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))
},
)
}
}

View File

@@ -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)
@@ -55,7 +54,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 -> {
@@ -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"

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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) }

View File

@@ -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,27 +31,27 @@ 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
if (app.hasFlag(ApplicationInfo.FLAG_SYSTEM)) return null
if (app.isSystemApp || app.isInstantApp) return null
return uninstallButton(app = app, enabled = isUninstallButtonEnabled(app))
}
/** 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<String>,
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()
}
}

View File

@@ -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
@@ -48,9 +49,8 @@ class PackageInfoPresenter(
private val coroutineScope: CoroutineScope,
) {
private val metricsFeatureProvider = FeatureFactory.getFactory(context).metricsFeatureProvider
private val 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<PackageInfo?> = MutableStateFlow(null)
val flow: StateFlow<PackageInfo?> = _flow
@@ -66,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()
}
}
}
}
@@ -85,7 +91,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,13 +102,32 @@ 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()
}
}
/** 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)
coroutineScope.launch(Dispatchers.IO) {
packageManagerAsUser.deletePackageAsUser(packageName, null, 0, userId)
notifyChange()
}
}
/** Force stops this package. */
fun forceStop() {
logAction(SettingsEnums.ACTION_APP_FORCE_STOP)

View File

@@ -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());
}
}