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