1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.settings.applications; 17 18 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; 19 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; 20 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; 21 22 import android.app.settings.SettingsEnums; 23 import android.content.pm.PackageManager; 24 import android.icu.text.MessageFormat; 25 import android.os.Bundle; 26 import android.text.TextUtils; 27 import android.util.Log; 28 import android.view.View; 29 30 import androidx.annotation.VisibleForTesting; 31 import androidx.appcompat.app.AlertDialog; 32 import androidx.preference.PreferenceCategory; 33 34 import com.android.settings.R; 35 import com.android.settings.Utils; 36 import com.android.settingslib.widget.FooterPreference; 37 import com.android.settingslib.widget.SelectorWithWidgetPreference; 38 39 import java.util.HashMap; 40 import java.util.Locale; 41 import java.util.Map; 42 import java.util.Set; 43 44 /** 45 * Display the Open Supported Links page. Allow users choose what kind supported links they need. 46 */ 47 public class OpenSupportedLinks extends AppInfoWithHeader implements 48 SelectorWithWidgetPreference.OnClickListener { 49 private static final String TAG = "OpenSupportedLinks"; 50 private static final String RADIO_GROUP_KEY = "supported_links_radio_group"; 51 private static final String FOOTER_KEY = "supported_links_footer"; 52 private static final String KEY_LINK_OPEN_ALWAYS = "app_link_open_always"; 53 private static final String KEY_LINK_OPEN_ASK = "app_link_open_ask"; 54 private static final String KEY_LINK_OPEN_NEVER = "app_link_open_never"; 55 56 private static final int ALLOW_ALWAYS_OPENING = 0; 57 private static final int ASK_EVERY_TIME = 1; 58 private static final int NOT_ALLOWED_OPENING = 2; 59 60 private int mCurrentIndex; 61 private String[] mRadioKeys = {KEY_LINK_OPEN_ALWAYS, KEY_LINK_OPEN_ASK, KEY_LINK_OPEN_NEVER}; 62 63 @VisibleForTesting 64 PackageManager mPackageManager; 65 @VisibleForTesting 66 PreferenceCategory mPreferenceCategory; 67 @VisibleForTesting 68 SelectorWithWidgetPreference mAllowOpening; 69 @VisibleForTesting 70 SelectorWithWidgetPreference mAskEveryTime; 71 @VisibleForTesting 72 SelectorWithWidgetPreference mNotAllowed; 73 74 @Override onCreate(Bundle savedInstanceState)75 public void onCreate(Bundle savedInstanceState) { 76 super.onCreate(savedInstanceState); 77 mPackageManager = getPackageManager(); 78 addPreferencesFromResource(R.xml.open_supported_links); 79 initRadioPreferencesGroup(); 80 updateFooterPreference(); 81 } 82 83 @Override getMetricsCategory()84 public int getMetricsCategory() { 85 return SettingsEnums.OPEN_SUPPORTED_LINKS; 86 } 87 88 /** 89 * Here to handle radio group and generate the radios. 90 */ 91 @VisibleForTesting initRadioPreferencesGroup()92 void initRadioPreferencesGroup() { 93 mPreferenceCategory = findPreference(RADIO_GROUP_KEY); 94 mAllowOpening = makeRadioPreference(KEY_LINK_OPEN_ALWAYS, R.string.app_link_open_always); 95 final int entriesNo = getEntriesNo(); 96 MessageFormat msgFormat = new MessageFormat( 97 getResources().getString(R.string.app_link_open_always_summary), 98 Locale.getDefault()); 99 Map<String, Object> arguments = new HashMap<>(); 100 arguments.put("count", entriesNo); 101 //This to avoid the summary line wrap 102 mAllowOpening.setAppendixVisibility(View.GONE); 103 mAllowOpening.setSummary(msgFormat.format(arguments)); 104 mAskEveryTime = makeRadioPreference(KEY_LINK_OPEN_ASK, R.string.app_link_open_ask); 105 mNotAllowed = makeRadioPreference(KEY_LINK_OPEN_NEVER, R.string.app_link_open_never); 106 107 final int state = mPackageManager.getIntentVerificationStatusAsUser(mPackageName, mUserId); 108 mCurrentIndex = linkStateToIndex(state); 109 setRadioStatus(mCurrentIndex); 110 } 111 112 @Override onRadioButtonClicked(SelectorWithWidgetPreference preference)113 public void onRadioButtonClicked(SelectorWithWidgetPreference preference) { 114 final int clickedIndex = preferenceKeyToIndex(preference.getKey()); 115 if (mCurrentIndex != clickedIndex) { 116 mCurrentIndex = clickedIndex; 117 setRadioStatus(mCurrentIndex); 118 updateAppLinkState(indexToLinkState(mCurrentIndex)); 119 } 120 } 121 makeRadioPreference(String key, int stringId)122 private SelectorWithWidgetPreference makeRadioPreference(String key, int stringId) { 123 final SelectorWithWidgetPreference pref = new SelectorWithWidgetPreference( 124 mPreferenceCategory.getContext()); 125 pref.setKey(key); 126 pref.setTitle(stringId); 127 pref.setOnClickListener(this); 128 mPreferenceCategory.addPreference(pref); 129 return pref; 130 } 131 132 @VisibleForTesting getEntriesNo()133 int getEntriesNo() { 134 return Utils.getHandledDomains(mPackageManager, mPackageName).size(); 135 } 136 linkStateToIndex(int state)137 private int linkStateToIndex(int state) { 138 switch (state) { 139 case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: 140 return ALLOW_ALWAYS_OPENING; 141 case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: 142 return NOT_ALLOWED_OPENING; 143 default: 144 return ASK_EVERY_TIME; 145 } 146 } 147 indexToLinkState(int index)148 private int indexToLinkState(int index) { 149 switch (index) { 150 case 0: 151 return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; 152 case 2: 153 return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; 154 default: 155 return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; 156 } 157 } 158 setRadioStatus(int index)159 private void setRadioStatus(int index) { 160 mAllowOpening.setChecked(index == ALLOW_ALWAYS_OPENING); 161 mAskEveryTime.setChecked(index == ASK_EVERY_TIME); 162 mNotAllowed.setChecked(index == NOT_ALLOWED_OPENING); 163 } 164 preferenceKeyToIndex(String key)165 private int preferenceKeyToIndex(String key) { 166 for (int i = 0; i < mRadioKeys.length; i++) { 167 if (TextUtils.equals(key, mRadioKeys[i])) { 168 return i; 169 } 170 } 171 return ASK_EVERY_TIME; 172 } 173 updateAppLinkState(final int newState)174 private void updateAppLinkState(final int newState) { 175 final int priorState = mPackageManager.getIntentVerificationStatusAsUser(mPackageName, 176 mUserId); 177 178 if (priorState == newState) { 179 return; 180 } 181 182 final boolean success = mPackageManager.updateIntentVerificationStatusAsUser(mPackageName, 183 newState, mUserId); 184 if (success) { 185 // Read back the state to see if the change worked 186 final int updatedState = mPackageManager.getIntentVerificationStatusAsUser(mPackageName, 187 mUserId); 188 } else { 189 Log.e(TAG, "Couldn't update intent verification status!"); 190 } 191 } 192 193 /** 194 * Here is handle the Footer. 195 */ updateFooterPreference()196 private void updateFooterPreference() { 197 final FooterPreference footer = findPreference(FOOTER_KEY); 198 if (footer == null) { 199 Log.w(TAG, "Can't find the footer preference."); 200 return; 201 } 202 addLinksToFooter(footer); 203 } 204 205 @VisibleForTesting addLinksToFooter(FooterPreference footer)206 void addLinksToFooter(FooterPreference footer) { 207 final Set<String> result = Utils.getHandledDomains(mPackageManager, mPackageName); 208 if (result.isEmpty()) { 209 Log.w(TAG, "Can't find any app links."); 210 return; 211 } 212 CharSequence title = footer.getTitle() + System.lineSeparator(); 213 for (String link : result) { 214 title = title + System.lineSeparator() + link; 215 } 216 footer.setTitle(title); 217 } 218 219 @Override refreshUi()220 protected boolean refreshUi() { 221 return true; 222 } 223 224 @Override createDialog(int id, int errorCode)225 protected AlertDialog createDialog(int id, int errorCode) { 226 return null; 227 } 228 } 229