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.os.Bundle;
25 import android.text.TextUtils;
26 import android.util.ArraySet;
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.RadioButtonPreference;
38 
39 /**
40  * Display the Open Supported Links page. Allow users choose what kind supported links they need.
41  */
42 public class OpenSupportedLinks extends AppInfoWithHeader implements
43         RadioButtonPreference.OnClickListener {
44     private static final String TAG = "OpenSupportedLinks";
45     private static final String RADIO_GROUP_KEY = "supported_links_radio_group";
46     private static final String FOOTER_KEY = "supported_links_footer";
47     private static final String KEY_LINK_OPEN_ALWAYS = "app_link_open_always";
48     private static final String KEY_LINK_OPEN_ASK = "app_link_open_ask";
49     private static final String KEY_LINK_OPEN_NEVER = "app_link_open_never";
50 
51     private static final int ALLOW_ALWAYS_OPENING = 0;
52     private static final int ASK_EVERY_TIME = 1;
53     private static final int NOT_ALLOWED_OPENING = 2;
54 
55     private int mCurrentIndex;
56     private String[] mRadioKeys = {KEY_LINK_OPEN_ALWAYS, KEY_LINK_OPEN_ASK, KEY_LINK_OPEN_NEVER};
57 
58     @VisibleForTesting
59     PackageManager mPackageManager;
60     @VisibleForTesting
61     PreferenceCategory mPreferenceCategory;
62     @VisibleForTesting
63     RadioButtonPreference mAllowOpening;
64     @VisibleForTesting
65     RadioButtonPreference mAskEveryTime;
66     @VisibleForTesting
67     RadioButtonPreference mNotAllowed;
68 
69     @Override
onCreate(Bundle savedInstanceState)70     public void onCreate(Bundle savedInstanceState) {
71         super.onCreate(savedInstanceState);
72         mPackageManager = getPackageManager();
73         addPreferencesFromResource(R.xml.open_supported_links);
74         initRadioPreferencesGroup();
75         updateFooterPreference();
76     }
77 
78     @Override
getMetricsCategory()79     public int getMetricsCategory() {
80         return SettingsEnums.OPEN_SUPPORTED_LINKS;
81     }
82 
83     /**
84      * Here to handle radio group and generate the radios.
85      */
86     @VisibleForTesting
initRadioPreferencesGroup()87     void initRadioPreferencesGroup() {
88         mPreferenceCategory = findPreference(RADIO_GROUP_KEY);
89         mAllowOpening = makeRadioPreference(KEY_LINK_OPEN_ALWAYS, R.string.app_link_open_always);
90         final int entriesNo = getEntriesNo();
91         //This to avoid the summary line wrap
92         mAllowOpening.setAppendixVisibility(View.GONE);
93         mAllowOpening.setSummary(getResources().getQuantityString(
94                 R.plurals.app_link_open_always_summary, entriesNo, entriesNo));
95         mAskEveryTime = makeRadioPreference(KEY_LINK_OPEN_ASK, R.string.app_link_open_ask);
96         mNotAllowed = makeRadioPreference(KEY_LINK_OPEN_NEVER, R.string.app_link_open_never);
97 
98         final int state = mPackageManager.getIntentVerificationStatusAsUser(mPackageName, mUserId);
99         mCurrentIndex = linkStateToIndex(state);
100         setRadioStatus(mCurrentIndex);
101     }
102 
103     @Override
onRadioButtonClicked(RadioButtonPreference preference)104     public void onRadioButtonClicked(RadioButtonPreference preference) {
105         final int clickedIndex = preferenceKeyToIndex(preference.getKey());
106         if (mCurrentIndex != clickedIndex) {
107             mCurrentIndex = clickedIndex;
108             setRadioStatus(mCurrentIndex);
109             updateAppLinkState(indexToLinkState(mCurrentIndex));
110         }
111     }
112 
makeRadioPreference(String key, int stringId)113     private RadioButtonPreference makeRadioPreference(String key, int stringId) {
114         final RadioButtonPreference pref = new RadioButtonPreference(
115                 mPreferenceCategory.getContext());
116         pref.setKey(key);
117         pref.setTitle(stringId);
118         pref.setOnClickListener(this);
119         mPreferenceCategory.addPreference(pref);
120         return pref;
121     }
122 
123     @VisibleForTesting
getEntriesNo()124     int getEntriesNo() {
125         return Utils.getHandledDomains(mPackageManager, mPackageName).size();
126     }
127 
linkStateToIndex(int state)128     private int linkStateToIndex(int state) {
129         switch (state) {
130             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
131                 return ALLOW_ALWAYS_OPENING;
132             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
133                 return NOT_ALLOWED_OPENING;
134             default:
135                 return ASK_EVERY_TIME;
136         }
137     }
138 
indexToLinkState(int index)139     private int indexToLinkState(int index) {
140         switch (index) {
141             case 0:
142                 return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
143             case 2:
144                 return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
145             default:
146                 return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
147         }
148     }
149 
setRadioStatus(int index)150     private void setRadioStatus(int index) {
151         mAllowOpening.setChecked(index == ALLOW_ALWAYS_OPENING);
152         mAskEveryTime.setChecked(index == ASK_EVERY_TIME);
153         mNotAllowed.setChecked(index == NOT_ALLOWED_OPENING);
154     }
155 
preferenceKeyToIndex(String key)156     private int preferenceKeyToIndex(String key) {
157         for (int i = 0; i < mRadioKeys.length; i++) {
158             if (TextUtils.equals(key, mRadioKeys[i])) {
159                 return i;
160             }
161         }
162         return ASK_EVERY_TIME;
163     }
164 
updateAppLinkState(final int newState)165     private void updateAppLinkState(final int newState) {
166         final int priorState = mPackageManager.getIntentVerificationStatusAsUser(mPackageName,
167                 mUserId);
168 
169         if (priorState == newState) {
170             return;
171         }
172 
173         final boolean success = mPackageManager.updateIntentVerificationStatusAsUser(mPackageName,
174                 newState, mUserId);
175         if (success) {
176             // Read back the state to see if the change worked
177             final int updatedState = mPackageManager.getIntentVerificationStatusAsUser(mPackageName,
178                     mUserId);
179         } else {
180             Log.e(TAG, "Couldn't update intent verification status!");
181         }
182     }
183 
184     /**
185      * Here is handle the Footer.
186      */
updateFooterPreference()187     private void updateFooterPreference() {
188         final FooterPreference footer = findPreference(FOOTER_KEY);
189         if (footer == null) {
190             Log.w(TAG, "Can't find the footer preference.");
191             return;
192         }
193         addLinksToFooter(footer);
194     }
195 
196     @VisibleForTesting
addLinksToFooter(FooterPreference footer)197     void addLinksToFooter(FooterPreference footer) {
198         final ArraySet<String> result = Utils.getHandledDomains(mPackageManager, mPackageName);
199         if (result.isEmpty()) {
200             Log.w(TAG, "Can't find any app links.");
201             return;
202         }
203         CharSequence title = footer.getTitle() + System.lineSeparator();
204         for (String link : result) {
205             title = title + System.lineSeparator() + link;
206         }
207         footer.setTitle(title);
208     }
209 
210     @Override
refreshUi()211     protected boolean refreshUi() {
212         return true;
213     }
214 
215     @Override
createDialog(int id, int errorCode)216     protected AlertDialog createDialog(int id, int errorCode) {
217         return null;
218     }
219 }
220