Files
packages_apps_Settings/src/com/android/settings/applications/intentpicker/AppLaunchSettings.java
Edgar Wang 68d12953e7 Update learn more string of FooterPrefernce
Replace FooterPreference#SetLearnMoreContentDescription() with
FooterPreference#SetLearnMoreText().

This is a requirement: All links, controls, and buttons should
also have comprehensible text or tooltip text, so users can identify its
purpose, independent of context, such as when using the URL list from
TalkBack’s local context menu.

Bug: 215045903
Test: manual & robotest
Change-Id: Ib657ba336c5688c1434a58611dea3891001afe14
2022-05-13 08:13:36 +00:00

424 lines
17 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.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.List;
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 VerifiedLinksPreference verifiedLinksPreference =
(VerifiedLinksPreference) mMainPreferenceCategory.findPreference(
VERIFIED_LINKS_PREF_KEY);
verifiedLinksPreference.setWidgetFrameClickListener(l -> {
showVerifiedLinksDialog();
});
final int verifiedLinksNo = getLinksNumber(DOMAIN_STATE_VERIFIED);
verifiedLinksPreference.setTitle(getVerifiedLinksTitle(verifiedLinksNo));
verifiedLinksPreference.setCheckBoxVisible(verifiedLinksNo > 0);
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) {
return getResources().getQuantityString(
R.plurals.app_launch_verified_links_title, linksNo, linksNo);
}
private String getVerifiedLinksMessage(int linksNo) {
return getResources().getQuantityString(
R.plurals.app_launch_verified_links_message, linksNo, linksNo);
}
/** 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();
}
}