[4/4] OmniGears: Audio Themes Settings
Change-Id: I8d9be84a55c892f159359b6fe47d1c9249bc0326
diff --git a/res/values/custom_strings.xml b/res/values/custom_strings.xml
index 807589d..7275598 100644
--- a/res/values/custom_strings.xml
+++ b/res/values/custom_strings.xml
@@ -405,4 +405,13 @@
<!-- navigation bar -->
<string name="soft_back_kill_app_title">Long press back to kill app</string>
<string name="soft_back_kill_app_summary">Long pressing back button will kill foreground app</string>
+
+ <!-- Sound themes -->
+ <string name="sound_packages_title">Sound packs</string>
+ <string name="install_sound_pack_title">Install sound pack</string>
+ <string name="install_sound_pack_summary">Install new sound packs added in SoundPacks folder on the internal storage</string>
+ <string name="select_sound_pack_title">Select sound pack</string>
+ <string name="select_sound_pack_summary">Select the active sound pack</string>
+ <string name="default_sound_pack">Default (System)</string>
+ <string name="error_pack_install">Error while installing the sound pack</string>
</resources>
diff --git a/res/xml/sound_package_settings.xml b/res/xml/sound_package_settings.xml
new file mode 100644
index 0000000..5adc37a
--- /dev/null
+++ b/res/xml/sound_package_settings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The OmniROM Project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:key="sound_packages"
+ android:title="@string/sound_packages_title"
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+ <ListPreference
+ android:key="install_sound_pack"
+ android:title="@string/install_sound_pack_title"
+ android:summary="@string/install_sound_pack_summary"
+ android:persistent="false"/>
+
+ <ListPreference
+ android:key="select_sound_pack"
+ android:title="@string/select_sound_pack_title"
+ android:summary="@string/select_sound_pack_summary"
+ android:persistent="false" />
+
+</PreferenceScreen>
diff --git a/src/org/omnirom/omnigears/sounds/SoundPackageSettings.java b/src/org/omnirom/omnigears/sounds/SoundPackageSettings.java
new file mode 100644
index 0000000..45e4745
--- /dev/null
+++ b/src/org/omnirom/omnigears/sounds/SoundPackageSettings.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2014 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package org.omnirom.omnigears.sounds;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+public class SoundPackageSettings extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener {
+ private static final String TAG = "SoundPackageSettings";
+
+ private static final String INSTALL_SOUND_PACK = "install_sound_pack";
+ private static final String SELECT_SOUND_PACK = "select_sound_pack";
+
+ private static final String SOUND_PACKS_LOCATION = Environment
+ .getExternalStorageDirectory().getAbsolutePath() + "/SoundPacks/";
+ private static final String INSTALLED_PACKS_LOCATION = "/data/system/soundpacks/";
+
+ private ListPreference mInstallSoundPack;
+ private ListPreference mSelectSoundPack;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.sound_package_settings);
+
+ PreferenceScreen prefSet = getPreferenceScreen();
+
+ mInstallSoundPack = (ListPreference) prefSet
+ .findPreference(INSTALL_SOUND_PACK);
+ mInstallSoundPack.setOnPreferenceChangeListener(this);
+
+ mSelectSoundPack = (ListPreference) prefSet
+ .findPreference(SELECT_SOUND_PACK);
+ mSelectSoundPack.setOnPreferenceChangeListener(this);
+
+ updatePacks();
+ }
+
+ private void updatePacks() {
+ final ContentResolver resolver = getActivity().getContentResolver();
+ List<String> installablePacks = getInstallablePacks();
+
+ if (installablePacks != null && installablePacks.size() > 0) {
+ CharSequence[] packs = listToCharSeqArray(installablePacks, false);
+ mInstallSoundPack.setEntries(packs);
+ mInstallSoundPack.setEntryValues(packs);
+ mInstallSoundPack.setEnabled(true);
+ } else {
+ mInstallSoundPack.setEnabled(false);
+ }
+
+ List<String> selectablePacks = getInstalledPacks();
+
+ if (selectablePacks != null && selectablePacks.size() > 0) {
+ CharSequence[] packs = listToCharSeqArray(selectablePacks, true);
+ mSelectSoundPack.setEntries(packs);
+ mSelectSoundPack.setEntryValues(packs);
+ mSelectSoundPack.setEnabled(true);
+ } else {
+ mSelectSoundPack.setEnabled(false);
+ }
+
+ String activePack = Settings.System.getString(resolver,
+ Settings.System.CUSTOM_SOUND_EFFECTS_PATH);
+ if (activePack == null
+ || activePack.equals(getResources().getString(
+ R.string.default_sound_pack)) || activePack.isEmpty()) {
+ mSelectSoundPack.setValueIndex(0);
+ } else {
+ mSelectSoundPack.setValue(packPathToName(activePack));
+ }
+ }
+
+ private static String packPathToName(String path) {
+ String output = path.substring(0, path.length() - 1);
+ output = output.substring(output.lastIndexOf('/') + 1);
+ return output;
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
+ Preference preference) {
+ return true;
+ }
+
+ public boolean onPreferenceChange(Preference preference, Object objValue) {
+ if (preference == mInstallSoundPack) {
+ try {
+ installPack((String) objValue);
+ } catch (IOException e) {
+ Toast.makeText(getActivity(),
+ getResources().getString(R.string.error_pack_install),
+ Toast.LENGTH_LONG).show();
+ Log.e(TAG, "Unable to install sound pack", e);
+ }
+ return true;
+ } else if (preference == mSelectSoundPack) {
+ selectPack((String) objValue);
+ return true;
+ }
+
+ return false;
+ }
+
+ private CharSequence[] listToCharSeqArray(List<String> list,
+ boolean addDefault) {
+ CharSequence[] packs = new CharSequence[addDefault ? list.size() + 1
+ : list.size()];
+ int i = addDefault ? 1 : 0;
+
+ if (addDefault) {
+ packs[0] = getResources().getString(R.string.default_sound_pack);
+ }
+
+ for (String pack : list) {
+ packs[i] = pack;
+ i++;
+ }
+
+ return packs;
+ }
+
+ public static String stripExtension(String str) {
+ if (str == null)
+ return null;
+ int pos = str.lastIndexOf(".");
+ if (pos == -1)
+ return str;
+ return str.substring(0, pos);
+ }
+
+ private List<String> getInstallablePacks() {
+ File packsDir = new File(SOUND_PACKS_LOCATION);
+
+ Log.d(TAG, "Looking for sound packs in " + SOUND_PACKS_LOCATION);
+
+ // Get all ZIP files in our soundpack install location
+ File[] availPacks = packsDir.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String filename) {
+ // Must be lowercase
+ return filename.endsWith(".zip");
+ }
+ });
+
+ if (availPacks == null || availPacks.length == 0) {
+ Log.d(TAG, "No installable packs found!");
+ return null;
+ }
+
+ // Filter the pack we already have
+ List<String> installedPacks = getInstalledPacks();
+ List<String> filteredPacks = new ArrayList<String>();
+
+ for (File pack : availPacks) {
+ String packName = stripExtension(pack.getName());
+
+ // XXX: originally, we had this. However, this can cause troubles
+ // when designing
+ // your sound package as you cannot reinstall a package that has
+ // already been
+ // installed (unless you change name every time).
+ // We keep on displaying installable already installed packs instead
+ // until we
+ // implement package uninstaller
+ /*
+ * if (installedPacks == null || (installedPacks.size() > 0 &&
+ * !installedPacks .contains(packName))) {
+ * filteredPacks.add(packName); }
+ */
+ filteredPacks.add(packName);
+ }
+
+ return filteredPacks;
+ }
+
+ private List<String> getInstalledPacks() {
+ File packsDir = new File(INSTALLED_PACKS_LOCATION);
+ if (packsDir.list() != null) {
+ return Arrays.asList(packsDir.list());
+ } else {
+ return null;
+ }
+ }
+
+ private void extractFile(ZipInputStream zipIn, String filePath)
+ throws IOException {
+ BufferedOutputStream bos = new BufferedOutputStream(
+ new FileOutputStream(filePath));
+ byte[] bytesIn = new byte[8192];
+ int read = 0;
+ while ((read = zipIn.read(bytesIn)) != -1) {
+ bos.write(bytesIn, 0, read);
+ }
+ bos.close();
+ }
+
+ private void selectPack(String packName) {
+ final ContentResolver resolver = getActivity().getContentResolver();
+
+ if (packName.equals(getResources().getString(
+ R.string.default_sound_pack))) {
+ Settings.System.putString(resolver,
+ Settings.System.CUSTOM_SOUND_EFFECTS_PATH, null);
+ } else {
+ Settings.System.putString(resolver,
+ Settings.System.CUSTOM_SOUND_EFFECTS_PATH,
+ INSTALLED_PACKS_LOCATION + packName + "/");
+ }
+
+ }
+
+ private void installPack(String packName) throws IOException {
+ // Copy package contents (.ogg and .xml only) to
+ // /data/system/soundpacks/<name>/
+ File rootDir = new File(INSTALLED_PACKS_LOCATION);
+ if (!rootDir.exists()) {
+ rootDir.mkdir();
+ rootDir.setWritable(true, true);
+ rootDir.setReadable(true, false);
+ rootDir.setExecutable(true, false);
+ }
+ File destDir = new File(INSTALLED_PACKS_LOCATION + packName);
+ if (!destDir.exists()) {
+ destDir.mkdir();
+ destDir.setWritable(true, true);
+ destDir.setReadable(true, false);
+ destDir.setExecutable(true, false);
+ }
+
+ ZipInputStream zipIn = new ZipInputStream(new FileInputStream(
+ SOUND_PACKS_LOCATION + packName + ".zip"));
+
+ ZipEntry entry = zipIn.getNextEntry();
+
+ while (entry != null) {
+ if (!entry.getName().endsWith(".xml")
+ && !entry.getName().endsWith(".ogg")) {
+ // We only care about xml and ogg files
+ zipIn.closeEntry();
+ entry = zipIn.getNextEntry();
+ continue;
+ }
+
+ String filePath = INSTALLED_PACKS_LOCATION + packName + "/"
+ + entry.getName();
+ if (!entry.isDirectory()) {
+ extractFile(zipIn, filePath);
+
+ // Update permissions (644 / rw-r--r--)
+ File copied = new File(filePath);
+ copied.setExecutable(false);
+ copied.setReadable(true, false);
+ copied.setWritable(true, true);
+ } else {
+ File dir = new File(filePath);
+ dir.mkdir();
+ destDir.setWritable(true, true);
+ destDir.setReadable(true, false);
+ destDir.setExecutable(true, false);
+ }
+
+ zipIn.closeEntry();
+ entry = zipIn.getNextEntry();
+ }
+ zipIn.close();
+
+ updatePacks();
+ }
+}