VolumeZen: Sound & Notifications settings.
- Add sound settings under the newly combined Sound & Notifications top-level settings page. - New slider preferences for setting audio stream volumes. - Migrate checkbox prefs to switch prefs. - New settings subpage for touch sounds. - New settings subpage for "Showing notifications" - New setting pref for displaying intercepted notifications in zen mode. - New tri-state lockscreen notifications pref. - Add priority + sensitivity options to app notification dialog. - Remove badges on app notifications row, migrate to subtitle text. - Migrate strings to common spot in strings.xml - Remove heads up setting. - Remove tweaks category (unused). - Clean up notification settings, separate out into a code section per option. - Clean up _settings.xml files, prefix each pref with option name. - Add appropriate indexing data to each settings page. Depends on f/b change Ic30aa575ae07650cee62c8c1d83b6bc69395cf0d Change-Id: If700385a7d0057f6c4c4bcf6e648dcf88b8ebff2
This commit is contained in:
@@ -34,6 +34,7 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.preference.SeekBarVolumizer;
|
||||
import android.preference.VolumePreference;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.System;
|
||||
@@ -173,10 +174,10 @@ public class RingerVolumePreference extends VolumePreference {
|
||||
mSeekBars[i] = seekBar;
|
||||
if (SEEKBAR_TYPE[i] == AudioManager.STREAM_MUSIC) {
|
||||
mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
|
||||
SEEKBAR_TYPE[i], getMediaVolumeUri(getContext()));
|
||||
SEEKBAR_TYPE[i], getMediaVolumeUri(getContext()), this);
|
||||
} else {
|
||||
mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
|
||||
SEEKBAR_TYPE[i]);
|
||||
SEEKBAR_TYPE[i], null, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +276,7 @@ public class RingerVolumePreference extends VolumePreference {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSampleStarting(SeekBarVolumizer volumizer) {
|
||||
public void onSampleStarting(SeekBarVolumizer volumizer) {
|
||||
super.onSampleStarting(volumizer);
|
||||
for (SeekBarVolumizer vol : mSeekBarVolumizer) {
|
||||
if (vol != null && vol != volumizer) vol.stopSample();
|
||||
|
@@ -88,6 +88,9 @@ public class Settings extends SettingsActivity {
|
||||
public static class PrintJobSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class ZenModeSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class NotificationSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class NotificationDisplaySettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class AppNotificationSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class TouchSoundSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
|
||||
public static class TopLevelSettings extends SettingsActivity { /* empty */ }
|
||||
}
|
||||
|
@@ -29,11 +29,8 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.Signature;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.shapes.PathShape;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@@ -57,6 +54,9 @@ import android.widget.SectionIndexer;
|
||||
import android.widget.TextView;
|
||||
|
||||
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.Collator;
|
||||
import java.util.ArrayList;
|
||||
@@ -64,7 +64,7 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class AppNotificationSettings extends ListFragment {
|
||||
public class AppNotificationSettings extends ListFragment implements Indexable {
|
||||
private static final String TAG = "AppNotificationSettings";
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
@@ -187,6 +187,9 @@ public class AppNotificationSettings extends ListFragment {
|
||||
final TextView title = (TextView) layout.findViewById(android.R.id.title);
|
||||
title.setText(row.label);
|
||||
final CheckBox showBox = (CheckBox) layout.findViewById(android.R.id.button1);
|
||||
final CheckBox priBox = (CheckBox) layout.findViewById(android.R.id.button2);
|
||||
final CheckBox senBox = (CheckBox) layout.findViewById(android.R.id.button3);
|
||||
|
||||
showBox.setChecked(!row.banned);
|
||||
final OnCheckedChangeListener showListener = new OnCheckedChangeListener() {
|
||||
@Override
|
||||
@@ -195,6 +198,8 @@ public class AppNotificationSettings extends ListFragment {
|
||||
if (success) {
|
||||
row.banned = !isChecked;
|
||||
mAdapter.bindView(v, row, true /*animate*/);
|
||||
priBox.setEnabled(!row.banned);
|
||||
senBox.setEnabled(!row.banned);
|
||||
} else {
|
||||
showBox.setOnCheckedChangeListener(null);
|
||||
showBox.setChecked(!isChecked);
|
||||
@@ -203,6 +208,44 @@ public class AppNotificationSettings extends ListFragment {
|
||||
}
|
||||
};
|
||||
showBox.setOnCheckedChangeListener(showListener);
|
||||
|
||||
priBox.setChecked(row.priority);
|
||||
final OnCheckedChangeListener priListener = new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
boolean success = mBackend.setHighPriority(row.pkg, row.uid, isChecked);
|
||||
if (success) {
|
||||
row.priority = isChecked;
|
||||
mAdapter.bindView(v, row, true /*animate*/);
|
||||
} else {
|
||||
priBox.setOnCheckedChangeListener(null);
|
||||
priBox.setChecked(!isChecked);
|
||||
priBox.setOnCheckedChangeListener(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
priBox.setOnCheckedChangeListener(priListener);
|
||||
|
||||
senBox.setChecked(row.sensitive);
|
||||
final OnCheckedChangeListener senListener = new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
boolean success = mBackend.setSensitive(row.pkg, row.uid, isChecked);
|
||||
if (success) {
|
||||
row.sensitive = isChecked;
|
||||
mAdapter.bindView(v, row, true /*animate*/);
|
||||
} else {
|
||||
senBox.setOnCheckedChangeListener(null);
|
||||
senBox.setChecked(!isChecked);
|
||||
senBox.setOnCheckedChangeListener(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
senBox.setOnCheckedChangeListener(senListener);
|
||||
|
||||
priBox.setEnabled(!row.banned);
|
||||
senBox.setEnabled(!row.banned);
|
||||
|
||||
final AlertDialog d = new AlertDialog.Builder(mContext)
|
||||
.setView(layout)
|
||||
.setPositiveButton(R.string.app_notifications_dialog_done, null)
|
||||
@@ -214,48 +257,16 @@ public class AppNotificationSettings extends ListFragment {
|
||||
ViewGroup row;
|
||||
ViewGroup appButton;
|
||||
ImageView icon;
|
||||
ImageView banBadge;
|
||||
ImageView priBadge;
|
||||
TextView title;
|
||||
TextView subtitle;
|
||||
View settingsDivider;
|
||||
ImageView settingsButton;
|
||||
View rowDivider;
|
||||
}
|
||||
|
||||
private class NotificationAppAdapter extends ArrayAdapter<Row> implements SectionIndexer {
|
||||
private final ShapeDrawable mBanShape, mPriShape;
|
||||
|
||||
public NotificationAppAdapter(Context context) {
|
||||
super(context, 0, 0);
|
||||
final int s = context.getResources()
|
||||
.getDimensionPixelSize(R.dimen.notification_app_icon_badge_size);
|
||||
mBanShape = shape(banPath(s), s);
|
||||
mPriShape = shape(priPath(s), s);
|
||||
}
|
||||
|
||||
private ShapeDrawable shape(Path path, int s) {
|
||||
final ShapeDrawable sd = new ShapeDrawable(new PathShape(path, s, s));
|
||||
sd.getPaint().setStyle(Paint.Style.STROKE);
|
||||
sd.getPaint().setColor(0xffffffff);
|
||||
sd.getPaint().setStrokeWidth(s / 12);
|
||||
sd.setIntrinsicWidth(s);
|
||||
sd.setIntrinsicHeight(s);
|
||||
return sd;
|
||||
}
|
||||
|
||||
private Path banPath(int s) {
|
||||
final Path p = new Path();
|
||||
final int d = s / 5;
|
||||
p.moveTo(d, d); p.lineTo(s - d, s - d);
|
||||
p.moveTo(d, s - d); p.lineTo(s - d, d);
|
||||
return p;
|
||||
}
|
||||
|
||||
private Path priPath(int s) {
|
||||
final Path p = new Path();
|
||||
final int d = s / 5;
|
||||
p.moveTo(s / 2, d); p.lineTo(s / 2, s - d);
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -302,11 +313,8 @@ public class AppNotificationSettings extends ListFragment {
|
||||
vh.appButton = (ViewGroup) v.findViewById(android.R.id.button1);
|
||||
vh.appButton.setLayoutTransition(new LayoutTransition());
|
||||
vh.icon = (ImageView) v.findViewById(android.R.id.icon);
|
||||
vh.banBadge = (ImageView) v.findViewById(android.R.id.icon1);
|
||||
vh.banBadge.setImageDrawable(mBanShape);
|
||||
vh.priBadge = (ImageView) v.findViewById(android.R.id.icon2);
|
||||
vh.priBadge.setImageDrawable(mPriShape);
|
||||
vh.title = (TextView) v.findViewById(android.R.id.title);
|
||||
vh.subtitle = (TextView) v.findViewById(android.R.id.text1);
|
||||
vh.settingsDivider = v.findViewById(R.id.settings_divider);
|
||||
vh.settingsButton = (ImageView) v.findViewById(android.R.id.button2);
|
||||
vh.rowDivider = v.findViewById(R.id.row_divider);
|
||||
@@ -326,6 +334,7 @@ public class AppNotificationSettings extends ListFragment {
|
||||
|
||||
public void bindView(final View view, Row r, boolean animate) {
|
||||
if (!(r instanceof AppRow)) {
|
||||
// it's a section row
|
||||
TextView tv = (TextView)view;
|
||||
tv.setText(r.section);
|
||||
return;
|
||||
@@ -343,9 +352,10 @@ public class AppNotificationSettings extends ListFragment {
|
||||
});
|
||||
enableLayoutTransitions(vh.appButton, animate);
|
||||
vh.icon.setImageDrawable(row.icon);
|
||||
vh.banBadge.setVisibility(row.banned ? View.VISIBLE : View.GONE);
|
||||
vh.priBadge.setVisibility(row.priority ? View.VISIBLE : View.GONE);
|
||||
vh.title.setText(row.label);
|
||||
final String sub = getSubtitle(row);
|
||||
vh.subtitle.setText(sub);
|
||||
vh.subtitle.setVisibility(!sub.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
final boolean showSettings = !row.banned && row.settingsIntent != null;
|
||||
vh.settingsDivider.setVisibility(showSettings ? View.VISIBLE : View.GONE);
|
||||
vh.settingsButton.setVisibility(showSettings ? View.VISIBLE : View.GONE);
|
||||
@@ -359,6 +369,17 @@ public class AppNotificationSettings extends ListFragment {
|
||||
});
|
||||
}
|
||||
|
||||
private String getSubtitle(AppRow row) {
|
||||
if (row.banned) return mContext.getString(R.string.app_notification_row_banned);
|
||||
if (!row.priority && !row.sensitive) return "";
|
||||
final String priString = mContext.getString(R.string.app_notification_row_priority);
|
||||
final String senString = mContext.getString(R.string.app_notification_row_sensitive);
|
||||
if (row.priority != row.sensitive) {
|
||||
return row.priority ? priString : senString;
|
||||
}
|
||||
return priString + mContext.getString(R.string.summary_divider_text) + senString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getSections() {
|
||||
return mSections.toArray(new Object[mSections.size()]);
|
||||
@@ -396,7 +417,8 @@ public class AppNotificationSettings extends ListFragment {
|
||||
public Intent settingsIntent;
|
||||
public boolean banned;
|
||||
public boolean priority;
|
||||
public boolean first;
|
||||
public boolean sensitive;
|
||||
public boolean first; // first app in section
|
||||
}
|
||||
|
||||
private static final Comparator<AppRow> mRowComparator = new Comparator<AppRow>() {
|
||||
@@ -435,6 +457,7 @@ public class AppNotificationSettings extends ListFragment {
|
||||
row.icon = pkg.applicationInfo.loadIcon(pm);
|
||||
row.banned = mBackend.getNotificationsBanned(row.pkg, row.uid);
|
||||
row.priority = mBackend.getHighPriority(row.pkg, row.uid);
|
||||
row.sensitive = mBackend.getSensitive(row.pkg, row.uid);
|
||||
mRows.put(row.pkg, row);
|
||||
}
|
||||
// collect config activities
|
||||
@@ -552,5 +575,38 @@ public class AppNotificationSettings extends ListFragment {
|
||||
// TODO save high-pri state to NoMan
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean getSensitive(String pkg, int uid) {
|
||||
// TODO get visibility state from NoMan
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean setSensitive(String pkg, int uid, boolean sensitive) {
|
||||
// TODO save visibility state to NoMan
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// === Indexing ===
|
||||
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
|
||||
final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
|
||||
add(result, context, R.string.app_notifications_title);
|
||||
add(result, context, R.string.app_notifications_dialog_show);
|
||||
add(result, context, R.string.app_notifications_dialog_priority);
|
||||
add(result, context, R.string.app_notifications_dialog_visibility);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void add(List<SearchIndexableRaw> result, Context context, int title) {
|
||||
final Resources res = context.getResources();
|
||||
final SearchIndexableRaw data = new SearchIndexableRaw(context);
|
||||
data.title = res.getString(title);
|
||||
data.screenTitle = res.getString(R.string.app_notifications_title);
|
||||
result.add(data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.preference.TwoStatePreference;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NotificationDisplaySettings extends SettingsPreferenceFragment implements Indexable {
|
||||
private static final String TAG = "NotificationDisplaySettings";
|
||||
|
||||
private static final String KEY_NOTIFICATION_PULSE = "notification_pulse";
|
||||
private static final String KEY_LOCK_SCREEN_NOTIFICATIONS = "lock_screen_notifications";
|
||||
private static final String KEY_ZEN_MODE_NOTIFICATIONS = "zen_mode_notifications";
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
private final SettingsObserver mSettingsObserver = new SettingsObserver();
|
||||
|
||||
private TwoStatePreference mNotificationPulse;
|
||||
private DropDownPreference mLockscreen;
|
||||
private DropDownPreference mZenModeNotifications;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
addPreferencesFromResource(R.xml.notification_display_settings);
|
||||
|
||||
final PreferenceScreen root = getPreferenceScreen();
|
||||
initPulse(root);
|
||||
initLockscreenNotifications(root);
|
||||
initZenModeNotifications(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
mSettingsObserver.register(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mSettingsObserver.register(false);
|
||||
}
|
||||
|
||||
// === Pulse notification light ===
|
||||
|
||||
private void initPulse(PreferenceScreen parent) {
|
||||
mNotificationPulse = (TwoStatePreference) parent.findPreference(KEY_NOTIFICATION_PULSE);
|
||||
if (mNotificationPulse == null) return;
|
||||
if (!getResources()
|
||||
.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
|
||||
parent.removePreference(mNotificationPulse);
|
||||
} else {
|
||||
updatePulse();
|
||||
mNotificationPulse.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final boolean val = (Boolean)newValue;
|
||||
return Settings.System.putInt(getContentResolver(),
|
||||
Settings.System.NOTIFICATION_LIGHT_PULSE,
|
||||
val ? 1 : 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePulse() {
|
||||
if (mNotificationPulse == null) return;
|
||||
try {
|
||||
mNotificationPulse.setChecked(Settings.System.getInt(getContentResolver(),
|
||||
Settings.System.NOTIFICATION_LIGHT_PULSE) == 1);
|
||||
} catch (Settings.SettingNotFoundException snfe) {
|
||||
Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found");
|
||||
}
|
||||
}
|
||||
|
||||
// === Lockscreen (public / private) notifications ===
|
||||
|
||||
private void initLockscreenNotifications(PreferenceScreen parent) {
|
||||
mLockscreen = (DropDownPreference) parent.findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS);
|
||||
if (mLockscreen == null) return;
|
||||
mLockscreen.addItem(R.string.lock_screen_notifications_summary_show,
|
||||
R.string.lock_screen_notifications_summary_show);
|
||||
mLockscreen.addItem(R.string.lock_screen_notifications_summary_hide,
|
||||
R.string.lock_screen_notifications_summary_hide);
|
||||
mLockscreen.addItem(R.string.lock_screen_notifications_summary_disable,
|
||||
R.string.lock_screen_notifications_summary_disable);
|
||||
updateLockscreenNotifications();
|
||||
mLockscreen.setCallback(new DropDownPreference.Callback() {
|
||||
@Override
|
||||
public boolean onItemSelected(int pos, Object value) {
|
||||
final int val = (Integer) value;
|
||||
final boolean enabled = val != R.string.lock_screen_notifications_summary_disable;
|
||||
final boolean show = val == R.string.lock_screen_notifications_summary_show;
|
||||
Settings.Secure.putInt(getContentResolver(),
|
||||
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, show ? 1 : 0);
|
||||
Settings.Global.putInt(getContentResolver(),
|
||||
Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, enabled ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateLockscreenNotifications() {
|
||||
if (mLockscreen == null) return;
|
||||
final boolean allowPrivate = getLockscreenAllowPrivateNotifications();
|
||||
final boolean enabled = getLockscreenNotificationsEnabled();
|
||||
final int val = !enabled ? R.string.lock_screen_notifications_summary_disable :
|
||||
allowPrivate ? R.string.lock_screen_notifications_summary_show :
|
||||
R.string.lock_screen_notifications_summary_hide;
|
||||
mLockscreen.setSelectedValue(val);
|
||||
}
|
||||
|
||||
private boolean getLockscreenNotificationsEnabled() {
|
||||
return Settings.Global.getInt(getContentResolver(),
|
||||
Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0;
|
||||
}
|
||||
|
||||
private boolean getLockscreenAllowPrivateNotifications() {
|
||||
return Settings.Secure.getInt(getContentResolver(),
|
||||
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0;
|
||||
}
|
||||
|
||||
// === Zen mode notifications ===
|
||||
|
||||
private void initZenModeNotifications(PreferenceScreen parent) {
|
||||
mZenModeNotifications = (DropDownPreference)
|
||||
parent.findPreference(KEY_ZEN_MODE_NOTIFICATIONS);
|
||||
if (mZenModeNotifications == null) return;
|
||||
mZenModeNotifications.addItem(R.string.zen_mode_notifications_summary_hide, 0);
|
||||
mZenModeNotifications.addItem(R.string.zen_mode_notifications_summary_show, 1);
|
||||
updateZenModeNotifications();
|
||||
mZenModeNotifications.setCallback(new DropDownPreference.Callback() {
|
||||
@Override
|
||||
public boolean onItemSelected(int pos, Object value) {
|
||||
final int val = (Integer) value;
|
||||
Settings.Secure.putInt(getContentResolver(),
|
||||
Settings.Secure.DISPLAY_INTERCEPTED_NOTIFICATIONS, val);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateZenModeNotifications() {
|
||||
if (mZenModeNotifications == null) return;
|
||||
mZenModeNotifications.setSelectedValue(Settings.Secure.getInt(getContentResolver(),
|
||||
Settings.Secure.DISPLAY_INTERCEPTED_NOTIFICATIONS, 0));
|
||||
}
|
||||
|
||||
// === Indexing ===
|
||||
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
|
||||
final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
|
||||
add(result, context, R.string.notification_display_settings);
|
||||
add(result, context, R.string.notification_pulse_title);
|
||||
add(result, context, R.string.lock_screen_notifications_title);
|
||||
add(result, context, R.string.zen_mode_notifications_title);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void add(List<SearchIndexableRaw> result, Context context, int title) {
|
||||
final Resources res = context.getResources();
|
||||
final SearchIndexableRaw data = new SearchIndexableRaw(context);
|
||||
data.title = res.getString(title);
|
||||
data.screenTitle = res.getString(R.string.notification_display_settings);
|
||||
result.add(data);
|
||||
}
|
||||
};
|
||||
|
||||
// === Callbacks ===
|
||||
|
||||
private final class SettingsObserver extends ContentObserver {
|
||||
private final Uri NOTIFICATION_LIGHT_PULSE_URI =
|
||||
Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
|
||||
private final Uri LOCK_SCREEN_PRIVATE_URI =
|
||||
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
|
||||
private final Uri LOCK_SCREEN_SHOW_URI =
|
||||
Settings.Global.getUriFor(Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS);
|
||||
private final Uri DISPLAY_INTERCEPTED_NOTIFICATIONS_URI =
|
||||
Settings.Secure.getUriFor(Settings.Secure.DISPLAY_INTERCEPTED_NOTIFICATIONS);
|
||||
|
||||
public SettingsObserver() {
|
||||
super(mHandler);
|
||||
}
|
||||
|
||||
public void register(boolean register) {
|
||||
final ContentResolver cr = getContentResolver();
|
||||
if (register) {
|
||||
cr.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, false, this);
|
||||
cr.registerContentObserver(LOCK_SCREEN_PRIVATE_URI, false, this);
|
||||
cr.registerContentObserver(LOCK_SCREEN_SHOW_URI, false, this);
|
||||
cr.registerContentObserver(DISPLAY_INTERCEPTED_NOTIFICATIONS_URI, false, this);
|
||||
} else {
|
||||
cr.unregisterContentObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
super.onChange(selfChange, uri);
|
||||
if (NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
|
||||
updatePulse();
|
||||
}
|
||||
if (LOCK_SCREEN_PRIVATE_URI.equals(uri) || LOCK_SCREEN_SHOW_URI.equals(uri)) {
|
||||
updateLockscreenNotifications();
|
||||
}
|
||||
if (DISPLAY_INTERCEPTED_NOTIFICATIONS_URI.equals(uri)) {
|
||||
updateZenModeNotifications();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,209 +16,212 @@
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.media.AudioManager;
|
||||
import android.media.RingtoneManager;
|
||||
import android.os.Build;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceGroup;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.preference.SeekBarVolumizer;
|
||||
import android.preference.TwoStatePreference;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.SoundSettings;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
|
||||
public class NotificationSettings extends SettingsPreferenceFragment implements
|
||||
Preference.OnPreferenceChangeListener, OnPreferenceClickListener {
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NotificationSettings extends SettingsPreferenceFragment implements Indexable {
|
||||
private static final String TAG = "NotificationSettings";
|
||||
|
||||
private static final String KEY_NOTIFICATION_SOUND = "notification_sound";
|
||||
private static final String KEY_NOTIFICATION_PULSE = "notification_pulse";
|
||||
private static final String KEY_HEADS_UP = "heads_up";
|
||||
private static final String KEY_LOCK_SCREEN_NOTIFICATIONS = "toggle_lock_screen_notifications";
|
||||
private static final String KEY_MEDIA_VOLUME = "media_volume";
|
||||
private static final String KEY_ALARM_VOLUME = "alarm_volume";
|
||||
private static final String KEY_RING_VOLUME = "ring_volume";
|
||||
private static final String KEY_RINGER_MODE = "ringer_mode";
|
||||
private static final String KEY_PHONE_RINGTONE = "ringtone";
|
||||
private static final String KEY_NOTIFICATION_RINGTONE = "notification_ringtone";
|
||||
private static final String KEY_VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
|
||||
private static final String KEY_NOTIFICATION_ACCESS = "manage_notification_access";
|
||||
|
||||
private static final String KEY_TWEAKS_CATEGORY = "category_tweaks"; // power toys, eng only
|
||||
private static final int SAMPLE_CUTOFF = 2000; // manually cap sample playback at 2 seconds
|
||||
|
||||
private static final int MSG_UPDATE_SOUND_SUMMARY = 2;
|
||||
private final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();
|
||||
private final H mHandler = new H();
|
||||
private final SettingsObserver mSettingsObserver = new SettingsObserver();
|
||||
|
||||
private Context mContext;
|
||||
private PackageManager mPM;
|
||||
|
||||
private Preference mNotificationSoundPreference;
|
||||
private DropDownPreference mRingerMode;
|
||||
private Preference mPhoneRingtonePreference;
|
||||
private Preference mNotificationRingtonePreference;
|
||||
private TwoStatePreference mVibrateWhenRinging;
|
||||
private Preference mNotificationAccess;
|
||||
private DropDownPreference mLockscreen;
|
||||
private TwoStatePreference mHeadsUp;
|
||||
private TwoStatePreference mNotificationPulse;
|
||||
|
||||
private final Runnable mRingtoneLookupRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mNotificationSoundPreference != null) {
|
||||
final CharSequence summary = SoundSettings.updateRingtoneName(
|
||||
mContext, RingtoneManager.TYPE_NOTIFICATION);
|
||||
if (summary != null) {
|
||||
mHandler.sendMessage(
|
||||
mHandler.obtainMessage(MSG_UPDATE_SOUND_SUMMARY, summary));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_SOUND_SUMMARY:
|
||||
mNotificationSoundPreference.setSummary((CharSequence) msg.obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mContext = getActivity();
|
||||
final ContentResolver resolver = mContext.getContentResolver();
|
||||
|
||||
mPM = mContext.getPackageManager();
|
||||
|
||||
addPreferencesFromResource(R.xml.notification_settings);
|
||||
|
||||
final PreferenceScreen root = getPreferenceScreen();
|
||||
|
||||
PreferenceGroup tweaksCategory = (PreferenceGroup)
|
||||
root.findPreference(KEY_TWEAKS_CATEGORY);
|
||||
|
||||
if (tweaksCategory != null
|
||||
&& !(Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug"))) {
|
||||
root.removePreference(tweaksCategory);
|
||||
tweaksCategory = null;
|
||||
}
|
||||
|
||||
mNotificationSoundPreference = findPreference(KEY_NOTIFICATION_SOUND);
|
||||
initVolumePreference(KEY_MEDIA_VOLUME, AudioManager.STREAM_MUSIC);
|
||||
initVolumePreference(KEY_ALARM_VOLUME, AudioManager.STREAM_ALARM);
|
||||
initVolumePreference(KEY_RING_VOLUME, AudioManager.STREAM_RING);
|
||||
initRingerMode(root);
|
||||
initRingtones(root);
|
||||
initVibrateWhenRinging(root);
|
||||
|
||||
mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
|
||||
refreshNotificationListeners();
|
||||
|
||||
mLockscreen = (DropDownPreference) root.findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS);
|
||||
if (mLockscreen != null) {
|
||||
if (!getDeviceLockscreenNotificationsEnabled()) {
|
||||
root.removePreference(mLockscreen);
|
||||
} else {
|
||||
mLockscreen.addItem(R.string.lock_screen_notifications_summary_show, 1);
|
||||
mLockscreen.addItem(R.string.lock_screen_notifications_summary_hide, 0);
|
||||
final int val = getLockscreenAllowPrivateNotifications() ? 1 : 0;
|
||||
mLockscreen.setSelectedValue(val);
|
||||
mLockscreen.setCallback(new DropDownPreference.Callback() {
|
||||
@Override
|
||||
public boolean onItemSelected(int pos, Object value) {
|
||||
final int val = (Integer) value;
|
||||
Settings.Secure.putInt(getContentResolver(),
|
||||
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, val);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
mHeadsUp = (TwoStatePreference) findPreference(KEY_HEADS_UP);
|
||||
if (mHeadsUp != null) {
|
||||
updateHeadsUpMode(resolver);
|
||||
mHeadsUp.setOnPreferenceChangeListener(this);
|
||||
resolver.registerContentObserver(
|
||||
Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
|
||||
false, new ContentObserver(mHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateHeadsUpMode(resolver);
|
||||
}
|
||||
});
|
||||
}
|
||||
mNotificationPulse = (TwoStatePreference) findPreference(KEY_NOTIFICATION_PULSE);
|
||||
|
||||
if (mNotificationPulse != null
|
||||
&& getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_intrusiveNotificationLed) == false) {
|
||||
getPreferenceScreen().removePreference(mNotificationPulse);
|
||||
} else {
|
||||
try {
|
||||
mNotificationPulse.setChecked(Settings.System.getInt(resolver,
|
||||
Settings.System.NOTIFICATION_LIGHT_PULSE) == 1);
|
||||
mNotificationPulse.setOnPreferenceChangeListener(this);
|
||||
} catch (Settings.SettingNotFoundException snfe) {
|
||||
Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
refreshNotificationListeners();
|
||||
lookupRingtoneNames();
|
||||
final IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
|
||||
mContext.registerReceiver(mReceiver, filter);
|
||||
mSettingsObserver.register(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
|
||||
final String key = preference.getKey();
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mVolumeCallback.stopSample();
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
mSettingsObserver.register(false);
|
||||
}
|
||||
|
||||
if (KEY_HEADS_UP.equals(key)) {
|
||||
setHeadsUpMode(getContentResolver(), mHeadsUp.isChecked());
|
||||
} else if (KEY_NOTIFICATION_PULSE.equals(key)) {
|
||||
Settings.System.putInt(getContentResolver(),
|
||||
Settings.System.NOTIFICATION_LIGHT_PULSE,
|
||||
mNotificationPulse.isChecked() ? 1 : 0);
|
||||
} else {
|
||||
return super.onPreferenceTreeClick(preferenceScreen, preference);
|
||||
// === Volumes ===
|
||||
|
||||
private void initVolumePreference(String key, int stream) {
|
||||
final VolumeSeekBarPreference volumePref = (VolumeSeekBarPreference) findPreference(key);
|
||||
volumePref.setStream(stream);
|
||||
volumePref.setCallback(mVolumeCallback);
|
||||
}
|
||||
|
||||
private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback {
|
||||
private SeekBarVolumizer mCurrent;
|
||||
|
||||
@Override
|
||||
public void onSampleStarting(SeekBarVolumizer sbv) {
|
||||
if (mCurrent != null && mCurrent != sbv) {
|
||||
mCurrent.stopSample();
|
||||
}
|
||||
mCurrent = sbv;
|
||||
if (mCurrent != null) {
|
||||
mHandler.removeMessages(H.STOP_SAMPLE);
|
||||
mHandler.sendEmptyMessageDelayed(H.STOP_SAMPLE, SAMPLE_CUTOFF);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
public void stopSample() {
|
||||
if (mCurrent != null) {
|
||||
mCurrent.stopSample();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// === Ringer mode ===
|
||||
|
||||
private void initRingerMode(PreferenceScreen root) {
|
||||
mRingerMode = (DropDownPreference) root.findPreference(KEY_RINGER_MODE);
|
||||
if (mRingerMode == null) return;
|
||||
final AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
mRingerMode.addItem(R.string.ringer_mode_audible, AudioManager.RINGER_MODE_NORMAL);
|
||||
mRingerMode.addItem(R.string.ringer_mode_vibrate, AudioManager.RINGER_MODE_VIBRATE);
|
||||
mRingerMode.addItem(R.string.ringer_mode_silent, AudioManager.RINGER_MODE_SILENT);
|
||||
updateRingerMode();
|
||||
mRingerMode.setCallback(new DropDownPreference.Callback() {
|
||||
@Override
|
||||
public boolean onItemSelected(int pos, Object value) {
|
||||
final int val = (Integer) value;
|
||||
am.setRingerMode(val);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object objValue) {
|
||||
return true;
|
||||
private void updateRingerMode() {
|
||||
final AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
mRingerMode.setSelectedValue(am.getRingerMode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
return false;
|
||||
// === Phone & notification ringtone ===
|
||||
|
||||
private void initRingtones(PreferenceScreen root) {
|
||||
mPhoneRingtonePreference = root.findPreference(KEY_PHONE_RINGTONE);
|
||||
mNotificationRingtonePreference = root.findPreference(KEY_NOTIFICATION_RINGTONE);
|
||||
}
|
||||
|
||||
// === Heads-up notifications ===
|
||||
|
||||
private void updateHeadsUpMode(ContentResolver resolver) {
|
||||
mHeadsUp.setChecked(Settings.Global.HEADS_UP_ON == Settings.Global.getInt(resolver,
|
||||
Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, Settings.Global.HEADS_UP_OFF));
|
||||
private void lookupRingtoneNames() {
|
||||
AsyncTask.execute(mLookupRingtoneNames);
|
||||
}
|
||||
|
||||
private void setHeadsUpMode(ContentResolver resolver, boolean value) {
|
||||
Settings.Global.putInt(resolver, Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
|
||||
value ? Settings.Global.HEADS_UP_ON : Settings.Global.HEADS_UP_OFF);
|
||||
private final Runnable mLookupRingtoneNames = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mPhoneRingtonePreference != null) {
|
||||
final CharSequence summary = SoundSettings.updateRingtoneName(
|
||||
mContext, RingtoneManager.TYPE_RINGTONE);
|
||||
if (summary != null) {
|
||||
mHandler.obtainMessage(H.UPDATE_PHONE_RINGTONE, summary).sendToTarget();
|
||||
}
|
||||
}
|
||||
if (mNotificationRingtonePreference != null) {
|
||||
final CharSequence summary = SoundSettings.updateRingtoneName(
|
||||
mContext, RingtoneManager.TYPE_NOTIFICATION);
|
||||
if (summary != null) {
|
||||
mHandler.obtainMessage(H.UPDATE_NOTIFICATION_RINGTONE, summary).sendToTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// === Vibrate when ringing ===
|
||||
|
||||
private void initVibrateWhenRinging(PreferenceScreen root) {
|
||||
mVibrateWhenRinging = (TwoStatePreference) findPreference(KEY_VIBRATE_WHEN_RINGING);
|
||||
if (mVibrateWhenRinging == null) return;
|
||||
mVibrateWhenRinging.setPersistent(false);
|
||||
updateVibrateWhenRinging();
|
||||
mVibrateWhenRinging.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final boolean val = (Boolean) newValue;
|
||||
return Settings.System.putInt(getContentResolver(),
|
||||
Settings.System.VIBRATE_WHEN_RINGING,
|
||||
val ? 1 : 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// === Lockscreen (public / private) notifications ===
|
||||
|
||||
private boolean getDeviceLockscreenNotificationsEnabled() {
|
||||
return 0 != Settings.Global.getInt(getContentResolver(),
|
||||
Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
|
||||
}
|
||||
|
||||
private boolean getLockscreenAllowPrivateNotifications() {
|
||||
return 0 != Settings.Secure.getInt(getContentResolver(),
|
||||
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
|
||||
private void updateVibrateWhenRinging() {
|
||||
if (mVibrateWhenRinging == null) return;
|
||||
mVibrateWhenRinging.setChecked(Settings.System.getInt(getContentResolver(),
|
||||
Settings.System.VIBRATE_WHEN_RINGING, 0) != 0);
|
||||
}
|
||||
|
||||
// === Notification listeners ===
|
||||
@@ -242,9 +245,92 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
|
||||
}
|
||||
}
|
||||
|
||||
// === Ringtone ===
|
||||
// === Indexing ===
|
||||
|
||||
private void lookupRingtoneNames() {
|
||||
new Thread(mRingtoneLookupRunnable).start();
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
|
||||
final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
|
||||
add(result, context, R.string.notification_settings);
|
||||
add(result, context, R.string.media_volume_option_title);
|
||||
add(result, context, R.string.alarm_volume_option_title);
|
||||
add(result, context, R.string.ring_volume_option_title);
|
||||
add(result, context, R.string.ringer_mode_title);
|
||||
add(result, context, R.string.ringtone_title);
|
||||
add(result, context, R.string.vibrate_when_ringing_title);
|
||||
add(result, context, R.string.manage_notification_access_title);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void add(List<SearchIndexableRaw> result, Context context, int title) {
|
||||
final Resources res = context.getResources();
|
||||
final SearchIndexableRaw data = new SearchIndexableRaw(context);
|
||||
data.title = res.getString(title);
|
||||
data.screenTitle = res.getString(R.string.notification_settings);
|
||||
result.add(data);
|
||||
}
|
||||
};
|
||||
|
||||
// === Callbacks ===
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
|
||||
updateRingerMode();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final class SettingsObserver extends ContentObserver {
|
||||
private final Uri VIBRATE_WHEN_RINGING_URI =
|
||||
Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING);
|
||||
|
||||
public SettingsObserver() {
|
||||
super(mHandler);
|
||||
}
|
||||
|
||||
public void register(boolean register) {
|
||||
final ContentResolver cr = getContentResolver();
|
||||
if (register) {
|
||||
cr.registerContentObserver(VIBRATE_WHEN_RINGING_URI, false, this);
|
||||
} else {
|
||||
cr.unregisterContentObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
super.onChange(selfChange, uri);
|
||||
if (VIBRATE_WHEN_RINGING_URI.equals(uri)) {
|
||||
updateVibrateWhenRinging();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class H extends Handler {
|
||||
private static final int UPDATE_PHONE_RINGTONE = 1;
|
||||
private static final int UPDATE_NOTIFICATION_RINGTONE = 2;
|
||||
private static final int STOP_SAMPLE = 3;
|
||||
|
||||
private H() {
|
||||
super(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case UPDATE_PHONE_RINGTONE:
|
||||
mPhoneRingtonePreference.setSummary((CharSequence) msg.obj);
|
||||
break;
|
||||
case UPDATE_NOTIFICATION_RINGTONE:
|
||||
mNotificationRingtonePreference.setSummary((CharSequence) msg.obj);
|
||||
break;
|
||||
case STOP_SAMPLE:
|
||||
mVolumeCallback.stopSample();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
208
src/com/android/settings/notification/TouchSoundSettings.java
Normal file
208
src/com/android/settings/notification/TouchSoundSettings.java
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Vibrator;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.preference.TwoStatePreference;
|
||||
import android.provider.Settings.System;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.search.Indexable;
|
||||
import com.android.settings.search.SearchIndexableRaw;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TouchSoundSettings extends SettingsPreferenceFragment implements Indexable {
|
||||
private static final String TAG = "TouchSoundSettings";
|
||||
|
||||
private static final String KEY_DIAL_PAD_TONES = "dial_pad_tones";
|
||||
private static final String KEY_SCREEN_LOCKING_SOUNDS = "screen_locking_sounds";
|
||||
private static final String KEY_OTHER_TOUCH_SOUNDS = "other_touch_sounds";
|
||||
private static final String KEY_VIBRATE_ON_TOUCH = "vibrate_on_touch";
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
private final SettingsObserver mSettingsObserver = new SettingsObserver();
|
||||
|
||||
private AudioManager mAudioManager;
|
||||
private SystemSettingPref mDialPadTones;
|
||||
private SystemSettingPref mScreenLockingSounds;
|
||||
private SystemSettingPref mOtherTouchSounds;
|
||||
private SystemSettingPref mVibrateOnTouch;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
addPreferencesFromResource(R.xml.touch_sound_settings);
|
||||
|
||||
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
|
||||
final boolean hasVoice = Utils.isVoiceCapable(getActivity());
|
||||
final Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
|
||||
final boolean hasHaptic = vibrator != null && vibrator.hasVibrator();
|
||||
|
||||
mDialPadTones = new SystemSettingPref(hasVoice,
|
||||
KEY_DIAL_PAD_TONES, System.DTMF_TONE_WHEN_DIALING);
|
||||
|
||||
mScreenLockingSounds = new SystemSettingPref(true,
|
||||
KEY_SCREEN_LOCKING_SOUNDS, System.LOCKSCREEN_SOUNDS_ENABLED);
|
||||
|
||||
mOtherTouchSounds = new SystemSettingPref(true,
|
||||
KEY_OTHER_TOUCH_SOUNDS,System.SOUND_EFFECTS_ENABLED) {
|
||||
@Override
|
||||
protected void setSetting(boolean value) {
|
||||
if (value) {
|
||||
mAudioManager.loadSoundEffects();
|
||||
} else {
|
||||
mAudioManager.unloadSoundEffects();
|
||||
}
|
||||
super.setSetting(value);
|
||||
}
|
||||
};
|
||||
|
||||
mVibrateOnTouch = new SystemSettingPref(hasHaptic,
|
||||
KEY_VIBRATE_ON_TOUCH, System.HAPTIC_FEEDBACK_ENABLED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
mSettingsObserver.register(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mSettingsObserver.register(false);
|
||||
}
|
||||
|
||||
// === Common system setting preference helper ===
|
||||
|
||||
private class SystemSettingPref {
|
||||
private final String mSetting;
|
||||
private final Uri mUri;
|
||||
|
||||
private TwoStatePreference mPref;
|
||||
|
||||
private SystemSettingPref(boolean applicable, String key, String setting) {
|
||||
mSetting = setting;
|
||||
mUri = System.getUriFor(mSetting);
|
||||
if (!applicable) removePreference(key);
|
||||
mPref = (TwoStatePreference) getPreferenceScreen().findPreference(key);
|
||||
if (mPref == null) return;
|
||||
update();
|
||||
mPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
setSetting((Boolean)newValue);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void setSetting(boolean value) {
|
||||
System.putInt(getContentResolver(), mSetting, value ? 1 : 0);
|
||||
}
|
||||
|
||||
private Uri getUri() {
|
||||
return mUri;
|
||||
}
|
||||
|
||||
private void update() {
|
||||
if (mPref == null) return;
|
||||
mPref.setChecked(System.getInt(getContentResolver(), mSetting, 1) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// === Indexing ===
|
||||
|
||||
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider() {
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
|
||||
final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
|
||||
add(result, context, R.string.touch_sound_settings);
|
||||
add(result, context, R.string.dial_pad_tones_title);
|
||||
add(result, context, R.string.screen_locking_sounds_title);
|
||||
add(result, context, R.string.other_touch_sounds_title);
|
||||
add(result, context, R.string.vibrate_on_touch_title);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void add(List<SearchIndexableRaw> result, Context context, int title) {
|
||||
final Resources res = context.getResources();
|
||||
final SearchIndexableRaw data = new SearchIndexableRaw(context);
|
||||
data.title = res.getString(title);
|
||||
data.screenTitle = res.getString(R.string.touch_sound_settings);
|
||||
result.add(data);
|
||||
}
|
||||
};
|
||||
|
||||
// === Callbacks ===
|
||||
|
||||
private final class SettingsObserver extends ContentObserver {
|
||||
public SettingsObserver() {
|
||||
super(mHandler);
|
||||
}
|
||||
|
||||
public void register(boolean register) {
|
||||
final ContentResolver cr = getContentResolver();
|
||||
if (register) {
|
||||
cr.registerContentObserver(mDialPadTones.getUri(), false, this);
|
||||
cr.registerContentObserver(mScreenLockingSounds.getUri(), false, this);
|
||||
cr.registerContentObserver(mOtherTouchSounds.getUri(), false, this);
|
||||
cr.registerContentObserver(mVibrateOnTouch.getUri(), false, this);
|
||||
} else {
|
||||
cr.unregisterContentObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
super.onChange(selfChange, uri);
|
||||
if (mDialPadTones.getUri().equals(uri)) {
|
||||
mDialPadTones.update();
|
||||
}
|
||||
if (mScreenLockingSounds.getUri().equals(uri)) {
|
||||
mScreenLockingSounds.update();
|
||||
}
|
||||
if (mOtherTouchSounds.getUri().equals(uri)) {
|
||||
mOtherTouchSounds.update();
|
||||
}
|
||||
if (mVibrateOnTouch.getUri().equals(uri)) {
|
||||
mVibrateOnTouch.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.SeekBarPreference;
|
||||
import android.preference.SeekBarVolumizer;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/** A slider preference that directly controls an audio stream volume (no dialog) **/
|
||||
public class VolumeSeekBarPreference extends SeekBarPreference
|
||||
implements PreferenceManager.OnActivityStopListener {
|
||||
private static final String TAG = "VolumeSeekBarPreference";
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private int mStream;
|
||||
private SeekBar mSeekBar;
|
||||
private SeekBarVolumizer mVolumizer;
|
||||
private Callback mCallback;
|
||||
|
||||
public VolumeSeekBarPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void setStream(int stream) {
|
||||
mStream = stream;
|
||||
}
|
||||
|
||||
public void setCallback(Callback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStop() {
|
||||
if (mVolumizer != null) {
|
||||
mVolumizer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindView(View view) {
|
||||
super.onBindView(view);
|
||||
if (mStream == 0) {
|
||||
Log.w(TAG, "No stream found, not binding volumizer");
|
||||
return;
|
||||
}
|
||||
getPreferenceManager().registerOnActivityStopListener(this);
|
||||
final SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
|
||||
if (seekBar == mSeekBar) return;
|
||||
mSeekBar = seekBar;
|
||||
final SeekBarVolumizer.Callback sbvc = new SeekBarVolumizer.Callback() {
|
||||
@Override
|
||||
public void onSampleStarting(SeekBarVolumizer sbv) {
|
||||
if (mCallback != null) {
|
||||
mCallback.onSampleStarting(sbv);
|
||||
}
|
||||
}
|
||||
};
|
||||
final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null;
|
||||
mVolumizer = new SeekBarVolumizer(mContext, seekBar, mStream, sampleUri, sbvc);
|
||||
}
|
||||
|
||||
private Uri getMediaVolumeUri() {
|
||||
return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
|
||||
+ mContext.getPackageName()
|
||||
+ "/" + R.raw.media_volume);
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void onSampleStarting(SeekBarVolumizer sbv);
|
||||
}
|
||||
}
|
@@ -329,7 +329,7 @@ public class ZenModeSettings extends SettingsPreferenceFragment implements Index
|
||||
if (automatic == null || automatic.length == 0) {
|
||||
return null;
|
||||
}
|
||||
final String divider = getString(R.string.zen_mode_entry_conditions_summary_divider);
|
||||
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);
|
||||
|
@@ -36,7 +36,10 @@ import com.android.settings.deviceinfo.Memory;
|
||||
import com.android.settings.fuelgauge.PowerUsageSummary;
|
||||
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
|
||||
import com.android.settings.location.LocationSettings;
|
||||
import com.android.settings.notification.AppNotificationSettings;
|
||||
import com.android.settings.notification.NotificationDisplaySettings;
|
||||
import com.android.settings.notification.NotificationSettings;
|
||||
import com.android.settings.notification.TouchSoundSettings;
|
||||
import com.android.settings.notification.ZenModeSettings;
|
||||
import com.android.settings.print.PrintSettingsFragment;
|
||||
import com.android.settings.users.UserSettings;
|
||||
@@ -137,12 +140,30 @@ public final class SearchIndexableResources {
|
||||
NotificationSettings.class.getName(),
|
||||
R.drawable.ic_settings_notifications));
|
||||
|
||||
sResMap.put(NotificationDisplaySettings.class.getName(),
|
||||
new SearchIndexableResource(RANK_NOTIFICATIONS,
|
||||
R.xml.notification_display_settings,
|
||||
NotificationDisplaySettings.class.getName(),
|
||||
R.drawable.ic_settings_notifications));
|
||||
|
||||
sResMap.put(TouchSoundSettings.class.getName(),
|
||||
new SearchIndexableResource(RANK_NOTIFICATIONS,
|
||||
R.xml.touch_sound_settings,
|
||||
TouchSoundSettings.class.getName(),
|
||||
R.drawable.ic_settings_notifications));
|
||||
|
||||
sResMap.put(ZenModeSettings.class.getName(),
|
||||
new SearchIndexableResource(RANK_NOTIFICATIONS,
|
||||
NO_DATA_RES_ID,
|
||||
ZenModeSettings.class.getName(),
|
||||
R.drawable.ic_settings_notifications));
|
||||
|
||||
sResMap.put(AppNotificationSettings.class.getName(),
|
||||
new SearchIndexableResource(RANK_NOTIFICATIONS,
|
||||
NO_DATA_RES_ID,
|
||||
AppNotificationSettings.class.getName(),
|
||||
R.drawable.ic_settings_notifications));
|
||||
|
||||
sResMap.put(Memory.class.getName(),
|
||||
new SearchIndexableResource(RANK_MEMORY,
|
||||
NO_DATA_RES_ID,
|
||||
|
Reference in New Issue
Block a user