diff --git a/Android.bp b/Android.bp index 2699c384c0c..045a9638486 100644 --- a/Android.bp +++ b/Android.bp @@ -94,6 +94,7 @@ android_library { "battery-event-protos-lite", "battery-usage-slot-protos-lite", "contextualcards", + "development_settings_flag_lib", "factory_reset_flags_lib", "fuelgauge-log-protos-lite", "fuelgauge-usage-state-protos-lite", diff --git a/aconfig/Android.bp b/aconfig/Android.bp index de6d0af9f94..d14cc6f1367 100644 --- a/aconfig/Android.bp +++ b/aconfig/Android.bp @@ -47,3 +47,16 @@ java_aconfig_library { name: "accessibility_settings_flags_lib", aconfig_declarations: "accessibility_flags", } + +aconfig_declarations { + name: "development_settings_flags", + package: "com.android.settings.development", + srcs: [ + "development/**/*.aconfig" + ], +} + +java_aconfig_library { + name: "development_settings_flag_lib", + aconfig_declarations: "development_settings_flags", +} diff --git a/aconfig/development/settings_core_flag_declarations.aconfig b/aconfig/development/settings_core_flag_declarations.aconfig new file mode 100644 index 00000000000..c0122631d96 --- /dev/null +++ b/aconfig/development/settings_core_flag_declarations.aconfig @@ -0,0 +1,8 @@ +package: "com.android.settings.development" + +flag { + name: "deprecate_list_activity" + namespace: "android_settings" + description: "Feature flag for deprecating ListActivity in Settings" + bug: "299195099" +} diff --git a/res/xml/development_app_picker.xml b/res/xml/development_app_picker.xml new file mode 100644 index 00000000000..15e4ce0a756 --- /dev/null +++ b/res/xml/development_app_picker.xml @@ -0,0 +1,18 @@ + + + + diff --git a/src/com/android/settings/development/DevelopmentAppPicker.java b/src/com/android/settings/development/DevelopmentAppPicker.java new file mode 100644 index 00000000000..7ef0869be72 --- /dev/null +++ b/src/com/android/settings/development/DevelopmentAppPicker.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development; + +import static android.app.Activity.RESULT_OK; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Process; +import android.os.UserHandle; + +import com.android.settings.R; +import com.android.settings.applications.defaultapps.DefaultAppPickerFragment; +import com.android.settingslib.applications.DefaultAppInfo; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class DevelopmentAppPicker extends DefaultAppPickerFragment { + public static final String EXTRA_REQUESTING_PERMISSION = "REQUESTING_PERMISSION"; + public static final String EXTRA_DEBUGGABLE = "DEBUGGABLE"; + public static final String EXTRA_SELECTING_APP = "SELECTING_APP"; + + private String mPermissionName; + private boolean mDebuggableOnly; + private String mSelectingApp; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + Bundle arguments = getArguments(); + if (arguments == null) { + return; + } + mPermissionName = arguments.getString(EXTRA_REQUESTING_PERMISSION); + mDebuggableOnly = arguments.getBoolean(EXTRA_DEBUGGABLE); + mSelectingApp = arguments.getString(EXTRA_SELECTING_APP); + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.DEVELOPMENT_APP_PICKER; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.development_app_picker; + } + + @Override + protected boolean shouldShowItemNone() { + return true; + } + + @Override + protected List getCandidates() { + List packageInfoList = new ArrayList(); + Context context = getContext(); + List installedApps = mPm.getInstalledApplications(0); + for (ApplicationInfo ai : installedApps) { + if (ai.uid == Process.SYSTEM_UID) { + continue; + } + // Filter out apps that are not debuggable if required. + if (mDebuggableOnly) { + // On a user build, we only allow debugging of apps that + // are marked as debuggable, otherwise (for platform development) + // we allow all apps. + if ((ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0 + && "user".equals(Build.TYPE)) { + continue; + } + } + + // Filter out apps that do not request the permission if required. + if (mPermissionName != null) { + boolean requestsPermission = false; + try { + PackageInfo pi = mPm.getPackageInfo(ai.packageName, + PackageManager.GET_PERMISSIONS); + if (pi.requestedPermissions == null) { + continue; + } + for (String requestedPermission : pi.requestedPermissions) { + if (requestedPermission.equals(mPermissionName)) { + requestsPermission = true; + break; + } + } + if (!requestsPermission) { + continue; + } + } catch (PackageManager.NameNotFoundException e) { + continue; + } + } + DefaultAppInfo appInfo = new DefaultAppInfo(context, mPm, UserHandle.myUserId(), ai); + packageInfoList.add(appInfo); + } + Collections.sort(packageInfoList, sLabelComparator); + return packageInfoList; + } + + @Override + protected String getDefaultKey() { + return mSelectingApp; + } + + @Override + protected boolean setDefaultKey(String key) { + DefaultAppInfo appInfo = (DefaultAppInfo) getCandidate(key); + Intent intent = new Intent(); + if (appInfo != null && appInfo.packageItemInfo != null) { + intent.setAction(appInfo.packageItemInfo.packageName); + } + setResult(RESULT_OK, intent); + finish(); + return true; + } + + private static final Comparator sLabelComparator = + new Comparator() { + public int compare(DefaultAppInfo a, DefaultAppInfo b) { + return Collator.getInstance().compare(a.loadLabel(), b.loadLabel()); + } + }; +} diff --git a/src/com/android/settings/development/MockLocationAppPreferenceController.java b/src/com/android/settings/development/MockLocationAppPreferenceController.java index b20ef0259f9..d927c313d3b 100644 --- a/src/com/android/settings/development/MockLocationAppPreferenceController.java +++ b/src/com/android/settings/development/MockLocationAppPreferenceController.java @@ -21,16 +21,20 @@ import static com.android.settings.development.DevelopmentOptionsActivityRequest import android.Manifest; import android.app.Activity; import android.app.AppOpsManager; +import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.os.Bundle; +import android.provider.Settings; import android.text.TextUtils; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.SubSettingLauncher; import com.android.settingslib.development.DeveloperOptionsPreferenceController; import java.util.List; @@ -64,10 +68,26 @@ public class MockLocationAppPreferenceController extends DeveloperOptionsPrefere if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { return false; } - final Intent intent = new Intent(mContext, AppPicker.class); - intent.putExtra(AppPicker.EXTRA_REQUESTIING_PERMISSION, - Manifest.permission.ACCESS_MOCK_LOCATION); - mFragment.startActivityForResult(intent, REQUEST_MOCK_LOCATION_APP); + if (Flags.deprecateListActivity()) { + final Bundle args = new Bundle(); + args.putString(DevelopmentAppPicker.EXTRA_REQUESTING_PERMISSION, + Manifest.permission.ACCESS_MOCK_LOCATION); + final String debugApp = Settings.Global.getString( + mContext.getContentResolver(), Settings.Global.DEBUG_APP); + args.putString(DevelopmentAppPicker.EXTRA_SELECTING_APP, debugApp); + new SubSettingLauncher(mContext) + .setDestination(DevelopmentAppPicker.class.getName()) + .setSourceMetricsCategory(SettingsEnums.DEVELOPMENT) + .setArguments(args) + .setTitleRes(com.android.settingslib.R.string.select_application) + .setResultListener(mFragment, REQUEST_MOCK_LOCATION_APP) + .launch(); + } else { + final Intent intent = new Intent(mContext, AppPicker.class); + intent.putExtra(AppPicker.EXTRA_REQUESTIING_PERMISSION, + Manifest.permission.ACCESS_MOCK_LOCATION); + mFragment.startActivityForResult(intent, REQUEST_MOCK_LOCATION_APP); + } return true; } diff --git a/src/com/android/settings/development/SelectDebugAppPreferenceController.java b/src/com/android/settings/development/SelectDebugAppPreferenceController.java index f0b0b27f072..d691149526e 100644 --- a/src/com/android/settings/development/SelectDebugAppPreferenceController.java +++ b/src/com/android/settings/development/SelectDebugAppPreferenceController.java @@ -19,16 +19,20 @@ package com.android.settings.development; import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_CODE_DEBUG_APP; import android.app.Activity; +import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.os.Bundle; import android.provider.Settings; +import android.text.TextUtils; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.SubSettingLauncher; import com.android.settingslib.development.DeveloperOptionsPreferenceController; public class SelectDebugAppPreferenceController extends DeveloperOptionsPreferenceController @@ -53,13 +57,29 @@ public class SelectDebugAppPreferenceController extends DeveloperOptionsPreferen @Override public boolean handlePreferenceTreeClick(Preference preference) { - if (DEBUG_APP_KEY.equals(preference.getKey())) { + if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) { + return false; + } + + if (Flags.deprecateListActivity()) { + final Bundle args = new Bundle(); + args.putBoolean(DevelopmentAppPicker.EXTRA_DEBUGGABLE, true /* value */); + final String debugApp = Settings.Global.getString( + mContext.getContentResolver(), Settings.Global.DEBUG_APP); + args.putString(DevelopmentAppPicker.EXTRA_SELECTING_APP, debugApp); + new SubSettingLauncher(mContext) + .setDestination(DevelopmentAppPicker.class.getName()) + .setSourceMetricsCategory(SettingsEnums.DEVELOPMENT) + .setArguments(args) + .setTitleRes(com.android.settingslib.R.string.select_application) + .setResultListener(mFragment, REQUEST_CODE_DEBUG_APP) + .launch(); + } else { final Intent intent = getActivityStartIntent(); intent.putExtra(AppPicker.EXTRA_DEBUGGABLE, true /* value */); mFragment.startActivityForResult(intent, REQUEST_CODE_DEBUG_APP); - return true; } - return false; + return true; } @Override