1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15package com.android.settings.applications; 16 17import android.app.Application; 18import android.content.ComponentName; 19import android.content.Context; 20import android.content.Intent; 21import android.content.pm.PackageManager; 22import android.os.Bundle; 23import android.os.UserHandle; 24import android.provider.Settings; 25import android.provider.Settings.Global; 26import android.support.annotation.VisibleForTesting; 27import android.support.v14.preference.SwitchPreference; 28import android.support.v7.preference.Preference; 29import android.support.v7.preference.Preference.OnPreferenceChangeListener; 30import android.support.v7.preference.Preference.OnPreferenceClickListener; 31import android.support.v7.preference.PreferenceCategory; 32import android.support.v7.preference.PreferenceGroup; 33import android.support.v7.preference.PreferenceViewHolder; 34import android.util.ArraySet; 35import android.view.View; 36 37import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 38import com.android.settings.R; 39import com.android.settings.SettingsPreferenceFragment; 40import com.android.settings.Utils; 41import com.android.settings.widget.AppPreference; 42import com.android.settingslib.applications.ApplicationsState; 43import com.android.settingslib.applications.ApplicationsState.AppEntry; 44 45import java.util.ArrayList; 46 47/** 48 * Activity to manage how Android handles URL resolution. Includes both per-app 49 * handling as well as system handling for Web Actions. 50 */ 51public class ManageDomainUrls extends SettingsPreferenceFragment 52 implements ApplicationsState.Callbacks, OnPreferenceChangeListener, 53 OnPreferenceClickListener { 54 55 // constant value that can be used to check return code from sub activity. 56 private static final int INSTALLED_APP_DETAILS = 1; 57 58 private ApplicationsState mApplicationsState; 59 private ApplicationsState.Session mSession; 60 private PreferenceGroup mDomainAppList; 61 private SwitchPreference mWebAction; 62 private Preference mInstantAppAccountPreference; 63 64 @Override 65 public void onCreate(Bundle icicle) { 66 super.onCreate(icicle); 67 setAnimationAllowed(true); 68 mApplicationsState = ApplicationsState.getInstance( 69 (Application) getContext().getApplicationContext()); 70 mSession = mApplicationsState.newSession(this, getLifecycle()); 71 setHasOptionsMenu(true); 72 } 73 74 @Override 75 protected int getPreferenceScreenResId() { 76 return R.xml.manage_domain_url_settings; 77 } 78 79 @Override 80 public void onViewCreated(View view, Bundle savedInstanceState) { 81 super.onViewCreated(view, savedInstanceState); 82 } 83 84 @Override 85 public void onRunningStateChanged(boolean running) { 86 } 87 88 @Override 89 public void onPackageListChanged() { 90 } 91 92 @Override 93 public void onRebuildComplete(ArrayList<AppEntry> apps) { 94 if (getContext() == null) { 95 return; 96 } 97 98 final boolean disableWebActions = Global.getInt(getContext().getContentResolver(), 99 Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0; 100 if (disableWebActions) { 101 mDomainAppList = getPreferenceScreen(); 102 } else { 103 final PreferenceGroup preferenceScreen = getPreferenceScreen(); 104 if (preferenceScreen.getPreferenceCount() == 0) { 105 // add preferences 106 final PreferenceCategory webActionCategory = 107 new PreferenceCategory(getPrefContext()); 108 webActionCategory.setTitle(R.string.web_action_section_title); 109 preferenceScreen.addPreference(webActionCategory); 110 111 // toggle to enable / disable Web Actions [aka Instant Apps] 112 mWebAction = new SwitchPreference(getPrefContext()); 113 mWebAction.setTitle(R.string.web_action_enable_title); 114 mWebAction.setSummary(R.string.web_action_enable_summary); 115 mWebAction.setChecked(Settings.Secure.getInt(getContentResolver(), 116 Settings.Secure.INSTANT_APPS_ENABLED, 1) != 0); 117 mWebAction.setOnPreferenceChangeListener(this); 118 webActionCategory.addPreference(mWebAction); 119 120 // Determine whether we should show the instant apps account chooser setting 121 ComponentName instantAppSettingsComponent = getActivity().getPackageManager() 122 .getInstantAppResolverSettingsComponent(); 123 Intent instantAppSettingsIntent = null; 124 if (instantAppSettingsComponent != null) { 125 instantAppSettingsIntent = 126 new Intent().setComponent(instantAppSettingsComponent); 127 } 128 if (instantAppSettingsIntent != null) { 129 final Intent launchIntent = instantAppSettingsIntent; 130 // TODO: Make this button actually launch the account chooser. 131 mInstantAppAccountPreference = new Preference(getPrefContext()); 132 mInstantAppAccountPreference.setTitle(R.string.instant_apps_settings); 133 mInstantAppAccountPreference.setOnPreferenceClickListener(pref -> { 134 startActivity(launchIntent); 135 return true; 136 }); 137 webActionCategory.addPreference(mInstantAppAccountPreference); 138 } 139 140 // list to manage link handling per app 141 mDomainAppList = new PreferenceCategory(getPrefContext()); 142 mDomainAppList.setTitle(R.string.domain_url_section_title); 143 preferenceScreen.addPreference(mDomainAppList); 144 } 145 } 146 rebuildAppList(mDomainAppList, apps); 147 } 148 149 @Override 150 public boolean onPreferenceChange(Preference preference, Object newValue) { 151 if (preference == mWebAction) { 152 boolean checked = (boolean) newValue; 153 Settings.Secure.putInt( 154 getContentResolver(), 155 Settings.Secure.INSTANT_APPS_ENABLED, checked ? 1 : 0); 156 return true; 157 } 158 return false; 159 } 160 161 private void rebuild() { 162 final ArrayList<AppEntry> apps = mSession.rebuild( 163 ApplicationsState.FILTER_WITH_DOMAIN_URLS, ApplicationsState.ALPHA_COMPARATOR); 164 if (apps != null) { 165 onRebuildComplete(apps); 166 } 167 } 168 169 private void rebuildAppList(PreferenceGroup group, ArrayList<AppEntry> apps) { 170 cacheRemoveAllPrefs(group); 171 final int N = apps.size(); 172 for (int i = 0; i < N; i++) { 173 AppEntry entry = apps.get(i); 174 String key = entry.info.packageName + "|" + entry.info.uid; 175 DomainAppPreference preference = (DomainAppPreference) getCachedPreference(key); 176 if (preference == null) { 177 preference = new DomainAppPreference(getPrefContext(), mApplicationsState, entry); 178 preference.setKey(key); 179 preference.setOnPreferenceClickListener(this); 180 group.addPreference(preference); 181 } else { 182 preference.reuse(); 183 } 184 preference.setOrder(i); 185 } 186 removeCachedPrefs(group); 187 } 188 189 @Override 190 public void onPackageIconChanged() { 191 } 192 193 @Override 194 public void onPackageSizeChanged(String packageName) { 195 } 196 197 @Override 198 public void onAllSizesComputed() { 199 } 200 201 @Override 202 public void onLauncherInfoChanged() { 203 } 204 205 @Override 206 public void onLoadEntriesCompleted() { 207 rebuild(); 208 } 209 210 @Override 211 public int getMetricsCategory() { 212 return MetricsEvent.MANAGE_DOMAIN_URLS; 213 } 214 215 @Override 216 public boolean onPreferenceClick(Preference preference) { 217 if (preference.getClass() == DomainAppPreference.class) { 218 ApplicationsState.AppEntry entry = ((DomainAppPreference) preference).mEntry; 219 AppInfoBase.startAppInfoFragment(AppLaunchSettings.class, R.string.auto_launch_label, 220 entry.info.packageName, entry.info.uid, this, 221 INSTALLED_APP_DETAILS, getMetricsCategory()); 222 return true; 223 } 224 return false; 225 } 226 227 @VisibleForTesting 228 static class DomainAppPreference extends AppPreference { 229 private final AppEntry mEntry; 230 private final PackageManager mPm; 231 private final ApplicationsState mApplicationsState; 232 233 public DomainAppPreference(final Context context, ApplicationsState applicationsState, 234 AppEntry entry) { 235 super(context); 236 mApplicationsState = applicationsState; 237 mPm = context.getPackageManager(); 238 mEntry = entry; 239 mEntry.ensureLabel(getContext()); 240 setState(); 241 if (mEntry.icon != null) { 242 setIcon(mEntry.icon); 243 } 244 } 245 246 private void setState() { 247 setTitle(mEntry.label); 248 setSummary(getDomainsSummary(mEntry.info.packageName)); 249 } 250 251 public void reuse() { 252 setState(); 253 notifyChanged(); 254 } 255 256 @Override 257 public void onBindViewHolder(PreferenceViewHolder holder) { 258 if (mEntry.icon == null) { 259 holder.itemView.post(new Runnable() { 260 @Override 261 public void run() { 262 // Ensure we have an icon before binding. 263 mApplicationsState.ensureIcon(mEntry); 264 // This might trigger us to bind again, but it gives an easy way to only 265 // load the icon once its needed, so its probably worth it. 266 setIcon(mEntry.icon); 267 } 268 }); 269 } 270 super.onBindViewHolder(holder); 271 } 272 273 private CharSequence getDomainsSummary(String packageName) { 274 // If the user has explicitly said "no" for this package, that's the 275 // string we should show. 276 int domainStatus = 277 mPm.getIntentVerificationStatusAsUser(packageName, UserHandle.myUserId()); 278 if (domainStatus == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { 279 return getContext().getString(R.string.domain_urls_summary_none); 280 } 281 // Otherwise, ask package manager for the domains for this package, 282 // and show the first one (or none if there aren't any). 283 ArraySet<String> result = Utils.getHandledDomains(mPm, packageName); 284 if (result.size() == 0) { 285 return getContext().getString(R.string.domain_urls_summary_none); 286 } else if (result.size() == 1) { 287 return getContext().getString(R.string.domain_urls_summary_one, result.valueAt(0)); 288 } else { 289 return getContext().getString(R.string.domain_urls_summary_some, result.valueAt(0)); 290 } 291 } 292 } 293} 294