Merge branch 'master' of ssh://github.com/osmandapp/Osmand into MyLocationSharingMode
This commit is contained in:
commit
7dc01155f5
32 changed files with 1444 additions and 127 deletions
|
@ -34,7 +34,8 @@ public class IndexConstants {
|
||||||
public static final String GEN_LOG_EXT = ".gen.log"; //$NON-NLS-1$
|
public static final String GEN_LOG_EXT = ".gen.log"; //$NON-NLS-1$
|
||||||
|
|
||||||
public static final String VOICE_INDEX_EXT_ZIP = ".voice.zip"; //$NON-NLS-1$
|
public static final String VOICE_INDEX_EXT_ZIP = ".voice.zip"; //$NON-NLS-1$
|
||||||
public static final String TTSVOICE_INDEX_EXT_ZIP = ".ttsvoice.zip"; //$NON-NLS-1$
|
public static final String TTSVOICE_INDEX_EXT_ZIP = ".ttsvoice.zip";
|
||||||
|
public static final String TTSVOICE_INDEX_EXT_JS = "tts.js";
|
||||||
public static final String ANYVOICE_INDEX_EXT_ZIP = "voice.zip"; //$NON-NLS-1$ //to cactch both voices, .voice.zip and .ttsvoice.zip
|
public static final String ANYVOICE_INDEX_EXT_ZIP = "voice.zip"; //$NON-NLS-1$ //to cactch both voices, .voice.zip and .ttsvoice.zip
|
||||||
|
|
||||||
public static final String FONT_INDEX_EXT = ".otf"; //$NON-NLS-1$
|
public static final String FONT_INDEX_EXT = ".otf"; //$NON-NLS-1$
|
||||||
|
@ -67,9 +68,4 @@ public class IndexConstants {
|
||||||
public static final String VOICE_INDEX_DIR = "voice/"; //$NON-NLS-1$
|
public static final String VOICE_INDEX_DIR = "voice/"; //$NON-NLS-1$
|
||||||
public static final String RENDERERS_DIR = "rendering/"; //$NON-NLS-1$
|
public static final String RENDERERS_DIR = "rendering/"; //$NON-NLS-1$
|
||||||
public static final String ROUTING_XML_FILE= "routing.xml";
|
public static final String ROUTING_XML_FILE= "routing.xml";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,9 +113,9 @@ object AndroidUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isGooglePlayInstalled(ctx: Context): Boolean {
|
fun isAppInstalled(ctx: Context, appPackage: String): Boolean {
|
||||||
try {
|
try {
|
||||||
ctx.packageManager.getPackageInfo("com.android.vending", 0)
|
ctx.packageManager.getPackageInfo(appPackage, 0)
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ object AndroidUtils {
|
||||||
Intent(Intent.ACTION_VIEW, Uri.parse(AndroidUtils.getPlayMarketLink(ctx, packageName)))
|
Intent(Intent.ACTION_VIEW, Uri.parse(AndroidUtils.getPlayMarketLink(ctx, packageName)))
|
||||||
|
|
||||||
fun getPlayMarketLink(ctx: Context, packageName: String): String {
|
fun getPlayMarketLink(ctx: Context, packageName: String): String {
|
||||||
if (isGooglePlayInstalled(ctx)) {
|
if (isAppInstalled(ctx, "com.android.vending")) {
|
||||||
return "market://details?id=$packageName"
|
return "market://details?id=$packageName"
|
||||||
}
|
}
|
||||||
return "https://play.google.com/store/apps/details?id=$packageName"
|
return "https://play.google.com/store/apps/details?id=$packageName"
|
||||||
|
|
|
@ -43,6 +43,45 @@
|
||||||
<asset source="voice/zh/ttsconfig.p" destination="voice/zh-tts/_ttsconfig.p" mode="alwaysOverwriteOrCopy" />
|
<asset source="voice/zh/ttsconfig.p" destination="voice/zh-tts/_ttsconfig.p" mode="alwaysOverwriteOrCopy" />
|
||||||
<asset source="voice/zh-hk/ttsconfig.p" destination="voice/zh-hk-tts/_ttsconfig.p" mode="overwriteOnlyIfExists" />
|
<asset source="voice/zh-hk/ttsconfig.p" destination="voice/zh-hk-tts/_ttsconfig.p" mode="overwriteOnlyIfExists" />
|
||||||
|
|
||||||
|
<!--JavaScript files-->
|
||||||
|
<!--<asset source="voice/ar/ar_tts.js" destination="voice/ar-tts/ar_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/be/be_tts.js" destination="voice/be-tts/be_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/cs/cs_tts.js" destination="voice/cs-tts/cs_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/da/da_tts.js" destination="voice/da-tts/da_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<asset source="voice/de/de_tts.js" destination="voice/de-tts/de_tts.js" mode="alwaysOverwriteOrCopy" />
|
||||||
|
<!--<asset source="voice/el/el_tts.js" destination="voice/el-tts/el_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<asset source="voice/en/en_tts.js" destination="voice/en-tts/en_tts.js" mode="alwaysOverwriteOrCopy" />
|
||||||
|
<!--<asset source="voice/en-gb/en-gb_tts.js" destination="voice/en-gb-tts/en-gb_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<asset source="voice/es/es_tts.js" destination="voice/es-tts/es_tts.js" mode="alwaysOverwriteOrCopy" />
|
||||||
|
<!--<asset source="voice/es-ar/es-ar_tts.js" destination="voice/es-ar-tts/es-ar_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/et/et_tts.js" destination="voice/et-tts/et_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/fa/fa_tts.js" destination="voice/fa-tts/fa_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/fi/fi_tts.js" destination="voice/fi-tts/fi_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<asset source="voice/fr/fr_tts.js" destination="voice/fr-tts/fr_tts.js" mode="alwaysOverwriteOrCopy" />
|
||||||
|
<!--<asset source="voice/he/he_tts.js" destination="voice/he-tts/he_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/hi/hi_tts.js" destination="voice/hi-tts/hi_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/hr/hr_tts.js" destination="voice/hr-tts/hr_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<asset source="voice/hu/hu_tts.js" destination="voice/hu-tts/hu_tts.js" mode="overwriteOnlyIfExists" />
|
||||||
|
<!--<asset source="voice/hu-formal/hu-formal_tts.js" destination="voice/hu_formal-tts/hu-formal_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<asset source="voice/it/it_tts.js" destination="voice/it-tts/it_tts.js" mode="alwaysOverwriteOrCopy" />
|
||||||
|
<!--<asset source="voice/ja/ja_tts.js" destination="voice/ja-tts/ja_tts.js" mode="alwaysOverwriteOrCopy" />-->
|
||||||
|
<!--<asset source="voice/ko/ko_tts.js" destination="voice/ko-tts/ko_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/lv/lv_tts.js" destination="voice/lv-tts/lv_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<asset source="voice/nl/nl_tts.js" destination="voice/nl-tts/nl_tts.js" mode="alwaysOverwriteOrCopy" />
|
||||||
|
<asset source="voice/pl/pl_tts.js" destination="voice/pl-tts/pl_tts.js" mode="alwaysOverwriteOrCopy" />
|
||||||
|
<asset source="voice/pt/pt_tts.js" destination="voice/pt-tts/pt_tts.js" mode="alwaysOverwriteOrCopy" />
|
||||||
|
<!--<asset source="voice/pt-br/pt-br_tts.js" destination="voice/pt-br-tts/pt-br_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/ro/ro_tts.js" destination="voice/ro-tts/ro_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<asset source="voice/ru/ru_tts.js" destination="voice/ru-tts/ru_tts.js" mode="alwaysOverwriteOrCopy" />
|
||||||
|
<!--<asset source="voice/sc/sc_tts.js" destination="voice/sc-tts/sc_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/sk/sk_tts.js" destination="voice/sk-tts/sk_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/sl/sl_tts.js" destination="voice/sl-tts/sl_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/sv/sv_tts.js" destination="voice/sv-tts/sv_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/sw/sw_tts.js" destination="voice/sw-tts/sw_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/uk/uk_tts.js" destination="voice/uk-tts/uk_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
<!--<asset source="voice/zh/zh_tts.js" destination="voice/zh-tts/zh_tts.js" mode="alwaysOverwriteOrCopy" />-->
|
||||||
|
<!--<asset source="voice/zh-hk/zh-hk_tts.js" destination="voice/zh-hk-tts/zh-hk_tts.js" mode="overwriteOnlyIfExists" />-->
|
||||||
|
|
||||||
<asset source="voice/de/config.p" destination="voice/de/_config.p" mode="overwriteOnlyIfExists" />
|
<asset source="voice/de/config.p" destination="voice/de/_config.p" mode="overwriteOnlyIfExists" />
|
||||||
<asset source="voice/en/config.p" destination="voice/en/_config.p" mode="overwriteOnlyIfExists" />
|
<asset source="voice/en/config.p" destination="voice/en/_config.p" mode="overwriteOnlyIfExists" />
|
||||||
<asset source="voice/es/config.p" destination="voice/es/_config.p" mode="overwriteOnlyIfExists" />
|
<asset source="voice/es/config.p" destination="voice/es/_config.p" mode="overwriteOnlyIfExists" />
|
||||||
|
|
|
@ -238,6 +238,7 @@ task collectVoiceAssets(type: Sync) {
|
||||||
from "../../resources/voice"
|
from "../../resources/voice"
|
||||||
into "assets/voice"
|
into "assets/voice"
|
||||||
include "**/*.p"
|
include "**/*.p"
|
||||||
|
include "**/*.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
task cleanNoTranslate(type: Delete) {
|
task cleanNoTranslate(type: Delete) {
|
||||||
|
@ -361,6 +362,9 @@ dependencies {
|
||||||
implementation 'com.vividsolutions:jts-core:1.14.0'
|
implementation 'com.vividsolutions:jts-core:1.14.0'
|
||||||
|
|
||||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||||
|
// JS core
|
||||||
|
implementation group: 'org.mozilla', name: 'rhino', version: '1.7.9'
|
||||||
|
|
||||||
// size restrictions
|
// size restrictions
|
||||||
// implementation 'com.ibm.icu:icu4j:50.1'
|
// implementation 'com.ibm.icu:icu4j:50.1'
|
||||||
// implementation 'net.sf.trove4j:trove4j:3.0.3'
|
// implementation 'net.sf.trove4j:trove4j:3.0.3'
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="5 selected"
|
tools:text="5 selected"
|
||||||
android:textColor="@color/color_white"
|
android:textColor="@color/color_white"
|
||||||
android:textSize="@dimen/default_list_text_size_large"/>
|
android:textSize="@dimen/default_list_text_size_large"/>
|
||||||
|
|
||||||
|
@ -264,7 +264,7 @@
|
||||||
android:paddingLeft="@dimen/context_menu_padding_margin_small"
|
android:paddingLeft="@dimen/context_menu_padding_margin_small"
|
||||||
android:paddingRight="@dimen/context_menu_padding_margin_small"
|
android:paddingRight="@dimen/context_menu_padding_margin_small"
|
||||||
android:paddingTop="@dimen/context_menu_padding_margin_small"
|
android:paddingTop="@dimen/context_menu_padding_margin_small"
|
||||||
android:text="Отправить"
|
android:text="@string/shared_string_send"
|
||||||
android:textColor="?attr/wikivoyage_active_color"
|
android:textColor="?attr/wikivoyage_active_color"
|
||||||
android:textSize="@dimen/default_desc_text_size"
|
android:textSize="@dimen/default_desc_text_size"
|
||||||
osmand:typeface="@string/font_roboto_medium" />
|
osmand:typeface="@string/font_roboto_medium" />
|
||||||
|
|
|
@ -3763,6 +3763,8 @@
|
||||||
<string name="poi_payment_centre">Platební centrum</string>
|
<string name="poi_payment_centre">Platební centrum</string>
|
||||||
<string name="poi_money_transfer">Služba převodu peněz</string>
|
<string name="poi_money_transfer">Služba převodu peněz</string>
|
||||||
|
|
||||||
<string name="poi_climbing_sport_yes">Sport: Ano</string>
|
<string name="poi_climbing_sport_yes">Sportovní horolezectví: Ano</string>
|
||||||
<string name="poi_climbing_sport_no">Sport: Ne</string>
|
<string name="poi_climbing_sport_no">Sportovní horolezectví: Ne</string>
|
||||||
|
<string name="poi_glacier_type_icecap">Ledovec typu ledová čepice</string>
|
||||||
|
<string name="poi_glacier_type_icefield">Ledovec typu ledové pole</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -3707,7 +3707,7 @@
|
||||||
<string name="poi_post_flats">Piso de oficina</string>
|
<string name="poi_post_flats">Piso de oficina</string>
|
||||||
|
|
||||||
<string name="poi_payment_centre">Centro de pagos</string>
|
<string name="poi_payment_centre">Centro de pagos</string>
|
||||||
<string name="poi_money_transfer">Transferencia de dinero</string>
|
<string name="poi_money_transfer">Transferencia de dinero;Giros de dinero</string>
|
||||||
|
|
||||||
<string name="poi_route_subway_ref">Subte</string>
|
<string name="poi_route_subway_ref">Subte</string>
|
||||||
|
|
||||||
|
|
|
@ -3469,7 +3469,7 @@
|
||||||
<string name="poi_post_flats">Piso de oficina</string>
|
<string name="poi_post_flats">Piso de oficina</string>
|
||||||
|
|
||||||
<string name="poi_payment_centre">Centro de pagos</string>
|
<string name="poi_payment_centre">Centro de pagos</string>
|
||||||
<string name="poi_money_transfer">Transferencia de dinero</string>
|
<string name="poi_money_transfer">Transferencia de dinero; Giros de dinero</string>
|
||||||
|
|
||||||
<string name="poi_route_subway_ref">Metro</string>
|
<string name="poi_route_subway_ref">Metro</string>
|
||||||
|
|
||||||
|
|
|
@ -2956,4 +2956,9 @@ Stendur fyrir svæði: %1$s x %2$s</string>
|
||||||
<string name="search_no_results_description">Engar niðurstöður?
|
<string name="search_no_results_description">Engar niðurstöður?
|
||||||
\nSegðu okkur meira um þetta.</string>
|
\nSegðu okkur meira um þetta.</string>
|
||||||
<string name="send_search_query">Senda leitarfyrirspurn?</string>
|
<string name="send_search_query">Senda leitarfyrirspurn?</string>
|
||||||
|
<string name="thank_you_for_feedback">Takk fyrir umsögn þína</string>
|
||||||
|
<string name="poi_cannot_be_found">Hnútur eða leið fannst ekki.</string>
|
||||||
|
<string name="search_no_results_feedback">Engar leitarniðurstöður?
|
||||||
|
\nLáttu okkur vita</string>
|
||||||
|
<string name="commiting_way">Sendi leið inn…</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -2961,7 +2961,7 @@ OsmAndとOSMをサポートする方法として現状最適な方法である
|
||||||
<string name="add_track_to_markers_descr">マーカーとして追加する経由地点を経路データから選択してください。(経由地点を含む経路のみリストアップされます)</string>
|
<string name="add_track_to_markers_descr">マーカーとして追加する経由地点を経路データから選択してください。(経由地点を含む経路のみリストアップされます)</string>
|
||||||
<string name="add_favourites_group_to_markers_descr">マーカーとして追加するお気に入りのカテゴリを選択してください。</string>
|
<string name="add_favourites_group_to_markers_descr">マーカーとして追加するお気に入りのカテゴリを選択してください。</string>
|
||||||
|
|
||||||
<string name="add_group_descr">お気に入り地点や経路データ内の経由地点をまとめてインポートできます。</string>
|
<string name="add_group_descr">お気に入り地点やGPX経由地点をグループとしてインポートできます。</string>
|
||||||
<string name="empty_state_markers_groups">グループでインポート</string>
|
<string name="empty_state_markers_groups">グループでインポート</string>
|
||||||
<string name="empty_state_markers_groups_desc">お気に入り地点や経路データ内の経由地点をマーカーとしてまとめて追加できます。</string>
|
<string name="empty_state_markers_groups_desc">お気に入り地点や経路データ内の経由地点をマーカーとしてまとめて追加できます。</string>
|
||||||
<string name="shared_string_two">二つ</string>
|
<string name="shared_string_two">二つ</string>
|
||||||
|
@ -3016,7 +3016,7 @@ OsmAndとOSMをサポートする方法として現状最適な方法である
|
||||||
<string name="add_group">グループの追加</string>
|
<string name="add_group">グループの追加</string>
|
||||||
<string name="empty_state_osm_edits_descr">OSM編集はPOIの作成や更新、注釈の追加などが可能です。記録したGPXファイルを用いてあなたもOSMに貢献することができるでしょう。</string>
|
<string name="empty_state_osm_edits_descr">OSM編集はPOIの作成や更新、注釈の追加などが可能です。記録したGPXファイルを用いてあなたもOSMに貢献することができるでしょう。</string>
|
||||||
<string name="mark_passed">通過済みにする</string>
|
<string name="mark_passed">通過済みにする</string>
|
||||||
<string name="add_segment_to_the_track">GPX経路に追加</string>
|
<string name="add_segment_to_the_track">GPXファイルに追加</string>
|
||||||
<string name="shared_string_current">現在</string>
|
<string name="shared_string_current">現在</string>
|
||||||
<string name="shared_string_actions">その他</string>
|
<string name="shared_string_actions">その他</string>
|
||||||
|
|
||||||
|
@ -3050,4 +3050,8 @@ OsmAndとOSMをサポートする方法として現状最適な方法である
|
||||||
<string name="default_render_descr">標準的な汎用スタイルです。人口密集地の場合描写の簡素化がなされます。主な機能としては等高線、ルート、路面品質、通行制限、道路標識、SACスケールの通路描写をサポートし、急流下りなどのウォータースポーツにも使用できます。</string>
|
<string name="default_render_descr">標準的な汎用スタイルです。人口密集地の場合描写の簡素化がなされます。主な機能としては等高線、ルート、路面品質、通行制限、道路標識、SACスケールの通路描写をサポートし、急流下りなどのウォータースポーツにも使用できます。</string>
|
||||||
<string name="open_wikipedia_link_online">Wikipediaをオンライン参照</string>
|
<string name="open_wikipedia_link_online">Wikipediaをオンライン参照</string>
|
||||||
<string name="open_wikipedia_link_online_description">このリンクを開くとウェブブラウザで閲覧することができます。</string>
|
<string name="open_wikipedia_link_online_description">このリンクを開くとウェブブラウザで閲覧することができます。</string>
|
||||||
|
<string name="import_as_gpx">GPXファイルとしてインポート</string>
|
||||||
|
<string name="empty_state_my_tracks">GPXファイルに追加しよう</string>
|
||||||
|
<string name="empty_state_my_tracks_desc">GPXファイルまたは記録した経路からインポートします。</string>
|
||||||
|
<string name="import_track">GPXファイルからインポート</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -2931,4 +2931,5 @@
|
||||||
<string name="wiki_article_search_text">Searching for the corresponding wiki article</string>
|
<string name="wiki_article_search_text">Searching for the corresponding wiki article</string>
|
||||||
<string name="wiki_article_not_found">Article not found</string>
|
<string name="wiki_article_not_found">Article not found</string>
|
||||||
<string name="how_to_open_wiki_title">How to open Wikipedia articles?</string>
|
<string name="how_to_open_wiki_title">How to open Wikipedia articles?</string>
|
||||||
|
<string name="test_voice_desrc">Tap a button and listen to the corresponding voice prompt to identify missing or faulty prompts.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -46,6 +46,8 @@ import net.osmand.plus.search.QuickSearchHelper;
|
||||||
import net.osmand.plus.views.corenative.NativeCoreContext;
|
import net.osmand.plus.views.corenative.NativeCoreContext;
|
||||||
import net.osmand.plus.voice.CommandPlayer;
|
import net.osmand.plus.voice.CommandPlayer;
|
||||||
import net.osmand.plus.voice.CommandPlayerException;
|
import net.osmand.plus.voice.CommandPlayerException;
|
||||||
|
import net.osmand.plus.voice.JSMediaCommandPlayerImpl;
|
||||||
|
import net.osmand.plus.voice.JSTTSCommandPlayerImpl;
|
||||||
import net.osmand.plus.voice.MediaCommandPlayerImpl;
|
import net.osmand.plus.voice.MediaCommandPlayerImpl;
|
||||||
import net.osmand.plus.voice.TTSCommandPlayerImpl;
|
import net.osmand.plus.voice.TTSCommandPlayerImpl;
|
||||||
import net.osmand.plus.wikivoyage.data.TravelDbHelper;
|
import net.osmand.plus.wikivoyage.data.TravelDbHelper;
|
||||||
|
@ -577,9 +579,12 @@ public class AppInitializer implements IProgress {
|
||||||
if (!voiceDir.exists()) {
|
if (!voiceDir.exists()) {
|
||||||
throw new CommandPlayerException(ctx.getString(R.string.voice_data_unavailable));
|
throw new CommandPlayerException(ctx.getString(R.string.voice_data_unavailable));
|
||||||
}
|
}
|
||||||
|
boolean useJs = app.getSettings().USE_JS_VOICE_GUIDANCE.get();
|
||||||
if (MediaCommandPlayerImpl.isMyData(voiceDir)) {
|
if (useJs && JSTTSCommandPlayerImpl.isMyData(voiceDir)) {
|
||||||
return new MediaCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider);
|
return new JSTTSCommandPlayerImpl(ctx, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider);
|
||||||
|
} else if (MediaCommandPlayerImpl.isMyData(voiceDir)) {
|
||||||
|
return useJs && JSMediaCommandPlayerImpl.isMyData(voiceDir) ? new JSMediaCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider)
|
||||||
|
: new MediaCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider);
|
||||||
} else if (TTSCommandPlayerImpl.isMyData(voiceDir)) {
|
} else if (TTSCommandPlayerImpl.isMyData(voiceDir)) {
|
||||||
return new TTSCommandPlayerImpl(ctx, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider);
|
return new TTSCommandPlayerImpl(ctx, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1346,6 +1346,7 @@ public class OsmandSettings {
|
||||||
|
|
||||||
public final OsmandPreference<Boolean> ANIMATE_MY_LOCATION = new BooleanPreference("animate_my_location", true).makeGlobal().cache();
|
public final OsmandPreference<Boolean> ANIMATE_MY_LOCATION = new BooleanPreference("animate_my_location", true).makeGlobal().cache();
|
||||||
|
|
||||||
|
public final OsmandPreference<Boolean> USE_JS_VOICE_GUIDANCE = new BooleanPreference("use_js_voice_guidance", false);
|
||||||
public final OsmandPreference<Boolean> ROUTE_MAP_MARKERS_START_MY_LOC = new BooleanPreference("route_map_markers_start_my_loc", false).makeGlobal().cache();
|
public final OsmandPreference<Boolean> ROUTE_MAP_MARKERS_START_MY_LOC = new BooleanPreference("route_map_markers_start_my_loc", false).makeGlobal().cache();
|
||||||
public final OsmandPreference<Boolean> ROUTE_MAP_MARKERS_ROUND_TRIP = new BooleanPreference("route_map_markers_round_trip", false).makeGlobal().cache();
|
public final OsmandPreference<Boolean> ROUTE_MAP_MARKERS_ROUND_TRIP = new BooleanPreference("route_map_markers_round_trip", false).makeGlobal().cache();
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.SQLiteTileSource;
|
import net.osmand.plus.SQLiteTileSource;
|
||||||
import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask;
|
import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask;
|
||||||
|
import net.osmand.plus.voice.JSTTSCommandPlayerImpl;
|
||||||
import net.osmand.plus.voice.MediaCommandPlayerImpl;
|
import net.osmand.plus.voice.MediaCommandPlayerImpl;
|
||||||
import net.osmand.plus.voice.TTSCommandPlayerImpl;
|
import net.osmand.plus.voice.TTSCommandPlayerImpl;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
@ -215,11 +216,12 @@ public class LocalIndexHelper {
|
||||||
|
|
||||||
private void loadVoiceData(File voiceDir, List<LocalIndexInfo> result, boolean backup, AbstractLoadLocalIndexTask loadTask) {
|
private void loadVoiceData(File voiceDir, List<LocalIndexInfo> result, boolean backup, AbstractLoadLocalIndexTask loadTask) {
|
||||||
if (voiceDir.canRead()) {
|
if (voiceDir.canRead()) {
|
||||||
|
boolean useJs = app.getSettings().USE_JS_VOICE_GUIDANCE.get();
|
||||||
//First list TTS files, they are preferred
|
//First list TTS files, they are preferred
|
||||||
for (File voiceF : listFilesSorted(voiceDir)) {
|
for (File voiceF : listFilesSorted(voiceDir)) {
|
||||||
if (voiceF.isDirectory() && !MediaCommandPlayerImpl.isMyData(voiceF) && (Build.VERSION.SDK_INT >= 4)) {
|
if (voiceF.isDirectory() && !MediaCommandPlayerImpl.isMyData(voiceF)) {
|
||||||
LocalIndexInfo info = null;
|
LocalIndexInfo info = null;
|
||||||
if (TTSCommandPlayerImpl.isMyData(voiceF)) {
|
if ((TTSCommandPlayerImpl.isMyData(voiceF) && !useJs) || (JSTTSCommandPlayerImpl.isMyData(voiceF) && useJs)) {
|
||||||
info = new LocalIndexInfo(LocalIndexType.TTS_VOICE_DATA, voiceF, backup, app);
|
info = new LocalIndexInfo(LocalIndexType.TTS_VOICE_DATA, voiceF, backup, app);
|
||||||
}
|
}
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
|
|
|
@ -52,6 +52,8 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static net.osmand.plus.mapcontextmenu.other.RoutePreferencesMenu.getVoiceFiles;
|
||||||
|
|
||||||
public class SettingsNavigationActivity extends SettingsBaseActivity {
|
public class SettingsNavigationActivity extends SettingsBaseActivity {
|
||||||
|
|
||||||
public static final String MORE_VALUE = "MORE_VALUE";
|
public static final String MORE_VALUE = "MORE_VALUE";
|
||||||
|
@ -244,7 +246,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity {
|
||||||
private void reloadVoiceListPreference(PreferenceScreen screen) {
|
private void reloadVoiceListPreference(PreferenceScreen screen) {
|
||||||
String[] entries;
|
String[] entries;
|
||||||
String[] entrieValues;
|
String[] entrieValues;
|
||||||
Set<String> voiceFiles = getVoiceFiles();
|
Set<String> voiceFiles = getVoiceFiles(this);
|
||||||
entries = new String[voiceFiles.size() + 2];
|
entries = new String[voiceFiles.size() + 2];
|
||||||
entrieValues = new String[voiceFiles.size() + 2];
|
entrieValues = new String[voiceFiles.size() + 2];
|
||||||
int k = 0;
|
int k = 0;
|
||||||
|
@ -263,20 +265,6 @@ public class SettingsNavigationActivity extends SettingsBaseActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Set<String> getVoiceFiles() {
|
|
||||||
// read available voice data
|
|
||||||
File extStorage = getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR);
|
|
||||||
Set<String> setFiles = new LinkedHashSet<String>();
|
|
||||||
if (extStorage.exists()) {
|
|
||||||
for (File f : extStorage.listFiles()) {
|
|
||||||
if (f.isDirectory()) {
|
|
||||||
setFiles.add(f.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return setFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addVoicePrefs(PreferenceGroup cat) {
|
private void addVoicePrefs(PreferenceGroup cat) {
|
||||||
if (!Version.isBlackberry((OsmandApplication) getApplication())) {
|
if (!Version.isBlackberry((OsmandApplication) getApplication())) {
|
||||||
ListPreference lp = createListPreference(
|
ListPreference lp = createListPreference(
|
||||||
|
|
|
@ -14,6 +14,7 @@ import android.preference.PreferenceScreen;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import net.osmand.StateChangedListener;
|
||||||
import net.osmand.plus.ApplicationMode;
|
import net.osmand.plus.ApplicationMode;
|
||||||
import net.osmand.plus.OsmAndLocationSimulation;
|
import net.osmand.plus.OsmAndLocationSimulation;
|
||||||
import net.osmand.plus.OsmandApplication;
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
@ -32,6 +33,7 @@ import java.util.Set;
|
||||||
|
|
||||||
public class SettingsDevelopmentActivity extends SettingsBaseActivity {
|
public class SettingsDevelopmentActivity extends SettingsBaseActivity {
|
||||||
|
|
||||||
|
private StateChangedListener<Boolean> useJSVoiceGuidanceListener;
|
||||||
|
|
||||||
@SuppressLint("SimpleDateFormat")
|
@SuppressLint("SimpleDateFormat")
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,6 +64,15 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity {
|
||||||
cat.addPreference(createCheckBoxPreference(settings.ANIMATE_MY_LOCATION,
|
cat.addPreference(createCheckBoxPreference(settings.ANIMATE_MY_LOCATION,
|
||||||
R.string.animate_my_location,
|
R.string.animate_my_location,
|
||||||
R.string.animate_my_location_desc));
|
R.string.animate_my_location_desc));
|
||||||
|
useJSVoiceGuidanceListener = new StateChangedListener<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(Boolean change) {
|
||||||
|
getMyApplication().getDownloadThread().runReloadIndexFilesSilent();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cat.addPreference(createCheckBoxPreference(settings.USE_JS_VOICE_GUIDANCE, "Use JS voice guidance",
|
||||||
|
"Use new voice guidance logic based on JavaScript"));
|
||||||
|
settings.USE_JS_VOICE_GUIDANCE.addListener(useJSVoiceGuidanceListener);
|
||||||
|
|
||||||
final Preference firstRunPreference = new Preference(this);
|
final Preference firstRunPreference = new Preference(this);
|
||||||
firstRunPreference.setTitle(R.string.simulate_initial_startup);
|
firstRunPreference.setTitle(R.string.simulate_initial_startup);
|
||||||
|
@ -249,6 +260,9 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity {
|
||||||
b.show();
|
b.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
settings.USE_JS_VOICE_GUIDANCE.removeListener(useJSVoiceGuidanceListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,9 @@ import net.osmand.plus.OsmandApplication;
|
||||||
import net.osmand.plus.R;
|
import net.osmand.plus.R;
|
||||||
import net.osmand.plus.activities.OsmandActionBarActivity;
|
import net.osmand.plus.activities.OsmandActionBarActivity;
|
||||||
import net.osmand.plus.voice.AbstractPrologCommandPlayer;
|
import net.osmand.plus.voice.AbstractPrologCommandPlayer;
|
||||||
|
import net.osmand.plus.voice.JSCommandBuilder;
|
||||||
|
import net.osmand.plus.voice.JSMediaCommandPlayerImpl;
|
||||||
|
import net.osmand.plus.voice.JSTTSCommandPlayerImpl;
|
||||||
import net.osmand.plus.voice.TTSCommandPlayerImpl;
|
import net.osmand.plus.voice.TTSCommandPlayerImpl;
|
||||||
import net.osmand.plus.voice.CommandBuilder;
|
import net.osmand.plus.voice.CommandBuilder;
|
||||||
import net.osmand.plus.voice.CommandPlayer;
|
import net.osmand.plus.voice.CommandPlayer;
|
||||||
|
@ -29,12 +32,16 @@ import net.osmand.plus.routing.VoiceRouter;
|
||||||
import net.osmand.util.Algorithms;
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import alice.tuprolog.Struct;
|
import alice.tuprolog.Struct;
|
||||||
import alice.tuprolog.Term;
|
import alice.tuprolog.Term;
|
||||||
|
|
||||||
|
import static net.osmand.plus.mapcontextmenu.other.RoutePreferencesMenu.getVoiceFiles;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test Voice activity
|
* Test Voice activity
|
||||||
|
@ -62,7 +69,7 @@ public class TestVoiceActivity extends OsmandActionBarActivity {
|
||||||
gl.setPadding(3, 3, 3, 3);
|
gl.setPadding(3, 3, 3, 3);
|
||||||
|
|
||||||
TextView tv = new TextView(this);
|
TextView tv = new TextView(this);
|
||||||
tv.setText("Tap a button and listen to the corresponding voice prompt to identify missing or faulty propmts.");
|
tv.setText(R.string.test_voice_desrc);
|
||||||
tv.setPadding(0, 5, 0, 7);
|
tv.setPadding(0, 5, 0, 7);
|
||||||
|
|
||||||
ScrollView sv = new ScrollView(this);
|
ScrollView sv = new ScrollView(this);
|
||||||
|
@ -75,28 +82,15 @@ public class TestVoiceActivity extends OsmandActionBarActivity {
|
||||||
|
|
||||||
// add buttons
|
// add buttons
|
||||||
setContentView(gl);
|
setContentView(gl);
|
||||||
getSupportActionBar(). setTitle(R.string.test_voice_prompts);
|
getSupportActionBar().setTitle(R.string.test_voice_prompts);
|
||||||
|
|
||||||
selectVoice(ll);
|
selectVoice(ll);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getVoiceFiles() {
|
|
||||||
// read available voice data
|
|
||||||
File extStorage = ((OsmandApplication) getApplication()).getAppPath(IndexConstants.VOICE_INDEX_DIR);
|
|
||||||
Set<String> setFiles = new LinkedHashSet<String>();
|
|
||||||
if (extStorage.exists()) {
|
|
||||||
for (File f : extStorage.listFiles()) {
|
|
||||||
if (f.isDirectory()) {
|
|
||||||
setFiles.add(f.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return setFiles;
|
|
||||||
}
|
|
||||||
private void selectVoice(final LinearLayout ll) {
|
private void selectVoice(final LinearLayout ll) {
|
||||||
String[] entries;
|
String[] entries;
|
||||||
final String[] entrieValues;
|
final String[] entrieValues;
|
||||||
Set<String> voiceFiles = getVoiceFiles();
|
Set<String> voiceFiles = getVoiceFiles(this);
|
||||||
entries = new String[voiceFiles.size() ];
|
entries = new String[voiceFiles.size() ];
|
||||||
entrieValues = new String[voiceFiles.size() ];
|
entrieValues = new String[voiceFiles.size() ];
|
||||||
int k = 0;
|
int k = 0;
|
||||||
|
@ -216,51 +210,72 @@ public class TestVoiceActivity extends OsmandActionBarActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addButtons(final LinearLayout ll, CommandPlayer p) {
|
private void addButtons(final LinearLayout ll, CommandPlayer p) {
|
||||||
|
boolean isJS = p instanceof JSTTSCommandPlayerImpl || p instanceof JSMediaCommandPlayerImpl;
|
||||||
addButton(ll, "Route calculated and number tests:", builder(p));
|
addButton(ll, "Route calculated and number tests:", builder(p));
|
||||||
addButton(ll, "\u25BA (1.1) New route calculated, 150m, 230sec (00:03:50)", builder(p).newRouteCalculated(150, 230));
|
addButton(ll, "\u25BA (1.1) New route calculated, 150m, 230sec (00:03:50)", !isJS ? builder(p).newRouteCalculated(150, 230) : jsBuilder(p).newRouteCalculated(150, 230));
|
||||||
addButton(ll, "\u25BA (1.2) New route calculated, 1350m, 3680sec (01:01:20)", builder(p).newRouteCalculated(1350, 3680));
|
addButton(ll, "\u25BA (1.2) New route calculated, 1350m, 3680sec (01:01:20)", !isJS ? builder(p).newRouteCalculated(1350, 3680) : jsBuilder(p).newRouteCalculated(1350, 3680));
|
||||||
addButton(ll, "\u25BA (1.3) New route calculated 3700m, 7320sec (02:02)", builder(p).newRouteCalculated(3700, 7320));
|
addButton(ll, "\u25BA (1.3) New route calculated 3700m, 7320sec (02:02)", !isJS ? builder(p).newRouteCalculated(3700, 7320) : jsBuilder(p).newRouteCalculated(3700, 7320));
|
||||||
addButton(ll, "\u25BA (1.4) New route calculated 9100m, 10980sec (03:03)", builder(p).newRouteCalculated(9100, 10980));
|
addButton(ll, "\u25BA (1.4) New route calculated 9100m, 10980sec (03:03)", !isJS ? builder(p).newRouteCalculated(9100, 10980) : jsBuilder(p).newRouteCalculated(9100, 10980));
|
||||||
addButton(ll, "\u25BA (2.1) Route recalculated 11500m, 18600sec (05:10)", builder(p).routeRecalculated(11500, 18600));
|
addButton(ll, "\u25BA (2.1) Route recalculated 11500m, 18600sec (05:10)", !isJS ? builder(p).routeRecalculated(11500, 18600) : jsBuilder(p).routeRecalculated(11500, 18600));
|
||||||
addButton(ll, "\u25BA (2.2) Route recalculated 19633m, 26700sec (07:25)", builder(p).routeRecalculated(19633, 26700));
|
addButton(ll, "\u25BA (2.2) Route recalculated 19633m, 26700sec (07:25)", !isJS ? builder(p).routeRecalculated(19633, 26700) : jsBuilder(p).routeRecalculated(19633, 26700));
|
||||||
addButton(ll, "\u25BA (2.3) Route recalculated 89750m, 55800sec (15:30)", builder(p).routeRecalculated(89750, 55800));
|
addButton(ll, "\u25BA (2.3) Route recalculated 89750m, 55800sec (15:30)", !isJS ? builder(p).routeRecalculated(89750, 55800) : jsBuilder(p).routeRecalculated(89750, 55800));
|
||||||
addButton(ll, "\u25BA (2.4) Route recalculated 125900m, 92700sec (25:45)", builder(p).routeRecalculated(125900, 92700));
|
addButton(ll, "\u25BA (2.4) Route recalculated 125900m, 92700sec (25:45)", !isJS ? builder(p).routeRecalculated(125900, 92700) : jsBuilder(p).routeRecalculated(125900, 92700));
|
||||||
|
|
||||||
addButton(ll, "All turn types: prepareTurn, makeTurnIn, turn:", builder(p));
|
addButton(ll, "All turn types: prepareTurn, makeTurnIn, turn:", builder(p));
|
||||||
addButton(ll, "\u25BA (3.1) After 1520m turn slightly left", builder(p).prepareTurn(AbstractPrologCommandPlayer.A_LEFT_SL, 1520, street(p, "")));
|
addButton(ll, "\u25BA (3.1) After 1520m turn slightly left", !isJS ? builder(p).prepareTurn(AbstractPrologCommandPlayer.A_LEFT_SL, 1520, street(p, "")) :
|
||||||
addButton(ll, "\u25BA (3.2) In 450m turn sharply left onto 'Hauptstra"+"\u00df"+"e', then bear right", builder(p).turn(AbstractPrologCommandPlayer.A_LEFT_SH, 450, street(p, "Hauptstraße")).then().bearRight(street(p, "")));
|
jsBuilder(p).prepareTurn(AbstractPrologCommandPlayer.A_LEFT_SL, 1520, jsStreet(p, "")));
|
||||||
addButton(ll, "\u25BA (3.3) Turn left, then in 100m turn slightly right", builder(p).turn(AbstractPrologCommandPlayer.A_LEFT, street(p, "")).then().turn(AbstractPrologCommandPlayer.A_RIGHT_SL, 100, street(p, "")));
|
addButton(ll, "\u25BA (3.2) In 450m turn sharply left onto 'Hauptstra"+"\u00df"+"e', then bear right", !isJS ? builder(p).turn(AbstractPrologCommandPlayer.A_LEFT_SH, 450, street(p, "Hauptstraße")).then().bearRight(street(p, "")) :
|
||||||
addButton(ll, "\u25BA (3.4) After 3100m turn right onto 'SR 80' toward 'Rome'", builder(p).prepareTurn(AbstractPrologCommandPlayer.A_RIGHT, 3100, street(p, "", "SR 80", "Rome")));
|
jsBuilder(p).turn(AbstractPrologCommandPlayer.A_LEFT_SH, 450, jsStreet(p, "Hauptstraße")).then().bearRight(jsStreet(p, "")));
|
||||||
addButton(ll, "\u25BA (3.5) In 370m turn slightly right onto 'Route 23' 'Main Street', then bear left", builder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_SL, 370, street(p, "Main Street", "Route 23")).then().bearLeft(street(p, "")));
|
addButton(ll, "\u25BA (3.3) Turn left, then in 100m turn slightly right", !isJS ? builder(p).turn(AbstractPrologCommandPlayer.A_LEFT, street(p, "")).then().turn(AbstractPrologCommandPlayer.A_RIGHT_SL, 100, street(p, "")) :
|
||||||
addButton(ll, "\u25BA (3.6) Turn sharply right onto 'Dr.-Quinn-Stra"+"\u00df"+"e'", builder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_SH, street(p, "Dr.-Quinn-Straße")));
|
jsBuilder(p).turn(AbstractPrologCommandPlayer.A_LEFT, jsStreet(p, "")).then().turn(AbstractPrologCommandPlayer.A_RIGHT_SL, 100, jsStreet(p, "")));
|
||||||
|
addButton(ll, "\u25BA (3.4) After 3100m turn right onto 'SR 80' toward 'Rome'", !isJS ? builder(p).prepareTurn(AbstractPrologCommandPlayer.A_RIGHT, 3100, street(p, "", "SR 80", "Rome")) :
|
||||||
|
jsBuilder(p).prepareTurn(AbstractPrologCommandPlayer.A_RIGHT, 3100, jsStreet(p, "", "SR 80", "Rome")));
|
||||||
|
addButton(ll, "\u25BA (3.5) In 370m turn slightly right onto 'Route 23' 'Main Street', then bear left", !isJS ? builder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_SL, 370, street(p, "Main Street", "Route 23")).then().bearLeft(street(p, "")) :
|
||||||
|
jsBuilder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_SL, 370, jsStreet(p, "Main jsStreet", "Route 23")).then().bearLeft(jsStreet(p, "")));
|
||||||
|
addButton(ll, "\u25BA (3.6) Turn sharply right onto 'Dr.-Quinn-Stra"+"\u00df"+"e'", !isJS ? builder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_SH, street(p, "Dr.-Quinn-Straße")) :
|
||||||
|
jsBuilder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_SH, jsStreet(p, "Dr.-Quinn-Straße")));
|
||||||
|
|
||||||
addButton(ll, "Keep left/right: prepareTurn, makeTurnIn, turn:", builder(p));
|
addButton(ll, "Keep left/right: prepareTurn, makeTurnIn, turn:", builder(p));
|
||||||
addButton(ll, "\u25BA (4.1) After 1810m keep left ' '", builder(p).prepareTurn(AbstractPrologCommandPlayer.A_LEFT_KEEP, 1810, street(p, "")));
|
addButton(ll, "\u25BA (4.1) After 1810m keep left ' '", !isJS ? builder(p).prepareTurn(AbstractPrologCommandPlayer.A_LEFT_KEEP, 1810, street(p, "")) :
|
||||||
addButton(ll, "\u25BA (4.2) In 400m keep left ' ' then in 80m keep right onto 'A1'", builder(p).turn(AbstractPrologCommandPlayer.A_LEFT_KEEP, 400, street(p, "")).then().turn(AbstractPrologCommandPlayer.A_RIGHT_KEEP, 80, street(p,"", "A1")));
|
jsBuilder(p).prepareTurn(AbstractPrologCommandPlayer.A_LEFT_KEEP, 1810, jsStreet(p, "")));
|
||||||
addButton(ll, "\u25BA (4.3) Keep right on 'Highway 60'", builder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_KEEP, street(p, "Highway 60", "", "", "Highway 60")));
|
addButton(ll, "\u25BA (4.2) In 400m keep left ' ' then in 80m keep right onto 'A1'", !isJS ? builder(p).turn(AbstractPrologCommandPlayer.A_LEFT_KEEP, 400, street(p, "")).then().turn(AbstractPrologCommandPlayer.A_RIGHT_KEEP, 80, street(p,"", "A1")) :
|
||||||
addButton(ll, "\u25BA (4.4) Turn left onto 'Broadway', then in 100m keep right and arrive at your destination 'Town Hall'",
|
jsBuilder(p).turn(AbstractPrologCommandPlayer.A_LEFT_KEEP, 400, jsStreet(p, "")).then().turn(AbstractPrologCommandPlayer.A_RIGHT_KEEP, 80, jsStreet(p,"", "A1")));
|
||||||
|
addButton(ll, "\u25BA (4.3) Keep right on 'Highway 60'", !isJS ? builder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_KEEP, street(p, "Highway 60", "", "", "Highway 60")) :
|
||||||
|
jsBuilder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_KEEP, jsStreet(p, "Highway 60", "", "", "Highway 60")));
|
||||||
|
addButton(ll, "\u25BA (4.4) Turn left onto 'Broadway', then in 100m keep right and arrive at your destination 'Town Hall'", !isJS ?
|
||||||
builder(p).turn(AbstractPrologCommandPlayer.A_LEFT, street(p, "Broadway"))
|
builder(p).turn(AbstractPrologCommandPlayer.A_LEFT, street(p, "Broadway"))
|
||||||
.then().turn(AbstractPrologCommandPlayer.A_RIGHT_KEEP, 100, street(p, "")).andArriveAtDestination("Town Hall"));
|
.then().turn(AbstractPrologCommandPlayer.A_RIGHT_KEEP, 100, street(p, "")).andArriveAtDestination("Town Hall") :
|
||||||
|
jsBuilder(p).turn(AbstractPrologCommandPlayer.A_LEFT, jsStreet(p, "Broadway"))
|
||||||
|
.then().turn(AbstractPrologCommandPlayer.A_RIGHT_KEEP, 100, jsStreet(p, "")).andArriveAtDestination("Town Hall"));
|
||||||
|
|
||||||
addButton(ll, "Roundabouts: prepareTurn, makeTurnIn, turn:", builder(p));
|
addButton(ll, "Roundabouts: prepareTurn, makeTurnIn, turn:", builder(p));
|
||||||
addButton(ll, "\u25BA (5.1) After 1250m enter a roundabout", builder(p).prepareRoundAbout(1250, 3, street(p,"", "I 15", "Los Angeles")));
|
addButton(ll, "\u25BA (5.1) After 1250m enter a roundabout", !isJS ? builder(p).prepareRoundAbout(1250, 3, street(p,"", "I 15", "Los Angeles")) :
|
||||||
addButton(ll, "\u25BA (5.2) In 450m enter the roundabout and take the 1st exit onto 'I 15' toward 'Los Angeles'", builder(p).roundAbout(450, 0, 1, street(p,"", "I 15", "Los Angeles")));
|
jsBuilder(p).prepareRoundAbout(1250, 3, jsStreet(p,"", "I 15", "Los Angeles")));
|
||||||
addButton(ll, "\u25BA (5.3) Roundabout: Take the 2nd exit onto 'Highway 60'", builder(p).roundAbout(0, 2, street(p, "Highway 60")));
|
addButton(ll, "\u25BA (5.2) In 450m enter the roundabout and take the 1st exit onto 'I 15' toward 'Los Angeles'", isJS ? builder(p).roundAbout(450, 0, 1, street(p,"", "I 15", "Los Angeles")) :
|
||||||
|
jsBuilder(p).roundAbout(450, 0, 1, jsStreet(p,"", "I 15", "Los Angeles")));
|
||||||
|
addButton(ll, "\u25BA (5.3) Roundabout: Take the 2nd exit onto 'Highway 60'", !isJS ? builder(p).roundAbout(0, 2, street(p, "Highway 60")) :
|
||||||
|
jsBuilder(p).roundAbout(0, 2, jsStreet(p, "Highway 60")));
|
||||||
|
|
||||||
addButton(ll, "U-turns: prepareTurn, makeTurnIn, turn, when possible:", builder(p));
|
addButton(ll, "U-turns: prepareTurn, makeTurnIn, turn, when possible:", builder(p));
|
||||||
addButton(ll, "\u25BA (6.1) After 640m make a U-turn", builder(p).prepareMakeUT(640, street(p, "")));
|
addButton(ll, "\u25BA (6.1) After 640m make a U-turn", !isJS ? builder(p).prepareMakeUT(640, street(p, "")) :
|
||||||
addButton(ll, "\u25BA (6.2) In 400m make a U-turn", builder(p).makeUT(400, street(p, "")));
|
jsBuilder(p).prepareMakeUT(640, jsStreet(p, "")));
|
||||||
addButton(ll, "\u25BA (6.3) Make a U-turn on 'Riviera'", builder(p).makeUT(street(p, "Riviera", "", "", "Riviera")));
|
addButton(ll, "\u25BA (6.2) In 400m make a U-turn", !isJS ? builder(p).makeUT(400, street(p, "")) :
|
||||||
|
jsBuilder(p).makeUT(400, jsStreet(p, "")));
|
||||||
|
addButton(ll, "\u25BA (6.3) Make a U-turn on 'Riviera'", !isJS ? builder(p).makeUT(street(p, "Riviera", "", "", "Riviera")) :
|
||||||
|
jsBuilder(p).makeUT(jsStreet(p, "Riviera", "", "", "Riviera")));
|
||||||
addButton(ll, "\u25BA (6.4) When possible, make a U-turn", builder(p).makeUTwp());
|
addButton(ll, "\u25BA (6.4) When possible, make a U-turn", builder(p).makeUTwp());
|
||||||
|
|
||||||
addButton(ll, "Go straight, follow the road, approaching:", builder(p));
|
addButton(ll, "Go straight, follow the road, approaching:", builder(p));
|
||||||
addButton(ll, "\u25BA (7.1) Straight ahead", builder(p).goAhead());
|
addButton(ll, "\u25BA (7.1) Straight ahead", builder(p).goAhead());
|
||||||
addButton(ll, "\u25BA (7.2) Continue for 2350m to ' '", builder(p).goAhead(2350, street(p, "")));
|
addButton(ll, "\u25BA (7.2) Continue for 2350m to ' '", !isJS ? builder(p).goAhead(2350, street(p, "")) :
|
||||||
addButton(ll, "\u25BA (7.3) Continue for 360m to 'Broadway' and arrive at your intermediate destination ' '", builder(p).goAhead(360, street(p,"Broadway")).andArriveAtIntermediatePoint(""));
|
jsBuilder(p).goAhead(2350, jsStreet(p, "")));
|
||||||
addButton(ll, "\u25BA (7.4) Continue for 800m to 'Dr Martin Luther King Jr Boulevard' and arrive at your destination ' '", builder(p).goAhead(800, street(p,"", "Dr Martin Luther King Jr Boulevard")).andArriveAtDestination(""));
|
addButton(ll, "\u25BA (7.3) Continue for 360m to 'Broadway' and arrive at your intermediate destination ' '", !isJS ? builder(p).goAhead(360, street(p,"Broadway")).andArriveAtIntermediatePoint("") :
|
||||||
addButton(ll, "\u25BA (7.5) Continue for 200m and pass GPX waypoint 'Trailhead'", builder(p).goAhead(200, null).andArriveAtWayPoint("Trailhead"));
|
jsBuilder(p).goAhead(360, jsStreet(p,"Broadway")).andArriveAtIntermediatePoint(""));
|
||||||
addButton(ll, "\u25BA (7.6) Continue for 400m and pass favorite 'Brewery'", builder(p).goAhead(400, null).andArriveAtFavorite("Brewery"));
|
addButton(ll, "\u25BA (7.4) Continue for 800m to 'Dr Martin Luther King Jr Boulevard' and arrive at your destination ' '", !isJS ? builder(p).goAhead(800, street(p,"", "Dr Martin Luther King Jr Boulevard")).andArriveAtDestination("") :
|
||||||
addButton(ll, "\u25BA (7.7) Continue for 600m and pass POI 'Museum'", builder(p).goAhead(600, null).andArriveAtPoi("Museum"));
|
jsBuilder(p).goAhead(800, jsStreet(p,"", "Dr Martin Luther King Jr Boulevard")).andArriveAtDestination(""));
|
||||||
|
addButton(ll, "\u25BA (7.5) Continue for 200m and pass GPX waypoint 'Trailhead'", !isJS ? builder(p).goAhead(200, null).andArriveAtWayPoint("Trailhead") : jsBuilder(p).goAhead(200, new HashMap<String, String>()).andArriveAtWayPoint("Trailhead"));
|
||||||
|
addButton(ll, "\u25BA (7.6) Continue for 400m and pass favorite 'Brewery'", !isJS ? builder(p).goAhead(400, null).andArriveAtFavorite("Brewery") : jsBuilder(p).goAhead(400, new HashMap<String, String>()).andArriveAtFavorite("Brewery"));
|
||||||
|
addButton(ll, "\u25BA (7.7) Continue for 600m and pass POI 'Museum'", !isJS ? builder(p).goAhead(600, null).andArriveAtPoi("Museum") : jsBuilder(p).goAhead(600, new HashMap<String, String>()).andArriveAtPoi("Museum"));
|
||||||
|
|
||||||
addButton(ll, "Arriving and passing points:", builder(p));
|
addButton(ll, "Arriving and passing points:", builder(p));
|
||||||
addButton(ll, "\u25BA (8.1) Arrive at your destination 'Home'", builder(p).arrivedAtDestination("Home"));
|
addButton(ll, "\u25BA (8.1) Arrive at your destination 'Home'", builder(p).arrivedAtDestination("Home"));
|
||||||
|
@ -292,10 +307,31 @@ public class TestVoiceActivity extends OsmandActionBarActivity {
|
||||||
ll.forceLayout();
|
ll.forceLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, String> jsStreet(CommandPlayer p, String... args) {
|
||||||
|
Map<String, String> res = new HashMap<>();
|
||||||
|
if (!p.supportsStructuredStreetNames()) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
String[] streetNames = new String[]{"toRef", "toStreetName", "toDest", "fromRef", "fromStreetName", "fromDest"};
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
res.put(streetNames[i], args[i]);
|
||||||
|
}
|
||||||
|
for (String streetName : streetNames) {
|
||||||
|
if (res.get(streetName) == null) {
|
||||||
|
res.put(streetName, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
private CommandBuilder builder(CommandPlayer p){
|
private CommandBuilder builder(CommandPlayer p){
|
||||||
return p.newCommandBuilder();
|
return p.newCommandBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JSCommandBuilder jsBuilder(CommandPlayer p) {
|
||||||
|
return (JSCommandBuilder) p.newCommandBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
public void addButton(ViewGroup layout, final String description, final CommandBuilder builder){
|
public void addButton(ViewGroup layout, final String description, final CommandBuilder builder){
|
||||||
final Button button = new Button(this);
|
final Button button = new Button(this);
|
||||||
button.setGravity(Gravity.LEFT);
|
button.setGravity(Gravity.LEFT);
|
||||||
|
|
|
@ -298,6 +298,8 @@ public class DownloadActivityType {
|
||||||
return FileNameTranslationHelper.getVoiceName(ctx, getBasename(indexItem));
|
return FileNameTranslationHelper.getVoiceName(ctx, getBasename(indexItem));
|
||||||
} else if (fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP)) {
|
} else if (fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP)) {
|
||||||
return FileNameTranslationHelper.getVoiceName(ctx, getBasename(indexItem));
|
return FileNameTranslationHelper.getVoiceName(ctx, getBasename(indexItem));
|
||||||
|
} else if (fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)) {
|
||||||
|
return FileNameTranslationHelper.getVoiceName(ctx, getBasename(indexItem));
|
||||||
}
|
}
|
||||||
return getBasename(indexItem);
|
return getBasename(indexItem);
|
||||||
}
|
}
|
||||||
|
@ -336,6 +338,9 @@ public class DownloadActivityType {
|
||||||
// if(fileName.endsWith(IndexConstants.VOICE_INDEX_EXT_ZIP) ||
|
// if(fileName.endsWith(IndexConstants.VOICE_INDEX_EXT_ZIP) ||
|
||||||
// fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP)) {
|
// fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP)) {
|
||||||
if (this == VOICE_FILE) {
|
if (this == VOICE_FILE) {
|
||||||
|
if (fileName.contains(".js")) {
|
||||||
|
return fileName.replace('_', '-').replaceAll(".js", "");
|
||||||
|
}
|
||||||
int l = fileName.lastIndexOf('_');
|
int l = fileName.lastIndexOf('_');
|
||||||
if (l == -1) {
|
if (l == -1) {
|
||||||
l = fileName.length();
|
l = fileName.length();
|
||||||
|
@ -408,7 +413,8 @@ public class DownloadActivityType {
|
||||||
if (l == -1) {
|
if (l == -1) {
|
||||||
l = fileName.length();
|
l = fileName.length();
|
||||||
}
|
}
|
||||||
return fileName.substring(0, l);
|
return fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS) ? fileName.replace('_', '-')
|
||||||
|
.replaceAll(".js", "") : fileName.substring(0, l);
|
||||||
}
|
}
|
||||||
if (this == FONT_FILE) {
|
if (this == FONT_FILE) {
|
||||||
int l = fileName.indexOf('.');
|
int l = fileName.indexOf('.');
|
||||||
|
|
|
@ -165,6 +165,13 @@ public class DownloadOsmandIndexesHelper {
|
||||||
|
|
||||||
result.add(new AssetIndexItem(voice + ext, "voice", date, dateModified, "0.1", destFile.length(), key,
|
result.add(new AssetIndexItem(voice + ext, "voice", date, dateModified, "0.1", destFile.length(), key,
|
||||||
destFile.getPath(), DownloadActivityType.VOICE_FILE));
|
destFile.getPath(), DownloadActivityType.VOICE_FILE));
|
||||||
|
} else if (target.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS) && target.startsWith("voice/")) {
|
||||||
|
String lang = target.substring("voice/".length(), target.indexOf("-"));
|
||||||
|
File destFile = new File(voicePath, target.substring("voice/".length(),
|
||||||
|
target.indexOf("/", "voice/".length())) + "/" + lang + "_tts.js");
|
||||||
|
result.add(new AssetIndexItem(lang + "_" + IndexConstants.TTSVOICE_INDEX_EXT_JS,
|
||||||
|
"voice", date, dateModified, "0.1", destFile.length(), key,
|
||||||
|
destFile.getPath(), DownloadActivityType.VOICE_FILE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.sort();
|
result.sort();
|
||||||
|
|
|
@ -83,7 +83,8 @@ public class DownloadResourceGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHeader() {
|
public boolean isHeader() {
|
||||||
return this == VOICE_HEADER_REC || this == VOICE_HEADER_TTS || this == SUBREGIONS
|
return this == VOICE_HEADER_REC || this == VOICE_HEADER_TTS
|
||||||
|
|| this == SUBREGIONS
|
||||||
|| this == WORLD_MAPS || this == REGION_MAPS || this == OTHER_GROUP
|
|| this == WORLD_MAPS || this == REGION_MAPS || this == OTHER_GROUP
|
||||||
|| this == HILLSHADE_HEADER || this == SRTM_HEADER
|
|| this == HILLSHADE_HEADER || this == SRTM_HEADER
|
||||||
|| this == OTHER_MAPS_HEADER || this == OTHER_MAPS_GROUP
|
|| this == OTHER_MAPS_HEADER || this == OTHER_MAPS_GROUP
|
||||||
|
|
|
@ -316,11 +316,14 @@ public class DownloadResources extends DownloadResourceGroup {
|
||||||
|
|
||||||
Map<WorldRegion, List<IndexItem> > groupByRegion = new LinkedHashMap<WorldRegion, List<IndexItem>>();
|
Map<WorldRegion, List<IndexItem> > groupByRegion = new LinkedHashMap<WorldRegion, List<IndexItem>>();
|
||||||
OsmandRegions regs = app.getRegions();
|
OsmandRegions regs = app.getRegions();
|
||||||
|
boolean useJS = app.getSettings().USE_JS_VOICE_GUIDANCE.get();
|
||||||
for (IndexItem ii : resources) {
|
for (IndexItem ii : resources) {
|
||||||
if (ii.getType() == DownloadActivityType.VOICE_FILE) {
|
if (ii.getType() == DownloadActivityType.VOICE_FILE) {
|
||||||
if (ii.getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP)) {
|
if (ii.getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP) && !useJS) {
|
||||||
voiceTTS.addItem(ii);
|
voiceTTS.addItem(ii);
|
||||||
} else {
|
} else if (ii.getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS) && useJS){
|
||||||
|
voiceTTS.addItem(ii);
|
||||||
|
} else if (ii.getFileName().endsWith(IndexConstants.VOICE_INDEX_EXT_ZIP)){
|
||||||
voiceRec.addItem(ii);
|
voiceRec.addItem(ii);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package net.osmand.plus.mapcontextmenu.other;
|
package net.osmand.plus.mapcontextmenu.other;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
@ -329,20 +330,36 @@ public class RoutePreferencesMenu {
|
||||||
app.initVoiceCommandPlayer(mapActivity, app.getRoutingHelper().getAppMode(), false, null, true, false);
|
app.initVoiceCommandPlayer(mapActivity, app.getRoutingHelper().getAppMode(), false, null, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<String> getVoiceFiles(MapActivity mapActivity) {
|
public static Set<String> getVoiceFiles(Activity activity) {
|
||||||
// read available voice data
|
// read available voice data
|
||||||
File extStorage = mapActivity.getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR);
|
OsmandApplication app = ((OsmandApplication) activity.getApplication());
|
||||||
Set<String> setFiles = new LinkedHashSet<>();
|
File extStorage = app.getAppPath(IndexConstants.VOICE_INDEX_DIR);
|
||||||
|
Set<String> setFiles = new LinkedHashSet<String>();
|
||||||
|
boolean addJS = app.getSettings().USE_JS_VOICE_GUIDANCE.get();
|
||||||
if (extStorage.exists()) {
|
if (extStorage.exists()) {
|
||||||
for (File f : extStorage.listFiles()) {
|
for (File f : extStorage.listFiles()) {
|
||||||
if (f.isDirectory()) {
|
if (f.isDirectory()) {
|
||||||
setFiles.add(f.getName());
|
if (addJS && hasJavaScript(f)) {
|
||||||
|
setFiles.add(f.getName());
|
||||||
|
} else if (!addJS) {
|
||||||
|
setFiles.add(f.getName());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return setFiles;
|
return setFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean hasJavaScript(File f) {
|
||||||
|
for (File file : f.listFiles()) {
|
||||||
|
if (file.getName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public OnItemClickListener getItemClickListener(final ArrayAdapter<?> listAdapter) {
|
public OnItemClickListener getItemClickListener(final ArrayAdapter<?> listAdapter) {
|
||||||
return new OnItemClickListener() {
|
return new OnItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -386,8 +386,11 @@ public class ResourceManager {
|
||||||
for (File f : lf) {
|
for (File f : lf) {
|
||||||
if (f.isDirectory()) {
|
if (f.isDirectory()) {
|
||||||
File conf = new File(f, "_config.p");
|
File conf = new File(f, "_config.p");
|
||||||
|
boolean useJS = context.getSettings().USE_JS_VOICE_GUIDANCE.get();
|
||||||
if (!conf.exists()) {
|
if (!conf.exists()) {
|
||||||
conf = new File(f, "_ttsconfig.p");
|
String lang = f.getName().replace("-tts", "");
|
||||||
|
conf = useJS ? new File(f, lang + "_" + IndexConstants.TTSVOICE_INDEX_EXT_JS) :
|
||||||
|
new File(f, "_ttsconfig.p");
|
||||||
}
|
}
|
||||||
if (conf.exists()) {
|
if (conf.exists()) {
|
||||||
indexFileNames.put(f.getName(), dateFormat.format(conf.lastModified())); //$NON-NLS-1$
|
indexFileNames.put(f.getName(), dateFormat.format(conf.lastModified())); //$NON-NLS-1$
|
||||||
|
|
330
OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java
Normal file
330
OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
package net.osmand.plus.routing;
|
||||||
|
|
||||||
|
import net.osmand.Location;
|
||||||
|
import net.osmand.binary.RouteDataObject;
|
||||||
|
import net.osmand.plus.OsmandSettings;
|
||||||
|
import net.osmand.plus.voice.CommandBuilder;
|
||||||
|
import net.osmand.plus.voice.JSCommandBuilder;
|
||||||
|
import net.osmand.router.RouteSegmentResult;
|
||||||
|
import net.osmand.router.TurnType;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class JSVoiceRouter extends VoiceRouter {
|
||||||
|
|
||||||
|
public JSVoiceRouter(RoutingHelper router, OsmandSettings settings) {
|
||||||
|
super(router, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getSpeakableJSStreetName(RouteSegmentResult currentSegment, RouteDirectionInfo i, boolean includeDest) {
|
||||||
|
Map<String, String> result = new HashMap<>();
|
||||||
|
if (i == null || !router.getSettings().SPEAK_STREET_NAMES.get()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (player != null && player.supportsStructuredStreetNames()) {
|
||||||
|
|
||||||
|
// Issue 2377: Play Dest here only if not already previously announced, to avoid repetition
|
||||||
|
if (includeDest == true) {
|
||||||
|
result.put("toRef", getNonNullString(getSpeakablePointName(i.getRef())));
|
||||||
|
result.put("toStreetName", getNonNullString(getSpeakablePointName(i.getStreetName())));
|
||||||
|
result.put("toDest", getNonNullString(getSpeakablePointName(i.getDestinationName())));
|
||||||
|
} else {
|
||||||
|
result.put("toRef", getNonNullString(getSpeakablePointName(i.getRef())));
|
||||||
|
result.put("toStreetName", getNonNullString(getSpeakablePointName(i.getStreetName())));
|
||||||
|
result.put("toDest", "");
|
||||||
|
}
|
||||||
|
if (currentSegment != null) {
|
||||||
|
// Issue 2377: Play Dest here only if not already previously announced, to avoid repetition
|
||||||
|
if (includeDest == true) {
|
||||||
|
RouteDataObject obj = currentSegment.getObject();
|
||||||
|
result.put("fromRef", getNonNullString(getSpeakablePointName(obj.getRef(settings.MAP_PREFERRED_LOCALE.get(),
|
||||||
|
settings.MAP_TRANSLITERATE_NAMES.get(), currentSegment.isForwardDirection()))));
|
||||||
|
result.put("fromStreetName", getNonNullString(getSpeakablePointName(obj.getName(settings.MAP_PREFERRED_LOCALE.get(),
|
||||||
|
settings.MAP_TRANSLITERATE_NAMES.get()))));
|
||||||
|
result.put("fromDest", getNonNullString(getSpeakablePointName(obj.getDestinationName(settings.MAP_PREFERRED_LOCALE.get(),
|
||||||
|
settings.MAP_TRANSLITERATE_NAMES.get(), currentSegment.isForwardDirection()))));
|
||||||
|
} else {
|
||||||
|
RouteDataObject obj = currentSegment.getObject();
|
||||||
|
result.put("fromRef", getNonNullString(getSpeakablePointName(obj.getRef(settings.MAP_PREFERRED_LOCALE.get(),
|
||||||
|
settings.MAP_TRANSLITERATE_NAMES.get(), currentSegment.isForwardDirection()))));
|
||||||
|
result.put("fromStreetName", getNonNullString(getSpeakablePointName(obj.getName(settings.MAP_PREFERRED_LOCALE.get(),
|
||||||
|
settings.MAP_TRANSLITERATE_NAMES.get()))));
|
||||||
|
result.put("fromDest", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
result.put("toRef", getNonNullString(getSpeakablePointName(i.getRef())));
|
||||||
|
result.put("toStreetName", getNonNullString(getSpeakablePointName(i.getStreetName())));
|
||||||
|
result.put("toDest", "");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getNonNullString(String speakablePointName) {
|
||||||
|
return speakablePointName == null ? "" : speakablePointName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates status of voice guidance
|
||||||
|
* @param currentLocation
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void updateStatus(Location currentLocation, boolean repeat) {
|
||||||
|
// Directly after turn: goAhead (dist), unless:
|
||||||
|
// < PREPARE_LONG_DISTANCE (e.g. 3500m): playPrepareTurn (-not played any more-)
|
||||||
|
// < PREPARE_DISTANCE (e.g. 1500m): playPrepareTurn ("Turn after ...")
|
||||||
|
// < TURN_IN_DISTANCE (e.g. 390m or 30sec): playMakeTurnIn ("Turn in ...")
|
||||||
|
// < TURN_DISTANCE (e.g. 50m or 7sec): playMakeTurn ("Turn ...")
|
||||||
|
float speed = DEFAULT_SPEED;
|
||||||
|
if (currentLocation != null && currentLocation.hasSpeed()) {
|
||||||
|
speed = Math.max(currentLocation.getSpeed(), speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteCalculationResult.NextDirectionInfo nextInfo = router.getNextRouteDirectionInfo(new RouteCalculationResult.NextDirectionInfo(), true);
|
||||||
|
RouteSegmentResult currentSegment = router.getCurrentSegmentResult();
|
||||||
|
if (nextInfo == null || nextInfo.directionInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int dist = nextInfo.distanceTo;
|
||||||
|
RouteDirectionInfo next = nextInfo.directionInfo;
|
||||||
|
|
||||||
|
// If routing is changed update status to unknown
|
||||||
|
if (next != nextRouteDirection) {
|
||||||
|
nextRouteDirection = next;
|
||||||
|
currentStatus = STATUS_UNKNOWN;
|
||||||
|
suppressDest = false;
|
||||||
|
playedAndArriveAtTarget = false;
|
||||||
|
announceBackOnRoute = false;
|
||||||
|
if (playGoAheadDist != -1) {
|
||||||
|
playGoAheadDist = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!repeat) {
|
||||||
|
if (dist <= 0) {
|
||||||
|
return;
|
||||||
|
} else if (needsInforming()) {
|
||||||
|
playGoAhead(dist, getSpeakableJSStreetName(currentSegment, next, false));
|
||||||
|
return;
|
||||||
|
} else if (currentStatus == STATUS_TOLD) {
|
||||||
|
// nothing said possibly that's wrong case we should say before that
|
||||||
|
// however it should be checked manually ?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentStatus == STATUS_UNKNOWN) {
|
||||||
|
// Play "Continue for ..." if (1) after route calculation no other prompt is due, or (2) after a turn if next turn is more than PREPARE_LONG_DISTANCE away
|
||||||
|
if ((playGoAheadDist == -1) || (dist > PREPARE_LONG_DISTANCE)) {
|
||||||
|
playGoAheadDist = dist - 3 * TURN_DISTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteCalculationResult.NextDirectionInfo nextNextInfo = router.getNextRouteDirectionInfoAfter(nextInfo, new RouteCalculationResult.NextDirectionInfo(), true); //I think "true" is correct here, not "!repeat"
|
||||||
|
// Note: getNextRouteDirectionInfoAfter(nextInfo, x, y).distanceTo is distance from nextInfo, not from current position!
|
||||||
|
|
||||||
|
// STATUS_TURN = "Turn (now)"
|
||||||
|
if ((repeat || statusNotPassed(STATUS_TURN)) && isDistanceLess(speed, dist, TURN_DISTANCE, TURN_DEFAULT_SPEED)) {
|
||||||
|
if (nextNextInfo.distanceTo < TURN_IN_DISTANCE_END && nextNextInfo != null) {
|
||||||
|
playMakeTurn(currentSegment, next, nextNextInfo);
|
||||||
|
} else {
|
||||||
|
playMakeTurn(currentSegment, next, null);
|
||||||
|
}
|
||||||
|
if (!next.getTurnType().goAhead() && isTargetPoint(nextNextInfo)) { // !goAhead() avoids isolated "and arrive.." prompt, as goAhead() is not pronounced
|
||||||
|
if (nextNextInfo.distanceTo < TURN_IN_DISTANCE_END) {
|
||||||
|
// Issue #2865: Ensure a distance associated with the destination arrival is always announced, either here, or in subsequent "Turn in" prompt
|
||||||
|
// Distance fon non-straights already announced in "Turn (now)"'s nextnext code above
|
||||||
|
if ((nextNextInfo != null) && (nextNextInfo.directionInfo != null) && nextNextInfo.directionInfo.getTurnType().goAhead()) {
|
||||||
|
playThen();
|
||||||
|
playGoAhead(nextNextInfo.distanceTo, new HashMap<String, String>());
|
||||||
|
}
|
||||||
|
playAndArriveAtDestination(nextNextInfo);
|
||||||
|
} else if (nextNextInfo.distanceTo < 1.2f * TURN_IN_DISTANCE_END) {
|
||||||
|
// 1.2 is safety margin should the subsequent "Turn in" prompt not fit in amy more
|
||||||
|
playThen();
|
||||||
|
playGoAhead(nextNextInfo.distanceTo, new HashMap<String, String>());
|
||||||
|
playAndArriveAtDestination(nextNextInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nextStatusAfter(STATUS_TURN);
|
||||||
|
|
||||||
|
// STATUS_TURN_IN = "Turn in ..."
|
||||||
|
} else if ((repeat || statusNotPassed(STATUS_TURN_IN)) && isDistanceLess(speed, dist, TURN_IN_DISTANCE, 0f)) {
|
||||||
|
if (repeat || dist >= TURN_IN_DISTANCE_END) {
|
||||||
|
if ((isDistanceLess(speed, nextNextInfo.distanceTo, TURN_DISTANCE, 0f) || nextNextInfo.distanceTo < TURN_IN_DISTANCE_END) &&
|
||||||
|
nextNextInfo != null) {
|
||||||
|
playMakeTurnIn(currentSegment, next, dist - (int) btScoDelayDistance, nextNextInfo.directionInfo);
|
||||||
|
} else {
|
||||||
|
playMakeTurnIn(currentSegment, next, dist - (int) btScoDelayDistance, null);
|
||||||
|
}
|
||||||
|
playGoAndArriveAtDestination(repeat, nextInfo, currentSegment);
|
||||||
|
}
|
||||||
|
nextStatusAfter(STATUS_TURN_IN);
|
||||||
|
|
||||||
|
// STATUS_PREPARE = "Turn after ..."
|
||||||
|
} else if ((repeat || statusNotPassed(STATUS_PREPARE)) && (dist <= PREPARE_DISTANCE)) {
|
||||||
|
if (repeat || dist >= PREPARE_DISTANCE_END) {
|
||||||
|
if (!repeat && (next.getTurnType().keepLeft() || next.getTurnType().keepRight())) {
|
||||||
|
// Do not play prepare for keep left/right
|
||||||
|
} else {
|
||||||
|
playPrepareTurn(currentSegment, next, dist);
|
||||||
|
playGoAndArriveAtDestination(repeat, nextInfo, currentSegment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nextStatusAfter(STATUS_PREPARE);
|
||||||
|
|
||||||
|
// STATUS_LONG_PREPARE = also "Turn after ...", we skip this now, users said this is obsolete
|
||||||
|
} else if ((repeat || statusNotPassed(STATUS_LONG_PREPARE)) && (dist <= PREPARE_LONG_DISTANCE)) {
|
||||||
|
if (repeat || dist >= PREPARE_LONG_DISTANCE_END) {
|
||||||
|
playPrepareTurn(currentSegment, next, dist);
|
||||||
|
playGoAndArriveAtDestination(repeat, nextInfo, currentSegment);
|
||||||
|
}
|
||||||
|
nextStatusAfter(STATUS_LONG_PREPARE);
|
||||||
|
|
||||||
|
// STATUS_UNKNOWN = "Continue for ..." if (1) after route calculation no other prompt is due, or (2) after a turn if next turn is more than PREPARE_LONG_DISTANCE away
|
||||||
|
} else if (statusNotPassed(STATUS_UNKNOWN)) {
|
||||||
|
// Strange how we get here but
|
||||||
|
nextStatusAfter(STATUS_UNKNOWN);
|
||||||
|
} else if (repeat || (statusNotPassed(STATUS_PREPARE) && dist < playGoAheadDist)) {
|
||||||
|
playGoAheadDist = 0;
|
||||||
|
playGoAhead(dist, getSpeakableJSStreetName(currentSegment, next, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playPrepareTurn(RouteSegmentResult currentSegment, RouteDirectionInfo next, int dist) {
|
||||||
|
JSCommandBuilder play = (JSCommandBuilder) getNewCommandPlayerToPlay();
|
||||||
|
if (play != null) {
|
||||||
|
String tParam = getTurnType(next.getTurnType());
|
||||||
|
if (tParam != null) {
|
||||||
|
notifyOnVoiceMessage();
|
||||||
|
play.prepareTurn(tParam, dist, getSpeakableJSStreetName(currentSegment, next, true)).play();
|
||||||
|
} else if (next.getTurnType().isRoundAbout()) {
|
||||||
|
notifyOnVoiceMessage();
|
||||||
|
play.prepareRoundAbout(dist, next.getTurnType().getExitOut(), getSpeakableJSStreetName(currentSegment, next, true)).play();
|
||||||
|
} else if (next.getTurnType().getValue() == TurnType.TU || next.getTurnType().getValue() == TurnType.TRU) {
|
||||||
|
notifyOnVoiceMessage();
|
||||||
|
play.prepareMakeUT(dist, getSpeakableJSStreetName(currentSegment, next, true)).play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playMakeTurnIn(RouteSegmentResult currentSegment, RouteDirectionInfo next, int dist, RouteDirectionInfo pronounceNextNext) {
|
||||||
|
JSCommandBuilder play = (JSCommandBuilder) getNewCommandPlayerToPlay();
|
||||||
|
if (play != null) {
|
||||||
|
String tParam = getTurnType(next.getTurnType());
|
||||||
|
boolean isPlay = true;
|
||||||
|
if (tParam != null) {
|
||||||
|
play.turn(tParam, dist, getSpeakableJSStreetName(currentSegment, next, true));
|
||||||
|
suppressDest = true;
|
||||||
|
} else if (next.getTurnType().isRoundAbout()) {
|
||||||
|
play.roundAbout(dist, next.getTurnType().getTurnAngle(), next.getTurnType().getExitOut(), getSpeakableJSStreetName(currentSegment, next, true));
|
||||||
|
// Other than in prepareTurn, in prepareRoundabout we do not announce destination, so we can repeat it one more time
|
||||||
|
suppressDest = false;
|
||||||
|
} else if (next.getTurnType().getValue() == TurnType.TU || next.getTurnType().getValue() == TurnType.TRU) {
|
||||||
|
play.makeUT(dist, getSpeakableJSStreetName(currentSegment, next, true));
|
||||||
|
suppressDest = true;
|
||||||
|
} else {
|
||||||
|
isPlay = false;
|
||||||
|
}
|
||||||
|
// 'then keep' preparation for next after next. (Also announces an interim straight segment, which is not pronounced above.)
|
||||||
|
if (pronounceNextNext != null) {
|
||||||
|
TurnType t = pronounceNextNext.getTurnType();
|
||||||
|
isPlay = true;
|
||||||
|
if (t.getValue() != TurnType.C && next.getTurnType().getValue() == TurnType.C) {
|
||||||
|
play.goAhead(dist, getSpeakableJSStreetName(currentSegment, next, true));
|
||||||
|
}
|
||||||
|
if (t.getValue() == TurnType.TL || t.getValue() == TurnType.TSHL || t.getValue() == TurnType.TSLL
|
||||||
|
|| t.getValue() == TurnType.TU || t.getValue() == TurnType.KL ) {
|
||||||
|
play.then().bearLeft(getSpeakableJSStreetName(currentSegment, next, false));
|
||||||
|
} else if (t.getValue() == TurnType.TR || t.getValue() == TurnType.TSHR || t.getValue() == TurnType.TSLR
|
||||||
|
|| t.getValue() == TurnType.TRU || t.getValue() == TurnType.KR) {
|
||||||
|
play.then().bearRight(getSpeakableJSStreetName(currentSegment, next, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isPlay) {
|
||||||
|
notifyOnVoiceMessage();
|
||||||
|
play.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playGoAhead(int dist, Map<String, String> streetName) {
|
||||||
|
CommandBuilder play = getNewCommandPlayerToPlay();
|
||||||
|
JSCommandBuilder playJs = (JSCommandBuilder) play;
|
||||||
|
if (play != null) {
|
||||||
|
notifyOnVoiceMessage();
|
||||||
|
playJs.goAhead(dist, streetName).play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playMakeTurn(RouteSegmentResult currentSegment, RouteDirectionInfo next, RouteCalculationResult.NextDirectionInfo nextNextInfo) {
|
||||||
|
JSCommandBuilder play = (JSCommandBuilder) getNewCommandPlayerToPlay();
|
||||||
|
if (play != null) {
|
||||||
|
String tParam = getTurnType(next.getTurnType());
|
||||||
|
boolean isplay = true;
|
||||||
|
if (tParam != null) {
|
||||||
|
play.turn(tParam, getSpeakableJSStreetName(currentSegment, next, !suppressDest));
|
||||||
|
} else if (next.getTurnType().isRoundAbout()) {
|
||||||
|
play.roundAbout(next.getTurnType().getTurnAngle(), next.getTurnType().getExitOut(), getSpeakableJSStreetName(currentSegment, next, !suppressDest));
|
||||||
|
} else if (next.getTurnType().getValue() == TurnType.TU || next.getTurnType().getValue() == TurnType.TRU) {
|
||||||
|
play.makeUT(getSpeakableJSStreetName(currentSegment, next, !suppressDest));
|
||||||
|
// Do not announce goAheads
|
||||||
|
//} else if (next.getTurnType().getValue() == TurnType.C)) {
|
||||||
|
// play.goAhead();
|
||||||
|
} else {
|
||||||
|
isplay = false;
|
||||||
|
}
|
||||||
|
// Add turn after next
|
||||||
|
if ((nextNextInfo != null) && (nextNextInfo.directionInfo != null)) {
|
||||||
|
|
||||||
|
// This case only needed should we want a prompt at the end of straight segments (equivalent of makeTurn) when nextNextInfo should be announced again there.
|
||||||
|
if (nextNextInfo.directionInfo.getTurnType().getValue() != TurnType.C && next.getTurnType().getValue() == TurnType.C) {
|
||||||
|
play.goAhead();
|
||||||
|
isplay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String t2Param = getTurnType(nextNextInfo.directionInfo.getTurnType());
|
||||||
|
if (t2Param != null) {
|
||||||
|
if (isplay) {
|
||||||
|
play.then();
|
||||||
|
play.turn(t2Param, nextNextInfo.distanceTo, new HashMap<String, String>());
|
||||||
|
}
|
||||||
|
} else if (nextNextInfo.directionInfo.getTurnType().isRoundAbout()) {
|
||||||
|
if (isplay) {
|
||||||
|
play.then();
|
||||||
|
play.roundAbout(nextNextInfo.distanceTo, nextNextInfo.directionInfo.getTurnType().getTurnAngle(),
|
||||||
|
nextNextInfo.directionInfo.getTurnType().getExitOut(), new HashMap<String, String>());
|
||||||
|
}
|
||||||
|
} else if (nextNextInfo.directionInfo.getTurnType().getValue() == TurnType.TU) {
|
||||||
|
if (isplay) {
|
||||||
|
play.then();
|
||||||
|
play.makeUT(nextNextInfo.distanceTo, new HashMap<String, String>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isplay) {
|
||||||
|
notifyOnVoiceMessage();
|
||||||
|
play.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playGoAndArriveAtDestination(boolean repeat, RouteCalculationResult.NextDirectionInfo nextInfo, RouteSegmentResult currentSegment) {
|
||||||
|
RouteDirectionInfo next = nextInfo.directionInfo;
|
||||||
|
if (isTargetPoint(nextInfo) && (!playedAndArriveAtTarget || repeat)) {
|
||||||
|
if (next.getTurnType().goAhead()) {
|
||||||
|
playGoAhead(nextInfo.distanceTo, getSpeakableJSStreetName(currentSegment, next, false));
|
||||||
|
playAndArriveAtDestination(nextInfo);
|
||||||
|
playedAndArriveAtTarget = true;
|
||||||
|
} else if (nextInfo.distanceTo <= 2 * TURN_IN_DISTANCE) {
|
||||||
|
playAndArriveAtDestination(nextInfo);
|
||||||
|
playedAndArriveAtTarget = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -97,7 +97,9 @@ public class RoutingHelper {
|
||||||
public RoutingHelper(OsmandApplication context){
|
public RoutingHelper(OsmandApplication context){
|
||||||
this.app = context;
|
this.app = context;
|
||||||
settings = context.getSettings();
|
settings = context.getSettings();
|
||||||
voiceRouter = new VoiceRouter(this, settings);
|
boolean useJS = settings.USE_JS_VOICE_GUIDANCE.get();
|
||||||
|
voiceRouter = useJS ? new JSVoiceRouter(this, settings)
|
||||||
|
: new VoiceRouter(this, settings);
|
||||||
provider = new RouteProvider();
|
provider = new RouteProvider();
|
||||||
setAppMode(settings.APPLICATION_MODE.get());
|
setAppMode(settings.APPLICATION_MODE.get());
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,27 +28,27 @@ import android.media.SoundPool;
|
||||||
|
|
||||||
public class VoiceRouter {
|
public class VoiceRouter {
|
||||||
private static final int STATUS_UTWP_TOLD = -1;
|
private static final int STATUS_UTWP_TOLD = -1;
|
||||||
private static final int STATUS_UNKNOWN = 0;
|
static final int STATUS_UNKNOWN = 0;
|
||||||
private static final int STATUS_LONG_PREPARE = 1;
|
static final int STATUS_LONG_PREPARE = 1;
|
||||||
private static final int STATUS_PREPARE = 2;
|
static final int STATUS_PREPARE = 2;
|
||||||
private static final int STATUS_TURN_IN = 3;
|
static final int STATUS_TURN_IN = 3;
|
||||||
private static final int STATUS_TURN = 4;
|
static final int STATUS_TURN = 4;
|
||||||
private static final int STATUS_TOLD = 5;
|
static final int STATUS_TOLD = 5;
|
||||||
|
|
||||||
private final RoutingHelper router;
|
protected final RoutingHelper router;
|
||||||
private static CommandPlayer player;
|
protected static CommandPlayer player;
|
||||||
private final OsmandSettings settings;
|
protected final OsmandSettings settings;
|
||||||
|
|
||||||
private static boolean mute = false;
|
private static boolean mute = false;
|
||||||
private static int currentStatus = STATUS_UNKNOWN;
|
static int currentStatus = STATUS_UNKNOWN;
|
||||||
private static boolean playedAndArriveAtTarget = false;
|
static boolean playedAndArriveAtTarget = false;
|
||||||
private static float playGoAheadDist = 0;
|
static float playGoAheadDist = 0;
|
||||||
private static long lastAnnouncedSpeedLimit = 0;
|
private static long lastAnnouncedSpeedLimit = 0;
|
||||||
private static long waitAnnouncedSpeedLimit = 0;
|
private static long waitAnnouncedSpeedLimit = 0;
|
||||||
private static long lastAnnouncedOffRoute = 0;
|
private static long lastAnnouncedOffRoute = 0;
|
||||||
private static long waitAnnouncedOffRoute = 0;
|
private static long waitAnnouncedOffRoute = 0;
|
||||||
private static boolean suppressDest = false;
|
static boolean suppressDest = false;
|
||||||
private static boolean announceBackOnRoute = false;
|
static boolean announceBackOnRoute = false;
|
||||||
// private static long lastTimeRouteRecalcAnnounced = 0;
|
// private static long lastTimeRouteRecalcAnnounced = 0;
|
||||||
// Remember when last announcement was made
|
// Remember when last announcement was made
|
||||||
private static long lastAnnouncement = 0;
|
private static long lastAnnouncement = 0;
|
||||||
|
@ -66,9 +66,11 @@ public class VoiceRouter {
|
||||||
protected int TURN_DISTANCE = 0;
|
protected int TURN_DISTANCE = 0;
|
||||||
|
|
||||||
protected static VoiceCommandPending pendingCommand = null;
|
protected static VoiceCommandPending pendingCommand = null;
|
||||||
private static RouteDirectionInfo nextRouteDirection;
|
static RouteDirectionInfo nextRouteDirection;
|
||||||
private Term empty;
|
private Term empty;
|
||||||
|
|
||||||
|
private boolean useJS;
|
||||||
|
|
||||||
public interface VoiceMessageListener {
|
public interface VoiceMessageListener {
|
||||||
void onVoiceMessage();
|
void onVoiceMessage();
|
||||||
}
|
}
|
||||||
|
@ -78,6 +80,7 @@ public class VoiceRouter {
|
||||||
public VoiceRouter(RoutingHelper router, final OsmandSettings settings) {
|
public VoiceRouter(RoutingHelper router, final OsmandSettings settings) {
|
||||||
this.router = router;
|
this.router = router;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
useJS = settings.USE_JS_VOICE_GUIDANCE.get();
|
||||||
this.mute = settings.VOICE_MUTE.get();
|
this.mute = settings.VOICE_MUTE.get();
|
||||||
empty = new Struct("");
|
empty = new Struct("");
|
||||||
voiceMessageListeners = new ConcurrentHashMap<VoiceRouter.VoiceMessageListener, Integer>();
|
voiceMessageListeners = new ConcurrentHashMap<VoiceRouter.VoiceMessageListener, Integer>();
|
||||||
|
@ -164,7 +167,7 @@ public class VoiceRouter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private double btScoDelayDistance = 0;
|
double btScoDelayDistance = 0;
|
||||||
|
|
||||||
public boolean isDistanceLess(float currentSpeed, double dist, double etalon, float defSpeed) {
|
public boolean isDistanceLess(float currentSpeed, double dist, double etalon, float defSpeed) {
|
||||||
if (defSpeed <= 0) {
|
if (defSpeed <= 0) {
|
||||||
|
@ -201,7 +204,7 @@ public class VoiceRouter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void nextStatusAfter(int previousStatus) {
|
void nextStatusAfter(int previousStatus) {
|
||||||
//STATUS_UNKNOWN=0 -> STATUS_LONG_PREPARE=1 -> STATUS_PREPARE=2 -> STATUS_TURN_IN=3 -> STATUS_TURN=4 -> STATUS_TOLD=5
|
//STATUS_UNKNOWN=0 -> STATUS_LONG_PREPARE=1 -> STATUS_PREPARE=2 -> STATUS_TURN_IN=3 -> STATUS_TURN=4 -> STATUS_TOLD=5
|
||||||
if (previousStatus != STATUS_TOLD) {
|
if (previousStatus != STATUS_TOLD) {
|
||||||
this.currentStatus = previousStatus + 1;
|
this.currentStatus = previousStatus + 1;
|
||||||
|
@ -210,7 +213,7 @@ public class VoiceRouter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean statusNotPassed(int statusToCheck) {
|
boolean statusNotPassed(int statusToCheck) {
|
||||||
return currentStatus <= statusToCheck;
|
return currentStatus <= statusToCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,14 +398,14 @@ public class VoiceRouter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isTargetPoint(NextDirectionInfo info) {
|
boolean isTargetPoint(NextDirectionInfo info) {
|
||||||
boolean in = info != null && info.intermediatePoint;
|
boolean in = info != null && info.intermediatePoint;
|
||||||
boolean target = info == null || info.directionInfo == null
|
boolean target = info == null || info.directionInfo == null
|
||||||
|| info.directionInfo.distance == 0;
|
|| info.directionInfo.distance == 0;
|
||||||
return in || target;
|
return in || target;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean needsInforming() {
|
boolean needsInforming() {
|
||||||
final Integer repeat = settings.KEEP_INFORMING.get();
|
final Integer repeat = settings.KEEP_INFORMING.get();
|
||||||
if (repeat == null || repeat == 0) return false;
|
if (repeat == null || repeat == 0) return false;
|
||||||
|
|
||||||
|
@ -557,7 +560,7 @@ public class VoiceRouter {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playThen() {
|
void playThen() {
|
||||||
CommandBuilder play = getNewCommandPlayerToPlay();
|
CommandBuilder play = getNewCommandPlayerToPlay();
|
||||||
if (play != null) {
|
if (play != null) {
|
||||||
notifyOnVoiceMessage();
|
notifyOnVoiceMessage();
|
||||||
|
@ -715,7 +718,7 @@ public class VoiceRouter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playAndArriveAtDestination(NextDirectionInfo info) {
|
void playAndArriveAtDestination(NextDirectionInfo info) {
|
||||||
if (isTargetPoint(info)) {
|
if (isTargetPoint(info)) {
|
||||||
String pointName = info == null ? "" : info.pointName;
|
String pointName = info == null ? "" : info.pointName;
|
||||||
CommandBuilder play = getNewCommandPlayerToPlay();
|
CommandBuilder play = getNewCommandPlayerToPlay();
|
||||||
|
@ -781,7 +784,7 @@ public class VoiceRouter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTurnType(TurnType t) {
|
String getTurnType(TurnType t) {
|
||||||
if (TurnType.TL == t.getValue()) {
|
if (TurnType.TL == t.getValue()) {
|
||||||
return AbstractPrologCommandPlayer.A_LEFT;
|
return AbstractPrologCommandPlayer.A_LEFT;
|
||||||
} else if (TurnType.TSHL == t.getValue()) {
|
} else if (TurnType.TSHL == t.getValue()) {
|
||||||
|
|
164
OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java
Normal file
164
OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
package net.osmand.plus.voice;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.AudioManager;
|
||||||
|
|
||||||
|
import net.osmand.PlatformUtil;
|
||||||
|
import net.osmand.StateChangedListener;
|
||||||
|
import net.osmand.plus.ApplicationMode;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.api.AudioFocusHelper;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import alice.tuprolog.InvalidLibraryException;
|
||||||
|
import alice.tuprolog.Prolog;
|
||||||
|
import alice.tuprolog.Struct;
|
||||||
|
import alice.tuprolog.Term;
|
||||||
|
import alice.tuprolog.Var;
|
||||||
|
|
||||||
|
public abstract class AbstractJSCommandPlayer implements CommandPlayer, StateChangedListener<ApplicationMode> {
|
||||||
|
private static final Log log = PlatformUtil.getLog(AbstractJSCommandPlayer.class);
|
||||||
|
|
||||||
|
protected String language = "";
|
||||||
|
protected OsmandApplication ctx;
|
||||||
|
protected int streamType;
|
||||||
|
private ApplicationMode applicationMode;
|
||||||
|
|
||||||
|
public static boolean btScoStatus = false;
|
||||||
|
private static AudioFocusHelper mAudioFocusHelper;
|
||||||
|
public static String btScoInit = "";
|
||||||
|
|
||||||
|
protected AbstractJSCommandPlayer(OsmandApplication ctx, ApplicationMode applicationMode,
|
||||||
|
String voiceProvider) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.applicationMode = applicationMode;
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
this.ctx = ctx;
|
||||||
|
|
||||||
|
if (log.isInfoEnabled()) {
|
||||||
|
log.info("Initializing prolog system : " + (System.currentTimeMillis() - time)); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
this.streamType = ctx.getSettings().AUDIO_STREAM_GUIDANCE.getModeValue(applicationMode);
|
||||||
|
language = voiceProvider.substring(0, voiceProvider.indexOf("-"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentVoice() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandBuilder newCommandBuilder() {
|
||||||
|
JSCommandBuilder commandBuilder = new JSCommandBuilder(this);
|
||||||
|
commandBuilder.setParameters("km-m", true);
|
||||||
|
return commandBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playCommands(CommandBuilder builder) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
if(ctx != null && ctx.getSettings() != null) {
|
||||||
|
ctx.getSettings().APPLICATION_MODE.removeListener(this);
|
||||||
|
}
|
||||||
|
abandonAudioFocus();
|
||||||
|
ctx = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> execute(List<Struct> listStruct) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateAudioStream(int streamType) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLanguage() {
|
||||||
|
return "en";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsStructuredStreetNames() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void abandonAudioFocus() {
|
||||||
|
log.debug("abandonAudioFocus");
|
||||||
|
if ((ctx != null && ctx.getSettings().AUDIO_STREAM_GUIDANCE.getModeValue(applicationMode) == 0) || (btScoStatus == true)) {
|
||||||
|
toggleBtSco(false);
|
||||||
|
}
|
||||||
|
if (ctx != null && mAudioFocusHelper != null) {
|
||||||
|
mAudioFocusHelper.abandonFocus(ctx, applicationMode, streamType);
|
||||||
|
}
|
||||||
|
mAudioFocusHelper = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized boolean toggleBtSco(boolean on) {
|
||||||
|
// Hardy, 2016-07-03: Establish a low quality BT SCO (Synchronous Connection-Oriented) link to interrupt e.g. a car stereo FM radio
|
||||||
|
if (on) {
|
||||||
|
try {
|
||||||
|
AudioManager mAudioManager = (AudioManager) ctx.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
if (mAudioManager == null || !mAudioManager.isBluetoothScoAvailableOffCall()) {
|
||||||
|
btScoInit = "Reported not available.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mAudioManager.setMode(0);
|
||||||
|
mAudioManager.startBluetoothSco();
|
||||||
|
mAudioManager.setBluetoothScoOn(true);
|
||||||
|
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||||
|
btScoStatus = true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("Exception starting BT SCO " + e.getMessage() );
|
||||||
|
btScoStatus = false;
|
||||||
|
btScoInit = "Available, but not initializad.\n(" + e.getMessage() + ")";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
btScoInit = "Available, initialized OK.";
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
AudioManager mAudioManager = (AudioManager) ctx.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
if (mAudioManager == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mAudioManager.setBluetoothScoOn(false);
|
||||||
|
mAudioManager.stopBluetoothSco();
|
||||||
|
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||||
|
btScoStatus = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationMode getApplicationMode() {
|
||||||
|
return applicationMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ApplicationMode change) {
|
||||||
|
//
|
||||||
|
// if(prologSystem != null) {
|
||||||
|
// try {
|
||||||
|
// prologSystem.getTheoryManager().retract(new Struct("appMode", new Var()));
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("Retract error: ", e);
|
||||||
|
// }
|
||||||
|
// prologSystem.getTheoryManager()
|
||||||
|
// .assertA(
|
||||||
|
// new Struct("appMode", new Struct(ctx.getSettings().APPLICATION_MODE.get().getStringKey()
|
||||||
|
// .toLowerCase())), true, "", true);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,8 +53,8 @@ public class CommandBuilder {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private final CommandPlayer commandPlayer;
|
protected final CommandPlayer commandPlayer;
|
||||||
private boolean alreadyExecuted = false;
|
protected boolean alreadyExecuted = false;
|
||||||
private List<Struct> listStruct = new ArrayList<Struct>();
|
private List<Struct> listStruct = new ArrayList<Struct>();
|
||||||
|
|
||||||
public CommandBuilder(CommandPlayer commandPlayer){
|
public CommandBuilder(CommandPlayer commandPlayer){
|
||||||
|
|
235
OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java
Normal file
235
OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
package net.osmand.plus.voice;
|
||||||
|
|
||||||
|
import net.osmand.PlatformUtil;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.mozilla.javascript.Callable;
|
||||||
|
import org.mozilla.javascript.Context;
|
||||||
|
import org.mozilla.javascript.Function;
|
||||||
|
import org.mozilla.javascript.NativeJSON;
|
||||||
|
import org.mozilla.javascript.Scriptable;
|
||||||
|
import org.mozilla.javascript.ScriptableObject;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class JSCommandBuilder extends CommandBuilder {
|
||||||
|
|
||||||
|
private static final Log log = PlatformUtil.getLog(JSCommandBuilder.class);
|
||||||
|
private static final String SET_MODE = "setMode";
|
||||||
|
private static final String SET_METRIC_CONST = "setMetricConst";
|
||||||
|
|
||||||
|
private Context jsContext;
|
||||||
|
private List<String> listStruct = new ArrayList<>();
|
||||||
|
private ScriptableObject jsScope;
|
||||||
|
|
||||||
|
JSCommandBuilder(CommandPlayer commandPlayer) {
|
||||||
|
super(commandPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJSContext(ScriptableObject jsScope) {
|
||||||
|
jsContext = Context.enter();
|
||||||
|
this.jsScope = jsScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object convertStreetName(Map<String, String> streetName) {
|
||||||
|
return NativeJSON.parse(jsContext, jsScope, new JSONObject(streetName).toString(),
|
||||||
|
new NullCallable());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParameters(String metricCons, boolean tts) {
|
||||||
|
Object mode = jsScope.get(SET_MODE, jsScope);
|
||||||
|
Object metrics = jsScope.get(SET_METRIC_CONST, jsScope);
|
||||||
|
callVoidJsFunction(mode, new Object[]{tts});
|
||||||
|
callVoidJsFunction(metrics, new Object[]{metricCons});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void callVoidJsFunction(Object function, Object[] params) {
|
||||||
|
if (function instanceof Function) {
|
||||||
|
Function jsFunction = (Function) function;
|
||||||
|
jsFunction.call(jsContext, jsScope, jsScope, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSCommandBuilder addCommand(String name, Object... args){
|
||||||
|
Object obj = jsScope.get(name);
|
||||||
|
if (obj instanceof Function) {
|
||||||
|
Function jsFunction = (Function) obj;
|
||||||
|
Object jsResult = jsFunction.call(jsContext, jsScope, jsScope, args);
|
||||||
|
listStruct.add(Context.toString(jsResult));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder goAhead(){
|
||||||
|
return goAhead(-1, new HashMap<String, String>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder goAhead(double dist, Map<String, String> streetName){
|
||||||
|
return addCommand(C_GO_AHEAD, dist, convertStreetName(streetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder makeUTwp(){
|
||||||
|
return makeUT(new HashMap<String, String>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder makeUT(Map<String, String> streetName){
|
||||||
|
return makeUT(-1, streetName);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder speedAlarm(int maxSpeed, float speed){
|
||||||
|
return addCommand(C_SPEAD_ALARM, maxSpeed, speed);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder attention(String type){
|
||||||
|
return addCommand(C_ATTENTION, type);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder offRoute(double dist){
|
||||||
|
return addCommand(C_OFF_ROUTE, dist);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public CommandBuilder backOnRoute(){
|
||||||
|
return addCommand(C_BACK_ON_ROUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder makeUT(double dist, Map<String,String> streetName){
|
||||||
|
return addCommand(C_MAKE_UT, dist, convertStreetName(streetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder prepareMakeUT(double dist, Map<String, String> streetName){
|
||||||
|
return addCommand(C_PREPARE_MAKE_UT, dist, convertStreetName(streetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public JSCommandBuilder turn(String param, Map<String, String> streetName) {
|
||||||
|
return turn(param, -1, streetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder turn(String param, double dist, Map<String, String> streetName) {
|
||||||
|
return addCommand(C_TURN, param, dist, convertStreetName(streetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param param A_LEFT, A_RIGHT, ...
|
||||||
|
* @param dist
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public JSCommandBuilder prepareTurn(String param, double dist, Map<String, String> streetName){
|
||||||
|
return addCommand(C_PREPARE_TURN, param, dist, convertStreetName(streetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder prepareRoundAbout(double dist, int exit, Map<String, String> streetName){
|
||||||
|
return addCommand(C_PREPARE_ROUNDABOUT, dist, exit, convertStreetName(streetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder roundAbout(double dist, double angle, int exit, Map<String, String> streetName){
|
||||||
|
return addCommand(C_ROUNDABOUT, dist, angle, exit, convertStreetName(streetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder roundAbout(double angle, int exit, Map<String, String> streetName) {
|
||||||
|
return roundAbout(-1, angle, exit, streetName);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder andArriveAtDestination(String name){
|
||||||
|
return addCommand(C_AND_ARRIVE_DESTINATION, name);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder arrivedAtDestination(String name){
|
||||||
|
return addCommand(C_REACHED_DESTINATION, name);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder andArriveAtIntermediatePoint(String name){
|
||||||
|
return addCommand(C_AND_ARRIVE_INTERMEDIATE, name);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder arrivedAtIntermediatePoint(String name) {
|
||||||
|
return addCommand(C_REACHED_INTERMEDIATE, name);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder andArriveAtWayPoint(String name){
|
||||||
|
return addCommand(C_AND_ARRIVE_WAYPOINT, name);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder arrivedAtWayPoint(String name) {
|
||||||
|
return addCommand(C_REACHED_WAYPOINT, name);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder andArriveAtFavorite(String name) {
|
||||||
|
return addCommand(C_AND_ARRIVE_FAVORITE, name);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder arrivedAtFavorite(String name) {
|
||||||
|
return addCommand(C_REACHED_FAVORITE, name);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder andArriveAtPoi(String name) {
|
||||||
|
return addCommand(C_AND_ARRIVE_POI_WAYPOINT, name);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder arrivedAtPoi(String name) {
|
||||||
|
return addCommand(C_REACHED_POI, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder bearLeft(Map<String,String> streetName){
|
||||||
|
return addCommand(C_BEAR_LEFT, convertStreetName(streetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSCommandBuilder bearRight(Map<String, String> streetName){
|
||||||
|
return addCommand(C_BEAR_RIGHT, convertStreetName(streetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder then(){
|
||||||
|
return addCommand(C_THEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder gpsLocationLost() {
|
||||||
|
return addCommand(C_LOCATION_LOST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder gpsLocationRecover() {
|
||||||
|
return addCommand(C_LOCATION_RECOVERED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder newRouteCalculated(double dist, int time){
|
||||||
|
return addCommand(C_ROUTE_NEW_CALC, dist, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder routeRecalculated(double dist, int time){
|
||||||
|
return addCommand(C_ROUTE_RECALC, dist, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void play(){
|
||||||
|
this.commandPlayer.playCommands(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> execute(){
|
||||||
|
alreadyExecuted = true;
|
||||||
|
return listStruct;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NullCallable implements Callable
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Object call(Context context, Scriptable scope, Scriptable holdable, Object[] objects)
|
||||||
|
{
|
||||||
|
return objects[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
OsmAnd/src/net/osmand/plus/voice/JSMediaCommandPlayerImpl.java
Normal file
101
OsmAnd/src/net/osmand/plus/voice/JSMediaCommandPlayerImpl.java
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package net.osmand.plus.voice;
|
||||||
|
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.system.Os;
|
||||||
|
|
||||||
|
import net.osmand.IndexConstants;
|
||||||
|
import net.osmand.PlatformUtil;
|
||||||
|
import net.osmand.plus.ApplicationMode;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.routing.VoiceRouter;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.mozilla.javascript.ScriptableObject;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static net.osmand.plus.voice.AbstractPrologCommandPlayer.DELAY_CONST;
|
||||||
|
|
||||||
|
public class JSMediaCommandPlayerImpl extends MediaCommandPlayerImpl {
|
||||||
|
|
||||||
|
private static final org.apache.commons.logging.Log log = PlatformUtil.getLog(JSMediaCommandPlayerImpl.class);
|
||||||
|
|
||||||
|
private ScriptableObject jsScope;
|
||||||
|
private OsmandApplication app;
|
||||||
|
|
||||||
|
public JSMediaCommandPlayerImpl(OsmandApplication ctx, ApplicationMode applicationMode, VoiceRouter vrt, String voiceProvider) throws CommandPlayerException {
|
||||||
|
super(ctx, applicationMode, vrt, voiceProvider);
|
||||||
|
app = ctx;
|
||||||
|
|
||||||
|
org.mozilla.javascript.Context context = org.mozilla.javascript.Context.enter();
|
||||||
|
context.setOptimizationLevel(-1);
|
||||||
|
jsScope = context.initSafeStandardObjects();
|
||||||
|
try {
|
||||||
|
BufferedReader br = new BufferedReader(new FileReader(new File(
|
||||||
|
app.getAppPath(IndexConstants.VOICE_INDEX_DIR).getAbsolutePath() +
|
||||||
|
"/" + voiceProvider + "/" + language + "_tts.js")));
|
||||||
|
context.evaluateReader(jsScope, br, "JS", 1, null);
|
||||||
|
br.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
org.mozilla.javascript.Context.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void playCommands(CommandBuilder builder) {
|
||||||
|
if(vrt.isMute()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filesToPlay.addAll(splitAnnouncements(builder.execute()));
|
||||||
|
|
||||||
|
// If we have not already started to play audio, start.
|
||||||
|
if (mediaPlayer == null) {
|
||||||
|
requestAudioFocus();
|
||||||
|
// Delay first prompt of each batch to allow BT SCO connection being established
|
||||||
|
if (ctx != null && ctx.getSettings().AUDIO_STREAM_GUIDANCE.getModeValue(getApplicationMode()) == 0) {
|
||||||
|
try {
|
||||||
|
log.debug("Delaying MediaCommandPlayer for BT SCO");
|
||||||
|
Thread.sleep(ctx.getSettings().BT_SCO_DELAY.get());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
playQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> splitAnnouncements(List<String> execute) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
for (String files : execute) {
|
||||||
|
result.addAll(Arrays.asList(files.split(" ")));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder newCommandBuilder() {
|
||||||
|
JSCommandBuilder commandBuilder = new JSCommandBuilder(this);
|
||||||
|
commandBuilder.setJSContext(jsScope);
|
||||||
|
commandBuilder.setParameters(app.getSettings().METRIC_SYSTEM.get().toTTSString(), false);
|
||||||
|
return commandBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMyData(File voiceDir) {
|
||||||
|
for (File f : voiceDir.listFiles()) {
|
||||||
|
if (f.getName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
348
OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java
Normal file
348
OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java
Normal file
|
@ -0,0 +1,348 @@
|
||||||
|
package net.osmand.plus.voice;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.speech.tts.TextToSpeech;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import net.osmand.IndexConstants;
|
||||||
|
import net.osmand.PlatformUtil;
|
||||||
|
import net.osmand.plus.ApplicationMode;
|
||||||
|
import net.osmand.plus.OsmandApplication;
|
||||||
|
import net.osmand.plus.R;
|
||||||
|
import net.osmand.plus.activities.SettingsActivity;
|
||||||
|
import net.osmand.plus.routing.VoiceRouter;
|
||||||
|
import net.osmand.util.Algorithms;
|
||||||
|
|
||||||
|
import org.mozilla.javascript.ScriptableObject;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer {
|
||||||
|
private static final org.apache.commons.logging.Log log = PlatformUtil.getLog(JSTTSCommandPlayerImpl.class);
|
||||||
|
|
||||||
|
private static final String TAG = JSTTSCommandPlayerImpl.class.getSimpleName();
|
||||||
|
private static TextToSpeech mTts;
|
||||||
|
private static String ttsVoiceStatus = "";
|
||||||
|
private static String ttsVoiceUsed = "";
|
||||||
|
|
||||||
|
private boolean speechAllowed = false;
|
||||||
|
private Context mTtsContext;
|
||||||
|
|
||||||
|
private ScriptableObject jsScope;
|
||||||
|
|
||||||
|
private float cSpeechRate = 1;
|
||||||
|
|
||||||
|
private static final class IntentStarter implements
|
||||||
|
DialogInterface.OnClickListener {
|
||||||
|
private final Context ctx;
|
||||||
|
private final String intentAction;
|
||||||
|
private final Uri intentData;
|
||||||
|
|
||||||
|
private IntentStarter(Context ctx, String intentAction) {
|
||||||
|
this(ctx,intentAction, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntentStarter(Context ctx, String intentAction, Uri intentData) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.intentAction = intentAction;
|
||||||
|
this.intentData = intentData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
Intent installIntent = new Intent();
|
||||||
|
installIntent.setAction(intentAction);
|
||||||
|
if (intentData != null) {
|
||||||
|
installIntent.setData(intentData);
|
||||||
|
}
|
||||||
|
ctx.startActivity(installIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private OsmandApplication app;
|
||||||
|
private ApplicationMode appMode;
|
||||||
|
private VoiceRouter vrt;
|
||||||
|
|
||||||
|
private HashMap<String, String> params = new HashMap<String, String>();
|
||||||
|
|
||||||
|
private static int ttsRequests = 0;
|
||||||
|
|
||||||
|
public JSTTSCommandPlayerImpl(Activity ctx, ApplicationMode applicationMode, VoiceRouter vrt, String voiceProvider) throws CommandPlayerException {
|
||||||
|
super((OsmandApplication) ctx.getApplication(), applicationMode, voiceProvider);
|
||||||
|
this.app = (OsmandApplication) ctx.getApplicationContext();
|
||||||
|
this.appMode = applicationMode;
|
||||||
|
this.vrt = vrt;
|
||||||
|
if (Algorithms.isEmpty(language)) {
|
||||||
|
throw new CommandPlayerException(
|
||||||
|
ctx.getString(R.string.voice_data_corrupted));
|
||||||
|
}
|
||||||
|
OsmandApplication app = (OsmandApplication) ctx.getApplicationContext();
|
||||||
|
if(app.accessibilityEnabled()) {
|
||||||
|
cSpeechRate = app.getSettings().SPEECH_RATE.get();
|
||||||
|
}
|
||||||
|
initializeEngine(app, ctx);
|
||||||
|
params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, app.getSettings().AUDIO_STREAM_GUIDANCE
|
||||||
|
.getModeValue(getApplicationMode()).toString());
|
||||||
|
org.mozilla.javascript.Context context = org.mozilla.javascript.Context.enter();
|
||||||
|
context.setOptimizationLevel(-1);
|
||||||
|
jsScope = context.initSafeStandardObjects();
|
||||||
|
try {
|
||||||
|
BufferedReader br = new BufferedReader(new FileReader(new File(
|
||||||
|
app.getAppPath(IndexConstants.VOICE_INDEX_DIR).getAbsolutePath() +
|
||||||
|
"/" + voiceProvider + "/" + language + "_tts.js")));
|
||||||
|
context.evaluateReader(jsScope, br, "JS", 1, null);
|
||||||
|
br.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
org.mozilla.javascript.Context.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeEngine(final Context ctx, final Activity act) {
|
||||||
|
if (mTtsContext != ctx) {
|
||||||
|
internalClear();
|
||||||
|
}
|
||||||
|
if (mTts == null) {
|
||||||
|
mTtsContext = ctx;
|
||||||
|
ttsVoiceStatus = "";
|
||||||
|
ttsVoiceUsed = "";
|
||||||
|
ttsRequests = 0;
|
||||||
|
final float speechRate = cSpeechRate;
|
||||||
|
|
||||||
|
final String[] lsplit = (language + "____.").split("[_\\-]");
|
||||||
|
// constructor supports lang_country_variant
|
||||||
|
Locale newLocale0 = new Locale(lsplit[0], lsplit[1], lsplit[2]);
|
||||||
|
// #3344: Try Locale builder instead of constructor (only available from API 21). Also supports script (for now supported as trailing x_x_x_Scrp)
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 21) {
|
||||||
|
try {
|
||||||
|
newLocale0 = new Locale.Builder().setLanguage(lsplit[0]).setScript(lsplit[3]).setRegion(lsplit[1]).setVariant(lsplit[2]).build();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
// Falls back to constructor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final Locale newLocale = newLocale0;
|
||||||
|
|
||||||
|
mTts = new TextToSpeech(ctx, new TextToSpeech.OnInitListener() {
|
||||||
|
@Override
|
||||||
|
public void onInit(int status) {
|
||||||
|
if (status != TextToSpeech.SUCCESS) {
|
||||||
|
ttsVoiceStatus = "NO INIT SUCCESS";
|
||||||
|
internalClear();
|
||||||
|
} else if (mTts != null) {
|
||||||
|
speechAllowed = true;
|
||||||
|
switch (mTts.isLanguageAvailable(newLocale)) {
|
||||||
|
case TextToSpeech.LANG_MISSING_DATA:
|
||||||
|
if (isSettingsActivity(act)) {
|
||||||
|
AlertDialog.Builder builder = createAlertDialog(
|
||||||
|
R.string.tts_missing_language_data_title,
|
||||||
|
R.string.tts_missing_language_data,
|
||||||
|
new JSTTSCommandPlayerImpl.IntentStarter(
|
||||||
|
act,
|
||||||
|
TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA),
|
||||||
|
act);
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
ttsVoiceStatus = newLocale.getDisplayName() + ": LANG_MISSING_DATA";
|
||||||
|
ttsVoiceUsed = getVoiceUsed();
|
||||||
|
break;
|
||||||
|
case TextToSpeech.LANG_AVAILABLE:
|
||||||
|
ttsVoiceStatus = newLocale.getDisplayName() + ": LANG_AVAILABLE";
|
||||||
|
case TextToSpeech.LANG_COUNTRY_AVAILABLE:
|
||||||
|
ttsVoiceStatus = "".equals(ttsVoiceStatus) ? newLocale.getDisplayName() + ": LANG_COUNTRY_AVAILABLE" : ttsVoiceStatus;
|
||||||
|
case TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE:
|
||||||
|
try {
|
||||||
|
mTts.setLanguage(newLocale);
|
||||||
|
} catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
mTts.setLanguage(Locale.getDefault());
|
||||||
|
}
|
||||||
|
if(speechRate != 1) {
|
||||||
|
mTts.setSpeechRate(speechRate);
|
||||||
|
}
|
||||||
|
ttsVoiceStatus = "".equals(ttsVoiceStatus) ? newLocale.getDisplayName() + ": LANG_COUNTRY_VAR_AVAILABLE" : ttsVoiceStatus;
|
||||||
|
ttsVoiceUsed = getVoiceUsed();
|
||||||
|
break;
|
||||||
|
case TextToSpeech.LANG_NOT_SUPPORTED:
|
||||||
|
//maybe weird, but I didn't want to introduce parameter in around 5 methods just to do this if condition
|
||||||
|
if (isSettingsActivity(act)) {
|
||||||
|
AlertDialog.Builder builder = createAlertDialog(
|
||||||
|
R.string.tts_language_not_supported_title,
|
||||||
|
R.string.tts_language_not_supported,
|
||||||
|
new JSTTSCommandPlayerImpl.IntentStarter(
|
||||||
|
act,
|
||||||
|
Intent.ACTION_VIEW, Uri.parse("market://search?q=text to speech engine"
|
||||||
|
)),
|
||||||
|
act);
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
ttsVoiceStatus = newLocale.getDisplayName() + ": LANG_NOT_SUPPORTED";
|
||||||
|
ttsVoiceUsed = getVoiceUsed();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSettingsActivity(final Context ctx) {
|
||||||
|
return ctx instanceof SettingsActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getVoiceUsed() {
|
||||||
|
try {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 21) {
|
||||||
|
if (mTts.getVoice() != null) {
|
||||||
|
return mTts.getVoice().toString() + " (API " + android.os.Build.VERSION.SDK_INT + ")";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return mTts.getLanguage() + " (API " + android.os.Build.VERSION.SDK_INT + " only reports language)";
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
// mTts.getVoice() might throw NPE
|
||||||
|
}
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mTts.setOnUtteranceCompletedListener(new TextToSpeech.OnUtteranceCompletedListener() {
|
||||||
|
// The call back is on a binder thread.
|
||||||
|
@Override
|
||||||
|
public synchronized void onUtteranceCompleted(String utteranceId) {
|
||||||
|
if (--ttsRequests <= 0)
|
||||||
|
abandonAudioFocus();
|
||||||
|
log.debug("ttsRequests="+ttsRequests);
|
||||||
|
if (ttsRequests < 0) {
|
||||||
|
ttsRequests = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentVoice() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSCommandBuilder newCommandBuilder() {
|
||||||
|
JSCommandBuilder commandBuilder = new JSCommandBuilder(this);
|
||||||
|
commandBuilder.setJSContext(jsScope);
|
||||||
|
commandBuilder.setParameters(app.getSettings().METRIC_SYSTEM.get().toTTSString(), true);
|
||||||
|
return commandBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playCommands(CommandBuilder builder) {
|
||||||
|
final List<String> execute = builder.execute(); //list of strings, the speech text, play it
|
||||||
|
StringBuilder bld = new StringBuilder();
|
||||||
|
for (String s : execute) {
|
||||||
|
bld.append(s).append(' ');
|
||||||
|
}
|
||||||
|
if (mTts != null && !vrt.isMute() && speechAllowed) {
|
||||||
|
if (ttsRequests++ == 0) {
|
||||||
|
// Delay first prompt of each batch to allow BT SCO connection being established
|
||||||
|
if (app.getSettings().AUDIO_STREAM_GUIDANCE.getModeValue(appMode) == 0) {
|
||||||
|
ttsRequests++;
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < 21) {
|
||||||
|
params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,""+System.currentTimeMillis());
|
||||||
|
mTts.playSilence(app.getSettings().BT_SCO_DELAY.get(), TextToSpeech.QUEUE_ADD, params);
|
||||||
|
} else {
|
||||||
|
mTts.playSilentUtterance(app.getSettings().BT_SCO_DELAY.get(), TextToSpeech.QUEUE_ADD, ""+System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d(TAG, "ttsRequests= "+ttsRequests);
|
||||||
|
params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,""+System.currentTimeMillis());
|
||||||
|
mTts.speak(bld.toString(), TextToSpeech.QUEUE_ADD, params);
|
||||||
|
// Audio focus will be released when onUtteranceCompleted() completed is called by the TTS engine.
|
||||||
|
} else if (app != null && vrt.isMute()) {
|
||||||
|
// sendAlertToAndroidWear(ctx, bld.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop(){
|
||||||
|
ttsRequests = 0;
|
||||||
|
if (mTts != null){
|
||||||
|
mTts.stop();
|
||||||
|
}
|
||||||
|
abandonAudioFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getTtsVoiceStatus() {
|
||||||
|
return ttsVoiceStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getTtsVoiceUsed() {
|
||||||
|
return ttsVoiceUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
super.clear();
|
||||||
|
internalClear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateAudioStream(int streamType) {
|
||||||
|
super.updateAudioStream(streamType);
|
||||||
|
params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, streamType+"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLanguage() {
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsStructuredStreetNames() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void internalClear() {
|
||||||
|
ttsRequests = 0;
|
||||||
|
speechAllowed = false;
|
||||||
|
if (mTts != null) {
|
||||||
|
mTts.shutdown();
|
||||||
|
mTts = null;
|
||||||
|
}
|
||||||
|
abandonAudioFocus();
|
||||||
|
mTtsContext = null;
|
||||||
|
ttsVoiceStatus = "";
|
||||||
|
ttsVoiceUsed = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMyData(File voiceDir) {
|
||||||
|
boolean isTTS = voiceDir.getName().contains("tts");
|
||||||
|
if (!isTTS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (File f : voiceDir.listFiles()) {
|
||||||
|
if (f.getName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AlertDialog.Builder createAlertDialog(int titleResID, int messageResID,
|
||||||
|
JSTTSCommandPlayerImpl.IntentStarter intentStarter, final Activity ctx) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
|
||||||
|
builder.setCancelable(true);
|
||||||
|
builder.setNegativeButton(R.string.shared_string_no, null);
|
||||||
|
builder.setPositiveButton(R.string.shared_string_yes, intentStarter);
|
||||||
|
builder.setTitle(titleResID);
|
||||||
|
builder.setMessage(messageResID);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,10 +29,10 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen
|
||||||
private static final Log log = PlatformUtil.getLog(MediaCommandPlayerImpl.class);
|
private static final Log log = PlatformUtil.getLog(MediaCommandPlayerImpl.class);
|
||||||
|
|
||||||
// playing media
|
// playing media
|
||||||
private MediaPlayer mediaPlayer;
|
MediaPlayer mediaPlayer;
|
||||||
// indicates that player is ready to play first file
|
// indicates that player is ready to play first file
|
||||||
private List<String> filesToPlay = Collections.synchronizedList(new ArrayList<String>());
|
List<String> filesToPlay = Collections.synchronizedList(new ArrayList<String>());
|
||||||
private VoiceRouter vrt;
|
VoiceRouter vrt;
|
||||||
|
|
||||||
|
|
||||||
public MediaCommandPlayerImpl(OsmandApplication ctx, ApplicationMode applicationMode, VoiceRouter vrt, String voiceProvider)
|
public MediaCommandPlayerImpl(OsmandApplication ctx, ApplicationMode applicationMode, VoiceRouter vrt, String voiceProvider)
|
||||||
|
@ -95,7 +95,7 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen
|
||||||
playQueue();
|
playQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void playQueue() {
|
synchronized void playQueue() {
|
||||||
if (mediaPlayer == null) {
|
if (mediaPlayer == null) {
|
||||||
mediaPlayer = new MediaPlayer();
|
mediaPlayer = new MediaPlayer();
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen
|
||||||
* @param file
|
* @param file
|
||||||
*/
|
*/
|
||||||
private void playFile(File file) {
|
private void playFile(File file) {
|
||||||
if (!file.exists()) {
|
if (!file.exists() || file.isDirectory()) {
|
||||||
log.error("Unable to play, does not exist: "+file);
|
log.error("Unable to play, does not exist: "+file);
|
||||||
playQueue();
|
playQueue();
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in a new issue