Refactor AppPicker to adopt the latest UX

- get rid of ListActivity
- add aconfig

Bug: 299195099
Test: manual
Change-Id: I922ffb46f3132d117b0f682d8076f9e975d02b2c
This commit is contained in:
Edgar Wang
2023-11-09 20:37:44 +08:00
parent a930f5eaf8
commit 58bed09373
7 changed files with 237 additions and 7 deletions

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen/>

View File

@@ -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<DefaultAppInfo> getCandidates() {
List<DefaultAppInfo> packageInfoList = new ArrayList<DefaultAppInfo>();
Context context = getContext();
List<ApplicationInfo> 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<DefaultAppInfo> sLabelComparator =
new Comparator<DefaultAppInfo>() {
public int compare(DefaultAppInfo a, DefaultAppInfo b) {
return Collator.getInstance().compare(a.loadLabel(), b.loadLabel());
}
};
}

View File

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

View File

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