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 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 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 RENDERERS_DIR = "rendering/"; //$NON-NLS-1$
|
||||
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 {
|
||||
ctx.packageManager.getPackageInfo("com.android.vending", 0)
|
||||
ctx.packageManager.getPackageInfo(appPackage, 0)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
return false
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ object AndroidUtils {
|
|||
Intent(Intent.ACTION_VIEW, Uri.parse(AndroidUtils.getPlayMarketLink(ctx, packageName)))
|
||||
|
||||
fun getPlayMarketLink(ctx: Context, packageName: String): String {
|
||||
if (isGooglePlayInstalled(ctx)) {
|
||||
if (isAppInstalled(ctx, "com.android.vending")) {
|
||||
return "market://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-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/en/config.p" destination="voice/en/_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"
|
||||
into "assets/voice"
|
||||
include "**/*.p"
|
||||
include "**/*.js"
|
||||
}
|
||||
|
||||
task cleanNoTranslate(type: Delete) {
|
||||
|
@ -361,6 +362,9 @@ dependencies {
|
|||
implementation 'com.vividsolutions:jts-core:1.14.0'
|
||||
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
// JS core
|
||||
implementation group: 'org.mozilla', name: 'rhino', version: '1.7.9'
|
||||
|
||||
// size restrictions
|
||||
// implementation 'com.ibm.icu:icu4j:50.1'
|
||||
// implementation 'net.sf.trove4j:trove4j:3.0.3'
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
android:gravity="center_vertical"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
android:text="5 selected"
|
||||
tools:text="5 selected"
|
||||
android:textColor="@color/color_white"
|
||||
android:textSize="@dimen/default_list_text_size_large"/>
|
||||
|
||||
|
@ -264,7 +264,7 @@
|
|||
android:paddingLeft="@dimen/context_menu_padding_margin_small"
|
||||
android:paddingRight="@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:textSize="@dimen/default_desc_text_size"
|
||||
osmand:typeface="@string/font_roboto_medium" />
|
||||
|
|
|
@ -3763,6 +3763,8 @@
|
|||
<string name="poi_payment_centre">Platební centrum</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_no">Sport: Ne</string>
|
||||
<string name="poi_climbing_sport_yes">Sportovní horolezectví: Ano</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>
|
||||
|
|
|
@ -3707,7 +3707,7 @@
|
|||
<string name="poi_post_flats">Piso de oficina</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>
|
||||
|
||||
|
|
|
@ -3469,7 +3469,7 @@
|
|||
<string name="poi_post_flats">Piso de oficina</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>
|
||||
|
||||
|
|
|
@ -2956,4 +2956,9 @@ Stendur fyrir svæði: %1$s x %2$s</string>
|
|||
<string name="search_no_results_description">Engar niðurstöður?
|
||||
\nSegðu okkur meira um þetta.</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>
|
||||
|
|
|
@ -2961,7 +2961,7 @@ OsmAndとOSMをサポートする方法として現状最適な方法である
|
|||
<string name="add_track_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_desc">お気に入り地点や経路データ内の経由地点をマーカーとしてまとめて追加できます。</string>
|
||||
<string name="shared_string_two">二つ</string>
|
||||
|
@ -3016,7 +3016,7 @@ OsmAndとOSMをサポートする方法として現状最適な方法である
|
|||
<string name="add_group">グループの追加</string>
|
||||
<string name="empty_state_osm_edits_descr">OSM編集はPOIの作成や更新、注釈の追加などが可能です。記録したGPXファイルを用いてあなたもOSMに貢献することができるでしょう。</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_actions">その他</string>
|
||||
|
||||
|
@ -3050,4 +3050,8 @@ OsmAndとOSMをサポートする方法として現状最適な方法である
|
|||
<string name="default_render_descr">標準的な汎用スタイルです。人口密集地の場合描写の簡素化がなされます。主な機能としては等高線、ルート、路面品質、通行制限、道路標識、SACスケールの通路描写をサポートし、急流下りなどのウォータースポーツにも使用できます。</string>
|
||||
<string name="open_wikipedia_link_online">Wikipediaをオンライン参照</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>
|
||||
|
|
|
@ -2931,4 +2931,5 @@
|
|||
<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="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>
|
||||
|
|
|
@ -46,6 +46,8 @@ import net.osmand.plus.search.QuickSearchHelper;
|
|||
import net.osmand.plus.views.corenative.NativeCoreContext;
|
||||
import net.osmand.plus.voice.CommandPlayer;
|
||||
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.TTSCommandPlayerImpl;
|
||||
import net.osmand.plus.wikivoyage.data.TravelDbHelper;
|
||||
|
@ -577,9 +579,12 @@ public class AppInitializer implements IProgress {
|
|||
if (!voiceDir.exists()) {
|
||||
throw new CommandPlayerException(ctx.getString(R.string.voice_data_unavailable));
|
||||
}
|
||||
|
||||
if (MediaCommandPlayerImpl.isMyData(voiceDir)) {
|
||||
return new MediaCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider);
|
||||
boolean useJs = app.getSettings().USE_JS_VOICE_GUIDANCE.get();
|
||||
if (useJs && JSTTSCommandPlayerImpl.isMyData(voiceDir)) {
|
||||
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)) {
|
||||
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> 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_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.SQLiteTileSource;
|
||||
import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask;
|
||||
import net.osmand.plus.voice.JSTTSCommandPlayerImpl;
|
||||
import net.osmand.plus.voice.MediaCommandPlayerImpl;
|
||||
import net.osmand.plus.voice.TTSCommandPlayerImpl;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
@ -215,11 +216,12 @@ public class LocalIndexHelper {
|
|||
|
||||
private void loadVoiceData(File voiceDir, List<LocalIndexInfo> result, boolean backup, AbstractLoadLocalIndexTask loadTask) {
|
||||
if (voiceDir.canRead()) {
|
||||
boolean useJs = app.getSettings().USE_JS_VOICE_GUIDANCE.get();
|
||||
//First list TTS files, they are preferred
|
||||
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;
|
||||
if (TTSCommandPlayerImpl.isMyData(voiceF)) {
|
||||
if ((TTSCommandPlayerImpl.isMyData(voiceF) && !useJs) || (JSTTSCommandPlayerImpl.isMyData(voiceF) && useJs)) {
|
||||
info = new LocalIndexInfo(LocalIndexType.TTS_VOICE_DATA, voiceF, backup, app);
|
||||
}
|
||||
if (info != null) {
|
||||
|
|
|
@ -52,6 +52,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static net.osmand.plus.mapcontextmenu.other.RoutePreferencesMenu.getVoiceFiles;
|
||||
|
||||
public class SettingsNavigationActivity extends SettingsBaseActivity {
|
||||
|
||||
public static final String MORE_VALUE = "MORE_VALUE";
|
||||
|
@ -244,7 +246,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity {
|
|||
private void reloadVoiceListPreference(PreferenceScreen screen) {
|
||||
String[] entries;
|
||||
String[] entrieValues;
|
||||
Set<String> voiceFiles = getVoiceFiles();
|
||||
Set<String> voiceFiles = getVoiceFiles(this);
|
||||
entries = new String[voiceFiles.size() + 2];
|
||||
entrieValues = new String[voiceFiles.size() + 2];
|
||||
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) {
|
||||
if (!Version.isBlackberry((OsmandApplication) getApplication())) {
|
||||
ListPreference lp = createListPreference(
|
||||
|
|
|
@ -14,6 +14,7 @@ import android.preference.PreferenceScreen;
|
|||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.View;
|
||||
|
||||
import net.osmand.StateChangedListener;
|
||||
import net.osmand.plus.ApplicationMode;
|
||||
import net.osmand.plus.OsmAndLocationSimulation;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
|
@ -32,6 +33,7 @@ import java.util.Set;
|
|||
|
||||
public class SettingsDevelopmentActivity extends SettingsBaseActivity {
|
||||
|
||||
private StateChangedListener<Boolean> useJSVoiceGuidanceListener;
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
@Override
|
||||
|
@ -62,6 +64,15 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity {
|
|||
cat.addPreference(createCheckBoxPreference(settings.ANIMATE_MY_LOCATION,
|
||||
R.string.animate_my_location,
|
||||
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);
|
||||
firstRunPreference.setTitle(R.string.simulate_initial_startup);
|
||||
|
@ -249,6 +260,9 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity {
|
|||
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.activities.OsmandActionBarActivity;
|
||||
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.CommandBuilder;
|
||||
import net.osmand.plus.voice.CommandPlayer;
|
||||
|
@ -29,12 +32,16 @@ import net.osmand.plus.routing.VoiceRouter;
|
|||
import net.osmand.util.Algorithms;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import alice.tuprolog.Struct;
|
||||
import alice.tuprolog.Term;
|
||||
|
||||
import static net.osmand.plus.mapcontextmenu.other.RoutePreferencesMenu.getVoiceFiles;
|
||||
|
||||
|
||||
/**
|
||||
* Test Voice activity
|
||||
|
@ -62,7 +69,7 @@ public class TestVoiceActivity extends OsmandActionBarActivity {
|
|||
gl.setPadding(3, 3, 3, 3);
|
||||
|
||||
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);
|
||||
|
||||
ScrollView sv = new ScrollView(this);
|
||||
|
@ -75,28 +82,15 @@ public class TestVoiceActivity extends OsmandActionBarActivity {
|
|||
|
||||
// add buttons
|
||||
setContentView(gl);
|
||||
getSupportActionBar(). setTitle(R.string.test_voice_prompts);
|
||||
getSupportActionBar().setTitle(R.string.test_voice_prompts);
|
||||
|
||||
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) {
|
||||
String[] entries;
|
||||
final String[] entrieValues;
|
||||
Set<String> voiceFiles = getVoiceFiles();
|
||||
Set<String> voiceFiles = getVoiceFiles(this);
|
||||
entries = new String[voiceFiles.size() ];
|
||||
entrieValues = new String[voiceFiles.size() ];
|
||||
int k = 0;
|
||||
|
@ -216,51 +210,72 @@ public class TestVoiceActivity extends OsmandActionBarActivity {
|
|||
}
|
||||
|
||||
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, "\u25BA (1.1) New route calculated, 150m, 230sec (00:03:50)", builder(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.3) New route calculated 3700m, 7320sec (02:02)", builder(p).newRouteCalculated(3700, 7320));
|
||||
addButton(ll, "\u25BA (1.4) New route calculated 9100m, 10980sec (03:03)", builder(p).newRouteCalculated(9100, 10980));
|
||||
addButton(ll, "\u25BA (2.1) Route recalculated 11500m, 18600sec (05:10)", builder(p).routeRecalculated(11500, 18600));
|
||||
addButton(ll, "\u25BA (2.2) Route recalculated 19633m, 26700sec (07:25)", builder(p).routeRecalculated(19633, 26700));
|
||||
addButton(ll, "\u25BA (2.3) Route recalculated 89750m, 55800sec (15:30)", builder(p).routeRecalculated(89750, 55800));
|
||||
addButton(ll, "\u25BA (2.4) Route recalculated 125900m, 92700sec (25:45)", builder(p).routeRecalculated(125900, 92700));
|
||||
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)", !isJS ? builder(p).newRouteCalculated(1350, 3680) : jsBuilder(p).newRouteCalculated(1350, 3680));
|
||||
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)", !isJS ? builder(p).newRouteCalculated(9100, 10980) : jsBuilder(p).newRouteCalculated(9100, 10980));
|
||||
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)", !isJS ? builder(p).routeRecalculated(19633, 26700) : jsBuilder(p).routeRecalculated(19633, 26700));
|
||||
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)", !isJS ? builder(p).routeRecalculated(125900, 92700) : jsBuilder(p).routeRecalculated(125900, 92700));
|
||||
|
||||
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.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, "")));
|
||||
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.4) After 3100m turn right onto 'SR 80' toward 'Rome'", builder(p).prepareTurn(AbstractPrologCommandPlayer.A_RIGHT, 3100, street(p, "", "SR 80", "Rome")));
|
||||
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.6) Turn sharply right onto 'Dr.-Quinn-Stra"+"\u00df"+"e'", builder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_SH, street(p, "Dr.-Quinn-Straße")));
|
||||
addButton(ll, "\u25BA (3.1) After 1520m turn slightly left", !isJS ? builder(p).prepareTurn(AbstractPrologCommandPlayer.A_LEFT_SL, 1520, street(p, "")) :
|
||||
jsBuilder(p).prepareTurn(AbstractPrologCommandPlayer.A_LEFT_SL, 1520, jsStreet(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, "")) :
|
||||
jsBuilder(p).turn(AbstractPrologCommandPlayer.A_LEFT_SH, 450, jsStreet(p, "Hauptstraße")).then().bearRight(jsStreet(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, "")) :
|
||||
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, "\u25BA (4.1) After 1810m keep left ' '", 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")));
|
||||
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.4) Turn left onto 'Broadway', then in 100m keep right and arrive at your destination 'Town Hall'",
|
||||
addButton(ll, "\u25BA (4.1) After 1810m keep left ' '", !isJS ? builder(p).prepareTurn(AbstractPrologCommandPlayer.A_LEFT_KEEP, 1810, street(p, "")) :
|
||||
jsBuilder(p).prepareTurn(AbstractPrologCommandPlayer.A_LEFT_KEEP, 1810, jsStreet(p, "")));
|
||||
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")) :
|
||||
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"))
|
||||
.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, "\u25BA (5.1) After 1250m enter a roundabout", 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")));
|
||||
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.1) After 1250m enter a roundabout", !isJS ? builder(p).prepareRoundAbout(1250, 3, street(p,"", "I 15", "Los Angeles")) :
|
||||
jsBuilder(p).prepareRoundAbout(1250, 3, jsStreet(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'", 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, "\u25BA (6.1) After 640m make a U-turn", builder(p).prepareMakeUT(640, street(p, "")));
|
||||
addButton(ll, "\u25BA (6.2) In 400m make a U-turn", builder(p).makeUT(400, street(p, "")));
|
||||
addButton(ll, "\u25BA (6.3) Make a U-turn on 'Riviera'", builder(p).makeUT(street(p, "Riviera", "", "", "Riviera")));
|
||||
addButton(ll, "\u25BA (6.1) After 640m make a U-turn", !isJS ? builder(p).prepareMakeUT(640, street(p, "")) :
|
||||
jsBuilder(p).prepareMakeUT(640, jsStreet(p, "")));
|
||||
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, "Go straight, follow the road, approaching:", builder(p));
|
||||
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.3) Continue for 360m to 'Broadway' and arrive at your intermediate destination ' '", builder(p).goAhead(360, street(p,"Broadway")).andArriveAtIntermediatePoint(""));
|
||||
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.5) Continue for 200m and pass GPX waypoint 'Trailhead'", builder(p).goAhead(200, null).andArriveAtWayPoint("Trailhead"));
|
||||
addButton(ll, "\u25BA (7.6) Continue for 400m and pass favorite 'Brewery'", builder(p).goAhead(400, null).andArriveAtFavorite("Brewery"));
|
||||
addButton(ll, "\u25BA (7.7) Continue for 600m and pass POI 'Museum'", builder(p).goAhead(600, null).andArriveAtPoi("Museum"));
|
||||
addButton(ll, "\u25BA (7.2) Continue for 2350m to ' '", !isJS ? builder(p).goAhead(2350, street(p, "")) :
|
||||
jsBuilder(p).goAhead(2350, jsStreet(p, "")));
|
||||
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("") :
|
||||
jsBuilder(p).goAhead(360, jsStreet(p,"Broadway")).andArriveAtIntermediatePoint(""));
|
||||
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("") :
|
||||
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, "\u25BA (8.1) Arrive at your destination 'Home'", builder(p).arrivedAtDestination("Home"));
|
||||
|
@ -292,10 +307,31 @@ public class TestVoiceActivity extends OsmandActionBarActivity {
|
|||
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){
|
||||
return p.newCommandBuilder();
|
||||
}
|
||||
|
||||
private JSCommandBuilder jsBuilder(CommandPlayer p) {
|
||||
return (JSCommandBuilder) p.newCommandBuilder();
|
||||
}
|
||||
|
||||
public void addButton(ViewGroup layout, final String description, final CommandBuilder builder){
|
||||
final Button button = new Button(this);
|
||||
button.setGravity(Gravity.LEFT);
|
||||
|
|
|
@ -298,6 +298,8 @@ public class DownloadActivityType {
|
|||
return FileNameTranslationHelper.getVoiceName(ctx, getBasename(indexItem));
|
||||
} else if (fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP)) {
|
||||
return FileNameTranslationHelper.getVoiceName(ctx, getBasename(indexItem));
|
||||
} else if (fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)) {
|
||||
return FileNameTranslationHelper.getVoiceName(ctx, getBasename(indexItem));
|
||||
}
|
||||
return getBasename(indexItem);
|
||||
}
|
||||
|
@ -336,6 +338,9 @@ public class DownloadActivityType {
|
|||
// if(fileName.endsWith(IndexConstants.VOICE_INDEX_EXT_ZIP) ||
|
||||
// fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP)) {
|
||||
if (this == VOICE_FILE) {
|
||||
if (fileName.contains(".js")) {
|
||||
return fileName.replace('_', '-').replaceAll(".js", "");
|
||||
}
|
||||
int l = fileName.lastIndexOf('_');
|
||||
if (l == -1) {
|
||||
l = fileName.length();
|
||||
|
@ -408,7 +413,8 @@ public class DownloadActivityType {
|
|||
if (l == -1) {
|
||||
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) {
|
||||
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,
|
||||
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();
|
||||
|
|
|
@ -83,7 +83,8 @@ public class DownloadResourceGroup {
|
|||
}
|
||||
|
||||
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 == HILLSHADE_HEADER || this == SRTM_HEADER
|
||||
|| 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>>();
|
||||
OsmandRegions regs = app.getRegions();
|
||||
boolean useJS = app.getSettings().USE_JS_VOICE_GUIDANCE.get();
|
||||
for (IndexItem ii : resources) {
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
continue;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package net.osmand.plus.mapcontextmenu.other;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
|
@ -329,20 +330,36 @@ public class RoutePreferencesMenu {
|
|||
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
|
||||
File extStorage = mapActivity.getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR);
|
||||
Set<String> setFiles = new LinkedHashSet<>();
|
||||
OsmandApplication app = ((OsmandApplication) activity.getApplication());
|
||||
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()) {
|
||||
for (File f : extStorage.listFiles()) {
|
||||
if (f.isDirectory()) {
|
||||
setFiles.add(f.getName());
|
||||
if (addJS && hasJavaScript(f)) {
|
||||
setFiles.add(f.getName());
|
||||
} else if (!addJS) {
|
||||
setFiles.add(f.getName());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
return new OnItemClickListener() {
|
||||
@Override
|
||||
|
|
|
@ -386,8 +386,11 @@ public class ResourceManager {
|
|||
for (File f : lf) {
|
||||
if (f.isDirectory()) {
|
||||
File conf = new File(f, "_config.p");
|
||||
boolean useJS = context.getSettings().USE_JS_VOICE_GUIDANCE.get();
|
||||
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()) {
|
||||
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){
|
||||
this.app = context;
|
||||
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();
|
||||
setAppMode(settings.APPLICATION_MODE.get());
|
||||
}
|
||||
|
|
|
@ -28,27 +28,27 @@ import android.media.SoundPool;
|
|||
|
||||
public class VoiceRouter {
|
||||
private static final int STATUS_UTWP_TOLD = -1;
|
||||
private static final int STATUS_UNKNOWN = 0;
|
||||
private static final int STATUS_LONG_PREPARE = 1;
|
||||
private static final int STATUS_PREPARE = 2;
|
||||
private static final int STATUS_TURN_IN = 3;
|
||||
private static final int STATUS_TURN = 4;
|
||||
private static final int STATUS_TOLD = 5;
|
||||
static final int STATUS_UNKNOWN = 0;
|
||||
static final int STATUS_LONG_PREPARE = 1;
|
||||
static final int STATUS_PREPARE = 2;
|
||||
static final int STATUS_TURN_IN = 3;
|
||||
static final int STATUS_TURN = 4;
|
||||
static final int STATUS_TOLD = 5;
|
||||
|
||||
private final RoutingHelper router;
|
||||
private static CommandPlayer player;
|
||||
private final OsmandSettings settings;
|
||||
protected final RoutingHelper router;
|
||||
protected static CommandPlayer player;
|
||||
protected final OsmandSettings settings;
|
||||
|
||||
private static boolean mute = false;
|
||||
private static int currentStatus = STATUS_UNKNOWN;
|
||||
private static boolean playedAndArriveAtTarget = false;
|
||||
private static float playGoAheadDist = 0;
|
||||
static int currentStatus = STATUS_UNKNOWN;
|
||||
static boolean playedAndArriveAtTarget = false;
|
||||
static float playGoAheadDist = 0;
|
||||
private static long lastAnnouncedSpeedLimit = 0;
|
||||
private static long waitAnnouncedSpeedLimit = 0;
|
||||
private static long lastAnnouncedOffRoute = 0;
|
||||
private static long waitAnnouncedOffRoute = 0;
|
||||
private static boolean suppressDest = false;
|
||||
private static boolean announceBackOnRoute = false;
|
||||
static boolean suppressDest = false;
|
||||
static boolean announceBackOnRoute = false;
|
||||
// private static long lastTimeRouteRecalcAnnounced = 0;
|
||||
// Remember when last announcement was made
|
||||
private static long lastAnnouncement = 0;
|
||||
|
@ -66,9 +66,11 @@ public class VoiceRouter {
|
|||
protected int TURN_DISTANCE = 0;
|
||||
|
||||
protected static VoiceCommandPending pendingCommand = null;
|
||||
private static RouteDirectionInfo nextRouteDirection;
|
||||
static RouteDirectionInfo nextRouteDirection;
|
||||
private Term empty;
|
||||
|
||||
private boolean useJS;
|
||||
|
||||
public interface VoiceMessageListener {
|
||||
void onVoiceMessage();
|
||||
}
|
||||
|
@ -78,6 +80,7 @@ public class VoiceRouter {
|
|||
public VoiceRouter(RoutingHelper router, final OsmandSettings settings) {
|
||||
this.router = router;
|
||||
this.settings = settings;
|
||||
useJS = settings.USE_JS_VOICE_GUIDANCE.get();
|
||||
this.mute = settings.VOICE_MUTE.get();
|
||||
empty = new Struct("");
|
||||
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) {
|
||||
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
|
||||
if (previousStatus != STATUS_TOLD) {
|
||||
this.currentStatus = previousStatus + 1;
|
||||
|
@ -210,7 +213,7 @@ public class VoiceRouter {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean statusNotPassed(int statusToCheck) {
|
||||
boolean statusNotPassed(int 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 target = info == null || info.directionInfo == null
|
||||
|| info.directionInfo.distance == 0;
|
||||
return in || target;
|
||||
}
|
||||
|
||||
private boolean needsInforming() {
|
||||
boolean needsInforming() {
|
||||
final Integer repeat = settings.KEEP_INFORMING.get();
|
||||
if (repeat == null || repeat == 0) return false;
|
||||
|
||||
|
@ -557,7 +560,7 @@ public class VoiceRouter {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void playThen() {
|
||||
void playThen() {
|
||||
CommandBuilder play = getNewCommandPlayerToPlay();
|
||||
if (play != null) {
|
||||
notifyOnVoiceMessage();
|
||||
|
@ -715,7 +718,7 @@ public class VoiceRouter {
|
|||
}
|
||||
}
|
||||
|
||||
private void playAndArriveAtDestination(NextDirectionInfo info) {
|
||||
void playAndArriveAtDestination(NextDirectionInfo info) {
|
||||
if (isTargetPoint(info)) {
|
||||
String pointName = info == null ? "" : info.pointName;
|
||||
CommandBuilder play = getNewCommandPlayerToPlay();
|
||||
|
@ -781,7 +784,7 @@ public class VoiceRouter {
|
|||
}
|
||||
}
|
||||
|
||||
private String getTurnType(TurnType t) {
|
||||
String getTurnType(TurnType t) {
|
||||
if (TurnType.TL == t.getValue()) {
|
||||
return AbstractPrologCommandPlayer.A_LEFT;
|
||||
} 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;
|
||||
private boolean alreadyExecuted = false;
|
||||
protected final CommandPlayer commandPlayer;
|
||||
protected boolean alreadyExecuted = false;
|
||||
private List<Struct> listStruct = new ArrayList<Struct>();
|
||||
|
||||
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);
|
||||
|
||||
// playing media
|
||||
private MediaPlayer mediaPlayer;
|
||||
MediaPlayer mediaPlayer;
|
||||
// indicates that player is ready to play first file
|
||||
private List<String> filesToPlay = Collections.synchronizedList(new ArrayList<String>());
|
||||
private VoiceRouter vrt;
|
||||
List<String> filesToPlay = Collections.synchronizedList(new ArrayList<String>());
|
||||
VoiceRouter vrt;
|
||||
|
||||
|
||||
public MediaCommandPlayerImpl(OsmandApplication ctx, ApplicationMode applicationMode, VoiceRouter vrt, String voiceProvider)
|
||||
|
@ -95,7 +95,7 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen
|
|||
playQueue();
|
||||
}
|
||||
|
||||
private synchronized void playQueue() {
|
||||
synchronized void playQueue() {
|
||||
if (mediaPlayer == null) {
|
||||
mediaPlayer = new MediaPlayer();
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implemen
|
|||
* @param file
|
||||
*/
|
||||
private void playFile(File file) {
|
||||
if (!file.exists()) {
|
||||
if (!file.exists() || file.isDirectory()) {
|
||||
log.error("Unable to play, does not exist: "+file);
|
||||
playQueue();
|
||||
return;
|
||||
|
|
Loading…
Reference in a new issue