[2/2] OmniGears: live Volume Steps

Change-Id: Ib796c884062415ecc1dde328a27ba97f6505ead3
diff --git a/res/values/custom_arrays.xml b/res/values/custom_arrays.xml
index a8f0951..a3c062b 100644
--- a/res/values/custom_arrays.xml
+++ b/res/values/custom_arrays.xml
@@ -473,4 +473,19 @@
         <item>1</item>
         <item>2</item>
     </string-array>
+
+    <!-- Volume step options. -->
+    <string-array name="volume_steps_entries" translatable="false">
+        <item>@string/volume_steps_30</item>
+        <item>@string/volume_steps_15</item>
+        <item>@string/volume_steps_7</item>
+        <item>@string/volume_steps_5</item>
+    </string-array>
+
+    <string-array name="volume_steps_values" translatable="false">
+        <item>30</item>
+        <item>15</item>
+        <item>7</item>
+        <item>5</item>
+    </string-array>
 </resources>
diff --git a/res/values/custom_strings.xml b/res/values/custom_strings.xml
index db487fd..032bf2b 100644
--- a/res/values/custom_strings.xml
+++ b/res/values/custom_strings.xml
@@ -615,4 +615,12 @@
     <string name="systemui_theme_style_auto">Wallpaper based</string>
     <string name="systemui_theme_style_light">Light style</string>
     <string name="systemui_theme_style_dark">Dark style</string>
-</resources>
+
+    <string name="volume_steps_title">Volume steps</string>
+    <string name="volume_steps_summary">Configure available steps for different audio volumes</string>
+    <string name="volume_steps_30">30 steps</string>
+    <string name="volume_steps_15">15 steps</string>
+    <string name="volume_steps_7">7 steps</string>
+    <string name="volume_steps_5">5 steps</string>
+    <string name="system_volume_option_title">System volume</string>
+    <string name="call_volume_option_title">Voice call volume</string></resources>
diff --git a/res/xml/more_settings.xml b/res/xml/more_settings.xml
index a193dea..7ba454d 100644
--- a/res/xml/more_settings.xml
+++ b/res/xml/more_settings.xml
@@ -72,5 +72,12 @@
                         android:targetPackage="org.omnirom.logcat"
                         android:targetClass="com.pluscubed.logcat.ui.LogcatActivity" />
             </Preference>
+
+            <Preference
+                android:key="volume_steps"
+                android:title="@string/volume_steps_title"
+                android:summary="@string/volume_steps_summary"
+                android:fragment="org.omnirom.omnigears.system.VolumeSteps" />
+
         </PreferenceCategory>
 </PreferenceScreen>
diff --git a/res/xml/volume_steps.xml b/res/xml/volume_steps.xml
new file mode 100644
index 0000000..e495ee7
--- /dev/null
+++ b/res/xml/volume_steps.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/volume_steps_title"
+        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+        <ListPreference
+                android:key="volume_steps_music"
+                android:title="@string/media_volume_option_title"
+                android:entries="@array/volume_steps_entries"
+                android:entryValues="@array/volume_steps_values" />
+
+        <ListPreference
+                android:key="volume_steps_alarm"
+                android:title="@string/alarm_volume_option_title"
+                android:entries="@array/volume_steps_entries"
+                android:entryValues="@array/volume_steps_values" />
+
+        <ListPreference
+                android:key="volume_steps_ring"
+                android:title="@string/ring_volume_option_title"
+                android:entries="@array/volume_steps_entries"
+                android:entryValues="@array/volume_steps_values" />
+
+        <ListPreference
+                android:key="volume_steps_notification"
+                android:title="@string/notification_volume_option_title"
+                android:entries="@array/volume_steps_entries"
+                android:entryValues="@array/volume_steps_values" />
+
+        <ListPreference
+                android:key="volume_steps_system"
+                android:title="@string/system_volume_option_title"
+                android:entries="@array/volume_steps_entries"
+                android:entryValues="@array/volume_steps_values" />
+
+        <ListPreference
+                android:key="volume_steps_voice_call"
+                android:title="@string/call_volume_option_title"
+                android:entries="@array/volume_steps_entries"
+                android:entryValues="@array/volume_steps_values" />
+</PreferenceScreen>
diff --git a/src/org/omnirom/omnigears/system/VolumeSteps.java b/src/org/omnirom/omnigears/system/VolumeSteps.java
new file mode 100644
index 0000000..7042354
--- /dev/null
+++ b/src/org/omnirom/omnigears/system/VolumeSteps.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2015 The Dirty Unicorns 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 org.omnirom.omnigears.system;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.Preference.OnPreferenceChangeListener;
+import android.support.v7.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.omni.DeviceUtils;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+public class VolumeSteps extends SettingsPreferenceFragment implements
+        Preference.OnPreferenceChangeListener {
+    private static final String TAG = "VolumeSteps";
+
+    // base map of all preference keys and the associated stream
+    private static final Map<String, Integer> volume_map = new HashMap<String, Integer>();
+    static {
+        volume_map.put("volume_steps_alarm", new Integer(AudioManager.STREAM_ALARM));
+        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<String> telephony_set = new HashSet<String>();
+    static {
+        telephony_set.add("volume_steps_ring");
+        telephony_set.add("volume_steps_voice_call");
+    }
+
+    // set of available pref keys after device configuration filter
+    private Set<String> mAvailableKeys = new HashSet<String>();
+    private AudioManager mAudioManager;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.volume_steps);
+        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+
+        final PreferenceScreen prefScreen = getPreferenceScreen();
+        mAvailableKeys = volume_map.keySet();
+
+        // remove invalid audio stream prefs
+        boolean isPhone = DeviceUtils.isPhone(getActivity());
+
+        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);
+                }
+            }
+        }
+
+        // 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));
+            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;
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.OMNI_SETTINGS;
+    }
+
+    private int getDefaultSteps(Preference pref) {
+        if (pref == null || !(pref instanceof ListPreference)) {
+            // unlikely
+            return -1;
+        }
+        String key = pref.getKey();
+        return mAudioManager.getDefaultStreamMaxVolume(volume_map.get(key));
+    }
+
+
+    private void updateVolumeStepPrefs(Preference pref, int steps) {
+        if (pref == null || !(pref instanceof ListPreference)) {
+            return;
+        }
+        final ListPreference listPref = (ListPreference) pref;
+        listPref.setValue(String.valueOf(steps));
+        listPref.setSummary(listPref.getEntry());
+    }
+
+    private void commitVolumeSteps(Preference pref, int steps) {
+        Settings.Global.putInt(getContentResolver(), pref.getKey(), steps);
+        mAudioManager.setStreamMaxVolume(volume_map.get(pref.getKey()), steps);
+        updateVolumeStepPrefs(pref, steps);
+    }
+}