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
424 lines
17 KiB
Java
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();
|
|
}
|
|
}
|