diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 995a5808f7086da9ff7e8304f5af5fb5f098b89b..714780308c07367e63af8b66bb591bb70a86acf5 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -72,11 +72,6 @@ android:name=".eSpeakActivity" android:label="@string/app_name" android:exported="true"> - - - - - diff --git a/android/src/com/reecedunn/espeak/TtsService.java b/android/src/com/reecedunn/espeak/TtsService.java index 22b9b4e39a75cbd1cc7e0d0ae974b7c777b06183..fe1f60becf830110e8c259721f8bf588415a9a13 100644 --- a/android/src/com/reecedunn/espeak/TtsService.java +++ b/android/src/com/reecedunn/espeak/TtsService.java @@ -31,6 +31,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioTrack; +import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; @@ -43,6 +44,11 @@ import android.util.Pair; import com.reecedunn.espeak.SpeechSynthesis.SynthReadyCallback; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -50,6 +56,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; /** * Implements the eSpeak engine as a {@link TextToSpeechService}. @@ -73,6 +81,9 @@ public class TtsService extends TextToSpeechService { private BroadcastReceiver mOnLanguagesDownloaded = null; + private AsyncExtract mAsyncExtract; + public static final String BROADCAST_LANGUAGES_UPDATED = "com.reecedunn.espeak.LANGUAGES_UPDATED"; + @Override public void onCreate() { storageContext = EspeakApp.getStorageContext(); @@ -88,6 +99,9 @@ public class TtsService extends TextToSpeechService { if (mOnLanguagesDownloaded != null) { unregisterReceiver(mOnLanguagesDownloaded); } + if (mAsyncExtract != null) { + mAsyncExtract.cancel(true); + } } /** @@ -136,9 +150,24 @@ public class TtsService extends TextToSpeechService { registerReceiver(mOnLanguagesDownloaded, filter); } - final Intent intent = new Intent(storageContext, DownloadVoiceData.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); + final File dataPath = CheckVoiceData.getDataPath(storageContext).getParentFile(); + + mAsyncExtract = new AsyncExtract(storageContext, R.raw.espeakdata, dataPath) { + @Override + protected void onPostExecute(Integer result) { + switch (result) { + case RESULT_OK: + final Intent intent = new Intent(DownloadVoiceData.BROADCAST_LANGUAGES_UPDATED); + sendBroadcast(intent); + break; + case RESULT_CANCELED: + // Do nothing? + break; + } + } + }; + + mAsyncExtract.execute(); return new Pair<>(null, TextToSpeech.LANG_MISSING_DATA); } @@ -346,4 +375,96 @@ public class TtsService extends TextToSpeechService { mCallback.done(); } }; + + /** + * Begin voice data download here instead of an activity. + */ + + private static final int PROGRESS_STARTING = 0; + private static final int PROGRESS_EXTRACTING = 1; + + + private static final int RESULT_OK = -1; + private static final int RESULT_CANCELED = -2; + + private static class ExtractProgress { + int total; + int progress = 0; + int state = PROGRESS_STARTING; + File file; + + public ExtractProgress(int total) { + this.total = total; + } + } + + private static class AsyncExtract extends AsyncTask { + private final Context mContext; + private final int mRawResId; + private final File mOutput; + + public AsyncExtract(Context context, int rawResId, File output) { + mContext = context; + mRawResId = rawResId; + mOutput = output; + } + + @Override + protected Integer doInBackground(Void... params) { + FileUtils.rmdir(CheckVoiceData.getDataPath(mContext)); + + final InputStream stream = mContext.getResources().openRawResource(mRawResId); + final ZipInputStream zipStream = new ZipInputStream(new BufferedInputStream(stream)); + + try { + ExtractProgress progress = new ExtractProgress(stream.available()); + progress.state = PROGRESS_EXTRACTING; + + final byte[] buffer = new byte[10240]; + + int bytesRead; + ZipEntry entry; + + while (!isCancelled() && ((entry = zipStream.getNextEntry()) != null)) { + progress.file = new File(mOutput, entry.getName()); + publishProgress(progress); + + if (entry.isDirectory()) { + progress.file.mkdirs(); + continue; + } + + // Ensure the target path exists. + progress.file.getParentFile().mkdirs(); + + final FileOutputStream outputStream = new FileOutputStream(progress.file); + try { + while (!isCancelled() && ((bytesRead = zipStream.read(buffer)) != -1)) { + outputStream.write(buffer, 0, bytesRead); + progress.total += bytesRead; + } + } finally { + outputStream.close(); + } + zipStream.closeEntry(); + } + + final String version = FileUtils.read(mContext.getResources().openRawResource(R.raw.espeakdata_version)); + final File outputFile = new File(mOutput, "espeak-ng-data/version"); + + FileUtils.write(outputFile, version); + return RESULT_OK; + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + zipStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return RESULT_CANCELED; + } + } }