Florianschmidtwelzow has uploaded a new change for review. https://gerrit.wikimedia.org/r/270557
Change subject: WIP: Implement Text to speech functionality for pages ...................................................................... WIP: Implement Text to speech functionality for pages This is an early status of the change, but works for most cases. ToDo: * Don't add a huge text variable to tts.speak(), the limit is somewhere at 4000 chars. Upstreamed from: http://gerrit.go2tech.de/r/1374 Bug: T126889 Change-Id: Idc13fc334320a0dd9ab436183161b338fc72d9a2 --- M app/src/main/java/org/wikipedia/page/PageActivity.java M app/src/main/java/org/wikipedia/page/PageFragment.java M app/src/main/res/menu/menu_page_actions.xml M app/src/main/res/values/strings.xml 4 files changed, 121 insertions(+), 1 deletion(-) git pull ssh://gerrit.wikimedia.org:29418/apps/android/wikipedia refs/changes/57/270557/1 diff --git a/app/src/main/java/org/wikipedia/page/PageActivity.java b/app/src/main/java/org/wikipedia/page/PageActivity.java index 9a03014..64b5dab 100644 --- a/app/src/main/java/org/wikipedia/page/PageActivity.java +++ b/app/src/main/java/org/wikipedia/page/PageActivity.java @@ -52,6 +52,9 @@ import android.os.Handler; import android.os.Looper; import android.preference.PreferenceManager; +import android.speech.tts.TextToSpeech; +import android.speech.tts.TextToSpeech.OnInitListener; +import android.speech.tts.UtteranceProgressListener; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.NavigationView; @@ -75,11 +78,13 @@ import android.widget.ProgressBar; import android.widget.TextView; +import java.util.Locale; + import static org.wikipedia.util.DeviceUtil.isBackKeyUp; import static org.wikipedia.util.DeviceUtil.hideSoftKeyboard; import static org.wikipedia.util.UriUtil.visitInExternalBrowser; -public class PageActivity extends ThemedActionBarActivity { +public class PageActivity extends ThemedActionBarActivity implements OnInitListener { public enum TabPosition { CURRENT_TAB, @@ -90,6 +95,7 @@ public static final int ACTIVITY_REQUEST_LANGLINKS = 0; public static final int ACTIVITY_REQUEST_EDIT_SECTION = 1; public static final int ACTIVITY_REQUEST_GALLERY = 2; + public static final int ACTIVITY_TTS_DATA_CHECK = 3; public static final int PROGRESS_BAR_MAX_VALUE = 10000; @@ -124,6 +130,7 @@ private RandomHandler randomHandler; private NavDrawerHelper navDrawerHelper; private boolean navItemSelected; + private TextToSpeech TTS; public View getContentView() { return fragmentContainerView; @@ -276,6 +283,11 @@ // Conditionally execute all recurring tasks new RecurringTasksExecutor(app).run(); + + Intent checkTTSIntent = new Intent(); + checkTTSIntent + .setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA); + startActivityForResult(checkTTSIntent, ACTIVITY_TTS_DATA_CHECK); } private void finishActionMode() { @@ -861,6 +873,16 @@ handleLoginActivityResult(resultCode); } else if (newArticleLanguageSelected(requestCode, resultCode) || galleryFilePageSelected(requestCode, resultCode)) { handleLangLinkOrFilePageResult(data); + } else if (requestCode == ACTIVITY_TTS_DATA_CHECK) { + if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) { + TTS = new TextToSpeech(this, this); + } else { + Intent installTTSIntent = new Intent(); + installTTSIntent + .setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA); + startActivity(installTTSIntent); + } + } else { super.onActivityResult(requestCode, resultCode, data); } @@ -881,6 +903,11 @@ themeChooser.dismiss(); } app.getSessionFunnel().persistSession(); + + if (TTS.isSpeaking()) { + getCurPageFragment().stopSpeechArticle(TTS); + } + super.onStop(); unregisterBus(); @@ -1004,4 +1031,40 @@ widgetIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids); sendBroadcast(widgetIntent); } + + public void onInit(int initStatus) { + final PageActivity activity = this; + + switch (initStatus) { + case TextToSpeech.ERROR: + FeedbackUtil.showMessage(this, R.string.tts_failed_unknown); + break; + case TextToSpeech.SUCCESS: + TTS.setOnUtteranceProgressListener(new UtteranceProgressListener() { + @Override + public void onDone(String utteranceId) { + getCurPageFragment().stopSpeechArticle(TTS); + } + + @Override + public void onStop(String utteranceId, boolean interrupted) { + getCurPageFragment().stopSpeechArticle(TTS); + } + + @Override + public void onError(String utteranceId) { + getCurPageFragment().stopSpeechArticle(TTS); + } + + @Override + public void onStart(String utteranceId) { + } + }); + break; + } + } + + public TextToSpeech getTTSEngine() { + return TTS; + } } diff --git a/app/src/main/java/org/wikipedia/page/PageFragment.java b/app/src/main/java/org/wikipedia/page/PageFragment.java index 0571bba..411d0d7 100755 --- a/app/src/main/java/org/wikipedia/page/PageFragment.java +++ b/app/src/main/java/org/wikipedia/page/PageFragment.java @@ -5,6 +5,8 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; +import android.speech.tts.TextToSpeech; +import android.speech.tts.UtteranceProgressListener; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; @@ -73,6 +75,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; import static butterknife.ButterKnife.findById; @@ -143,6 +146,7 @@ private ActionMode findInPageActionMode; @NonNull private ShareHandler shareHandler; private TabsProvider tabsProvider; + @NonNull private Menu mOptionsMenu; private WikipediaApp app; @@ -633,6 +637,7 @@ @Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); + mOptionsMenu = menu; if (!isAdded() || getPageActivity().isSearching()) { return; } @@ -695,8 +700,45 @@ case R.id.menu_page_show_tabs: tabsProvider.enterTabMode(); return true; + case R.id.menu_page_tts: + speechArticle(); + return true; default: return super.onOptionsItemSelected(item); + } + } + + public void stopSpeechArticle(TextToSpeech tts) { + MenuItem speechArticle = mOptionsMenu.findItem(R.id.menu_page_tts); + tts.stop(); + speechArticle.setTitle(R.string.tts_read_aloud); + } + + private void speechArticle() { + TextToSpeech tts = getPageActivity().getTTSEngine(); + MenuItem speechArticle = mOptionsMenu.findItem(R.id.menu_page_tts); + if (tts.isSpeaking()) { + stopSpeechArticle(tts); + } else { + speechArticle.setTitle(R.string.tts_stop_read_aloud); + Locale lang = Locale.forLanguageTag(getTitle().getSite().getLanguageCode()); + if (tts.isLanguageAvailable(lang) == TextToSpeech.LANG_AVAILABLE) { + tts.setLanguage(lang); + L.d("Set TTS language to: " + lang.getDisplayLanguage()); + } else { + FeedbackUtil.showMessage(getActivity(), R.string.tts_lang_not_available); + return; + } + + String text = Html.fromHtml(getPage().getDisplayTitle()).toString(); + List<Section> sections = getPage().getSections(); + for (Section section : sections) { + text += ". " + Html.fromHtml(section.getHeading() + " " + section.getContent()).toString(); + } + + Bundle params = new Bundle(); + params.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, getPage().getDisplayTitle()); + tts.speak(text, TextToSpeech.QUEUE_ADD, params, getPage().getDisplayTitle()); } } @@ -919,10 +961,14 @@ private void updateMenuPageInfo(@NonNull Menu menu) { MenuItem contentIssues = menu.findItem(R.id.menu_page_content_issues); MenuItem similarTitles = menu.findItem(R.id.menu_page_similar_titles); + MenuItem speechArticle = menu.findItem(R.id.menu_page_tts); contentIssues.setVisible(pageInfo != null && pageInfo.hasContentIssues()); contentIssues.setEnabled(true); similarTitles.setVisible(pageInfo != null && pageInfo.hasSimilarTitles()); similarTitles.setEnabled(true); + Locale lang = Locale.forLanguageTag(getTitle().getSite().getLanguageCode()); + speechArticle.setVisible(getPageActivity().getTTSEngine().isLanguageAvailable(lang) == TextToSpeech.LANG_AVAILABLE); + speechArticle.setEnabled(true); } private void showContentIssues() { diff --git a/app/src/main/res/menu/menu_page_actions.xml b/app/src/main/res/menu/menu_page_actions.xml index 62238e9..cb23af3 100644 --- a/app/src/main/res/menu/menu_page_actions.xml +++ b/app/src/main/res/menu/menu_page_actions.xml @@ -23,6 +23,10 @@ android:title="@string/menu_page_similar_titles" app:showAsAction="never" android:enabled="false" /> + <item android:id="@+id/menu_page_tts" + android:title="@string/tts_read_aloud" + app:showAsAction="never" + android:enabled="false" /> <item android:id="@+id/menu_page_font_and_theme" android:title="@string/menu_page_font_and_theme" app:showAsAction="never" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6f03757..1291ae7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -305,4 +305,11 @@ <string name="article_menu_bar_share">Share the article link</string> <string name="article_menu_bar_navigate">Navigate to the location of the article</string> <!-- /Article menu bar --> + + <!-- Text-to-Speech --> + <string name="tts_failed_unknown">The initialization of the Text-to-Speech engine failed.</string> + <string name="tts_lang_not_available">The Text-to-Speech engine does not provide the language the article is written in.</string> + <string name="tts_read_aloud">Read aloud (Text-to-Speech)</string> + <string name="tts_stop_read_aloud">Stop reading aloud...</string> + <!-- /Text-to-Speech --> </resources> -- To view, visit https://gerrit.wikimedia.org/r/270557 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Idc13fc334320a0dd9ab436183161b338fc72d9a2 Gerrit-PatchSet: 1 Gerrit-Project: apps/android/wikipedia Gerrit-Branch: master Gerrit-Owner: Florianschmidtwelzow <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
