From d5824967ee8d046f6337dfd068a015ac78eb71d3 Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Thu, 25 Aug 2016 10:17:47 -0700 Subject: [PATCH] Implement Web Action settings Users can enable / disable Web Action from "Opening links" under the apps setting. Bug: 28140107 Change-Id: I4ed4b77953952a56316b780a54a482d0c564cbe4 Test: manual testing of the settings app --- res/values/strings.xml | 11 + res/xml/advanced_apps.xml | 5 +- src/com/android/settings/Settings.java | 2 +- .../applications/ManageApplications.java | 42 +-- .../applications/ManageDomainUrls.java | 273 ++++++++++++++++++ 5 files changed, 288 insertions(+), 45 deletions(-) create mode 100644 src/com/android/settings/applications/ManageDomainUrls.java diff --git a/res/values/strings.xml b/res/values/strings.xml index cc3ee448ebe..b2ab245a96e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7763,5 +7763,16 @@ %1$s total made available\n\nLast ran on %2$s + + Open links in apps + + Open links in supported apps, even if the + apps aren’t installed on your device + + + Apps not installed + + + Installed apps diff --git a/res/xml/advanced_apps.xml b/res/xml/advanced_apps.xml index 267356953b9..405d5af667b 100644 --- a/res/xml/advanced_apps.xml +++ b/res/xml/advanced_apps.xml @@ -30,10 +30,7 @@ - + android:fragment="com.android.settings.applications.ManageDomainUrls"> mIndex; private SectionInfo[] mSections = EMPTY_SECTIONS; private int[] mPositionToSectionIndex; @@ -984,7 +969,7 @@ public class ManageApplications extends InstrumentedFragment if (locales.size() == 0) { locales = new LocaleList(Locale.ENGLISH); } - AlphabeticIndex index = new AlphabeticIndex<>(locales.get(0)); + AlphabeticIndex index = new AlphabeticIndex<>(locales.get(0)); int localeCount = locales.size(); for (int i = 1; i < localeCount; i++) { index.addLabels(locales.get(i)); @@ -1181,10 +1166,6 @@ public class ManageApplications extends InstrumentedFragment } break; - case LIST_TYPE_DOMAINS_URLS: - holder.summary.setText(getDomainsSummary(holder.entry.info.packageName)); - break; - case LIST_TYPE_USAGE_ACCESS: if (holder.entry.extraInfo != null) { holder.summary.setText((new UsageState((PermissionState) holder.entry @@ -1224,25 +1205,6 @@ public class ManageApplications extends InstrumentedFragment mActive.remove(view); } - private CharSequence getDomainsSummary(String packageName) { - // If the user has explicitly said "no" for this package, that's the - // string we should show. - int domainStatus = mPm.getIntentVerificationStatusAsUser(packageName, UserHandle.myUserId()); - if (domainStatus == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { - return mContext.getString(R.string.domain_urls_summary_none); - } - // Otherwise, ask package manager for the domains for this package, - // and show the first one (or none if there aren't any). - ArraySet result = Utils.getHandledDomains(mPm, packageName); - if (result.size() == 0) { - return mContext.getString(R.string.domain_urls_summary_none); - } else if (result.size() == 1) { - return mContext.getString(R.string.domain_urls_summary_one, result.valueAt(0)); - } else { - return mContext.getString(R.string.domain_urls_summary_some, result.valueAt(0)); - } - } - @Override public Object[] getSections() { return mSections; diff --git a/src/com/android/settings/applications/ManageDomainUrls.java b/src/com/android/settings/applications/ManageDomainUrls.java new file mode 100644 index 00000000000..9b3f09e5063 --- /dev/null +++ b/src/com/android/settings/applications/ManageDomainUrls.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.settings.applications; + +import android.app.Application; +import android.content.Context; +import android.content.DialogInterface; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.Preference.OnPreferenceChangeListener; +import android.support.v7.preference.PreferenceCategory; +import android.support.v7.preference.PreferenceGroup; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.ArraySet; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; + +import com.android.internal.logging.MetricsProto.MetricsEvent; +import com.android.settings.AppHeader; +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.Utils; +import com.android.settings.applications.AppInfoBase; +import com.android.settings.applications.AppStateBaseBridge; +import com.android.settings.applications.InstalledAppDetails; +import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState; +import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.applications.ApplicationsState.AppEntry; +import com.android.settingslib.applications.ApplicationsState.AppFilter; + +import java.util.ArrayList; + +/** + * Activity to manage how Android handles URL resolution. Includes both per-app + * handling as well as system handling for Web Actions. + */ +public class ManageDomainUrls extends SettingsPreferenceFragment + implements ApplicationsState.Callbacks, OnPreferenceChangeListener { + + // STOPSHIP; b/30256615 + private static final boolean DISABLE_WEB_ACTIONS = !Build.IS_DEBUGGABLE; + + private ApplicationsState mApplicationsState; + private ApplicationsState.Session mSession; + private PreferenceGroup mDomainAppList; + private SwitchPreference mWebAction; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + setAnimationAllowed(true); + setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext())); + mApplicationsState = ApplicationsState.getInstance( + (Application) getContext().getApplicationContext()); + mSession = mApplicationsState.newSession(this); + setHasOptionsMenu(true); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + setLoading(true, false); + } + + @Override + public void onResume() { + super.onResume(); + mSession.resume(); + } + + @Override + public void onPause() { + super.onPause(); + mSession.pause(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mSession.release(); + } + + @Override + public void onRunningStateChanged(boolean running) { + } + + @Override + public void onPackageListChanged() { + } + + @Override + public void onRebuildComplete(ArrayList apps) { + if (getContext() == null) { + return; + } + + if (DISABLE_WEB_ACTIONS) { + mDomainAppList = getPreferenceScreen(); + } else { + final PreferenceGroup preferenceScreen = getPreferenceScreen(); + if (preferenceScreen.getPreferenceCount() == 0) { + // add preferences + final PreferenceCategory webActionCategory = + new PreferenceCategory(getPrefContext()); + webActionCategory.setTitle(R.string.web_action_section_title); + preferenceScreen.addPreference(webActionCategory); + + // toggle to enable / disable Web Actions [aka Instant Apps] + mWebAction = new SwitchPreference(getPrefContext()); + mWebAction.setTitle(R.string.web_action_enable_title); + mWebAction.setSummary(R.string.web_action_enable_summary); + mWebAction.setChecked(Settings.Secure.getInt(getContentResolver(), + Settings.Secure.WEB_ACTION_ENABLED, 1) != 0); + mWebAction.setOnPreferenceChangeListener(this); + webActionCategory.addPreference(mWebAction); + + // list to manage link handling per app + mDomainAppList = new PreferenceCategory(getPrefContext()); + mDomainAppList.setTitle(R.string.domain_url_section_title); + preferenceScreen.addPreference(mDomainAppList); + } + } + rebuildAppList(mDomainAppList, apps); + setLoading(false, true); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mWebAction) { + final int enabled = (boolean) newValue ? 1 : 0; + Settings.Secure.putInt( + getContentResolver(), Settings.Secure.WEB_ACTION_ENABLED, enabled); + return true; + } + return false; + } + + private void rebuild() { + final ArrayList apps = mSession.rebuild( + ApplicationsState.FILTER_WITH_DOMAIN_URLS, ApplicationsState.ALPHA_COMPARATOR); + if (apps != null) { + onRebuildComplete(apps); + } + } + + private void rebuildAppList(PreferenceGroup group, ArrayList apps) { + cacheRemoveAllPrefs(group); + final int N = apps.size(); + for (int i = 0; i < N; i++) { + AppEntry entry = apps.get(i); + String key = entry.info.packageName + "|" + entry.info.uid; + DomainAppPreference preference = (DomainAppPreference) getCachedPreference(key); + if (preference == null) { + preference = new DomainAppPreference(getPrefContext(), entry); + preference.setKey(key); + group.addPreference(preference); + } else { + preference.reuse(); + } + preference.setOrder(i); + } + removeCachedPrefs(group); + } + + @Override + public void onPackageIconChanged() { + } + + @Override + public void onPackageSizeChanged(String packageName) { + } + + @Override + public void onAllSizesComputed() { + } + + @Override + public void onLauncherInfoChanged() { + } + + @Override + public void onLoadEntriesCompleted() { + rebuild(); + } + + @Override + protected int getMetricsCategory() { + return MetricsEvent.MANAGE_DOMAIN_URLS; + } + + private class DomainAppPreference extends Preference { + private final AppEntry mEntry; + private final PackageManager mPm; + + public DomainAppPreference(final Context context, AppEntry entry) { + super(context); + mPm = context.getPackageManager(); + mEntry = entry; + mEntry.ensureLabel(getContext()); + setState(); + if (mEntry.icon != null) { + setIcon(mEntry.icon); + } + } + + private void setState() { + setTitle(mEntry.label); + setSummary(getDomainsSummary(mEntry.info.packageName)); + } + + public void reuse() { + setState(); + notifyChanged(); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + if (mEntry.icon == null) { + holder.itemView.post(new Runnable() { + @Override + public void run() { + // Ensure we have an icon before binding. + mApplicationsState.ensureIcon(mEntry); + // This might trigger us to bind again, but it gives an easy way to only + // load the icon once its needed, so its probably worth it. + setIcon(mEntry.icon); + } + }); + } + super.onBindViewHolder(holder); + } + + private CharSequence getDomainsSummary(String packageName) { + // If the user has explicitly said "no" for this package, that's the + // string we should show. + int domainStatus = + mPm.getIntentVerificationStatusAsUser(packageName, UserHandle.myUserId()); + if (domainStatus == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) { + return getContext().getString(R.string.domain_urls_summary_none); + } + // Otherwise, ask package manager for the domains for this package, + // and show the first one (or none if there aren't any). + ArraySet result = Utils.getHandledDomains(mPm, packageName); + if (result.size() == 0) { + return getContext().getString(R.string.domain_urls_summary_none); + } else if (result.size() == 1) { + return getContext().getString(R.string.domain_urls_summary_one, result.valueAt(0)); + } else { + return getContext().getString(R.string.domain_urls_summary_some, result.valueAt(0)); + } + } + } +}