| @@ -0,0 +1,14 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
| android:layout_width="match_parent" | |||
| android:layout_height="match_parent" | |||
| android:orientation="vertical" > | |||
| <Spinner | |||
| android:layout_width="match_parent" | |||
| android:layout_height="wrap_content" | |||
| android:id="@+id/dictionaries" | |||
| android:gravity="bottom" | |||
| android:layout_gravity="center_vertical" /> | |||
| </LinearLayout> | |||
| @@ -92,4 +92,6 @@ | |||
| <string name="variant_old">Old</string> | |||
| <string name="variant_croak">Croak</string> | |||
| <string name="variant_whisper">Whisper</string> | |||
| <string name="import_voice_title">Import eSpeak dictionary</string> | |||
| <string name="import_voice_description">Import an eSpeak voice dictionary file from the SD card.</string> | |||
| </resources> | |||
| @@ -0,0 +1,69 @@ | |||
| /* | |||
| * 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.view.LayoutInflater; | |||
| import android.view.View; | |||
| import android.view.ViewGroup; | |||
| import android.widget.ArrayAdapter; | |||
| import android.widget.TextView; | |||
| import java.io.File; | |||
| public class FileListAdapter extends ArrayAdapter<File> | |||
| { | |||
| private final LayoutInflater mInflater; | |||
| static class ViewHolder | |||
| { | |||
| public TextView text; | |||
| } | |||
| public FileListAdapter(Activity context, File[] 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(); | |||
| } | |||
| File file = getItem(position); | |||
| holder.text.setText(file.getName()); | |||
| return convertView; | |||
| } | |||
| @Override | |||
| public View getDropDownView(int position, View convertView, ViewGroup parent) | |||
| { | |||
| return getView(position, convertView, parent); | |||
| } | |||
| } | |||
| @@ -26,14 +26,30 @@ import java.io.InputStream; | |||
| public class FileUtils { | |||
| public static String read(File file) throws IOException { | |||
| return read(new FileInputStream(file), (int)file.length()); | |||
| return readByteArray(new FileInputStream(file), (int)file.length()).toString(); | |||
| } | |||
| public static String read(InputStream stream) throws IOException { | |||
| return read(stream, stream.available()); | |||
| return readByteArray(stream, stream.available()).toString(); | |||
| } | |||
| public static String read(InputStream stream, int length) throws IOException { | |||
| return readByteArray(stream, length).toString(); | |||
| } | |||
| public static byte[] readBinary(File file) throws IOException { | |||
| return readByteArray(new FileInputStream(file), (int)file.length()).toByteArray(); | |||
| } | |||
| public static byte[] readBinary(InputStream stream) throws IOException { | |||
| return readByteArray(stream, stream.available()).toByteArray(); | |||
| } | |||
| public static byte[] readBinary(InputStream stream, int length) throws IOException { | |||
| return readByteArray(stream, length).toByteArray(); | |||
| } | |||
| private static ByteArrayOutputStream readByteArray(InputStream stream, int length) throws IOException { | |||
| ByteArrayOutputStream content = new ByteArrayOutputStream(length); | |||
| int c = stream.read(); | |||
| while (c != -1) | |||
| @@ -41,13 +57,17 @@ public class FileUtils { | |||
| content.write((byte)c); | |||
| c = stream.read(); | |||
| } | |||
| return content.toString(); | |||
| return content; | |||
| } | |||
| public static void write(File outputFile, String contents) throws IOException { | |||
| write(outputFile, contents.getBytes()); | |||
| } | |||
| public static void write(File outputFile, byte[] contents) throws IOException { | |||
| FileOutputStream outputStream = new FileOutputStream(outputFile); | |||
| try { | |||
| outputStream.write(contents.getBytes(), 0, contents.length()); | |||
| outputStream.write(contents, 0, contents.length); | |||
| } finally { | |||
| outputStream.close(); | |||
| } | |||
| @@ -29,6 +29,7 @@ import android.preference.PreferenceFragment; | |||
| import android.preference.PreferenceGroup; | |||
| import android.preference.PreferenceManager; | |||
| import com.reecedunn.espeak.preference.ImportVoicePreference; | |||
| import com.reecedunn.espeak.preference.SeekBarPreference; | |||
| import com.reecedunn.espeak.preference.SpeakPunctuationPreference; | |||
| import com.reecedunn.espeak.preference.VoiceVariantPreference; | |||
| @@ -101,6 +102,17 @@ public class TtsSettingsActivity extends PreferenceActivity { | |||
| } | |||
| } | |||
| private static Preference createImportVoicePreference(Context context) { | |||
| final String title = context.getString(R.string.import_voice_title); | |||
| final ImportVoicePreference pref = new ImportVoicePreference(context); | |||
| pref.setTitle(title); | |||
| pref.setDialogTitle(title); | |||
| pref.setOnPreferenceChangeListener(mOnPreferenceChanged); | |||
| pref.setDescription(R.string.import_voice_description); | |||
| return pref; | |||
| } | |||
| private static Preference createVoiceVariantPreference(Context context, VoiceSettings settings, int titleRes) { | |||
| final String title = context.getString(titleRes); | |||
| @@ -172,6 +184,7 @@ public class TtsSettingsActivity extends PreferenceActivity { | |||
| SpeechSynthesis engine = new SpeechSynthesis(context, null); | |||
| VoiceSettings settings = new VoiceSettings(PreferenceManager.getDefaultSharedPreferences(context), engine); | |||
| group.addPreference(createImportVoicePreference(context)); | |||
| 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, VoiceSettings.PREF_RATE, R.string.setting_default_rate)); | |||
| @@ -0,0 +1,117 @@ | |||
| /* | |||
| * 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.preference; | |||
| import android.app.Activity; | |||
| import android.app.DownloadManager; | |||
| import android.content.Context; | |||
| import android.content.DialogInterface; | |||
| import android.content.Intent; | |||
| import android.os.AsyncTask; | |||
| import android.os.Environment; | |||
| import android.preference.DialogPreference; | |||
| import android.util.AttributeSet; | |||
| import android.view.View; | |||
| import android.widget.Spinner; | |||
| import com.reecedunn.espeak.CheckVoiceData; | |||
| import com.reecedunn.espeak.DownloadVoiceData; | |||
| import com.reecedunn.espeak.FileListAdapter; | |||
| import com.reecedunn.espeak.FileUtils; | |||
| import com.reecedunn.espeak.R; | |||
| import java.io.File; | |||
| import java.io.FileFilter; | |||
| import java.io.FileInputStream; | |||
| import java.io.FileNotFoundException; | |||
| import java.io.IOException; | |||
| import java.util.Arrays; | |||
| public class ImportVoicePreference extends DialogPreference { | |||
| private File mRoot; | |||
| private Spinner mDictionaries; | |||
| public ImportVoicePreference(Context context, AttributeSet attrs, int defStyle) { | |||
| super(context, attrs, defStyle); | |||
| setDialogLayoutResource(R.layout.import_voice_preference); | |||
| setLayoutResource(R.layout.information_view); | |||
| setPositiveButtonText(android.R.string.ok); | |||
| setNegativeButtonText(android.R.string.cancel); | |||
| mRoot = Environment.getExternalStorageDirectory(); | |||
| } | |||
| public ImportVoicePreference(Context context, AttributeSet attrs) { | |||
| this(context, attrs, 0); | |||
| } | |||
| public ImportVoicePreference(Context context) { | |||
| this(context, null); | |||
| } | |||
| public void setDescription(int resId) { | |||
| callChangeListener(getContext().getString(resId)); | |||
| } | |||
| @Override | |||
| protected View onCreateDialogView() { | |||
| View root = super.onCreateDialogView(); | |||
| mDictionaries = (Spinner)root.findViewById(R.id.dictionaries); | |||
| return root; | |||
| } | |||
| @Override | |||
| protected void onBindDialogView(View view) { | |||
| super.onBindDialogView(view); | |||
| File[] dictionaries = mRoot.listFiles(new FileFilter() { | |||
| @Override | |||
| public boolean accept(File file) { | |||
| return !file.isDirectory() && file.getName().endsWith("_dict"); | |||
| } | |||
| }); | |||
| Arrays.sort(dictionaries); | |||
| mDictionaries.setAdapter(new FileListAdapter((Activity)getContext(), dictionaries)); | |||
| } | |||
| @Override | |||
| public void onClick(DialogInterface dialog, int which) { | |||
| switch (which) { | |||
| case DialogInterface.BUTTON_POSITIVE: | |||
| new AsyncTask<Object,Object,Object>() { | |||
| @Override | |||
| protected Object doInBackground(Object... objects) { | |||
| File source = (File)mDictionaries.getSelectedItem(); | |||
| File destination = new File(CheckVoiceData.getDataPath(getContext()), source.getName()); | |||
| try { | |||
| byte[] data = FileUtils.readBinary(source); | |||
| FileUtils.write(destination, data); | |||
| } catch (IOException e) { | |||
| } | |||
| return null; | |||
| } | |||
| @Override | |||
| protected void onPostExecute(Object object) { | |||
| final Intent intent = new Intent(DownloadVoiceData.BROADCAST_LANGUAGES_UPDATED); | |||
| getContext().sendBroadcast(intent); | |||
| } | |||
| }.execute(); | |||
| break; | |||
| } | |||
| super.onClick(dialog, which); | |||
| } | |||
| } | |||