diff options
Diffstat (limited to 'src')
22 files changed, 1137 insertions, 563 deletions
diff --git a/src/com/android/settings/LinkifyUtils.java b/src/com/android/settings/LinkifyUtils.java new file mode 100644 index 0000000..5550db5 --- /dev/null +++ b/src/com/android/settings/LinkifyUtils.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 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; + +import android.text.Spannable; +import android.text.TextPaint; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.view.View; +import android.widget.TextView; +import android.widget.TextView.BufferType; + +/** + * Utility class to create clickable links inside {@link TextView TextViews}. + */ +public class LinkifyUtils { + private static final String PLACE_HOLDER_LINK_BEGIN = "LINK_BEGIN"; + private static final String PLACE_HOLDER_LINK_END = "LINK_END"; + + private LinkifyUtils() { + } + + /** Interface that handles the click event of the link */ + public interface OnClickListener { + void onClick(); + } + + /** + * Applies the text into the {@link TextView} and part of it a clickable link. + * The text surrounded with "LINK_BEGIN" and "LINK_END" will become a clickable link. Only + * supports at most one link. + * @return true if the link has been successfully applied, or false if the original text + * contains no link place holders. + */ + public static boolean linkify(TextView textView, StringBuilder text, + final OnClickListener listener) { + // Remove place-holders from the string and record their positions + final int beginIndex = text.indexOf(PLACE_HOLDER_LINK_BEGIN); + if (beginIndex == -1) { + textView.setText(text); + return false; + } + text.delete(beginIndex, beginIndex + PLACE_HOLDER_LINK_BEGIN.length()); + final int endIndex = text.indexOf(PLACE_HOLDER_LINK_END); + if (endIndex == -1) { + textView.setText(text); + return false; + } + text.delete(endIndex, endIndex + PLACE_HOLDER_LINK_END.length()); + + textView.setText(text.toString(), BufferType.SPANNABLE); + textView.setMovementMethod(LinkMovementMethod.getInstance()); + Spannable spannableContent = (Spannable) textView.getText(); + ClickableSpan spannableLink = new ClickableSpan() { + @Override + public void onClick(View widget) { + listener.onClick(); + } + + @Override + public void updateDrawState(TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + } + }; + spannableContent.setSpan(spannableLink, beginIndex, endIndex, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + return true; + } +} diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 4ed1026..70455a4 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -95,10 +95,12 @@ public class Settings extends SettingsActivity { public static class PrintJobSettingsActivity extends SettingsActivity { /* empty */ } public static class ZenModeSettingsActivity extends SettingsActivity { /* empty */ } public static class ZenModePrioritySettingsActivity extends SettingsActivity { /* empty */ } + public static class ZenModeAutomationSettingsActivity extends SettingsActivity { /* empty */ } public static class NotificationSettingsActivity extends SettingsActivity { /* empty */ } public static class NotificationAppListActivity extends SettingsActivity { /* empty */ } public static class AppNotificationSettingsActivity extends SettingsActivity { /* empty */ } public static class OtherSoundSettingsActivity extends SettingsActivity { /* empty */ } + public static class DomainsURLsAppListActivity extends SettingsActivity { /* empty */ } public static class TopLevelSettings extends SettingsActivity { /* empty */ } public static class ApnSettingsActivity extends SettingsActivity { /* empty */ } diff --git a/src/com/android/settings/SetupChooseLockGeneric.java b/src/com/android/settings/SetupChooseLockGeneric.java index 10bac15..a631caf 100644 --- a/src/com/android/settings/SetupChooseLockGeneric.java +++ b/src/com/android/settings/SetupChooseLockGeneric.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; import android.preference.PreferenceFragment; +import android.hardware.fingerprint.FingerprintManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -82,6 +83,13 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric final View header = inflater.inflate(R.layout.setup_wizard_header, list, false); list.addHeaderView(header, null, false); } + final FingerprintManager fpm = (FingerprintManager) + getActivity().getSystemService(Context.FINGERPRINT_SERVICE); + if (fpm != null && fpm.isHardwareDetected()) { + final View footer = inflater.inflate( + R.layout.setup_screen_lock_fingerprint_details, list, false); + list.addFooterView(footer, null, false); + } return view; } diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index bc6cbed..23b2c85 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -25,10 +25,12 @@ import android.app.AlertDialog; import android.app.Dialog; import android.app.Fragment; import android.app.IActivityManager; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -43,6 +45,7 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; +import android.hardware.usb.IUsbManager; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.Uri; @@ -76,6 +79,7 @@ import android.widget.TabWidget; import com.android.internal.util.UserIcons; import com.android.settings.UserAdapter.UserDetails; +import com.android.settings.applications.ApplicationsState; import com.android.settings.dashboard.DashboardTile; import com.android.settings.drawable.CircleFramedDrawable; @@ -84,6 +88,7 @@ import java.io.InputStream; import java.net.InetAddress; import java.text.NumberFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -1101,7 +1106,6 @@ public final class Utils { return (sm.getStorageBytesUntilLow(context.getFilesDir()) < 0); } - /** * Returns a default user icon (as a {@link Bitmap}) for the given user. * @@ -1119,4 +1123,39 @@ public final class Utils { } return bitmap; } + + public static boolean hasUsbDefaults(IUsbManager usbManager, String packageName) { + try { + if (usbManager != null) { + return usbManager.hasDefaults(packageName, UserHandle.myUserId()); + } + } catch (RemoteException e) { + Log.e(TAG, "mUsbManager.hasDefaults", e); + } + return false; + } + + public static boolean hasPreferredActivities(PackageManager pm, String packageName) { + // Get list of preferred activities + List<ComponentName> prefActList = new ArrayList<>(); + // Intent list cannot be null. so pass empty list + List<IntentFilter> intentList = new ArrayList<>(); + pm.getPreferredActivities(intentList, prefActList, packageName); + Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list"); + return prefActList.size() > 0; + } + + public static CharSequence getLaunchByDeafaultSummary(ApplicationsState.AppEntry appEntry, + IUsbManager usbManager, PackageManager pm, Context context) { + String packageName = appEntry.info.packageName; + boolean hasPreferred = hasPreferredActivities(pm, packageName) + || hasUsbDefaults(usbManager, packageName); + int status = pm.getIntentVerificationStatus(packageName, UserHandle.myUserId()); + boolean hasDomainURLsPreference = + (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) || + (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER); + return context.getString(hasPreferred || hasDomainURLsPreference + ? R.string.launch_defaults_some + : R.string.launch_defaults_none); + } } diff --git a/src/com/android/settings/applications/AdvancedAppSettings.java b/src/com/android/settings/applications/AdvancedAppSettings.java index d5ab8dc..3d3c9f6 100644 --- a/src/com/android/settings/applications/AdvancedAppSettings.java +++ b/src/com/android/settings/applications/AdvancedAppSettings.java @@ -18,6 +18,7 @@ package com.android.settings.applications; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; +import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; import android.app.AppOpsManager; @@ -26,7 +27,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.NetworkPolicyManager; import android.os.AsyncTask; @@ -35,11 +35,9 @@ import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; -import android.os.UserManager; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.util.Log; -import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -63,14 +61,16 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements C private static final String KEY_APP_PERM = "manage_perms"; private static final String KEY_ALL_APPS = "all_apps"; + private static final String KEY_APP_DOMAIN_URLS = "domain_urls"; private static final String KEY_RESET_ALL = "reset_all"; private static final String EXTRA_RESET_DIALOG = "resetDialog"; private ApplicationsState mApplicationsState; private Session mSession; - private Preference mAppPerms; - private Preference mAllApps; - private Preference mResetAll; + private Preference mAppPermsPreference; + private Preference mAppDomainURLsPreference; + private Preference mAllAppsPreference; + private Preference mResetAllPreference; AlertDialog mResetDialog; @@ -91,17 +91,18 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements C mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); mSession = mApplicationsState.newSession(this); - mAppPerms = findPreference(KEY_APP_PERM); - mAllApps = findPreference(KEY_ALL_APPS); - mResetAll = findPreference(KEY_RESET_ALL); - mResetAll.setOnPreferenceClickListener(new OnPreferenceClickListener() { + mAppPermsPreference = findPreference(KEY_APP_PERM); + mAppDomainURLsPreference = findPreference(KEY_APP_DOMAIN_URLS); + mAllAppsPreference = findPreference(KEY_ALL_APPS); + mResetAllPreference = findPreference(KEY_RESET_ALL); + mResetAllPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { buildResetDialog(); return true; } }); - updateAllAppsSummary(); + updateUI(); mPm = getActivity().getPackageManager(); mIPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); @@ -112,8 +113,19 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements C mHandler = new Handler(getActivity().getMainLooper()); } - private void updateAllAppsSummary() { - mAllApps.setSummary(getString(R.string.all_apps_summary, mSession.getAllApps().size())); + private void updateUI() { + ArrayList<AppEntry> allApps = mSession.getAllApps(); + mAllAppsPreference.setSummary(getString(R.string.all_apps_summary, allApps.size())); + + int countAppWithDomainURLs = 0; + for (AppEntry entry : allApps) { + boolean hasDomainURLs = + (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0; + if (hasDomainURLs) countAppWithDomainURLs++; + } + String summary = getResources().getQuantityString( + R.plurals.domain_urls_apps_summary, countAppWithDomainURLs, countAppWithDomainURLs); + mAppDomainURLsPreference.setSummary(summary); } @Override @@ -241,7 +253,7 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements C @Override public void onPackageListChanged() { - updateAllAppsSummary(); + updateUI(); } @Override @@ -276,7 +288,9 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements C @Override public void onPermissionLoadComplete() { - mAppPerms.setSummary(getActivity().getString(R.string.app_permissions_summary, + Activity activity = getActivity(); + if (activity == null) return; + mAppPermsPreference.setSummary(activity.getString(R.string.app_permissions_summary, mPermissionsInfo.getRuntimePermAppsGrantedCount(), mPermissionsInfo.getRuntimePermAppsCount())); } diff --git a/src/com/android/settings/applications/AppDomainsPreference.java b/src/com/android/settings/applications/AppDomainsPreference.java new file mode 100644 index 0000000..7e29a20 --- /dev/null +++ b/src/com/android/settings/applications/AppDomainsPreference.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 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.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; +import com.android.settings.accessibility.ListDialogPreference; + +import com.android.settings.R; + +public class AppDomainsPreference extends ListDialogPreference { + + public AppDomainsPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + setDialogLayoutResource(R.layout.app_domains_dialog); + setListItemLayoutResource(R.layout.app_domains_item); + } + + @Override + protected void onBindListItem(View view, int index) { + final CharSequence title = getTitleAt(index); + if (title != null) { + final TextView domainName = (TextView) view.findViewById(R.id.domain_name); + domainName.setText(title); + } + } +} diff --git a/src/com/android/settings/applications/AppLaunchSettings.java b/src/com/android/settings/applications/AppLaunchSettings.java index ffb84ef..c0a662a 100644 --- a/src/com/android/settings/applications/AppLaunchSettings.java +++ b/src/com/android/settings/applications/AppLaunchSettings.java @@ -17,117 +17,78 @@ package com.android.settings.applications; import android.app.AlertDialog; -import android.appwidget.AppWidgetManager; -import android.content.ComponentName; -import android.content.Context; -import android.content.IntentFilter; +import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageManager; -import android.hardware.usb.IUsbManager; import android.os.Bundle; -import android.os.RemoteException; import android.os.UserHandle; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.style.BulletSpan; -import android.util.Log; -import android.view.LayoutInflater; +import android.preference.Preference; +import android.preference.SwitchPreference; +import android.util.ArraySet; import android.view.View; import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TextView; import com.android.internal.logging.MetricsLogger; import com.android.settings.R; -import com.android.settings.Utils; -import com.android.settings.applications.ApplicationsState.AppEntry; -import java.util.ArrayList; import java.util.List; -public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListener { +public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListener, + Preference.OnPreferenceChangeListener { - private Button mActivitiesButton; - private AppWidgetManager mAppWidgetManager; + private static final String KEY_OPEN_DOMAIN_URLS = "app_launch_open_domain_urls"; + private static final String KEY_SUPPORTED_DOMAIN_URLS = "app_launch_supported_domain_urls"; + private static final String KEY_CLEAR_DEFAULTS = "app_launch_clear_defaults"; - private View mRootView; + private PackageManager mPm; + + private SwitchPreference mOpenDomainUrls; + private AppDomainsPreference mAppDomainUrls; + private ClearDefaultsPreference mClearDefaultsPreference; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mAppWidgetManager = AppWidgetManager.getInstance(getActivity()); - } - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.app_preferred_settings, container, false); + addPreferencesFromResource(R.xml.installed_app_launch_settings); + + mPm = getActivity().getPackageManager(); + final int myUserId = UserHandle.myUserId(); - final ViewGroup allDetails = (ViewGroup) view.findViewById(R.id.all_details); - Utils.forceCustomPadding(allDetails, true /* additive padding */); + mOpenDomainUrls = (SwitchPreference) findPreference(KEY_OPEN_DOMAIN_URLS); + mOpenDomainUrls.setOnPreferenceChangeListener(this); - mActivitiesButton = (Button) view.findViewById(R.id.clear_activities_button); - mRootView = view; + final int status = mPm.getIntentVerificationStatus(mPackageName, myUserId); + boolean checked = status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; + mOpenDomainUrls.setChecked(checked); - return view; + mAppDomainUrls = (AppDomainsPreference) findPreference(KEY_SUPPORTED_DOMAIN_URLS); + CharSequence[] entries = getEntries(mPackageName); + mAppDomainUrls.setTitles(entries); + mAppDomainUrls.setValues(new int[entries.length]); + + mClearDefaultsPreference = (ClearDefaultsPreference) findPreference(KEY_CLEAR_DEFAULTS); } - @Override - protected boolean refreshUi() { - retrieveAppEntry(); - boolean hasBindAppWidgetPermission = - mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName); - - TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title); - TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); - boolean autoLaunchEnabled = hasPreferredActivities(mPm, mPackageName) - || hasUsbDefaults(mUsbManager, mPackageName); - if (!autoLaunchEnabled && !hasBindAppWidgetPermission) { - resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); - } else { - boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled; - - if (hasBindAppWidgetPermission) { - autoLaunchTitleView.setText(R.string.auto_launch_label_generic); - } else { - autoLaunchTitleView.setText(R.string.auto_launch_label); - } + private CharSequence[] getEntries(String packageName) { + ArraySet<String> result = new ArraySet<>(); - CharSequence text = null; - int bulletIndent = getResources() - .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset); - if (autoLaunchEnabled) { - CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text); - SpannableString s = new SpannableString(autoLaunchEnableText); - if (useBullets) { - s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0); - } - text = (text == null) ? - TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); - } - if (hasBindAppWidgetPermission) { - CharSequence alwaysAllowBindAppWidgetsText = - getText(R.string.always_allow_bind_appwidgets_text); - SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText); - if (useBullets) { - s.setSpan(new BulletSpan(bulletIndent), - 0, alwaysAllowBindAppWidgetsText.length(), 0); - } - text = (text == null) ? - TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); + List<IntentFilterVerificationInfo> list = + mPm.getIntentFilterVerifications(packageName); + for (IntentFilterVerificationInfo ivi : list) { + for (String host : ivi.getDomains()) { + result.add(host); } - autoLaunchView.setText(text); - mActivitiesButton.setEnabled(true); - mActivitiesButton.setOnClickListener(this); } - return true; + + return result.toArray(new CharSequence[0]); } - private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) { - title.setText(R.string.auto_launch_label); - autoLaunchView.setText(R.string.auto_launch_disable_text); - // Disable clear activities button - mActivitiesButton.setEnabled(false); + @Override + protected boolean refreshUi() { + mClearDefaultsPreference.setPackageName(mPackageName); + mClearDefaultsPreference.setAppEntry(mAppEntry); + + return true; } @Override @@ -136,56 +97,24 @@ public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListe return null; } + @Override public void onClick(View v) { - if (v == mActivitiesButton) { - if (mUsbManager != null) { - mPm.clearPackagePreferredActivities(mPackageName); - try { - mUsbManager.clearDefaults(mPackageName, UserHandle.myUserId()); - } catch (RemoteException e) { - Log.e(TAG, "mUsbManager.clearDefaults", e); - } - mAppWidgetManager.setBindAppWidgetPermission(mPackageName, false); - TextView autoLaunchTitleView = - (TextView) mRootView.findViewById(R.id.auto_launch_title); - TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); - resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); - } - } - } - - private static boolean hasUsbDefaults(IUsbManager usbManager, String packageName) { - try { - if (usbManager != null) { - return usbManager.hasDefaults(packageName, UserHandle.myUserId()); - } - } catch (RemoteException e) { - Log.e(TAG, "mUsbManager.hasDefaults", e); - } - return false; + // Nothing to do } - private static boolean hasPreferredActivities(PackageManager pm, String packageName) { - // Get list of preferred activities - List<ComponentName> prefActList = new ArrayList<>(); - // Intent list cannot be null. so pass empty list - List<IntentFilter> intentList = new ArrayList<>(); - pm.getPreferredActivities(intentList, prefActList, packageName); - if (localLOGV) { - Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list"); + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean ret = false; + final String key = preference.getKey(); + if (KEY_OPEN_DOMAIN_URLS.equals(key)) { + SwitchPreference pref = (SwitchPreference) preference; + int status = !pref.isChecked() ? + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS : + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; + ret = mPm.updateIntentVerificationStatus(mPackageName, status, UserHandle.myUserId()); } - return prefActList.size() > 0; - } - - public static CharSequence getSummary(AppEntry appEntry, IUsbManager usbManager, - PackageManager pm, Context context) { - String packageName = appEntry.info.packageName; - boolean hasPreferred = hasPreferredActivities(pm, packageName) - || hasUsbDefaults(usbManager, packageName); - return context.getString(hasPreferred - ? R.string.launch_defaults_some - : R.string.launch_defaults_none); + return ret; } @Override diff --git a/src/com/android/settings/applications/AppOpsState.java b/src/com/android/settings/applications/AppOpsState.java index 1ab3c73..77c4582 100644 --- a/src/com/android/settings/applications/AppOpsState.java +++ b/src/com/android/settings/applications/AppOpsState.java @@ -191,7 +191,9 @@ public class AppOpsState { AppOpsManager.OP_SYSTEM_ALERT_WINDOW, AppOpsManager.OP_WAKE_LOCK, AppOpsManager.OP_PROJECT_MEDIA, - AppOpsManager.OP_ACTIVATE_VPN, }, + AppOpsManager.OP_ACTIVATE_VPN, + AppOpsManager.OP_ASSIST_STRUCTURE, + AppOpsManager.OP_ASSIST_SCREENSHOT}, new boolean[] { false, true, true, @@ -199,7 +201,9 @@ public class AppOpsState { true, true, false, - false, } + false, + false, + false } ); public static final OpsTemplate[] ALL_TEMPLATES = new OpsTemplate[] { diff --git a/src/com/android/settings/applications/AppViewHolder.java b/src/com/android/settings/applications/AppViewHolder.java index 176ccca..92aa87a 100644 --- a/src/com/android/settings/applications/AppViewHolder.java +++ b/src/com/android/settings/applications/AppViewHolder.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2015 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 com.android.settings.R; diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java index daa3842..71d0790 100644 --- a/src/com/android/settings/applications/ApplicationsState.java +++ b/src/com/android/settings/applications/ApplicationsState.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2015 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.ActivityManager; @@ -116,7 +132,7 @@ public class ApplicationsState { } // Need to synchronize on 'this' for the following. - ApplicationInfo info; + public ApplicationInfo info; Drawable icon; String sizeStr; String internalSizeStr; @@ -323,6 +339,16 @@ public class ApplicationsState { } }; + public static final AppFilter FILTER_WITH_DOMAIN_URLS = new AppFilter() { + public void init() { + } + + @Override + public boolean filterApp(AppEntry entry) { + return (entry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0; + } + }; + final Context mContext; final PackageManager mPm; final IPackageManager mIpm; diff --git a/src/com/android/settings/applications/ClearDefaultsPreference.java b/src/com/android/settings/applications/ClearDefaultsPreference.java new file mode 100644 index 0000000..1799cad --- /dev/null +++ b/src/com/android/settings/applications/ClearDefaultsPreference.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2015 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.appwidget.AppWidgetManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.usb.IUsbManager; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.preference.Preference; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.style.BulletSpan; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import com.android.settings.R; +import com.android.settings.Utils; + +public class ClearDefaultsPreference extends Preference { + + protected static final String TAG = ClearDefaultsPreference.class.getSimpleName(); + + private View mRootView; + private Button mActivitiesButton; + + private AppWidgetManager mAppWidgetManager; + private IUsbManager mUsbManager; + private PackageManager mPm; + private String mPackageName; + protected ApplicationsState.AppEntry mAppEntry; + + public ClearDefaultsPreference(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + setLayoutResource(R.layout.app_preferred_settings); + + mAppWidgetManager = AppWidgetManager.getInstance(context); + mPm = context.getPackageManager(); + IBinder b = ServiceManager.getService(Context.USB_SERVICE); + mUsbManager = IUsbManager.Stub.asInterface(b); + } + + public ClearDefaultsPreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public ClearDefaultsPreference(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ClearDefaultsPreference(Context context) { + this(context, null); + } + + public void setPackageName(String packageName) { + mPackageName = packageName; + } + + public void setAppEntry(ApplicationsState.AppEntry entry) { + mAppEntry = entry; + } + + @Override + protected View onCreateView(ViewGroup parent) { + mRootView = super.onCreateView(parent); + + mActivitiesButton = (Button) mRootView.findViewById(R.id.clear_activities_button); + mActivitiesButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mUsbManager != null) { + mPm.clearPackagePreferredActivities(mPackageName); + try { + mUsbManager.clearDefaults(mPackageName, UserHandle.myUserId()); + } catch (RemoteException e) { + Log.e(TAG, "mUsbManager.clearDefaults", e); + } + mAppWidgetManager.setBindAppWidgetPermission(mPackageName, false); + TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); + resetLaunchDefaultsUi(autoLaunchView); + } + } + }); + + return mRootView; + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + + updateUI(); + } + + public boolean updateUI() { + boolean hasBindAppWidgetPermission = + mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName); + + TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); + boolean autoLaunchEnabled = Utils.hasPreferredActivities(mPm, mPackageName) + || Utils.hasUsbDefaults(mUsbManager, mPackageName); + if (!autoLaunchEnabled && !hasBindAppWidgetPermission) { + resetLaunchDefaultsUi(autoLaunchView); + } else { + boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled; + + if (hasBindAppWidgetPermission) { + autoLaunchView.setText(R.string.auto_launch_label_generic); + } else { + autoLaunchView.setText(R.string.auto_launch_label); + } + + Context context = getContext(); + CharSequence text = null; + int bulletIndent = context.getResources().getDimensionPixelSize( + R.dimen.installed_app_details_bullet_offset); + if (autoLaunchEnabled) { + CharSequence autoLaunchEnableText = context.getText( + R.string.auto_launch_enable_text); + SpannableString s = new SpannableString(autoLaunchEnableText); + if (useBullets) { + s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0); + } + text = (text == null) ? + TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); + } + if (hasBindAppWidgetPermission) { + CharSequence alwaysAllowBindAppWidgetsText = + context.getText(R.string.always_allow_bind_appwidgets_text); + SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText); + if (useBullets) { + s.setSpan(new BulletSpan(bulletIndent), + 0, alwaysAllowBindAppWidgetsText.length(), 0); + } + text = (text == null) ? + TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); + } + autoLaunchView.setText(text); + mActivitiesButton.setEnabled(true); + } + return true; + } + + private void resetLaunchDefaultsUi(TextView autoLaunchView) { + autoLaunchView.setText(R.string.auto_launch_disable_text); + // Disable clear activities button + mActivitiesButton.setEnabled(false); + } +} diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 9f6c969..33a7428 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -261,10 +261,17 @@ public class InstalledAppDetails extends AppInfoBase mStoragePreference.setOnPreferenceClickListener(this); mPermissionsPreference = findPreference(KEY_PERMISSION); mPermissionsPreference.setOnPreferenceClickListener(this); - mLaunchPreference = findPreference(KEY_LAUNCH); - mLaunchPreference.setOnPreferenceClickListener(this); mDataPreference = findPreference(KEY_DATA); mDataPreference.setOnPreferenceClickListener(this); + + mLaunchPreference = findPreference(KEY_LAUNCH); + if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { + mLaunchPreference.setEnabled(false); + } else if (!mAppEntry.info.enabled) { + mLaunchPreference.setEnabled(false); + } else { + mLaunchPreference.setOnPreferenceClickListener(this); + } } private void handleHeader() { @@ -421,7 +428,7 @@ public class InstalledAppDetails extends AppInfoBase Activity context = getActivity(); mStoragePreference.setSummary(AppStorageSettings.getSummary(mAppEntry, context)); mPermissionsPreference.setSummary(AppPermissionSettings.getSummary(mAppEntry, context)); - mLaunchPreference.setSummary(AppLaunchSettings.getSummary(mAppEntry, mUsbManager, + mLaunchPreference.setSummary(Utils.getLaunchByDeafaultSummary(mAppEntry, mUsbManager, mPm, context)); mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context, mBackend)); diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java index 84b9763..b416aef 100644 --- a/src/com/android/settings/applications/ManageApplications.java +++ b/src/com/android/settings/applications/ManageApplications.java @@ -17,12 +17,13 @@ package com.android.settings.applications; import android.app.Activity; -import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; +import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Environment; @@ -32,6 +33,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.preference.PreferenceFrameLayout; import android.provider.Settings; +import android.util.ArraySet; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -63,10 +65,12 @@ import com.android.settings.applications.ApplicationsState.AppEntry; import com.android.settings.applications.ApplicationsState.AppFilter; import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.NotificationBackend.AppRow; +import com.android.settings.Settings.DomainsURLsAppListActivity; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.List; final class CanBeOnSdCardChecker { final IPackageManager mPm; @@ -145,6 +149,7 @@ public class ManageApplications extends InstrumentedFragment public static final int FILTER_APPS_SENSITIVE = 6; public static final int FILTER_APPS_PERSONAL = 7; public static final int FILTER_APPS_WORK = 8; + public static final int FILTER_APPS_WITH_DOMAIN_URLS = 9; // This is the string labels for the filter modes above, the order must be kept in sync. public static final int[] FILTER_LABELS = new int[] { @@ -157,6 +162,7 @@ public class ManageApplications extends InstrumentedFragment R.string.filter_notif_sensitive_apps, // Sensitive Notifications R.string.filter_personal_apps, // Personal R.string.filter_work_apps, // Work + R.string.filter_with_domain_urls_apps, // Domain URLs }; // This is the actual mapping to filters from FILTER_ constants above, the order must // be kept in sync. @@ -170,6 +176,7 @@ public class ManageApplications extends InstrumentedFragment AppStateNotificationBridge.FILTER_APP_NOTIFICATION_SENSITIVE, // Sensitive Notifications ApplicationsState.FILTER_PERSONAL, // Personal ApplicationsState.FILTER_WORK, // Work + ApplicationsState.FILTER_WITH_DOMAIN_URLS, // Apps with Domain URLs }; // sort order that can be changed through the menu can be sorted alphabetically @@ -209,6 +216,7 @@ public class ManageApplications extends InstrumentedFragment public static final int LIST_TYPE_MAIN = 0; public static final int LIST_TYPE_ALL = 1; public static final int LIST_TYPE_NOTIFICATION = 2; + public static final int LIST_TYPE_DOMAINS_URLS = 3; private View mRootView; @@ -224,7 +232,6 @@ public class ManageApplications extends InstrumentedFragment mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication()); Intent intent = getActivity().getIntent(); - String action = intent.getAction(); String className = getArguments() != null ? getArguments().getString("classname") : null; if (className == null) { @@ -235,6 +242,8 @@ public class ManageApplications extends InstrumentedFragment } else if (className.equals(NotificationAppListActivity.class.getName())) { mListType = LIST_TYPE_NOTIFICATION; mNotifBackend = new NotificationBackend(); + } else if (className.equals(DomainsURLsAppListActivity.class.getName())) { + mListType = LIST_TYPE_DOMAINS_URLS; } else { mListType = LIST_TYPE_MAIN; } @@ -311,12 +320,17 @@ public class ManageApplications extends InstrumentedFragment mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED); mFilterAdapter.enableFilter(FILTER_APPS_PRIORITY); mFilterAdapter.enableFilter(FILTER_APPS_SENSITIVE); + } else if (mListType == LIST_TYPE_DOMAINS_URLS) { + mFilterAdapter.disableFilter(FILTER_APPS_ALL); + mFilterAdapter.enableFilter(FILTER_APPS_WITH_DOMAIN_URLS); } } private int getDefaultFilter() { if (mListType == LIST_TYPE_MAIN) { return FILTER_APPS_DOWNLOADED_AND_LAUNCHER; + } else if (mListType == LIST_TYPE_DOMAINS_URLS) { + return FILTER_APPS_WITH_DOMAIN_URLS; } return FILTER_APPS_ALL; } @@ -330,6 +344,8 @@ public class ManageApplications extends InstrumentedFragment return MetricsLogger.MANAGE_APPLICATIONS_ALL; case LIST_TYPE_NOTIFICATION: return MetricsLogger.MANAGE_APPLICATIONS_NOTIFICATIONS; + case LIST_TYPE_DOMAINS_URLS: + return MetricsLogger.MANAGE_DOMAIN_URLS; default: return MetricsLogger.VIEW_UNKNOWN; } @@ -382,24 +398,39 @@ public class ManageApplications extends InstrumentedFragment // utility method used to start sub activity private void startApplicationDetailsActivity() { + Activity activity = getActivity(); if (mListType == LIST_TYPE_NOTIFICATION) { - getActivity().startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) + activity.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) .putExtra(Settings.EXTRA_APP_PACKAGE, mCurrentPkgName) .putExtra(Settings.EXTRA_APP_UID, mCurrentUid)); + } else if (mListType == LIST_TYPE_DOMAINS_URLS) { + final String title = getString(R.string.auto_launch_label); + startAppInfoFragment(AppLaunchSettings.class, title); } else { // TODO: Figure out if there is a way where we can spin up the profile's settings // process ahead of time, to avoid a long load of data when user clicks on a managed app. // Maybe when they load the list of apps that contains managed profile apps. Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.fromParts("package", mCurrentPkgName, null)); - getActivity().startActivityAsUser(intent, - new UserHandle(UserHandle.getUserId(mCurrentUid))); + activity.startActivityAsUser(intent, new UserHandle(UserHandle.getUserId(mCurrentUid))); } } + private void startAppInfoFragment(Class<? extends AppInfoBase> fragment, CharSequence title) { + Bundle args = new Bundle(); + args.putString("package", mCurrentPkgName); + + SettingsActivity sa = (SettingsActivity) getActivity(); + sa.startPreferencePanel(fragment.getName(), args, -1, title, this, 0); + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (mListType == LIST_TYPE_DOMAINS_URLS) { + // No option menu + return; + } mOptionsMenu = menu; if (mListType == LIST_TYPE_MAIN) { // Only show advanced options when in the main app list (from dashboard). @@ -426,7 +457,6 @@ public class ManageApplications extends InstrumentedFragment if (mOptionsMenu == null) { return; } - if (mListType != LIST_TYPE_MAIN) { // Allow sorting except on main apps list. mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA); @@ -580,6 +610,7 @@ public class ManageApplications extends InstrumentedFragment private int mLastSortMode=-1; private int mWhichSize = SIZE_TOTAL; CharSequence mCurFilterPrefix; + private PackageManager mPm; private Filter mFilter = new Filter() { @Override @@ -607,6 +638,7 @@ public class ManageApplications extends InstrumentedFragment mSession = state.newSession(this); mManageApplications = manageApplications; mContext = manageApplications.getActivity(); + mPm = mContext.getPackageManager(); mFilterMode = filterMode; if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { mNotifBridge = new AppStateNotificationBridge( @@ -842,6 +874,24 @@ public class ManageApplications extends InstrumentedFragment return mEntries.get(position).id; } + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public boolean isEnabled(int position) { + ApplicationsState.AppEntry entry = mEntries.get(position); + synchronized (entry) { + if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { + return false; + } else if (!entry.info.enabled) { + return false; + } + return true; + } + } + public View getView(int position, View convertView, ViewGroup parent) { // A ViewHolder keeps references to children views to avoid unnecessary calls // to findViewById() on each row. @@ -860,15 +910,23 @@ public class ManageApplications extends InstrumentedFragment if (entry.icon != null) { holder.appIcon.setImageDrawable(entry.icon); } - if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) { - if (entry.extraInfo != null) { - holder.summary.setText(InstalledAppDetails.getNotificationSummary( - (AppRow) entry.extraInfo, mContext)); - } else { - holder.summary.setText(""); - } - } else { - holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize); + switch (mManageApplications.mListType) { + case LIST_TYPE_NOTIFICATION: + if (entry.extraInfo != null) { + holder.summary.setText(InstalledAppDetails.getNotificationSummary( + (AppRow) entry.extraInfo, mContext)); + } else { + holder.summary.setText(""); + } + break; + + case LIST_TYPE_DOMAINS_URLS: + holder.summary.setText(getDomainsSummary(entry.info.packageName)); + break; + + default: + holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize); + break; } if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { holder.disabled.setVisibility(View.VISIBLE); @@ -895,5 +953,23 @@ public class ManageApplications extends InstrumentedFragment public void onMovedToScrapHeap(View view) { mActive.remove(view); } + + private CharSequence getDomainsSummary(String packageName) { + ArraySet<String> result = new ArraySet<>(); + List<IntentFilterVerificationInfo> list = + mPm.getIntentFilterVerifications(packageName); + for (IntentFilterVerificationInfo ivi : list) { + for (String host : ivi.getDomains()) { + result.add(host); + } + } + 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)); + } + } } } diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index b1eed51..1d0dc7e 100755 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -33,7 +33,10 @@ import android.preference.Preference; import android.preference.PreferenceCategory; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; +import android.text.Spannable; +import android.text.style.TextAppearanceSpan; import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -45,8 +48,10 @@ import android.widget.EditText; import android.widget.TextView; import com.android.internal.logging.MetricsLogger; +import com.android.settings.LinkifyUtils; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.location.ScanningSettings; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settings.search.SearchIndexableRaw; @@ -136,6 +141,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem mEmptyView = (TextView) getView().findViewById(android.R.id.empty); getListView().setEmptyView(mEmptyView); + mEmptyView.setGravity(Gravity.START | Gravity.CENTER_VERTICAL); final SettingsActivity activity = (SettingsActivity) getActivity(); mSwitchBar = activity.getSwitchBar(); @@ -352,7 +358,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem break; case BluetoothAdapter.STATE_OFF: - messageId = R.string.bluetooth_empty_list_bluetooth_off; + setOffMessage(); if (isUiRestricted()) { messageId = R.string.bluetooth_empty_list_user_restricted; } @@ -366,12 +372,39 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem setDeviceListGroup(preferenceScreen); removeAllDevices(); - mEmptyView.setText(messageId); + if (messageId != 0) { + mEmptyView.setText(messageId); + } if (!isUiRestricted()) { getActivity().invalidateOptionsMenu(); } } + private void setOffMessage() { + if (mEmptyView == null) { + return; + } + final CharSequence briefText = getText(R.string.bluetooth_empty_list_bluetooth_off); + final StringBuilder contentBuilder = new StringBuilder(); + contentBuilder.append(briefText); + contentBuilder.append("\n\n"); + contentBuilder.append(getText(R.string.ble_scan_notify_text)); + getPreferenceScreen().removeAll(); + LinkifyUtils.linkify(mEmptyView, contentBuilder, new LinkifyUtils.OnClickListener() { + @Override + public void onClick() { + final SettingsActivity activity = + (SettingsActivity) BluetoothSettings.this.getActivity(); + activity.startPreferencePanel(ScanningSettings.class.getName(), null, + R.string.location_scanning_screen_title, null, null, 0); + } + }); + Spannable boldSpan = (Spannable) mEmptyView.getText(); + boldSpan.setSpan( + new TextAppearanceSpan(getActivity(), android.R.style.TextAppearance_Medium), 0, + briefText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + @Override public void onBluetoothStateChanged(int bluetoothState) { super.onBluetoothStateChanged(bluetoothState); diff --git a/src/com/android/settings/notification/ZenModeAutomationSettings.java b/src/com/android/settings/notification/ZenModeAutomationSettings.java new file mode 100644 index 0000000..32ad3ce --- /dev/null +++ b/src/com/android/settings/notification/ZenModeAutomationSettings.java @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2015 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.notification; + +import static com.android.settings.notification.ZenModeDowntimeDaysSelection.DAYS; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.FragmentManager; +import android.app.INotificationManager; +import android.app.TimePickerDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnDismissListener; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.ServiceManager; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceCategory; +import android.preference.PreferenceScreen; +import android.service.notification.Condition; +import android.service.notification.ZenModeConfig; +import android.text.format.DateFormat; +import android.util.Log; +import android.util.SparseArray; +import android.widget.TimePicker; + +import com.android.internal.logging.MetricsLogger; +import com.android.settings.DropDownPreference; +import com.android.settings.R; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Indexable; +import com.android.settings.search.SearchIndexableRaw; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Objects; + +public class ZenModeAutomationSettings extends ZenModeSettingsBase implements Indexable { + private static final String KEY_DOWNTIME = "downtime"; + private static final String KEY_DAYS = "days"; + private static final String KEY_START_TIME = "start_time"; + private static final String KEY_END_TIME = "end_time"; + private static final String KEY_DOWNTIME_MODE = "downtime_mode"; + + private static final String KEY_AUTOMATION = "automation"; + private static final String KEY_ENTRY = "entry"; + private static final String KEY_CONDITION_PROVIDERS = "manage_condition_providers"; + + private static final SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("EEE"); + + private PackageManager mPM; + private boolean mDisableListeners; + private boolean mDowntimeSupported; + + private Preference mDays; + private TimePickerPreference mStart; + private TimePickerPreference mEnd; + private DropDownPreference mDowntimeMode; + private PreferenceCategory mAutomationCategory; + private Preference mEntry; + private Preference mConditionProviders; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + mPM = mContext.getPackageManager(); + + addPreferencesFromResource(R.xml.zen_mode_automation_settings); + final PreferenceScreen root = getPreferenceScreen(); + + onCreateDowntimeSettings(root); + + mAutomationCategory = (PreferenceCategory) findPreference(KEY_AUTOMATION); + mEntry = findPreference(KEY_ENTRY); + mEntry.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + new AlertDialog.Builder(mContext) + .setTitle(R.string.zen_mode_entry_conditions_title) + .setView(new ZenModeAutomaticConditionSelection(mContext)) + .setOnDismissListener(new OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + refreshAutomationSection(); + } + }) + .setPositiveButton(R.string.dlg_ok, null) + .show(); + return true; + } + }); + mConditionProviders = findPreference(KEY_CONDITION_PROVIDERS); + } + + private void onCreateDowntimeSettings(PreferenceScreen root) { + mDowntimeSupported = isDowntimeSupported(mContext); + if (!mDowntimeSupported) { + removePreference(KEY_DOWNTIME); + return; + } + mDays = root.findPreference(KEY_DAYS); + mDays.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + new AlertDialog.Builder(mContext) + .setTitle(R.string.zen_mode_downtime_days) + .setView(new ZenModeDowntimeDaysSelection(mContext, mConfig.sleepMode) { + @Override + protected void onChanged(String mode) { + if (mDisableListeners) return; + if (Objects.equals(mode, mConfig.sleepMode)) return; + if (DEBUG) Log.d(TAG, "days.onChanged sleepMode=" + mode); + final ZenModeConfig newConfig = mConfig.copy(); + newConfig.sleepMode = mode; + setZenModeConfig(newConfig); + } + }) + .setOnDismissListener(new OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + updateDays(); + } + }) + .setPositiveButton(R.string.done_button, null) + .show(); + return true; + } + }); + + final FragmentManager mgr = getFragmentManager(); + + mStart = new TimePickerPreference(mContext, mgr); + mStart.setKey(KEY_START_TIME); + mStart.setTitle(R.string.zen_mode_start_time); + mStart.setCallback(new TimePickerPreference.Callback() { + @Override + public boolean onSetTime(int hour, int minute) { + if (mDisableListeners) return true; + if (!ZenModeConfig.isValidHour(hour)) return false; + if (!ZenModeConfig.isValidMinute(minute)) return false; + if (hour == mConfig.sleepStartHour && minute == mConfig.sleepStartMinute) { + return true; + } + if (DEBUG) Log.d(TAG, "onPrefChange sleepStart h=" + hour + " m=" + minute); + final ZenModeConfig newConfig = mConfig.copy(); + newConfig.sleepStartHour = hour; + newConfig.sleepStartMinute = minute; + return setZenModeConfig(newConfig); + } + }); + root.addPreference(mStart); + mStart.setDependency(mDays.getKey()); + + mEnd = new TimePickerPreference(mContext, mgr); + mEnd.setKey(KEY_END_TIME); + mEnd.setTitle(R.string.zen_mode_end_time); + mEnd.setCallback(new TimePickerPreference.Callback() { + @Override + public boolean onSetTime(int hour, int minute) { + if (mDisableListeners) return true; + if (!ZenModeConfig.isValidHour(hour)) return false; + if (!ZenModeConfig.isValidMinute(minute)) return false; + if (hour == mConfig.sleepEndHour && minute == mConfig.sleepEndMinute) { + return true; + } + if (DEBUG) Log.d(TAG, "onPrefChange sleepEnd h=" + hour + " m=" + minute); + final ZenModeConfig newConfig = mConfig.copy(); + newConfig.sleepEndHour = hour; + newConfig.sleepEndMinute = minute; + return setZenModeConfig(newConfig); + } + }); + root.addPreference(mEnd); + mEnd.setDependency(mDays.getKey()); + + mDowntimeMode = (DropDownPreference) root.findPreference(KEY_DOWNTIME_MODE); + mDowntimeMode.addItem(R.string.zen_mode_downtime_mode_priority, false); + mDowntimeMode.addItem(R.string.zen_mode_downtime_mode_none, true); + mDowntimeMode.setCallback(new DropDownPreference.Callback() { + @Override + public boolean onItemSelected(int pos, Object value) { + if (mDisableListeners) return true; + final boolean sleepNone = value instanceof Boolean ? ((Boolean) value) : false; + if (mConfig == null || mConfig.sleepNone == sleepNone) return false; + final ZenModeConfig newConfig = mConfig.copy(); + newConfig.sleepNone = sleepNone; + if (DEBUG) Log.d(TAG, "onPrefChange sleepNone=" + sleepNone); + return setZenModeConfig(newConfig); + } + }); + mDowntimeMode.setOrder(10); // sort at the bottom of the category + mDowntimeMode.setDependency(mDays.getKey()); + } + + private void updateDays() { + // Compute an ordered, delimited list of day names based on the persisted user config. + if (mConfig != null) { + final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode); + if (days != null && days.length != 0) { + final StringBuilder sb = new StringBuilder(); + final Calendar c = Calendar.getInstance(); + for (int i = 0; i < DAYS.length; i++) { + final int day = DAYS[i]; + for (int j = 0; j < days.length; j++) { + if (day == days[j]) { + c.set(Calendar.DAY_OF_WEEK, day); + if (sb.length() > 0) { + sb.append(mContext.getString(R.string.summary_divider_text)); + } + sb.append(DAY_FORMAT.format(c.getTime())); + break; + } + } + } + if (sb.length() > 0) { + mDays.setSummary(sb); + mDays.notifyDependencyChange(false); + return; + } + } + } + mDays.setSummary(R.string.zen_mode_downtime_days_none); + mDays.notifyDependencyChange(true); + } + + private void updateEndSummary() { + if (!mDowntimeSupported) return; + final int startMin = 60 * mConfig.sleepStartHour + mConfig.sleepStartMinute; + final int endMin = 60 * mConfig.sleepEndHour + mConfig.sleepEndMinute; + final boolean nextDay = startMin >= endMin; + final int summaryFormat; + if (mConfig.sleepNone) { + summaryFormat = nextDay ? R.string.zen_mode_end_time_none_next_day_summary_format + : R.string.zen_mode_end_time_none_same_day_summary_format; + } else { + summaryFormat = nextDay ? R.string.zen_mode_end_time_priority_next_day_summary_format + : 0; + } + mEnd.setSummaryFormat(summaryFormat); + } + + @Override + protected void onZenModeChanged() { + // don't care + } + + @Override + protected void updateControls() { + mDisableListeners = true; + if (mDowntimeSupported) { + updateDays(); + mStart.setTime(mConfig.sleepStartHour, mConfig.sleepStartMinute); + mEnd.setTime(mConfig.sleepEndHour, mConfig.sleepEndMinute); + mDowntimeMode.setSelectedValue(mConfig.sleepNone); + } + mDisableListeners = false; + refreshAutomationSection(); + updateEndSummary(); + } + + @Override + protected int getMetricsCategory() { + return MetricsLogger.NOTIFICATION_ZEN_MODE_AUTOMATION; + } + + private void refreshAutomationSection() { + if (mConditionProviders != null) { + final int total = ConditionProviderSettings.getProviderCount(mPM); + if (total == 0) { + getPreferenceScreen().removePreference(mAutomationCategory); + } else { + final int n = ConditionProviderSettings.getEnabledProviderCount(mContext); + if (n == 0) { + mConditionProviders.setSummary(getResources().getString( + R.string.manage_condition_providers_summary_zero)); + } else { + mConditionProviders.setSummary(String.format(getResources().getQuantityString( + R.plurals.manage_condition_providers_summary_nonzero, + n, n))); + } + final String entrySummary = getEntryConditionSummary(); + if (n == 0 || entrySummary == null) { + mEntry.setSummary(R.string.zen_mode_entry_conditions_summary_none); + } else { + mEntry.setSummary(entrySummary); + } + } + } + } + + private String getEntryConditionSummary() { + final INotificationManager nm = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + try { + final Condition[] automatic = nm.getAutomaticZenModeConditions(); + if (automatic == null || automatic.length == 0) { + return null; + } + final String divider = getString(R.string.summary_divider_text); + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < automatic.length; i++) { + if (i > 0) sb.append(divider); + sb.append(automatic[i].summary); + } + return sb.toString(); + } catch (Exception e) { + Log.w(TAG, "Error calling getAutomaticZenModeConditions", e); + return null; + } + } + + private static SparseArray<String> allKeyTitles(Context context) { + final SparseArray<String> rt = new SparseArray<String>(); + rt.put(R.string.zen_mode_downtime_category, KEY_DOWNTIME); + rt.put(R.string.zen_mode_downtime_days, KEY_DAYS); + rt.put(R.string.zen_mode_start_time, KEY_START_TIME); + rt.put(R.string.zen_mode_end_time, KEY_END_TIME); + rt.put(R.string.zen_mode_downtime_mode_title, KEY_DOWNTIME_MODE); + rt.put(R.string.zen_mode_automation_category, KEY_AUTOMATION); + rt.put(R.string.manage_condition_providers, KEY_CONDITION_PROVIDERS); + return rt; + } + + // Enable indexing of searchable data + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + + @Override + public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { + final SparseArray<String> keyTitles = allKeyTitles(context); + final int N = keyTitles.size(); + final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>(N); + final Resources res = context.getResources(); + for (int i = 0; i < N; i++) { + final SearchIndexableRaw data = new SearchIndexableRaw(context); + data.key = keyTitles.valueAt(i); + data.title = res.getString(keyTitles.keyAt(i)); + data.screenTitle = res.getString(R.string.zen_mode_automation_settings_title); + result.add(data); + } + return result; + } + + @Override + public List<String> getNonIndexableKeys(Context context) { + final ArrayList<String> rt = new ArrayList<String>(); + if (!isDowntimeSupported(context)) { + rt.add(KEY_DOWNTIME); + rt.add(KEY_DAYS); + rt.add(KEY_START_TIME); + rt.add(KEY_END_TIME); + rt.add(KEY_DOWNTIME_MODE); + } + return rt; + } + }; + + private static class TimePickerPreference extends Preference { + private final Context mContext; + + private int mSummaryFormat; + private int mHourOfDay; + private int mMinute; + private Callback mCallback; + + public TimePickerPreference(Context context, final FragmentManager mgr) { + super(context); + mContext = context; + setPersistent(false); + setOnPreferenceClickListener(new OnPreferenceClickListener(){ + @Override + public boolean onPreferenceClick(Preference preference) { + final TimePickerFragment frag = new TimePickerFragment(); + frag.pref = TimePickerPreference.this; + frag.show(mgr, TimePickerPreference.class.getName()); + return true; + } + }); + } + + public void setCallback(Callback callback) { + mCallback = callback; + } + + public void setSummaryFormat(int resId) { + mSummaryFormat = resId; + updateSummary(); + } + + public void setTime(int hourOfDay, int minute) { + if (mCallback != null && !mCallback.onSetTime(hourOfDay, minute)) return; + mHourOfDay = hourOfDay; + mMinute = minute; + updateSummary(); + } + + private void updateSummary() { + final Calendar c = Calendar.getInstance(); + c.set(Calendar.HOUR_OF_DAY, mHourOfDay); + c.set(Calendar.MINUTE, mMinute); + String time = DateFormat.getTimeFormat(mContext).format(c.getTime()); + if (mSummaryFormat != 0) { + time = mContext.getResources().getString(mSummaryFormat, time); + } + setSummary(time); + } + + public static class TimePickerFragment extends DialogFragment implements + TimePickerDialog.OnTimeSetListener { + public TimePickerPreference pref; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final boolean usePref = pref != null && pref.mHourOfDay >= 0 && pref.mMinute >= 0; + final Calendar c = Calendar.getInstance(); + final int hour = usePref ? pref.mHourOfDay : c.get(Calendar.HOUR_OF_DAY); + final int minute = usePref ? pref.mMinute : c.get(Calendar.MINUTE); + return new TimePickerDialog(getActivity(), this, hour, minute, + DateFormat.is24HourFormat(getActivity())); + } + + public void onTimeSet(TimePicker view, int hourOfDay, int minute) { + if (pref != null) { + pref.setTime(hourOfDay, minute); + } + } + } + + public interface Callback { + boolean onSetTime(int hour, int minute); + } + } +} diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java index 053d2ef..47d1f5d 100644 --- a/src/com/android/settings/notification/ZenModeSettings.java +++ b/src/com/android/settings/notification/ZenModeSettings.java @@ -16,61 +16,31 @@ package com.android.settings.notification; -import static com.android.settings.notification.ZenModeDowntimeDaysSelection.DAYS; - import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.app.FragmentManager; -import android.app.INotificationManager; -import android.app.NotificationManager; -import android.app.TimePickerDialog; import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; -import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Bundle; -import android.os.ServiceManager; import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceCategory; import android.preference.PreferenceScreen; import android.provider.Settings.Global; -import android.service.notification.Condition; -import android.service.notification.ZenModeConfig; -import android.text.format.DateFormat; -import android.util.Log; import android.util.SparseArray; import android.widget.ScrollView; -import android.widget.TimePicker; import com.android.internal.logging.MetricsLogger; -import com.android.settings.DropDownPreference; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settings.search.SearchIndexableRaw; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Calendar; import java.util.List; -import java.util.Objects; public class ZenModeSettings extends ZenModeSettingsBase implements Indexable { private static final String KEY_ZEN_MODE = "zen_mode"; private static final String KEY_PRIORITY_SETTINGS = "priority_settings"; - private static final String KEY_DOWNTIME = "downtime"; - private static final String KEY_DAYS = "days"; - private static final String KEY_START_TIME = "start_time"; - private static final String KEY_END_TIME = "end_time"; - private static final String KEY_DOWNTIME_MODE = "downtime_mode"; - - private static final String KEY_AUTOMATION = "automation"; - private static final String KEY_ENTRY = "entry"; - private static final String KEY_CONDITION_PROVIDERS = "manage_condition_providers"; + private static final String KEY_AUTOMATION_SETTINGS = "automation_settings"; private static final SettingPrefWithCallback PREF_ZEN_MODE = new SettingPrefWithCallback( SettingPref.TYPE_GLOBAL, KEY_ZEN_MODE, Global.ZEN_MODE, Global.ZEN_MODE_OFF, @@ -90,35 +60,17 @@ public class ZenModeSettings extends ZenModeSettingsBase implements Indexable { } }; - private static final SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("EEE"); + private Preference mPrioritySettings; + private AlertDialog mDialog; private static SparseArray<String> allKeyTitles(Context context) { final SparseArray<String> rt = new SparseArray<String>(); rt.put(R.string.zen_mode_option_title, KEY_ZEN_MODE); rt.put(R.string.zen_mode_priority_settings_title, KEY_PRIORITY_SETTINGS); - rt.put(R.string.zen_mode_downtime_category, KEY_DOWNTIME); - rt.put(R.string.zen_mode_downtime_days, KEY_DAYS); - rt.put(R.string.zen_mode_start_time, KEY_START_TIME); - rt.put(R.string.zen_mode_end_time, KEY_END_TIME); - rt.put(R.string.zen_mode_downtime_mode_title, KEY_DOWNTIME_MODE); - rt.put(R.string.zen_mode_automation_category, KEY_AUTOMATION); - rt.put(R.string.manage_condition_providers, KEY_CONDITION_PROVIDERS); + rt.put(R.string.zen_mode_automation_settings_title, KEY_AUTOMATION_SETTINGS); return rt; } - private PackageManager mPM; - private boolean mDisableListeners; - private boolean mDowntimeSupported; - private Preference mPrioritySettings; - private Preference mDays; - private TimePickerPreference mStart; - private TimePickerPreference mEnd; - private DropDownPreference mDowntimeMode; - private PreferenceCategory mAutomationCategory; - private Preference mEntry; - private Preference mConditionProviders; - private AlertDialog mDialog; - @Override protected int getMetricsCategory() { return MetricsLogger.NOTIFICATION_ZEN_MODE; @@ -132,7 +84,6 @@ public class ZenModeSettings extends ZenModeSettingsBase implements Indexable { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mPM = mContext.getPackageManager(); addPreferencesFromResource(R.xml.zen_mode_settings); final PreferenceScreen root = getPreferenceScreen(); @@ -148,188 +99,15 @@ public class ZenModeSettings extends ZenModeSettingsBase implements Indexable { }); mPrioritySettings = root.findPreference(KEY_PRIORITY_SETTINGS); - - final PreferenceCategory downtime = (PreferenceCategory) root.findPreference(KEY_DOWNTIME); - mDowntimeSupported = isDowntimeSupported(mContext); - if (!mDowntimeSupported) { - removePreference(KEY_DOWNTIME); - } else { - mDays = downtime.findPreference(KEY_DAYS); - mDays.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - new AlertDialog.Builder(mContext) - .setTitle(R.string.zen_mode_downtime_days) - .setView(new ZenModeDowntimeDaysSelection(mContext, mConfig.sleepMode) { - @Override - protected void onChanged(String mode) { - if (mDisableListeners) return; - if (Objects.equals(mode, mConfig.sleepMode)) return; - if (DEBUG) Log.d(TAG, "days.onChanged sleepMode=" + mode); - final ZenModeConfig newConfig = mConfig.copy(); - newConfig.sleepMode = mode; - setZenModeConfig(newConfig); - } - }) - .setOnDismissListener(new OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - updateDays(); - } - }) - .setPositiveButton(R.string.done_button, null) - .show(); - return true; - } - }); - - final FragmentManager mgr = getFragmentManager(); - - mStart = new TimePickerPreference(mContext, mgr); - mStart.setKey(KEY_START_TIME); - mStart.setTitle(R.string.zen_mode_start_time); - mStart.setCallback(new TimePickerPreference.Callback() { - @Override - public boolean onSetTime(int hour, int minute) { - if (mDisableListeners) return true; - if (!ZenModeConfig.isValidHour(hour)) return false; - if (!ZenModeConfig.isValidMinute(minute)) return false; - if (hour == mConfig.sleepStartHour && minute == mConfig.sleepStartMinute) { - return true; - } - if (DEBUG) Log.d(TAG, "onPrefChange sleepStart h=" + hour + " m=" + minute); - final ZenModeConfig newConfig = mConfig.copy(); - newConfig.sleepStartHour = hour; - newConfig.sleepStartMinute = minute; - return setZenModeConfig(newConfig); - } - }); - downtime.addPreference(mStart); - mStart.setDependency(mDays.getKey()); - - mEnd = new TimePickerPreference(mContext, mgr); - mEnd.setKey(KEY_END_TIME); - mEnd.setTitle(R.string.zen_mode_end_time); - mEnd.setCallback(new TimePickerPreference.Callback() { - @Override - public boolean onSetTime(int hour, int minute) { - if (mDisableListeners) return true; - if (!ZenModeConfig.isValidHour(hour)) return false; - if (!ZenModeConfig.isValidMinute(minute)) return false; - if (hour == mConfig.sleepEndHour && minute == mConfig.sleepEndMinute) { - return true; - } - if (DEBUG) Log.d(TAG, "onPrefChange sleepEnd h=" + hour + " m=" + minute); - final ZenModeConfig newConfig = mConfig.copy(); - newConfig.sleepEndHour = hour; - newConfig.sleepEndMinute = minute; - return setZenModeConfig(newConfig); - } - }); - downtime.addPreference(mEnd); - mEnd.setDependency(mDays.getKey()); - - mDowntimeMode = (DropDownPreference) downtime.findPreference(KEY_DOWNTIME_MODE); - mDowntimeMode.addItem(R.string.zen_mode_downtime_mode_priority, false); - mDowntimeMode.addItem(R.string.zen_mode_downtime_mode_none, true); - mDowntimeMode.setCallback(new DropDownPreference.Callback() { - @Override - public boolean onItemSelected(int pos, Object value) { - if (mDisableListeners) return true; - final boolean sleepNone = value instanceof Boolean ? ((Boolean) value) : false; - if (mConfig == null || mConfig.sleepNone == sleepNone) return false; - final ZenModeConfig newConfig = mConfig.copy(); - newConfig.sleepNone = sleepNone; - if (DEBUG) Log.d(TAG, "onPrefChange sleepNone=" + sleepNone); - return setZenModeConfig(newConfig); - } - }); - mDowntimeMode.setOrder(10); // sort at the bottom of the category - mDowntimeMode.setDependency(mDays.getKey()); + if (!isDowntimeSupported(mContext)) { + removePreference(KEY_AUTOMATION_SETTINGS); } - mAutomationCategory = (PreferenceCategory) findPreference(KEY_AUTOMATION); - mEntry = findPreference(KEY_ENTRY); - mEntry.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - new AlertDialog.Builder(mContext) - .setTitle(R.string.zen_mode_entry_conditions_title) - .setView(new ZenModeAutomaticConditionSelection(mContext)) - .setOnDismissListener(new OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - refreshAutomationSection(); - } - }) - .setPositiveButton(R.string.dlg_ok, null) - .show(); - return true; - } - }); - mConditionProviders = findPreference(KEY_CONDITION_PROVIDERS); - updateControls(); } - private void updateDays() { - if (mConfig != null) { - final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode); - if (days != null && days.length != 0) { - final StringBuilder sb = new StringBuilder(); - final Calendar c = Calendar.getInstance(); - for (int i = 0; i < DAYS.length; i++) { - final int day = DAYS[i]; - for (int j = 0; j < days.length; j++) { - if (day == days[j]) { - c.set(Calendar.DAY_OF_WEEK, day); - if (sb.length() > 0) { - sb.append(mContext.getString(R.string.summary_divider_text)); - } - sb.append(DAY_FORMAT.format(c.getTime())); - break; - } - } - } - if (sb.length() > 0) { - mDays.setSummary(sb); - mDays.notifyDependencyChange(false); - return; - } - } - } - mDays.setSummary(R.string.zen_mode_downtime_days_none); - mDays.notifyDependencyChange(true); - } - - private void updateEndSummary() { - if (!mDowntimeSupported) return; - final int startMin = 60 * mConfig.sleepStartHour + mConfig.sleepStartMinute; - final int endMin = 60 * mConfig.sleepEndHour + mConfig.sleepEndMinute; - final boolean nextDay = startMin >= endMin; - final int summaryFormat; - if (mConfig.sleepNone) { - summaryFormat = nextDay ? R.string.zen_mode_end_time_none_next_day_summary_format - : R.string.zen_mode_end_time_none_same_day_summary_format; - } else { - summaryFormat = nextDay ? R.string.zen_mode_end_time_priority_next_day_summary_format - : 0; - } - mEnd.setSummaryFormat(summaryFormat); - } - @Override protected void updateControls() { - mDisableListeners = true; - if (mDowntimeSupported) { - updateDays(); - mStart.setTime(mConfig.sleepStartHour, mConfig.sleepStartMinute); - mEnd.setTime(mConfig.sleepEndHour, mConfig.sleepEndMinute); - mDowntimeMode.setSelectedValue(mConfig.sleepNone); - } - mDisableListeners = false; - refreshAutomationSection(); - updateEndSummary(); updatePrioritySettingsSummary(); } @@ -350,56 +128,6 @@ public class ZenModeSettings extends ZenModeSettingsBase implements Indexable { return s; } - private void refreshAutomationSection() { - if (mConditionProviders != null) { - final int total = ConditionProviderSettings.getProviderCount(mPM); - if (total == 0) { - getPreferenceScreen().removePreference(mAutomationCategory); - } else { - final int n = ConditionProviderSettings.getEnabledProviderCount(mContext); - if (n == 0) { - mConditionProviders.setSummary(getResources().getString( - R.string.manage_condition_providers_summary_zero)); - } else { - mConditionProviders.setSummary(String.format(getResources().getQuantityString( - R.plurals.manage_condition_providers_summary_nonzero, - n, n))); - } - final String entrySummary = getEntryConditionSummary(); - if (n == 0 || entrySummary == null) { - mEntry.setSummary(R.string.zen_mode_entry_conditions_summary_none); - } else { - mEntry.setSummary(entrySummary); - } - } - } - } - - private String getEntryConditionSummary() { - final INotificationManager nm = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); - try { - final Condition[] automatic = nm.getAutomaticZenModeConditions(); - if (automatic == null || automatic.length == 0) { - return null; - } - final String divider = getString(R.string.summary_divider_text); - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < automatic.length; i++) { - if (i > 0) sb.append(divider); - sb.append(automatic[i].summary); - } - return sb.toString(); - } catch (Exception e) { - Log.w(TAG, "Error calling getAutomaticZenModeConditions", e); - return null; - } - } - - protected void putZenModeSetting(int value) { - Global.putInt(getContentResolver(), Global.ZEN_MODE, value); - } - protected void showConditionSelection(final int newSettingsValue) { if (mDialog != null) return; @@ -440,11 +168,6 @@ public class ZenModeSettings extends ZenModeSettingsBase implements Indexable { mDialog = null; } - private static boolean isDowntimeSupported(Context context) { - return NotificationManager.from(context) - .isSystemConditionProviderEnabled(ZenModeConfig.DOWNTIME_PATH); - } - // Enable indexing of searchable data public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider() { @@ -469,11 +192,7 @@ public class ZenModeSettings extends ZenModeSettingsBase implements Indexable { public List<String> getNonIndexableKeys(Context context) { final ArrayList<String> rt = new ArrayList<String>(); if (!isDowntimeSupported(context)) { - rt.add(KEY_DOWNTIME); - rt.add(KEY_DAYS); - rt.add(KEY_START_TIME); - rt.add(KEY_END_TIME); - rt.add(KEY_DOWNTIME_MODE); + rt.add(KEY_AUTOMATION_SETTINGS); } return rt; } @@ -532,80 +251,4 @@ public class ZenModeSettings extends ZenModeSettingsBase implements Indexable { void onSettingSelected(int value); } } - - private static class TimePickerPreference extends Preference { - private final Context mContext; - - private int mSummaryFormat; - private int mHourOfDay; - private int mMinute; - private Callback mCallback; - - public TimePickerPreference(Context context, final FragmentManager mgr) { - super(context); - mContext = context; - setPersistent(false); - setOnPreferenceClickListener(new OnPreferenceClickListener(){ - @Override - public boolean onPreferenceClick(Preference preference) { - final TimePickerFragment frag = new TimePickerFragment(); - frag.pref = TimePickerPreference.this; - frag.show(mgr, TimePickerPreference.class.getName()); - return true; - } - }); - } - - public void setCallback(Callback callback) { - mCallback = callback; - } - - public void setSummaryFormat(int resId) { - mSummaryFormat = resId; - updateSummary(); - } - - public void setTime(int hourOfDay, int minute) { - if (mCallback != null && !mCallback.onSetTime(hourOfDay, minute)) return; - mHourOfDay = hourOfDay; - mMinute = minute; - updateSummary(); - } - - private void updateSummary() { - final Calendar c = Calendar.getInstance(); - c.set(Calendar.HOUR_OF_DAY, mHourOfDay); - c.set(Calendar.MINUTE, mMinute); - String time = DateFormat.getTimeFormat(mContext).format(c.getTime()); - if (mSummaryFormat != 0) { - time = mContext.getResources().getString(mSummaryFormat, time); - } - setSummary(time); - } - - public static class TimePickerFragment extends DialogFragment implements - TimePickerDialog.OnTimeSetListener { - public TimePickerPreference pref; - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final boolean usePref = pref != null && pref.mHourOfDay >= 0 && pref.mMinute >= 0; - final Calendar c = Calendar.getInstance(); - final int hour = usePref ? pref.mHourOfDay : c.get(Calendar.HOUR_OF_DAY); - final int minute = usePref ? pref.mMinute : c.get(Calendar.MINUTE); - return new TimePickerDialog(getActivity(), this, hour, minute, - DateFormat.is24HourFormat(getActivity())); - } - - public void onTimeSet(TimePicker view, int hourOfDay, int minute) { - if (pref != null) { - pref.setTime(hourOfDay, minute); - } - } - } - - public interface Callback { - boolean onSetTime(int hour, int minute); - } - } } diff --git a/src/com/android/settings/notification/ZenModeSettingsBase.java b/src/com/android/settings/notification/ZenModeSettingsBase.java index d436965..ec17d86 100644 --- a/src/com/android/settings/notification/ZenModeSettingsBase.java +++ b/src/com/android/settings/notification/ZenModeSettingsBase.java @@ -17,6 +17,7 @@ package com.android.settings.notification; import android.app.INotificationManager; +import android.app.NotificationManager; import android.content.Context; import android.database.ContentObserver; import android.net.Uri; @@ -83,6 +84,11 @@ abstract public class ZenModeSettingsBase extends SettingsPreferenceFragment { } } + protected static boolean isDowntimeSupported(Context context) { + return NotificationManager.from(context) + .isSystemConditionProviderEnabled(ZenModeConfig.DOWNTIME_PATH); + } + private void updateZenModeConfig() { final ZenModeConfig config = getZenModeConfig(); if (Objects.equals(config, mConfig)) return; diff --git a/src/com/android/settings/search/Ranking.java b/src/com/android/settings/search/Ranking.java index e4e5d12..f37e1fc 100644 --- a/src/com/android/settings/search/Ranking.java +++ b/src/com/android/settings/search/Ranking.java @@ -42,6 +42,7 @@ import com.android.settings.location.ScanningSettings; import com.android.settings.net.DataUsageMeteredSettings; import com.android.settings.notification.NotificationSettings; import com.android.settings.notification.OtherSoundSettings; +import com.android.settings.notification.ZenModeAutomationSettings; import com.android.settings.notification.ZenModePrioritySettings; import com.android.settings.notification.ZenModeSettings; import com.android.settings.print.PrintSettingsFragment; @@ -125,6 +126,7 @@ public final class Ranking { sRankMap.put(OtherSoundSettings.class.getName(), RANK_NOTIFICATIONS); sRankMap.put(ZenModeSettings.class.getName(), RANK_NOTIFICATIONS); sRankMap.put(ZenModePrioritySettings.class.getName(), RANK_NOTIFICATIONS); + sRankMap.put(ZenModeAutomationSettings.class.getName(), RANK_NOTIFICATIONS); // Storage sRankMap.put(Memory.class.getName(), RANK_STORAGE); diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java index d57a1f1..75d7d62 100644 --- a/src/com/android/settings/search/SearchIndexableResources.java +++ b/src/com/android/settings/search/SearchIndexableResources.java @@ -43,6 +43,7 @@ import com.android.settings.location.ScanningSettings; import com.android.settings.net.DataUsageMeteredSettings; import com.android.settings.notification.NotificationSettings; import com.android.settings.notification.OtherSoundSettings; +import com.android.settings.notification.ZenModeAutomationSettings; import com.android.settings.notification.ZenModePrioritySettings; import com.android.settings.notification.ZenModeSettings; import com.android.settings.print.PrintSettingsFragment; @@ -170,6 +171,13 @@ public final class SearchIndexableResources { ZenModePrioritySettings.class.getName(), R.drawable.ic_settings_notifications)); + sResMap.put(ZenModeAutomationSettings.class.getName(), + new SearchIndexableResource( + Ranking.getRankForClassName(ZenModeAutomationSettings.class.getName()), + NO_DATA_RES_ID, + ZenModeAutomationSettings.class.getName(), + R.drawable.ic_settings_notifications)); + sResMap.put(Memory.class.getName(), new SearchIndexableResource( Ranking.getRankForClassName(Memory.class.getName()), diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java index e09d650..a166f0d 100644 --- a/src/com/android/settings/users/AppRestrictionsFragment.java +++ b/src/com/android/settings/users/AppRestrictionsFragment.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.RestrictionEntry; +import android.content.RestrictionsManager; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -841,7 +842,7 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen continue; } mUserManager.setApplicationRestrictions(packageName, - RestrictionUtils.restrictionsToBundle(restrictions), + RestrictionsManager.convertRestrictionsToBundle(restrictions), mUser); break; } @@ -915,7 +916,7 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen onRestrictionsReceived(preference, packageName, restrictions); if (mRestrictedProfile) { mUserManager.setApplicationRestrictions(packageName, - RestrictionUtils.restrictionsToBundle(restrictions), mUser); + RestrictionsManager.convertRestrictionsToBundle(restrictions), mUser); } } else if (restrictionsIntent != null) { preference.setRestrictions(restrictions); @@ -1046,7 +1047,7 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen // If there's a valid result, persist it to the user manager. pref.setRestrictions(list); mUserManager.setApplicationRestrictions(packageName, - RestrictionUtils.restrictionsToBundle(list), mUser); + RestrictionsManager.convertRestrictionsToBundle(list), mUser); } else if (bundle != null) { // If there's a valid result, persist it to the user manager. mUserManager.setApplicationRestrictions(packageName, bundle, mUser); diff --git a/src/com/android/settings/users/RestrictionUtils.java b/src/com/android/settings/users/RestrictionUtils.java index e8d46e9..b36cb3e 100644 --- a/src/com/android/settings/users/RestrictionUtils.java +++ b/src/com/android/settings/users/RestrictionUtils.java @@ -90,18 +90,4 @@ public class RestrictionUtils { } um.setUserRestrictions(userRestrictions, user); } - - public static Bundle restrictionsToBundle(ArrayList<RestrictionEntry> entries) { - final Bundle bundle = new Bundle(); - for (RestrictionEntry entry : entries) { - if (entry.getType() == RestrictionEntry.TYPE_BOOLEAN) { - bundle.putBoolean(entry.getKey(), entry.getSelectedState()); - } else if (entry.getType() == RestrictionEntry.TYPE_MULTI_SELECT) { - bundle.putStringArray(entry.getKey(), entry.getAllSelectedStrings()); - } else { - bundle.putString(entry.getKey(), entry.getSelectedString()); - } - } - return bundle; - } } diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 238db69..cb6be53 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -18,6 +18,7 @@ package com.android.settings.wifi; import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID; import static android.os.UserManager.DISALLOW_CONFIG_WIFI; + import android.app.Activity; import android.app.Dialog; import android.content.Context; @@ -25,7 +26,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.content.res.TypedArray; -import android.location.LocationManager; +import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.NetworkInfo.State; @@ -36,21 +37,27 @@ import android.nfc.NfcAdapter; import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceScreen; +import android.text.Spannable; +import android.text.style.TextAppearanceSpan; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; +import android.view.Gravity; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.TextView; +import android.widget.TextView.BufferType; import android.widget.Toast; import com.android.internal.logging.MetricsLogger; +import com.android.settings.LinkifyUtils; import com.android.settings.R; import com.android.settings.RestrictedSettingsFragment; import com.android.settings.SettingsActivity; +import com.android.settings.location.ScanningSettings; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settings.search.SearchIndexableRaw; @@ -608,27 +615,41 @@ public class WifiSettings extends RestrictedSettingsFragment protected TextView initEmptyView() { TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty); + emptyView.setGravity(Gravity.START | Gravity.CENTER_VERTICAL); getListView().setEmptyView(emptyView); return emptyView; } private void setOffMessage() { - if (mEmptyView != null) { - mEmptyView.setText(R.string.wifi_empty_list_wifi_off); - if (android.provider.Settings.Global.getInt(getActivity().getContentResolver(), - android.provider.Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1) { - mEmptyView.append("\n\n"); - int resId; - if (android.provider.Settings.Secure.isLocationProviderEnabled( - getActivity().getContentResolver(), LocationManager.NETWORK_PROVIDER)) { - resId = R.string.wifi_scan_notify_text_location_on; - } else { - resId = R.string.wifi_scan_notify_text_location_off; + if (mEmptyView == null) { + return; + } + + final CharSequence briefText = getText(R.string.wifi_empty_list_wifi_off); + if (isUiRestricted()) { + // Show only the brief text if the user is not allowed to configure scanning settings. + mEmptyView.setText(briefText, BufferType.SPANNABLE); + } else { + // Append the description of scanning settings with link. + final StringBuilder contentBuilder = new StringBuilder(); + contentBuilder.append(briefText); + contentBuilder.append("\n\n"); + contentBuilder.append(getText(R.string.wifi_scan_notify_text)); + LinkifyUtils.linkify(mEmptyView, contentBuilder, new LinkifyUtils.OnClickListener() { + @Override + public void onClick() { + final SettingsActivity activity = + (SettingsActivity) WifiSettings.this.getActivity(); + activity.startPreferencePanel(ScanningSettings.class.getName(), null, + R.string.location_scanning_screen_title, null, null, 0); } - CharSequence charSeq = getText(resId); - mEmptyView.append(charSeq); - } + }); } + // Embolden and enlarge the brief description anyway. + Spannable boldSpan = (Spannable) mEmptyView.getText(); + boldSpan.setSpan( + new TextAppearanceSpan(getActivity(), android.R.style.TextAppearance_Medium), 0, + briefText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); getPreferenceScreen().removeAll(); } |
