diff --git a/res/values/cherish_arrays.xml b/res/values/cherish_arrays.xml
index a1615bf..8da8b6f 100644
--- a/res/values/cherish_arrays.xml
+++ b/res/values/cherish_arrays.xml
@@ -359,5 +359,15 @@
- 2
- 3
+
+
+
+ - 60
+ - 45
+ - 30
+ - 15
+ - 7
+ - 5
+
diff --git a/res/values/cherish_strings.xml b/res/values/cherish_strings.xml
index 7ee4c3a..a6273fe 100644
--- a/res/values/cherish_strings.xml
+++ b/res/values/cherish_strings.xml
@@ -452,4 +452,16 @@
Screen off FOD
Keep FOD enabled when the screen is off
+
+ Volume steps
+ Customize volume steps
+ Volume steps: Alarm
+ Volume steps: DTMF
+ Volume steps: Media
+ Volume steps: Notification
+ Volume steps: Ringer
+ Volume steps: System
+ Volume steps: Voice Call
+ Reset
+
diff --git a/res/xml/cherish_settings_button.xml b/res/xml/cherish_settings_button.xml
index b1836ae..e14911d 100644
--- a/res/xml/cherish_settings_button.xml
+++ b/res/xml/cherish_settings_button.xml
@@ -33,6 +33,12 @@
android:summary="@string/volume_rocker_wake_summary"
android:title="@string/volume_rocker_wake_title"
android:defaultValue="false" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/com/cherish/settings/fragments/VolumeStepsFragment.java b/src/com/cherish/settings/fragments/VolumeStepsFragment.java
new file mode 100644
index 0000000..9d1d501
--- /dev/null
+++ b/src/com/cherish/settings/fragments/VolumeStepsFragment.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2015 The Pure Nexus 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.cherish.settings.fragments;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.provider.SearchIndexableResource;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.PreferenceFragment;
+
+import com.android.settings.R;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settingslib.search.SearchIndexable;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@SearchIndexable
+public class VolumeStepsFragment extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener {
+
+ private static final String TAG = "VolumeSteps";
+ private static final String VOLUME_STEP_DEFAULTS = "volume_step_defaults";
+ private static final String FIRST_RUN_KEY = "first_run";
+
+ // base map of all preference keys and the associated stream
+ private static final Map volume_map = new HashMap();
+ static {
+ volume_map.put("volume_steps_alarm", new Integer(AudioManager.STREAM_ALARM));
+ volume_map.put("volume_steps_dtmf", new Integer(AudioManager.STREAM_DTMF));
+ volume_map.put("volume_steps_music", new Integer(AudioManager.STREAM_MUSIC));
+ volume_map.put("volume_steps_notification", new Integer(AudioManager.STREAM_NOTIFICATION));
+ volume_map.put("volume_steps_ring", new Integer(AudioManager.STREAM_RING));
+ volume_map.put("volume_steps_system", new Integer(AudioManager.STREAM_SYSTEM));
+ volume_map.put("volume_steps_voice_call", new Integer(AudioManager.STREAM_VOICE_CALL));
+ }
+
+ // entries to remove on non-telephony devices
+ private static final Set telephony_set = new HashSet();
+ static {
+ telephony_set.add("volume_steps_dtmf");
+ telephony_set.add("volume_steps_ring");
+ telephony_set.add("volume_steps_voice_call");
+ }
+
+ // set of available pref keys after device configuration filter
+ private Set mAvailableKeys = new HashSet();
+ private AudioManager mAudioManager;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.cherish_settings_volume_steps);
+ mAudioManager = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+
+ final PreferenceScreen prefScreen = getPreferenceScreen();
+ mAvailableKeys = volume_map.keySet();
+
+ // remove invalid audio stream prefs
+ boolean isPhone = TelephonyManager.getDefault().getCurrentPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
+
+ if (!isPhone) {
+ // remove telephony keys from available set
+ mAvailableKeys.removeAll(telephony_set);
+ for (String key : telephony_set) {
+ Preference toRemove = prefScreen.findPreference(key);
+ if (toRemove != null) {
+ prefScreen.removePreference(toRemove);
+ }
+ }
+ }
+
+ // check prefs for initial run of this fragment
+ final boolean firstRun = checkForFirstRun();
+
+ // entries array isn't translatable ugh
+ final String defEntry = getString(R.string.volume_steps_reset);
+
+ // initialize prefs: set defaults if first run, set listeners and update values
+ for (String key : mAvailableKeys) {
+ Preference pref = prefScreen.findPreference(key);
+ if (pref == null || !(pref instanceof ListPreference)) {
+ continue;
+ }
+ final ListPreference listPref = (ListPreference) pref;
+ int steps = mAudioManager.getStreamMaxVolume(volume_map.get(key));
+ if (firstRun) {
+ saveDefaultSteps(listPref, steps);
+ }
+ final int defSteps = getDefaultSteps(listPref);
+ CharSequence[] entries = new CharSequence[listPref.getEntries().length + 1];
+ CharSequence[] values = new CharSequence[listPref.getEntryValues().length + 1];
+
+ for (int i = 0; i < entries.length; i++) {
+ if (i == 0) {
+ entries[i] = defEntry;
+ values[i] = String.valueOf(defSteps);
+ continue;
+ }
+ entries[i] = listPref.getEntries()[i - 1];
+ values[i] = listPref.getEntryValues()[i - 1];
+ }
+ listPref.setEntries(entries);
+ listPref.setEntryValues(values);
+ updateVolumeStepPrefs(listPref, steps);
+ listPref.setOnPreferenceChangeListener(this);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object objValue) {
+ if (preference.hasKey() && mAvailableKeys.contains(preference.getKey())) {
+ commitVolumeSteps(preference, Integer.parseInt(objValue.toString()));
+ }
+ return true;
+ }
+
+ private SharedPreferences getDefaultStepsPrefs() {
+ return getActivity().getSharedPreferences(VOLUME_STEP_DEFAULTS,
+ Context.MODE_PRIVATE);
+ }
+
+ // test for initial run of this fragment
+ private boolean checkForFirstRun() {
+ String isFirstRun = getDefaultStepsPrefs().getString(FIRST_RUN_KEY, null);
+ if (isFirstRun == null) {
+ getDefaultStepsPrefs().edit().putString(FIRST_RUN_KEY, "first_run_initialized").commit();
+ return true;
+ }
+ return false;
+ }
+
+ private int getDefaultSteps(Preference pref) {
+ if (pref == null || !(pref instanceof ListPreference)) {
+ // unlikely
+ return -1;
+ }
+ String key = pref.getKey();
+ String value = getDefaultStepsPrefs().getString(key, null);
+ if (value == null) {
+ // unlikely
+ return -1;
+ }
+ return Integer.parseInt(value);
+ }
+
+ // on the initial run, let's store true device defaults in sharedPrefs
+ private void saveDefaultSteps(Preference volPref, int defaultSteps) {
+ String key = volPref.getKey();
+ getDefaultStepsPrefs().edit().putString(key, String.valueOf(defaultSteps)).commit();
+ }
+
+ private void updateVolumeStepPrefs(Preference pref, int steps) {
+ if (pref == null || !(pref instanceof ListPreference)) {
+ return;
+ }
+ final ListPreference listPref = (ListPreference) pref;
+ listPref.setSummary(String.valueOf(steps));
+ listPref.setValue(String.valueOf(steps));
+ }
+
+ private void commitVolumeSteps(Preference pref, int steps) {
+ Settings.System.putInt(getActivity().getContentResolver(), pref.getKey(), steps);
+ mAudioManager.setStreamMaxVolume(volume_map.get(pref.getKey()), steps);
+ updateVolumeStepPrefs(pref, steps);
+ Log.i(TAG, "Volume steps:" + pref.getKey() + "" + String.valueOf(steps));
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.CHERISH_SETTINGS;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.cherish_settings_volume_steps);
+}