• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 
17 package com.android.settings.applications;
18 
19 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
20 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
21 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
22 
23 import android.app.settings.SettingsEnums;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.IntentFilterVerificationInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ResolveInfo;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.os.UserHandle;
33 import android.util.ArraySet;
34 import android.util.Log;
35 import android.view.View;
36 import android.view.View.OnClickListener;
37 
38 import androidx.appcompat.app.AlertDialog;
39 import androidx.preference.DropDownPreference;
40 import androidx.preference.Preference;
41 import androidx.preference.Preference.OnPreferenceChangeListener;
42 
43 import com.android.settings.R;
44 import com.android.settings.Utils;
45 
46 import java.util.List;
47 
48 public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListener,
49         Preference.OnPreferenceChangeListener {
50     private static final String TAG = "AppLaunchSettings";
51 
52     private static final String KEY_APP_LINK_STATE = "app_link_state";
53     private static final String KEY_SUPPORTED_DOMAIN_URLS = "app_launch_supported_domain_urls";
54     private static final String KEY_CLEAR_DEFAULTS = "app_launch_clear_defaults";
55 
56     private static final Intent sBrowserIntent;
57     static {
58         sBrowserIntent = new Intent()
59                 .setAction(Intent.ACTION_VIEW)
60                 .addCategory(Intent.CATEGORY_BROWSABLE)
61                 .setData(Uri.parse("http:"));
62     }
63 
64     private PackageManager mPm;
65 
66     private boolean mIsBrowser;
67     private boolean mHasDomainUrls;
68     private DropDownPreference mAppLinkState;
69     private AppDomainsPreference mAppDomainUrls;
70     private ClearDefaultsPreference mClearDefaultsPreference;
71 
72     @Override
onCreate(Bundle savedInstanceState)73     public void onCreate(Bundle savedInstanceState) {
74         super.onCreate(savedInstanceState);
75 
76         addPreferencesFromResource(R.xml.installed_app_launch_settings);
77         mAppDomainUrls = (AppDomainsPreference) findPreference(KEY_SUPPORTED_DOMAIN_URLS);
78         mClearDefaultsPreference = (ClearDefaultsPreference) findPreference(KEY_CLEAR_DEFAULTS);
79         mAppLinkState = (DropDownPreference) findPreference(KEY_APP_LINK_STATE);
80 
81         mPm = getActivity().getPackageManager();
82 
83         mIsBrowser = isBrowserApp(mPackageName);
84         mHasDomainUrls =
85                 (mAppEntry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
86 
87         if (!mIsBrowser) {
88             List<IntentFilterVerificationInfo> iviList = mPm.getIntentFilterVerifications(mPackageName);
89             List<IntentFilter> filters = mPm.getAllIntentFilters(mPackageName);
90             CharSequence[] entries = getEntries(mPackageName, iviList, filters);
91             mAppDomainUrls.setTitles(entries);
92             mAppDomainUrls.setValues(new int[entries.length]);
93         }
94         buildStateDropDown();
95     }
96 
97     // An app is a "browser" if it has an activity resolution that wound up
98     // marked with the 'handleAllWebDataURI' flag.
isBrowserApp(String packageName)99     private boolean isBrowserApp(String packageName) {
100         sBrowserIntent.setPackage(packageName);
101         List<ResolveInfo> list = mPm.queryIntentActivitiesAsUser(sBrowserIntent,
102                 PackageManager.MATCH_ALL, UserHandle.myUserId());
103         final int count = list.size();
104         for (int i = 0; i < count; i++) {
105             ResolveInfo info = list.get(i);
106             if (info.activityInfo != null && info.handleAllWebDataURI) {
107                 return true;
108             }
109         }
110         return false;
111     }
112 
buildStateDropDown()113     private void buildStateDropDown() {
114         if (mIsBrowser) {
115             // Browsers don't show the app-link prefs
116             mAppLinkState.setShouldDisableView(true);
117             mAppLinkState.setEnabled(false);
118             mAppDomainUrls.setShouldDisableView(true);
119             mAppDomainUrls.setEnabled(false);
120         } else {
121             // Designed order of states in the dropdown:
122             //
123             // * always
124             // * ask
125             // * never
126             //
127             // Make sure to update linkStateToIndex() if this presentation order is changed.
128             mAppLinkState.setEntries(new CharSequence[] {
129                     getString(R.string.app_link_open_always),
130                     getString(R.string.app_link_open_ask),
131                     getString(R.string.app_link_open_never),
132             });
133             mAppLinkState.setEntryValues(new CharSequence[] {
134                     Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS),
135                     Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK),
136                     Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER),
137             });
138 
139             mAppLinkState.setEnabled(mHasDomainUrls);
140             if (mHasDomainUrls) {
141                 // Present 'undefined' as 'ask' because the OS treats them identically for
142                 // purposes of the UI (and does the right thing around pending domain
143                 // verifications that might arrive after the user chooses 'ask' in this UI).
144                 final int state = mPm.getIntentVerificationStatusAsUser(mPackageName, UserHandle.myUserId());
145                 mAppLinkState.setValueIndex(linkStateToIndex(state));
146 
147                 // Set the callback only after setting the initial selected item
148                 mAppLinkState.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
149                     @Override
150                     public boolean onPreferenceChange(Preference preference, Object newValue) {
151                         return updateAppLinkState(Integer.parseInt((String) newValue));
152                     }
153                 });
154             }
155         }
156     }
157 
linkStateToIndex(final int state)158     private int linkStateToIndex(final int state) {
159         switch (state) {
160             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
161                 return 0; // Always
162             case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
163                 return 2; // Never
164             default:
165                 return 1; // Ask
166         }
167     }
168 
updateAppLinkState(final int newState)169     private boolean updateAppLinkState(final int newState) {
170         if (mIsBrowser) {
171             // We shouldn't get into this state, but if we do make sure
172             // not to cause any permanent mayhem.
173             return false;
174         }
175 
176         final int userId = UserHandle.myUserId();
177         final int priorState = mPm.getIntentVerificationStatusAsUser(mPackageName, userId);
178 
179         if (priorState == newState) {
180             return false;
181         }
182 
183         boolean success = mPm.updateIntentVerificationStatusAsUser(mPackageName, newState, userId);
184         if (success) {
185             // Read back the state to see if the change worked
186             final int updatedState = mPm.getIntentVerificationStatusAsUser(mPackageName, userId);
187             success = (newState == updatedState);
188         } else {
189             Log.e(TAG, "Couldn't update intent verification status!");
190         }
191         return success;
192     }
193 
getEntries(String packageName, List<IntentFilterVerificationInfo> iviList, List<IntentFilter> filters)194     private CharSequence[] getEntries(String packageName, List<IntentFilterVerificationInfo> iviList,
195             List<IntentFilter> filters) {
196         ArraySet<String> result = Utils.getHandledDomains(mPm, packageName);
197         return result.toArray(new CharSequence[result.size()]);
198     }
199 
200     @Override
refreshUi()201     protected boolean refreshUi() {
202         mClearDefaultsPreference.setPackageName(mPackageName);
203         mClearDefaultsPreference.setAppEntry(mAppEntry);
204         return true;
205     }
206 
207     @Override
createDialog(int id, int errorCode)208     protected AlertDialog createDialog(int id, int errorCode) {
209         // No dialogs for preferred launch settings.
210         return null;
211     }
212 
213     @Override
onClick(View v)214     public void onClick(View v) {
215         // Nothing to do
216     }
217 
218     @Override
onPreferenceChange(Preference preference, Object newValue)219     public boolean onPreferenceChange(Preference preference, Object newValue) {
220         // actual updates are handled by the app link dropdown callback
221         return true;
222     }
223 
224     @Override
getMetricsCategory()225     public int getMetricsCategory() {
226         return SettingsEnums.APPLICATIONS_APP_LAUNCH;
227     }
228 }
229