blob: cebb4f786895030f35a91e9f925e45ef59dc5392 [file] [log] [blame]
/*
* 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 */)
.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() {
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);
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);
} 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() {
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();
}
}