diff --git a/android/res/layout/voice_variant_preference.xml b/android/res/layout/voice_variant_preference.xml
new file mode 100644
index 00000000..f593dc89
--- /dev/null
+++ b/android/res/layout/voice_variant_preference.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/res/values/donottranslate.xml b/android/res/values/donottranslate.xml
index 86437a09..75d1f9fc 100644
--- a/android/res/values/donottranslate.xml
+++ b/android/res/values/donottranslate.xml
@@ -6,32 +6,7 @@
-->
eSpeak
-
- - male
- - m1
- - m2
- - m3
- - m4
- - m5
- - m6
- - m7
- - female
- - f1
- - f2
- - f3
- - f4
- - f5
- - klatt
- - klatt2
- - klatt3
- - klatt4
- - croak
- - whisper
- - whisperf
- - male-young
- - female-young
- - male-old
- - female-old
-
+
+ Klatt
\ No newline at end of file
diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml
index 7a2f5909..f84d1fca 100644
--- a/android/res/values/strings.xml
+++ b/android/res/values/strings.xml
@@ -65,41 +65,6 @@
Description: Sample text spoken aloud when the user is trying out a language.
-->
This is a sample of text spoken in %s
-
-
- - Male
- - Male (Variant 1)
- - Male (Variant 2)
- - Male (Variant 3)
- - Male (Variant 4)
- - Male (Variant 5)
- - Male (Variant 6)
- - Male (Variant 7)
- - Female
- - Female (Variant 1)
- - Female (Variant 2)
- - Female (Variant 3)
- - Female (Variant 4)
- - Female (Variant 5)
- - Klatt (Variant 1)
- - Klatt (Variant 2)
- - Klatt (Variant 3)
- - Klatt (Variant 4)
- - Croak (Male)
- - Whisper (Male)
- - Whisper (Female)
- - Young (Male)
- - Young (Female)
- - Old (Male)
- - Old (Female)
-
-
Status
eSpeak version
Speak
@@ -117,4 +82,14 @@
Custom: %s
None
Punctuation characters
+ Category
+ Variant
+ Male
+ Female
+ Default
+ Variant %d
+ Young
+ Old
+ Croak
+ Whisper
diff --git a/android/res/xml/preferences.xml b/android/res/xml/preferences.xml
index fe20d547..5c099e78 100644
--- a/android/res/xml/preferences.xml
+++ b/android/res/xml/preferences.xml
@@ -1,12 +1,3 @@
-
-
-
\ No newline at end of file
diff --git a/android/src/com/reecedunn/espeak/ResourceIdListAdapter.java b/android/src/com/reecedunn/espeak/ResourceIdListAdapter.java
new file mode 100644
index 00000000..844422e5
--- /dev/null
+++ b/android/src/com/reecedunn/espeak/ResourceIdListAdapter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 Reece H. Dunn
+ *
+ * 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.reecedunn.espeak;
+
+import android.app.Activity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+public class ResourceIdListAdapter extends ArrayAdapter
+{
+ private final LayoutInflater mInflater;
+
+ static class ViewHolder
+ {
+ public TextView text;
+ }
+
+ public ResourceIdListAdapter(Activity context, Integer[] resources)
+ {
+ super(context, android.R.layout.simple_list_item_1, resources);
+ mInflater = context.getLayoutInflater();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent)
+ {
+ ViewHolder holder;
+ if (convertView == null)
+ {
+ convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
+ holder = new ViewHolder();
+ holder.text = (TextView)convertView.findViewById(android.R.id.text1);
+ convertView.setTag(holder);
+ }
+ else
+ {
+ holder = (ViewHolder)convertView.getTag();
+ }
+
+ holder.text.setText(getItem(position));
+ return convertView;
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent)
+ {
+ return getView(position, convertView, parent);
+ }
+}
diff --git a/android/src/com/reecedunn/espeak/TtsSettingsActivity.java b/android/src/com/reecedunn/espeak/TtsSettingsActivity.java
index fc090772..343b63b0 100644
--- a/android/src/com/reecedunn/espeak/TtsSettingsActivity.java
+++ b/android/src/com/reecedunn/espeak/TtsSettingsActivity.java
@@ -97,7 +97,19 @@ public class TtsSettingsActivity extends PreferenceActivity {
}
}
- private static Preference createSpeakPunctuationPreference(Context context, SpeechSynthesis engine, int titleRes) {
+ private static Preference createVoiceVariantPreference(Context context, VoiceSettings settings, int titleRes) {
+ final String title = context.getString(titleRes);
+
+ final VoiceVariantPreference pref = new VoiceVariantPreference(context);
+ pref.setTitle(title);
+ pref.setDialogTitle(title);
+ pref.setOnPreferenceChangeListener(mOnPreferenceChanged);
+ pref.setPersistent(true);
+ pref.setVoiceVariant(settings.getVoiceVariant());
+ return pref;
+ }
+
+ private static Preference createSpeakPunctuationPreference(Context context, VoiceSettings settings, int titleRes) {
final String title = context.getString(titleRes);
final SpeakPunctuationPreference pref = new SpeakPunctuationPreference(context);
@@ -105,7 +117,7 @@ public class TtsSettingsActivity extends PreferenceActivity {
pref.setDialogTitle(title);
pref.setOnPreferenceChangeListener(mOnPreferenceChanged);
pref.setPersistent(true);
- pref.setVoiceSettings(new VoiceSettings(PreferenceManager.getDefaultSharedPreferences(context), engine));
+ pref.setVoiceSettings(settings);
return pref;
}
@@ -153,31 +165,11 @@ public class TtsSettingsActivity extends PreferenceActivity {
* summary with the current entry value.
*/
private static void createPreferences(Context context, PreferenceGroup group) {
- if (group == null) {
- return;
- }
-
- final int count = group.getPreferenceCount();
-
- for (int i = 0; i < count; i++) {
- final Preference preference = group.getPreference(i);
-
- if (preference instanceof PreferenceGroup) {
- createPreferences(null, (PreferenceGroup) preference);
- } else if (preference instanceof ListPreference) {
- preference.setOnPreferenceChangeListener(mOnPreferenceChanged);
- }
- }
-
- if (context == null) {
- return;
- }
-
- // Bind eSpeak parameters to preference settings:
-
SpeechSynthesis engine = new SpeechSynthesis(context, null);
+ VoiceSettings settings = new VoiceSettings(PreferenceManager.getDefaultSharedPreferences(context), engine);
- group.addPreference(createSpeakPunctuationPreference(context, engine, R.string.espeak_speak_punctuation));
+ group.addPreference(createVoiceVariantPreference(context, settings, R.string.espeak_variant));
+ group.addPreference(createSpeakPunctuationPreference(context, settings, R.string.espeak_speak_punctuation));
group.addPreference(createSeekBarPreference(context, engine.Rate, "espeak_rate", R.string.setting_default_rate));
group.addPreference(createSeekBarPreference(context, engine.Pitch, "espeak_pitch", R.string.setting_default_pitch));
group.addPreference(createSeekBarPreference(context, engine.PitchRange, "espeak_pitch_range", R.string.espeak_pitch_range));
diff --git a/android/src/com/reecedunn/espeak/VoiceVariant.java b/android/src/com/reecedunn/espeak/VoiceVariant.java
index 006b6acf..e2a92f76 100644
--- a/android/src/com/reecedunn/espeak/VoiceVariant.java
+++ b/android/src/com/reecedunn/espeak/VoiceVariant.java
@@ -60,6 +60,17 @@ public class VoiceVariant {
return ret;
}
+ public boolean equals(Object o) {
+ if (o instanceof VoiceVariant) {
+ VoiceVariant other = (VoiceVariant)o;
+ if (variant == null || other.variant == null) {
+ return other.variant == null && variant == null && other.gender == gender && other.age == age;
+ }
+ return other.variant.equals(variant) && other.gender == gender && other.age == age;
+ }
+ return false;
+ }
+
public static VoiceVariant parseVoiceVariant(String value) {
String[] parts = mVariantPattern.split(value);
int age = SpeechSynthesis.AGE_ANY;
diff --git a/android/src/com/reecedunn/espeak/VoiceVariantPreference.java b/android/src/com/reecedunn/espeak/VoiceVariantPreference.java
new file mode 100644
index 00000000..93f5d45e
--- /dev/null
+++ b/android/src/com/reecedunn/espeak/VoiceVariantPreference.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2013 Reece H. Dunn
+ *
+ * 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.reecedunn.espeak;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.preference.DialogPreference;
+import android.text.Editable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.RadioButton;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class VoiceVariantPreference extends DialogPreference {
+ private Spinner mCategory;
+ private Spinner mVariant;
+
+ private int mCategoryIndex = 0;
+ private int mVariantIndex = 0;
+
+ static class ViewHolder
+ {
+ public TextView text;
+ }
+
+ private class VariantData {
+ private final int name;
+ private final Object arg;
+ private final VoiceVariant variant;
+
+ protected VariantData(int name, String variant) {
+ this(name, null, variant);
+ }
+
+ protected VariantData(int name, Object arg, String variant) {
+ this.name = name;
+ this.arg = arg;
+ this.variant = VoiceVariant.parseVoiceVariant(variant);
+ }
+
+ public String getDisplayName(Context context) {
+ String text = context.getText(name).toString();
+ if (arg == null) {
+ return text;
+ }
+ return String.format(text, arg);
+ }
+
+ public VoiceVariant getVariant() {
+ return variant;
+ }
+ }
+
+ public class VariantDataListAdapter extends ArrayAdapter
+ {
+ private final LayoutInflater mInflater;
+
+ public VariantDataListAdapter(Activity context, VariantData[] resources)
+ {
+ super(context, android.R.layout.simple_list_item_1, resources);
+ mInflater = context.getLayoutInflater();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent)
+ {
+ ViewHolder holder;
+ if (convertView == null)
+ {
+ convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
+ holder = new ViewHolder();
+ holder.text = (TextView)convertView.findViewById(android.R.id.text1);
+ convertView.setTag(holder);
+ }
+ else
+ {
+ holder = (ViewHolder)convertView.getTag();
+ }
+
+ holder.text.setText(getItem(position).getDisplayName(getContext()));
+ return convertView;
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent)
+ {
+ return getView(position, convertView, parent);
+ }
+ }
+
+ private Integer[] categories = {
+ R.string.variant_male,
+ R.string.variant_female,
+ R.string.variant_klatt,
+ R.string.variant_young,
+ R.string.variant_old,
+ R.string.variant_croak,
+ R.string.variant_whisper,
+ };
+
+ private VariantData[][] variants = {
+ { // Male
+ new VariantData(R.string.variant_default, "male"),
+ new VariantData(R.string.variant_n, 1, "m1"),
+ new VariantData(R.string.variant_n, 2, "m2"),
+ new VariantData(R.string.variant_n, 3, "m3"),
+ new VariantData(R.string.variant_n, 4, "m4"),
+ new VariantData(R.string.variant_n, 5, "m5"),
+ new VariantData(R.string.variant_n, 6, "m6"),
+ new VariantData(R.string.variant_n, 7, "m7"),
+ },{ // Female
+ new VariantData(R.string.variant_default, "female"),
+ new VariantData(R.string.variant_n, 1, "f1"),
+ new VariantData(R.string.variant_n, 2, "f2"),
+ new VariantData(R.string.variant_n, 3, "f3"),
+ new VariantData(R.string.variant_n, 4, "f4"),
+ new VariantData(R.string.variant_n, 5, "f5"),
+ },{ // Klatt
+ new VariantData(R.string.variant_n, 1, "klatt"),
+ new VariantData(R.string.variant_n, 2, "klatt2"),
+ new VariantData(R.string.variant_n, 3, "klatt3"),
+ new VariantData(R.string.variant_n, 4, "klatt4"),
+ },{ // Young
+ new VariantData(R.string.variant_male, "male-young"),
+ new VariantData(R.string.variant_female, "female-young"),
+ },{ // Old
+ new VariantData(R.string.variant_male, "male-old"),
+ new VariantData(R.string.variant_female, "female-old"),
+ },{ // Croak
+ new VariantData(R.string.variant_male, "croak"),
+ },{ // Whisper
+ new VariantData(R.string.variant_male, "whisper"),
+ new VariantData(R.string.variant_female, "whisperf"),
+ },
+ };
+
+ public VoiceVariantPreference(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setDialogLayoutResource(R.layout.voice_variant_preference);
+ setLayoutResource(R.layout.information_view);
+ setPositiveButtonText(android.R.string.ok);
+ setNegativeButtonText(android.R.string.cancel);
+ }
+
+ public VoiceVariantPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public VoiceVariantPreference(Context context) {
+ this(context, null);
+ }
+
+ public void setVoiceVariant(VoiceVariant variant) {
+ for (int i = 0; i < variants.length; ++i) {
+ VariantData[] items = variants[i];
+ for (int j = 0; j < items.length; ++j) {
+ if (items[j].getVariant().equals(variant)) {
+ mCategoryIndex = i;
+ mVariantIndex = j;
+ onDataChanged();
+ return;
+ }
+ }
+ }
+ onDataChanged();
+ }
+
+ @Override
+ protected View onCreateDialogView() {
+ View root = super.onCreateDialogView();
+ mCategory = (Spinner)root.findViewById(R.id.category);
+ mVariant = (Spinner)root.findViewById(R.id.variant);
+ return root;
+ }
+
+ @Override
+ protected void onBindDialogView(View view) {
+ super.onBindDialogView(view);
+
+ // Cache the indices so they don't get overwritten by the OnItemSelectedListener handlers.
+ final int category = mCategoryIndex;
+ final int variant = mVariantIndex;
+
+ mCategory.setAdapter(new ResourceIdListAdapter((Activity)getContext(), categories));
+ mCategory.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ private boolean mInitializing = true;
+
+ @Override
+ public void onItemSelected(AdapterView> adapterView, View view, int position, long id) {
+ mVariant.setAdapter(new VariantDataListAdapter((Activity) getContext(), variants[position]));
+ if (mInitializing) {
+ mVariant.setSelection(variant);
+ mInitializing = false;
+ }
+ mCategoryIndex = position;
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> adapterView) {
+ }
+ });
+ mVariant.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> adapterView, View view, int position, long id) {
+ mVariantIndex = position;
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> adapterView) {
+ }
+ });
+
+ mCategory.setSelection(category);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ onDataChanged();
+ if (shouldCommit()) {
+ SharedPreferences.Editor editor = getEditor();
+ if (editor != null) {
+ VoiceVariant variant = variants[mCategoryIndex][mVariantIndex].getVariant();
+ editor.putString(VoiceSettings.PREF_VARIANT, variant.toString());
+ editor.commit();
+ }
+ }
+ break;
+ }
+ super.onClick(dialog, which);
+ }
+
+ private void onDataChanged() {
+ Context context = getContext();
+ CharSequence category = context.getText(categories[mCategoryIndex]);
+ CharSequence variant = variants[mCategoryIndex][mVariantIndex].getDisplayName(context);
+ callChangeListener(String.format("%s (%s)", category, variant));
+ }
+}