Files
app_Settings/src/com/android/settings/applications/intentpicker/AppLaunchSettings.java
Maryam Dehaini 11825b0bc7 Change "Open by default" setting page selection to radio buttons
Selection to open page in app or browser needs to be displayed via radio
buttons as discussed here: http://b/346622495#comment14

Bug: 359262490
Test: Open links in app and browser
Flag: EXEMPT small UI change
Change-Id: Ib23084bcc7f899b97b59019775584807e5b70d7e
2024-09-23 14:32:08 -07:00

456 lines
19 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.annotation.Nullable;
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.TextView;
import androidx.annotation.NonNull;
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.SelectorWithWidgetPreference;
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, SelectorWithWidgetPreference.OnClickListener {
private static final String TAG = "AppLaunchSettings";
// Preference keys
private static final String OPEN_IN_APP_PREF_KEY = "app_launch_open_in_app";
private static final String OPEN_IN_BROWSER_PREF_KEY = "app_launch_open_in_browser";
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;
@Nullable
private SelectorWithWidgetPreference mOpenInAppSelector;
@Nullable
private SelectorWithWidgetPreference mOpenInBrowserSelector;
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 onRadioButtonClicked(@NonNull SelectorWithWidgetPreference selected) {
final boolean openSupportedLinks = selected.getKey().equals(OPEN_IN_APP_PREF_KEY);
IntentPickerUtils.logd("onRadioButtonClicked: openInApp =" + openSupportedLinks);
setOpenByDefaultPreference(openSupportedLinks /* openInApp */);
if (mMainPreferenceCategory != null) {
mMainPreferenceCategory.setVisible(openSupportedLinks);
}
if (mDomainVerificationManager != null) {
try {
mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(mPackageName,
openSupportedLinks);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "onRadioButtonClicked: " + 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 */)
.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(getPrefContext());
getPreferenceScreen().addPreference(pref);
}
private void initUIComponents() {
initMainSwitchAndCategories();
if (canUpdateMainSwitchAndCategories()) {
initVerifiedLinksPreference();
initAddLinkPreference();
addSelectedLinksPreference();
initFooter();
}
}
private void initMainSwitchAndCategories() {
mOpenInAppSelector = findPreference(OPEN_IN_APP_PREF_KEY);
mOpenInBrowserSelector = findPreference(OPEN_IN_BROWSER_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 || mOpenInAppSelector == null || mOpenInBrowserSelector == null) {
disabledPreference();
return false;
}
IntentPickerUtils.logd("isLinkHandlingAllowed() : " + userState.isLinkHandlingAllowed());
setOpenByDefaultPreference(userState.isLinkHandlingAllowed());
mOpenInAppSelector.setOnClickListener(this);
mOpenInBrowserSelector.setOnClickListener(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 setOpenByDefaultPreference(boolean openInApp) {
if (mOpenInBrowserSelector == null || mOpenInAppSelector == null) return;
mOpenInAppSelector.setChecked(openInApp);
mOpenInBrowserSelector.setChecked(!openInApp);
}
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);
AlertDialog dialog = 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();
if (dialog.getListView() != null) {
dialog.getListView().setTextDirection(View.TEXT_DIRECTION_LOCALE);
dialog.getListView().setEnabled(false);
} else {
Log.w(TAG, "createVerifiedLinksDialog: dialog.getListView() is null, please check it.");
}
return dialog;
}
@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() {
if (mOpenInAppSelector == null ||mOpenInBrowserSelector == null) return;
setOpenByDefaultPreference(false /* openInApp */);
mOpenInAppSelector.setSelectable(false);
mOpenInAppSelector.setEnabled(false);
mOpenInBrowserSelector.setSelectable(false);
mOpenInBrowserSelector.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();
}
}