Files
packages_apps_Settings/src/com/android/settings/applications/intentpicker/AppLaunchSettings.java
xinghailu b5293a2d1c Cleanup plurals in Settings, change <one> to <1> in string res file.
"One" and "1" are not same, such as "1st place" vs "a
place". Also in many languages, plurals expression is different with English, for more detail please check: go/android-i18n-plurals.
So in string res file, replace "one" with excat value "1" is a more
proper way.

Test: Existing unit tests still pass.
Bug: 199230342
Change-Id: I832abc38afc5d8816fa803865c25e6017cffa2c6
2022-12-13 18:31:08 +08:00

435 lines
18 KiB
Java

/*
* Copyright (C) 2015 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.applications.intentpicker;
import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_NONE;
import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_SELECTED;
import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_VERIFIED;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.icu.text.MessageFormat;
import android.net.Uri;
import android.os.Bundle;
import android.util.ArraySet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.ClearDefaultsPreference;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.MainSwitchPreference;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/** The page of the Open by default */
public class AppLaunchSettings extends AppInfoBase implements
Preference.OnPreferenceChangeListener, OnMainSwitchChangeListener {
private static final String TAG = "AppLaunchSettings";
// Preference keys
private static final String MAIN_SWITCH_PREF_KEY = "open_by_default_supported_links";
private static final String VERIFIED_LINKS_PREF_KEY = "open_by_default_verified_links";
private static final String ADD_LINK_PREF_KEY = "open_by_default_add_link";
private static final String CLEAR_DEFAULTS_PREF_KEY = "app_launch_clear_defaults";
private static final String FOOTER_PREF_KEY = "open_by_default_footer";
private static final String MAIN_PREF_CATEGORY_KEY = "open_by_default_main_category";
private static final String SELECTED_LINKS_CATEGORY_KEY =
"open_by_default_selected_links_category";
private static final String OTHER_DETAILS_PREF_CATEGORY_KEY = "app_launch_other_defaults";
private static final String LEARN_MORE_URI =
"https://developer.android.com/training/app-links/verify-site-associations";
// Dialogs id
private static final int DLG_VERIFIED_LINKS = DLG_BASE + 1;
// Arguments key
public static final String APP_PACKAGE_KEY = "app_package";
private ClearDefaultsPreference mClearDefaultsPreference;
private MainSwitchPreference mMainSwitchPreference;
private Preference mAddLinkPreference;
private PreferenceCategory mMainPreferenceCategory;
private PreferenceCategory mSelectedLinksPreferenceCategory;
private PreferenceCategory mOtherDefaultsPreferenceCategory;
private boolean mActivityCreated;
@VisibleForTesting
Context mContext;
@VisibleForTesting
DomainVerificationManager mDomainVerificationManager;
@Override
public void onAttach(Context context) {
super.onAttach(context);
mContext = context;
mActivityCreated = false;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mAppEntry == null) {
Log.w(TAG, "onCreate: mAppEntry is null, please check the reason!!!");
getActivity().finish();
return;
}
addPreferencesFromResource(R.xml.installed_app_launch_settings);
mDomainVerificationManager = mContext.getSystemService(DomainVerificationManager.class);
initUIComponents();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
createHeaderPreference();
}
@Override
public int getMetricsCategory() {
return SettingsEnums.APPLICATIONS_APP_LAUNCH;
}
@Override
protected AlertDialog createDialog(int id, int errorCode) {
if (id == DLG_VERIFIED_LINKS) {
return createVerifiedLinksDialog();
}
return null;
}
@Override
protected boolean refreshUi() {
mClearDefaultsPreference.setPackageName(mPackageName);
mClearDefaultsPreference.setAppEntry(mAppEntry);
return true;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean isChecked = (boolean) newValue;
IntentPickerUtils.logd(
"onPreferenceChange: " + preference.getTitle() + " isChecked: " + isChecked);
if ((preference instanceof LeftSideCheckBoxPreference) && !isChecked) {
final Set<String> domainSet = new ArraySet<>();
domainSet.add(preference.getTitle().toString());
removePreference(preference.getKey());
final DomainVerificationUserState userState =
IntentPickerUtils.getDomainVerificationUserState(mDomainVerificationManager,
mPackageName);
if (userState == null) {
return false;
}
setDomainVerificationUserSelection(userState.getIdentifier(), domainSet, /* enabled= */
false);
mAddLinkPreference.setEnabled(isAddLinksNotEmpty());
}
return true;
}
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
IntentPickerUtils.logd("onSwitchChanged: isChecked=" + isChecked);
if (mMainSwitchPreference != null) { //mMainSwitchPreference synced with Switch
mMainSwitchPreference.setChecked(isChecked);
}
if (mMainPreferenceCategory != null) {
mMainPreferenceCategory.setVisible(isChecked);
}
if (mDomainVerificationManager != null) {
try {
mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(mPackageName,
isChecked);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "onSwitchChanged: " + e.getMessage());
}
}
}
private void createHeaderPreference() {
if (mActivityCreated) {
Log.w(TAG, "onParentActivityCreated: ignoring duplicate call.");
return;
}
mActivityCreated = true;
if (mPackageInfo == null) {
Log.w(TAG, "onParentActivityCreated: PakcageInfo is null.");
return;
}
final Activity activity = getActivity();
final String summary = activity.getString(R.string.app_launch_top_intro_message);
final Preference pref = EntityHeaderController
.newInstance(activity, this, null /* header */)
.setRecyclerView(getListView(), getSettingsLifecycle())
.setIcon(Utils.getBadgedIcon(mContext, mPackageInfo.applicationInfo))
.setLabel(mPackageInfo.applicationInfo.loadLabel(mPm))
.setSummary(summary) // add intro text
.setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
.setPackageName(mPackageName)
.setUid(mPackageInfo.applicationInfo.uid)
.setHasAppInfoLink(true)
.setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
EntityHeaderController.ActionType.ACTION_NONE)
.done(activity, getPrefContext());
getPreferenceScreen().addPreference(pref);
}
private void initUIComponents() {
initMainSwitchAndCategories();
if (canUpdateMainSwitchAndCategories()) {
initVerifiedLinksPreference();
initAddLinkPreference();
addSelectedLinksPreference();
initFooter();
}
}
private void initMainSwitchAndCategories() {
mMainSwitchPreference = (MainSwitchPreference) findPreference(MAIN_SWITCH_PREF_KEY);
mMainPreferenceCategory = findPreference(MAIN_PREF_CATEGORY_KEY);
mSelectedLinksPreferenceCategory = findPreference(SELECTED_LINKS_CATEGORY_KEY);
// Initialize the "Other Default Category" section
initOtherDefaultsSection();
}
private boolean canUpdateMainSwitchAndCategories() {
final DomainVerificationUserState userState =
IntentPickerUtils.getDomainVerificationUserState(mDomainVerificationManager,
mPackageName);
if (userState == null) {
disabledPreference();
return false;
}
IntentPickerUtils.logd("isLinkHandlingAllowed() : " + userState.isLinkHandlingAllowed());
mMainSwitchPreference.updateStatus(userState.isLinkHandlingAllowed());
mMainSwitchPreference.addOnSwitchChangeListener(this);
mMainPreferenceCategory.setVisible(userState.isLinkHandlingAllowed());
return true;
}
/** Initialize verified links preference */
private void initVerifiedLinksPreference() {
final Preference verifiedLinksPreference = mMainPreferenceCategory.findPreference(
VERIFIED_LINKS_PREF_KEY);
verifiedLinksPreference.setOnPreferenceClickListener(preference -> {
showVerifiedLinksDialog();
return true;
});
final int verifiedLinksNo = getLinksNumber(DOMAIN_STATE_VERIFIED);
verifiedLinksPreference.setTitle(getVerifiedLinksTitle(verifiedLinksNo));
verifiedLinksPreference.setEnabled(verifiedLinksNo > 0);
}
private void showVerifiedLinksDialog() {
final int linksNo = getLinksNumber(DOMAIN_STATE_VERIFIED);
if (linksNo == 0) {
return;
}
showDialogInner(DLG_VERIFIED_LINKS, /* moveErrorCode= */ 0);
}
private AlertDialog createVerifiedLinksDialog() {
final int linksNo = getLinksNumber(DOMAIN_STATE_VERIFIED);
final View titleView = LayoutInflater.from(mContext).inflate(
R.layout.app_launch_verified_links_title, /* root= */ null);
((TextView) titleView.findViewById(R.id.dialog_title)).setText(
getVerifiedLinksTitle(linksNo));
((TextView) titleView.findViewById(R.id.dialog_message)).setText(
getVerifiedLinksMessage(linksNo));
final List<String> verifiedLinksList = IntentPickerUtils.getLinksList(
mDomainVerificationManager, mPackageName, DOMAIN_STATE_VERIFIED);
return new AlertDialog.Builder(mContext)
.setCustomTitle(titleView)
.setCancelable(true)
.setItems(verifiedLinksList.toArray(new String[0]), /* listener= */ null)
.setPositiveButton(R.string.app_launch_dialog_ok, /* listener= */ null)
.create();
}
@VisibleForTesting
String getVerifiedLinksTitle(int linksNo) {
MessageFormat msgFormat = new MessageFormat(
getResources().getString(R.string.app_launch_verified_links_title),
Locale.getDefault());
Map<String, Object> arguments = new HashMap<>();
arguments.put("count", linksNo);
return msgFormat.format(arguments);
}
private String getVerifiedLinksMessage(int linksNo) {
MessageFormat msgFormat = new MessageFormat(
getResources().getString(R.string.app_launch_verified_links_message),
Locale.getDefault());
Map<String, Object> arguments = new HashMap<>();
arguments.put("count", linksNo);
return msgFormat.format(arguments);
}
/** Add selected links items */
void addSelectedLinksPreference() {
if (getLinksNumber(DOMAIN_STATE_SELECTED) == 0) {
return;
}
mSelectedLinksPreferenceCategory.removeAll();
final List<String> selectedLinks = IntentPickerUtils.getLinksList(
mDomainVerificationManager, mPackageName, DOMAIN_STATE_SELECTED);
for (String host : selectedLinks) {
generateCheckBoxPreference(mSelectedLinksPreferenceCategory, host);
}
mAddLinkPreference.setEnabled(isAddLinksNotEmpty());
}
/** Initialize add link preference */
private void initAddLinkPreference() {
mAddLinkPreference = findPreference(ADD_LINK_PREF_KEY);
mAddLinkPreference.setVisible(isAddLinksShown());
mAddLinkPreference.setEnabled(isAddLinksNotEmpty());
mAddLinkPreference.setOnPreferenceClickListener(preference -> {
final int stateNoneLinksNo = getLinksNumber(DOMAIN_STATE_NONE);
IntentPickerUtils.logd("The number of the state none links: " + stateNoneLinksNo);
if (stateNoneLinksNo > 0) {
showProgressDialogFragment();
}
return true;
});
}
private boolean isAddLinksNotEmpty() {
return getLinksNumber(DOMAIN_STATE_NONE) > 0;
}
private boolean isAddLinksShown() {
return (isAddLinksNotEmpty() || getLinksNumber(DOMAIN_STATE_SELECTED) > 0);
}
private void showProgressDialogFragment() {
final Bundle args = new Bundle();
args.putString(APP_PACKAGE_KEY, mPackageName);
final ProgressDialogFragment dialogFragment = new ProgressDialogFragment();
dialogFragment.setArguments(args);
dialogFragment.showDialog(getActivity().getSupportFragmentManager());
}
private void disabledPreference() {
mMainSwitchPreference.updateStatus(false);
mMainSwitchPreference.setSelectable(false);
mMainSwitchPreference.setEnabled(false);
mMainPreferenceCategory.setVisible(false);
}
/** Init OTHER DEFAULTS category */
private void initOtherDefaultsSection() {
mOtherDefaultsPreferenceCategory = findPreference(OTHER_DETAILS_PREF_CATEGORY_KEY);
mOtherDefaultsPreferenceCategory.setVisible(isClearDefaultsEnabled());
mClearDefaultsPreference = (ClearDefaultsPreference) findPreference(
CLEAR_DEFAULTS_PREF_KEY);
}
private void initFooter() {
final CharSequence footerText = mContext.getText(R.string.app_launch_footer);
final FooterPreference footerPreference = (FooterPreference) findPreference(
FOOTER_PREF_KEY);
footerPreference.setTitle(footerText);
// learn more
footerPreference.setLearnMoreAction(view -> {
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(LEARN_MORE_URI));
mContext.startActivity(intent);
});
final String learnMoreText = mContext.getString(
R.string.footer_learn_more_content_description, getLabelName());
footerPreference.setLearnMoreText(learnMoreText);
}
private String getLabelName() {
return mContext.getString(R.string.launch_by_default);
}
private boolean isClearDefaultsEnabled() {
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
final boolean hasBindAppWidgetPermission =
appWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName);
final boolean isAutoLaunchEnabled = AppUtils.hasPreferredActivities(mPm, mPackageName)
|| AppUtils.isDefaultBrowser(mContext, mPackageName)
|| AppUtils.hasUsbDefaults(mUsbManager, mPackageName);
IntentPickerUtils.logd("isClearDefaultsEnabled hasBindAppWidgetPermission : "
+ hasBindAppWidgetPermission);
IntentPickerUtils.logd(
"isClearDefaultsEnabled isAutoLaunchEnabled : " + isAutoLaunchEnabled);
return (isAutoLaunchEnabled || hasBindAppWidgetPermission);
}
private void setDomainVerificationUserSelection(UUID identifier, Set<String> domainSet,
boolean isEnabled) {
try {
mDomainVerificationManager.setDomainVerificationUserSelection(identifier, domainSet,
isEnabled);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "addSelectedItems : " + e.getMessage());
}
}
private void generateCheckBoxPreference(PreferenceCategory parent, String title) {
final LeftSideCheckBoxPreference checkBoxPreference = new LeftSideCheckBoxPreference(
parent.getContext(), /* isChecked= */ true);
checkBoxPreference.setTitle(title);
checkBoxPreference.setOnPreferenceChangeListener(this);
checkBoxPreference.setKey(UUID.randomUUID().toString());
parent.addPreference(checkBoxPreference);
}
/** get the number of the specify links */
private int getLinksNumber(@DomainVerificationUserState.DomainState int state) {
final List<String> linkList = IntentPickerUtils.getLinksList(
mDomainVerificationManager, mPackageName, state);
if (linkList == null) {
return 0;
}
return linkList.size();
}
}