From 638f487edab5a3e9d6a9429c8ad236dba2db5435 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Fri, 27 Jul 2018 16:30:45 +0300 Subject: [PATCH 01/28] Started adding js voice guidance --- OsmAnd/res/values/strings.xml | 2 + .../src/net/osmand/plus/AppInitializer.java | 5 +- .../src/net/osmand/plus/OsmandSettings.java | 2 + .../SettingsDevelopmentActivity.java | 3 + .../net/osmand/plus/routing/VoiceRouter.java | 3 + .../plus/voice/AbstractJSCommandPlayer.java | 54 +++++ .../net/osmand/plus/voice/CommandBuilder.java | 4 +- .../osmand/plus/voice/JSCommandBuilder.java | 220 ++++++++++++++++++ .../plus/voice/JSTTSCommandPlayerImpl.java | 106 +++++++++ 9 files changed, 396 insertions(+), 3 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java create mode 100644 OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java create mode 100644 OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 367bcab4bc..6ef6f2f9ca 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -2913,4 +2913,6 @@ Searching for the corresponding wiki article Article not found How to open Wikipedia articles? + Use JS voice guidance + Use new voice guidance logic based on JavaScript diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index 02b129ced4..dd4dd2d1d8 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -46,6 +46,7 @@ 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.JSTTSCommandPlayerImpl; import net.osmand.plus.voice.MediaCommandPlayerImpl; import net.osmand.plus.voice.TTSCommandPlayerImpl; import net.osmand.plus.wikivoyage.data.TravelDbHelper; @@ -577,7 +578,9 @@ public class AppInitializer implements IProgress { if (!voiceDir.exists()) { throw new CommandPlayerException(ctx.getString(R.string.voice_data_unavailable)); } - + if (app.getSettings().USE_JS_VOICE_GUIDANCE.get()) { + return new JSTTSCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider); + } if (MediaCommandPlayerImpl.isMyData(voiceDir)) { return new MediaCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider); } else if (TTSCommandPlayerImpl.isMyData(voiceDir)) { diff --git a/OsmAnd/src/net/osmand/plus/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/OsmandSettings.java index a83a591968..949cdaf490 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/OsmandSettings.java @@ -1340,6 +1340,8 @@ public class OsmandSettings { public final OsmandPreference ANIMATE_MY_LOCATION = new BooleanPreference("animate_my_location", true).makeGlobal().cache(); + public final OsmandPreference USE_JS_VOICE_GUIDANCE = new BooleanPreference("use_js_voice_guidance", false); + public final OsmandPreference ROUTE_MAP_MARKERS_START_MY_LOC = new BooleanPreference("route_map_markers_start_my_loc", false).makeGlobal().cache(); public final OsmandPreference ROUTE_MAP_MARKERS_ROUND_TRIP = new BooleanPreference("route_map_markers_round_trip", false).makeGlobal().cache(); diff --git a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java index bece3ea19a..be1ae15e49 100644 --- a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java @@ -63,6 +63,9 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { R.string.animate_my_location, R.string.animate_my_location_desc)); + cat.addPreference(createCheckBoxPreference(settings.USE_JS_VOICE_GUIDANCE, getString(R.string.use_js_voice_guidance), + getString(R.string.use_js_voice_guidance_description))); + final Preference firstRunPreference = new Preference(this); firstRunPreference.setTitle(R.string.simulate_initial_startup); firstRunPreference.setSummary(R.string.simulate_initial_startup_descr); diff --git a/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java b/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java index af27e575ea..cab1e395ff 100644 --- a/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java +++ b/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java @@ -69,6 +69,8 @@ public class VoiceRouter { private 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(); diff --git a/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java b/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java new file mode 100644 index 0000000000..09db3cee5b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java @@ -0,0 +1,54 @@ +package net.osmand.plus.voice; + +import java.util.List; + +import alice.tuprolog.Struct; + +public class AbstractJSCommandPlayer implements CommandPlayer { + @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() { + + } + + @Override + public List execute(List listStruct) { + return null; + } + + @Override + public void updateAudioStream(int streamType) { + + } + + @Override + public String getLanguage() { + return null; + } + + @Override + public boolean supportsStructuredStreetNames() { + return true; + } + + @Override + public void stop() { + + } +} diff --git a/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java b/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java index 6e17ac0543..01640b1c34 100644 --- a/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java +++ b/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java @@ -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 listStruct = new ArrayList(); public CommandBuilder(CommandPlayer commandPlayer){ diff --git a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java new file mode 100644 index 0000000000..e8bffb4912 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java @@ -0,0 +1,220 @@ +package net.osmand.plus.voice; + +import net.osmand.PlatformUtil; + +import org.apache.commons.logging.Log; + +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); + + protected static final String C_PREPARE_TURN = "prepare_turn"; //$NON-NLS-1$ + protected static final String C_PREPARE_ROUNDABOUT = "prepare_roundabout"; //$NON-NLS-1$ + protected static final String C_PREPARE_MAKE_UT = "prepare_make_ut"; //$NON-NLS-1$ + protected static final String C_ROUNDABOUT = "roundabout"; //$NON-NLS-1$ + protected static final String C_GO_AHEAD = "go_ahead"; //$NON-NLS-1$ + protected static final String C_TURN = "turn"; //$NON-NLS-1$ + protected static final String C_MAKE_UT = "make_ut"; //$NON-NLS-1$ + protected static final String C_MAKE_UTWP = "make_ut_wp"; //$NON-NLS-1$ + protected static final String C_AND_ARRIVE_DESTINATION = "and_arrive_destination"; //$NON-NLS-1$ + protected static final String C_REACHED_DESTINATION = "reached_destination"; //$NON-NLS-1$ + protected static final String C_AND_ARRIVE_INTERMEDIATE = "and_arrive_intermediate"; //$NON-NLS-1$ + protected static final String C_REACHED_INTERMEDIATE = "reached_intermediate"; //$NON-NLS-1$ + protected static final String C_AND_ARRIVE_WAYPOINT = "and_arrive_waypoint"; //$NON-NLS-1$ + protected static final String C_AND_ARRIVE_FAVORITE = "and_arrive_favorite"; //$NON-NLS-1$ + protected static final String C_AND_ARRIVE_POI_WAYPOINT = "and_arrive_poi"; //$NON-NLS-1$ + protected static final String C_REACHED_WAYPOINT = "reached_waypoint"; //$NON-NLS-1$ + protected static final String C_REACHED_FAVORITE = "reached_favorite"; //$NON-NLS-1$ + protected static final String C_REACHED_POI = "reached_poi"; //$NON-NLS-1$ + protected static final String C_THEN = "then"; //$NON-NLS-1$ + protected static final String C_SPEAD_ALARM = "speed_alarm"; //$NON-NLS-1$ + protected static final String C_ATTENTION = "attention"; //$NON-NLS-1$ + protected static final String C_OFF_ROUTE = "off_route"; //$NON-NLS-1$ + protected static final String C_BACK_ON_ROUTE ="back_on_route"; //$NON-NLS-1$ + + + protected static final String C_BEAR_LEFT = "bear_left"; //$NON-NLS-1$ + protected static final String C_BEAR_RIGHT = "bear_right"; //$NON-NLS-1$ + protected static final String C_ROUTE_RECALC = "route_recalc"; //$NON-NLS-1$ + protected static final String C_ROUTE_NEW_CALC = "route_new_calc"; //$NON-NLS-1$ + protected static final String C_LOCATION_LOST = "location_lost"; //$NON-NLS-1$ + protected static final String C_LOCATION_RECOVERED = "location_recovered"; //$NON-NLS-1$ + + + private List listStruct = new ArrayList<>(); + + public JSCommandBuilder(CommandPlayer commandPlayer) { + super(commandPlayer); + } + + + public void setParameters(String metricCons, boolean tts) { + // TODO Set the parameters to js context + } + + private JSCommandBuilder addCommand(String name, Object... args){ + // TODO add JSCore + listStruct.add(name); + return this; + } + + public JSCommandBuilder goAhead(){ + return goAhead(-1, new HashMap()); + } + + public JSCommandBuilder goAhead(double dist, Map streetName){ + return addCommand(C_GO_AHEAD, dist, streetName); + } + + public JSCommandBuilder makeUTwp(){ + return makeUT(new HashMap()); + } + + public JSCommandBuilder makeUT(Map streetName){ + return addCommand(C_MAKE_UT, 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 streetName){ + return addCommand(C_MAKE_UT, dist, streetName); + } + + public JSCommandBuilder prepareMakeUT(double dist, Map streetName){ + return addCommand(C_PREPARE_MAKE_UT, dist, streetName); + } + + + public JSCommandBuilder turn(String param, Map streetName) { + return addCommand(C_TURN, param, streetName); + } + + public JSCommandBuilder turn(String param, double dist, Map streetName){ + return addCommand(C_TURN, param, dist, streetName); + } + + /** + * + * @param param A_LEFT, A_RIGHT, ... + * @param dist + * @return + */ + public JSCommandBuilder prepareTurn(String param, double dist, Map streetName){ + return addCommand(C_PREPARE_TURN, param, dist, streetName); + } + + public JSCommandBuilder prepareRoundAbout(double dist, int exit, Map streetName){ + return addCommand(C_PREPARE_ROUNDABOUT, dist, exit, streetName); + } + + public JSCommandBuilder roundAbout(double dist, double angle, int exit, Map streetName){ + return addCommand(C_ROUNDABOUT, dist, angle, exit, streetName); + } + + public JSCommandBuilder roundAbout(double angle, int exit, Map 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 streetName){ + return addCommand(C_BEAR_LEFT, streetName); + } + + public JSCommandBuilder bearRight(Map streetName){ + return addCommand(C_BEAR_RIGHT, 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 execute(){ + alreadyExecuted = true; + return listStruct; + } +} diff --git a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java new file mode 100644 index 0000000000..4caefad1f7 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java @@ -0,0 +1,106 @@ +package net.osmand.plus.voice; + +import android.app.Activity; +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.plus.ApplicationMode; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.routing.VoiceRouter; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; + +public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { + + private static final String TAG = JSTTSCommandPlayerImpl.class.getSimpleName(); + private static TextToSpeech mTts; + + private OsmandApplication app; + private ApplicationMode appMode; + private VoiceRouter vrt; + private String voiceProvider; + + private HashMap params = new HashMap(); + + private static int ttsRequests = 0; + + public JSTTSCommandPlayerImpl(OsmandApplication ctx, ApplicationMode applicationMode, VoiceRouter vrt, String voiceProvider) { + this.app = ctx; + this.appMode = applicationMode; + this.vrt = vrt; + this.voiceProvider = voiceProvider; + mTts = new TextToSpeech(ctx, null); + } + + @Override + public String getCurrentVoice() { + return null; + } + + @Override + public JSCommandBuilder newCommandBuilder() { + JSCommandBuilder commandBuilder = new JSCommandBuilder(this); + commandBuilder.setParameters(app.getSettings().METRIC_SYSTEM.get().toHumanString(app), true); + return commandBuilder; + } + + @Override + public void playCommands(CommandBuilder builder) { + final List 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()) { + 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 clear() { + + } + + @Override + public void updateAudioStream(int streamType) { + + } + + @Override + public String getLanguage() { + return null; + } + + @Override + public boolean supportsStructuredStreetNames() { + return false; + } + + @Override + public void stop() { + + } +} From cd1235be8aeb4eb6d95a3ba6c15f490fe02cc406 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Sat, 28 Jul 2018 15:25:24 +0300 Subject: [PATCH 02/28] Added all the necessary code to run js (except js core) --- OsmAnd/build.gradle | 2 + .../osmand/plus/routing/JSVoiceRouter.java | 324 ++++++++++++++++++ .../osmand/plus/routing/RoutingHelper.java | 3 +- .../net/osmand/plus/routing/VoiceRouter.java | 46 +-- .../plus/voice/AbstractJSCommandPlayer.java | 2 +- .../osmand/plus/voice/JSCommandBuilder.java | 51 +-- .../plus/voice/JSTTSCommandPlayerImpl.java | 3 +- 7 files changed, 372 insertions(+), 59 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java diff --git a/OsmAnd/build.gradle b/OsmAnd/build.gradle index ccfc56a840..267a49b5b4 100644 --- a/OsmAnd/build.gradle +++ b/OsmAnd/build.gradle @@ -363,6 +363,8 @@ dependencies { implementation 'com.vividsolutions:jts-core:1.14.0' implementation 'com.squareup.picasso:picasso:2.71828' + // JS core + //implementation 'com.github.LiquidPlayer:LiquidCore:0.4.6' // size restrictions // implementation 'com.ibm.icu:icu4j:50.1' // implementation 'net.sf.trove4j:trove4j:3.0.3' diff --git a/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java b/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java new file mode 100644 index 0000000000..26cb438580 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java @@ -0,0 +1,324 @@ +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 getSpeakableJSStreetName(RouteSegmentResult currentSegment, RouteDirectionInfo i, boolean includeDest) { + Map 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", getSpeakablePointName(i.getRef())); + result.put("toStreetName", getSpeakablePointName(i.getStreetName())); + result.put("toDest", getSpeakablePointName(i.getDestinationName())); + } else { + result.put("toRef", getSpeakablePointName(i.getRef())); + result.put("toStreetName", 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", getSpeakablePointName(obj.getRef(settings.MAP_PREFERRED_LOCALE.get(), + settings.MAP_TRANSLITERATE_NAMES.get(), currentSegment.isForwardDirection()))); + result.put("fromStreetName", getSpeakablePointName(obj.getName(settings.MAP_PREFERRED_LOCALE.get(), + settings.MAP_TRANSLITERATE_NAMES.get()))); + result.put("fromDest", getSpeakablePointName(obj.getDestinationName(settings.MAP_PREFERRED_LOCALE.get(), + settings.MAP_TRANSLITERATE_NAMES.get(), currentSegment.isForwardDirection()))); + } else { + RouteDataObject obj = currentSegment.getObject(); + result.put("fromRef", getSpeakablePointName(obj.getRef(settings.MAP_PREFERRED_LOCALE.get(), + settings.MAP_TRANSLITERATE_NAMES.get(), currentSegment.isForwardDirection()))); + result.put("fromStreetName", getSpeakablePointName(obj.getName(settings.MAP_PREFERRED_LOCALE.get(), + settings.MAP_TRANSLITERATE_NAMES.get()))); + result.put("fromDest", ""); + } + } + + } else { + result.put("toRef", getSpeakablePointName(i.getRef())); + result.put("toStreetName", getSpeakablePointName(i.getStreetName())); + } + return result; + } + + /** + * Updates status of voice guidance + * @param currentLocation + */ + 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()); + } + 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()); + 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 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()); + } + } else if (nextNextInfo.directionInfo.getTurnType().isRoundAbout()) { + if (isplay) { + play.then(); + play.roundAbout(nextNextInfo.distanceTo, nextNextInfo.directionInfo.getTurnType().getTurnAngle(), + nextNextInfo.directionInfo.getTurnType().getExitOut(), new HashMap()); + } + } else if (nextNextInfo.directionInfo.getTurnType().getValue() == TurnType.TU) { + if (isplay) { + play.then(); + play.makeUT(nextNextInfo.distanceTo, new HashMap()); + } + } + } + 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; + } + } + } + +} diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java index fa15b79b5d..c34f97f44b 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java @@ -97,7 +97,8 @@ public class RoutingHelper { public RoutingHelper(OsmandApplication context){ this.app = context; settings = context.getSettings(); - voiceRouter = new VoiceRouter(this, settings); + voiceRouter = settings.USE_JS_VOICE_GUIDANCE.get() ?new JSVoiceRouter(this, settings) + : new VoiceRouter(this, settings); provider = new RouteProvider(); setAppMode(settings.APPLICATION_MODE.get()); } diff --git a/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java b/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java index cab1e395ff..6ef3f69def 100644 --- a/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java +++ b/OsmAnd/src/net/osmand/plus/routing/VoiceRouter.java @@ -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,7 +66,7 @@ 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; @@ -167,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) { @@ -204,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; @@ -213,7 +213,7 @@ public class VoiceRouter { } } - private boolean statusNotPassed(int statusToCheck) { + boolean statusNotPassed(int statusToCheck) { return currentStatus <= statusToCheck; } @@ -398,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; @@ -560,7 +560,7 @@ public class VoiceRouter { return false; } - private void playThen() { + void playThen() { CommandBuilder play = getNewCommandPlayerToPlay(); if (play != null) { notifyOnVoiceMessage(); @@ -718,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(); @@ -784,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()) { diff --git a/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java b/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java index 09db3cee5b..bb36ecfa0a 100644 --- a/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java +++ b/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java @@ -39,7 +39,7 @@ public class AbstractJSCommandPlayer implements CommandPlayer { @Override public String getLanguage() { - return null; + return "en"; } @Override diff --git a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java index e8bffb4912..a3116afcfd 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java @@ -3,7 +3,12 @@ package net.osmand.plus.voice; import net.osmand.PlatformUtil; import org.apache.commons.logging.Log; +//import org.liquidplayer.javascript.JSContext; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -13,45 +18,25 @@ public class JSCommandBuilder extends CommandBuilder { private static final Log log = PlatformUtil.getLog(JSCommandBuilder.class); - protected static final String C_PREPARE_TURN = "prepare_turn"; //$NON-NLS-1$ - protected static final String C_PREPARE_ROUNDABOUT = "prepare_roundabout"; //$NON-NLS-1$ - protected static final String C_PREPARE_MAKE_UT = "prepare_make_ut"; //$NON-NLS-1$ - protected static final String C_ROUNDABOUT = "roundabout"; //$NON-NLS-1$ - protected static final String C_GO_AHEAD = "go_ahead"; //$NON-NLS-1$ - protected static final String C_TURN = "turn"; //$NON-NLS-1$ - protected static final String C_MAKE_UT = "make_ut"; //$NON-NLS-1$ - protected static final String C_MAKE_UTWP = "make_ut_wp"; //$NON-NLS-1$ - protected static final String C_AND_ARRIVE_DESTINATION = "and_arrive_destination"; //$NON-NLS-1$ - protected static final String C_REACHED_DESTINATION = "reached_destination"; //$NON-NLS-1$ - protected static final String C_AND_ARRIVE_INTERMEDIATE = "and_arrive_intermediate"; //$NON-NLS-1$ - protected static final String C_REACHED_INTERMEDIATE = "reached_intermediate"; //$NON-NLS-1$ - protected static final String C_AND_ARRIVE_WAYPOINT = "and_arrive_waypoint"; //$NON-NLS-1$ - protected static final String C_AND_ARRIVE_FAVORITE = "and_arrive_favorite"; //$NON-NLS-1$ - protected static final String C_AND_ARRIVE_POI_WAYPOINT = "and_arrive_poi"; //$NON-NLS-1$ - protected static final String C_REACHED_WAYPOINT = "reached_waypoint"; //$NON-NLS-1$ - protected static final String C_REACHED_FAVORITE = "reached_favorite"; //$NON-NLS-1$ - protected static final String C_REACHED_POI = "reached_poi"; //$NON-NLS-1$ - protected static final String C_THEN = "then"; //$NON-NLS-1$ - protected static final String C_SPEAD_ALARM = "speed_alarm"; //$NON-NLS-1$ - protected static final String C_ATTENTION = "attention"; //$NON-NLS-1$ - protected static final String C_OFF_ROUTE = "off_route"; //$NON-NLS-1$ - protected static final String C_BACK_ON_ROUTE ="back_on_route"; //$NON-NLS-1$ - - - protected static final String C_BEAR_LEFT = "bear_left"; //$NON-NLS-1$ - protected static final String C_BEAR_RIGHT = "bear_right"; //$NON-NLS-1$ - protected static final String C_ROUTE_RECALC = "route_recalc"; //$NON-NLS-1$ - protected static final String C_ROUTE_NEW_CALC = "route_new_calc"; //$NON-NLS-1$ - protected static final String C_LOCATION_LOST = "location_lost"; //$NON-NLS-1$ - protected static final String C_LOCATION_RECOVERED = "location_recovered"; //$NON-NLS-1$ - - +// private JSContext jsContext; private List listStruct = new ArrayList<>(); public JSCommandBuilder(CommandPlayer commandPlayer) { super(commandPlayer); +// jsContext = new JSContext(); } + public void setJSContext(String path) { +// String script = null; +// try { +// byte[] encoded = Files.readAllBytes(Paths.get(path)); +// script = new String(encoded, "UTF-8"); +// } catch (IOException e) { +// log.error(e.getMessage()); +// } +// if (script != null) +//// jsContext.evaluateScript(script); + } public void setParameters(String metricCons, boolean tts) { // TODO Set the parameters to js context diff --git a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java index 4caefad1f7..f602c1df0d 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java @@ -46,6 +46,7 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { @Override public JSCommandBuilder newCommandBuilder() { JSCommandBuilder commandBuilder = new JSCommandBuilder(this); + commandBuilder.setJSContext(app.getSettings().VOICE_PROVIDER.get() + "en_tts.js"); commandBuilder.setParameters(app.getSettings().METRIC_SYSTEM.get().toHumanString(app), true); return commandBuilder; } @@ -91,7 +92,7 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { @Override public String getLanguage() { - return null; + return "en"; } @Override From c4d16c542d3e9c71b66a8bfe3ea64729d8340a9b Mon Sep 17 00:00:00 2001 From: PaulStets Date: Wed, 1 Aug 2018 17:05:23 +0300 Subject: [PATCH 03/28] Added Rhino library to parse js --- .../osmand/plus/routing/JSVoiceRouter.java | 2 + .../plus/voice/AbstractJSCommandPlayer.java | 2 +- .../osmand/plus/voice/JSCommandBuilder.java | 101 +++++++++++++----- .../plus/voice/JSTTSCommandPlayerImpl.java | 6 +- 4 files changed, 80 insertions(+), 31 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java b/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java index 26cb438580..c40a2a79aa 100644 --- a/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java +++ b/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java @@ -59,6 +59,7 @@ public class JSVoiceRouter extends VoiceRouter { } else { result.put("toRef", getSpeakablePointName(i.getRef())); result.put("toStreetName", getSpeakablePointName(i.getStreetName())); + result.put("toDest", ""); } return result; } @@ -67,6 +68,7 @@ public class JSVoiceRouter extends VoiceRouter { * 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-) diff --git a/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java b/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java index bb36ecfa0a..60894872ff 100644 --- a/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java +++ b/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java @@ -4,7 +4,7 @@ import java.util.List; import alice.tuprolog.Struct; -public class AbstractJSCommandPlayer implements CommandPlayer { +public abstract class AbstractJSCommandPlayer implements CommandPlayer { @Override public String getCurrentVoice() { return null; diff --git a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java index a3116afcfd..28d86aed3d 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java @@ -3,12 +3,18 @@ package net.osmand.plus.voice; import net.osmand.PlatformUtil; import org.apache.commons.logging.Log; -//import org.liquidplayer.javascript.JSContext; +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 org.mozilla.javascript.WrapFactory; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -18,33 +24,63 @@ public class JSCommandBuilder extends CommandBuilder { private static final Log log = PlatformUtil.getLog(JSCommandBuilder.class); -// private JSContext jsContext; + private Context jsContext; private List listStruct = new ArrayList<>(); + ScriptableObject jsScope; public JSCommandBuilder(CommandPlayer commandPlayer) { super(commandPlayer); -// jsContext = new JSContext(); } public void setJSContext(String path) { -// String script = null; -// try { -// byte[] encoded = Files.readAllBytes(Paths.get(path)); -// script = new String(encoded, "UTF-8"); -// } catch (IOException e) { -// log.error(e.getMessage()); -// } -// if (script != null) -//// jsContext.evaluateScript(script); + String script = readFileContents(path); + jsContext = Context.enter(); + jsContext.setOptimizationLevel(-1); + jsScope = jsContext.initStandardObjects(); + jsContext.evaluateString(jsScope, script, "JS", 1, null); + } + + private Object convertStreetName(Map streetName) { + return NativeJSON.parse(jsContext, jsScope, new JSONObject(streetName).toString(), new NullCallable()); + } + + + private String readFileContents(String path) { + FileInputStream fis = null; + StringBuilder fileContent = new StringBuilder(""); + try { + fis = new FileInputStream(new File(path)); + byte[] buffer = new byte[1024]; + int n; + while ((n = fis.read(buffer)) != -1) + { + fileContent.append(new String(buffer, 0, n)); + } + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return fileContent.toString(); } public void setParameters(String metricCons, boolean tts) { - // TODO Set the parameters to js context +// jsContext.property("setMode").toFunction().call(jsContext, tts); + Object obj = jsScope.get("setMode", jsScope); + + if (obj instanceof Function) { + Function jsFunction = (Function) obj; + jsFunction.call(jsContext, jsScope, jsScope, new Object[]{metricCons}); + jsFunction.call(jsContext, jsScope, jsScope, new Object[]{tts}); + } } private JSCommandBuilder addCommand(String name, Object... args){ // TODO add JSCore - listStruct.add(name); + 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; } @@ -53,7 +89,7 @@ public class JSCommandBuilder extends CommandBuilder { } public JSCommandBuilder goAhead(double dist, Map streetName){ - return addCommand(C_GO_AHEAD, dist, streetName); + return addCommand(C_GO_AHEAD, dist, convertStreetName(streetName)); } public JSCommandBuilder makeUTwp(){ @@ -61,7 +97,7 @@ public class JSCommandBuilder extends CommandBuilder { } public JSCommandBuilder makeUT(Map streetName){ - return addCommand(C_MAKE_UT, streetName); + return addCommand(C_MAKE_UT, convertStreetName(streetName)); } @Override public JSCommandBuilder speedAlarm(int maxSpeed, float speed){ @@ -81,20 +117,20 @@ public class JSCommandBuilder extends CommandBuilder { } public JSCommandBuilder makeUT(double dist, Map streetName){ - return addCommand(C_MAKE_UT, dist, streetName); + return addCommand(C_MAKE_UT, dist, convertStreetName(streetName)); } public JSCommandBuilder prepareMakeUT(double dist, Map streetName){ - return addCommand(C_PREPARE_MAKE_UT, dist, streetName); + return addCommand(C_PREPARE_MAKE_UT, dist, convertStreetName(streetName)); } public JSCommandBuilder turn(String param, Map streetName) { - return addCommand(C_TURN, param, streetName); + return addCommand(C_TURN, param, convertStreetName(streetName)); } public JSCommandBuilder turn(String param, double dist, Map streetName){ - return addCommand(C_TURN, param, dist, streetName); + return addCommand(C_TURN, param, dist, convertStreetName(streetName)); } /** @@ -104,15 +140,15 @@ public class JSCommandBuilder extends CommandBuilder { * @return */ public JSCommandBuilder prepareTurn(String param, double dist, Map streetName){ - return addCommand(C_PREPARE_TURN, param, dist, streetName); + return addCommand(C_PREPARE_TURN, param, dist, convertStreetName(streetName)); } public JSCommandBuilder prepareRoundAbout(double dist, int exit, Map streetName){ - return addCommand(C_PREPARE_ROUNDABOUT, dist, exit, streetName); + return addCommand(C_PREPARE_ROUNDABOUT, dist, exit, convertStreetName(streetName)); } public JSCommandBuilder roundAbout(double dist, double angle, int exit, Map streetName){ - return addCommand(C_ROUNDABOUT, dist, angle, exit, streetName); + return addCommand(C_ROUNDABOUT, dist, angle, exit, convertStreetName(streetName)); } public JSCommandBuilder roundAbout(double angle, int exit, Map streetName) { @@ -160,11 +196,11 @@ public class JSCommandBuilder extends CommandBuilder { } public JSCommandBuilder bearLeft(Map streetName){ - return addCommand(C_BEAR_LEFT, streetName); + return addCommand(C_BEAR_LEFT, convertStreetName(streetName)); } public JSCommandBuilder bearRight(Map streetName){ - return addCommand(C_BEAR_RIGHT, streetName); + return addCommand(C_BEAR_RIGHT, convertStreetName(streetName)); } @Override @@ -202,4 +238,13 @@ public class JSCommandBuilder extends CommandBuilder { alreadyExecuted = true; return listStruct; } + + public class NullCallable implements Callable + { + @Override + public Object call(Context context, Scriptable scope, Scriptable holdable, Object[] objects) + { + return objects[1]; + } + } } diff --git a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java index f602c1df0d..b1dc216067 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java @@ -7,11 +7,13 @@ import android.speech.tts.TextToSpeech; import android.support.v7.app.AlertDialog; import android.util.Log; +import net.osmand.IndexConstants; import net.osmand.plus.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.routing.VoiceRouter; +import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -46,7 +48,7 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { @Override public JSCommandBuilder newCommandBuilder() { JSCommandBuilder commandBuilder = new JSCommandBuilder(this); - commandBuilder.setJSContext(app.getSettings().VOICE_PROVIDER.get() + "en_tts.js"); + commandBuilder.setJSContext(app.getAppPath(IndexConstants.VOICE_INDEX_DIR).getAbsolutePath() + "/" + voiceProvider + "/en_tts.js"); commandBuilder.setParameters(app.getSettings().METRIC_SYSTEM.get().toHumanString(app), true); return commandBuilder; } @@ -97,7 +99,7 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { @Override public boolean supportsStructuredStreetNames() { - return false; + return true; } @Override From 28c0489fd6c22565023e1c3c4cfd7e5dd10bc64e Mon Sep 17 00:00:00 2001 From: PaulStets Date: Wed, 1 Aug 2018 17:07:03 +0300 Subject: [PATCH 04/28] Added Rhino library to parse js --- OsmAnd/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OsmAnd/build.gradle b/OsmAnd/build.gradle index 267a49b5b4..ad430d7c3a 100644 --- a/OsmAnd/build.gradle +++ b/OsmAnd/build.gradle @@ -364,7 +364,8 @@ dependencies { implementation 'com.squareup.picasso:picasso:2.71828' // JS core - //implementation 'com.github.LiquidPlayer:LiquidCore:0.4.6' + 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' From 55232223a382e27b82fb9597c5dacd703c0e4e49 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Thu, 2 Aug 2018 16:35:19 +0300 Subject: [PATCH 05/28] Added full english JS tts support (needs performance optimization) --- OsmAnd/res/values/strings.xml | 1 + .../src/net/osmand/plus/AppInitializer.java | 2 +- .../plus/development/TestVoiceActivity.java | 110 +++++++- .../osmand/plus/routing/JSVoiceRouter.java | 42 +-- .../plus/voice/AbstractJSCommandPlayer.java | 114 +++++++- .../osmand/plus/voice/JSCommandBuilder.java | 33 ++- .../plus/voice/JSTTSCommandPlayerImpl.java | 264 +++++++++++++++++- 7 files changed, 515 insertions(+), 51 deletions(-) diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index d8e7597de7..3a72ced33a 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -2933,4 +2933,5 @@ How to open Wikipedia articles? Use JS voice guidance Use new voice guidance logic based on JavaScript + Tap a button and listen to the corresponding voice prompt to identify missing or faulty propmts. diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index dd4dd2d1d8..0bb92adeaf 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -579,7 +579,7 @@ public class AppInitializer implements IProgress { throw new CommandPlayerException(ctx.getString(R.string.voice_data_unavailable)); } if (app.getSettings().USE_JS_VOICE_GUIDANCE.get()) { - return new JSTTSCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider); + return new JSTTSCommandPlayerImpl(ctx, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider); } if (MediaCommandPlayerImpl.isMyData(voiceDir)) { return new MediaCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider); diff --git a/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java b/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java index f968488b7e..c7988582b7 100644 --- a/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java @@ -22,6 +22,8 @@ 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.JSTTSCommandPlayerImpl; import net.osmand.plus.voice.TTSCommandPlayerImpl; import net.osmand.plus.voice.CommandBuilder; import net.osmand.plus.voice.CommandPlayer; @@ -29,7 +31,9 @@ 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; @@ -62,7 +66,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,7 +79,7 @@ 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); } @@ -216,6 +220,10 @@ public class TestVoiceActivity extends OsmandActionBarActivity { } private void addButtons(final LinearLayout ll, CommandPlayer p) { + if (p instanceof JSTTSCommandPlayerImpl) { + addJSTTSPrompts(ll, (JSTTSCommandPlayerImpl) p); + return; + } 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)); @@ -292,10 +300,108 @@ public class TestVoiceActivity extends OsmandActionBarActivity { ll.forceLayout(); } + private Map jsStreet(CommandPlayer p, String... args) { + Map 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 void addJSTTSPrompts(LinearLayout ll, JSTTSCommandPlayerImpl p) { + addButton(ll, "Route calculated and number tests:", jsBuilder(p)); + addButton(ll, "\u25BA (1.1) New route calculated, 150m, 230sec (00:03:50)", jsBuilder(p).newRouteCalculated(150, 230)); + addButton(ll, "\u25BA (1.2) New route calculated, 1350m, 3680sec (01:01:20)", jsBuilder(p).newRouteCalculated(1350, 3680)); + addButton(ll, "\u25BA (1.3) New route calculated 3700m, 7320sec (02:02)", jsBuilder(p).newRouteCalculated(3700, 7320)); + addButton(ll, "\u25BA (1.4) New route calculated 9100m, 10980sec (03:03)", jsBuilder(p).newRouteCalculated(9100, 10980)); + addButton(ll, "\u25BA (2.1) Route recalculated 11500m, 18600sec (05:10)", jsBuilder(p).routeRecalculated(11500, 18600)); + addButton(ll, "\u25BA (2.2) Route recalculated 19633m, 26700sec (07:25)", jsBuilder(p).routeRecalculated(19633, 26700)); + addButton(ll, "\u25BA (2.3) Route recalculated 89750m, 55800sec (15:30)", jsBuilder(p).routeRecalculated(89750, 55800)); + addButton(ll, "\u25BA (2.4) Route recalculated 125900m, 92700sec (25:45)", jsBuilder(p).routeRecalculated(125900, 92700)); + + addButton(ll, "All turn types: prepareTurn, makeTurnIn, turn:", jsBuilder(p)); + addButton(ll, "\u25BA (3.1) After 1520m turn slightly left", 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", 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", 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'", 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 jsStreet', then bear left", 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'", jsBuilder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_SH, jsStreet(p, "Dr.-Quinn-Straße"))); + + addButton(ll, "Keep left/right: prepareTurn, makeTurnIn, turn:", jsBuilder(p)); + addButton(ll, "\u25BA (4.1) After 1810m keep left ' '", 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'", 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'", 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'", + 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:", jsBuilder(p)); + addButton(ll, "\u25BA (5.1) After 1250m enter a roundabout", 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'", 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'", jsBuilder(p).roundAbout(0, 2, jsStreet(p, "Highway 60"))); + + addButton(ll, "U-turns: prepareTurn, makeTurnIn, turn, when possible:", jsBuilder(p)); + addButton(ll, "\u25BA (6.1) After 640m make a U-turn", jsBuilder(p).prepareMakeUT(640, jsStreet(p, ""))); + addButton(ll, "\u25BA (6.2) In 400m make a U-turn", jsBuilder(p).makeUT(400, jsStreet(p, ""))); + addButton(ll, "\u25BA (6.3) Make a U-turn on 'Riviera'", jsBuilder(p).makeUT(jsStreet(p, "Riviera", "", "", "Riviera"))); + addButton(ll, "\u25BA (6.4) When possible, make a U-turn", jsBuilder(p).makeUTwp()); + + addButton(ll, "Go straight, follow the road, approaching:", jsBuilder(p)); + addButton(ll, "\u25BA (7.1) Straight ahead", jsBuilder(p).goAhead()); + addButton(ll, "\u25BA (7.2) Continue for 2350m to ' '", jsBuilder(p).goAhead(2350, jsStreet(p, ""))); + addButton(ll, "\u25BA (7.3) Continue for 360m to 'Broadway' and arrive at your intermediate destination ' '", 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 ' '", 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'", jsBuilder(p).goAhead(200, new HashMap()).andArriveAtWayPoint("Trailhead")); + addButton(ll, "\u25BA (7.6) Continue for 400m and pass favorite 'Brewery'", jsBuilder(p).goAhead(400, new HashMap()).andArriveAtFavorite("Brewery")); + addButton(ll, "\u25BA (7.7) Continue for 600m and pass POI 'Museum'", jsBuilder(p).goAhead(600, new HashMap()).andArriveAtPoi("Museum")); + + addButton(ll, "Arriving and passing points:", jsBuilder(p)); + addButton(ll, "\u25BA (8.1) Arrive at your destination 'Home'", jsBuilder(p).arrivedAtDestination("Home")); + addButton(ll, "\u25BA (8.2) Arrive at your intermediate destination 'Friend'", jsBuilder(p).arrivedAtIntermediatePoint("Friend")); + addButton(ll, "\u25BA (8.3) Passing GPX waypoint 'Trailhead'", jsBuilder(p).arrivedAtWayPoint("Trailhead")); + addButton(ll, "\u25BA (8.4) Passing favorite 'Brewery'", jsBuilder(p).arrivedAtFavorite("Brewery")); + addButton(ll, "\u25BA (8.5) Passing POI 'Museum'", jsBuilder(p).arrivedAtPoi("Museum")); + + addButton(ll, "Attention prompts:", jsBuilder(p)); + addButton(ll, "\u25BA (9.1) You are exceeding the speed limit '50' (18 m/s)", jsBuilder(p).speedAlarm(50, 18f)); + addButton(ll, "\u25BA (9.2) Attention, speed camera", jsBuilder(p).attention("SPEED_CAMERA")); + addButton(ll, "\u25BA (9.3) Attention, border control", jsBuilder(p).attention("BORDER_CONTROL")); + addButton(ll, "\u25BA (9.4) Attention, railroad crossing", jsBuilder(p).attention("RAILWAY")); + addButton(ll, "\u25BA (9.5) Attention, traffic calming", jsBuilder(p).attention("TRAFFIC_CALMING")); + addButton(ll, "\u25BA (9.6) Attention, toll booth", jsBuilder(p).attention("TOLL_BOOTH")); + addButton(ll, "\u25BA (9.7) Attention, stop sign", jsBuilder(p).attention("STOP")); + addButton(ll, "\u25BA (9.8) Attention, pedestrian crosswalk", jsBuilder(p).attention("PEDESTRIAN")); + addButton(ll, "\u25BA (9.9) Attention, tunnel", jsBuilder(p).attention("TUNNEL")); + + addButton(ll, "Other prompts:", jsBuilder(p)); + addButton(ll, "\u25BA (10.1) GPS signal lost", jsBuilder(p).gpsLocationLost()); + addButton(ll, "\u25BA (10.2) GPS signal recovered", jsBuilder(p).gpsLocationRecover()); + addButton(ll, "\u25BA (10.3) You have been off the route for 1050m", jsBuilder(p).offRoute(1050)); + addButton(ll, "\u25BA (10.4) You are back on the route", jsBuilder(p).backOnRoute()); + + addButton(ll, "Voice system info:", jsBuilder(p)); + addButton(ll, "\u25BA (11.1) (Tap to refresh)\n" + getVoiceSystemInfo(), jsBuilder(p).attention("")); + addButton(ll, "\u25BA (11.2) Tap to change Phone call audio delay (if car stereo cuts off prompts). Default is 1500\u00A0ms.", jsBuilder(p).attention("")); + ll.forceLayout(); + } + private CommandBuilder builder(CommandPlayer p){ return p.newCommandBuilder(); } + private JSCommandBuilder jsBuilder(JSTTSCommandPlayerImpl p) { + return p.newCommandBuilder(); + } + public void addButton(ViewGroup layout, final String description, final CommandBuilder builder){ final Button button = new Button(this); button.setGravity(Gravity.LEFT); diff --git a/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java b/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java index c40a2a79aa..393c39957a 100644 --- a/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java +++ b/OsmAnd/src/net/osmand/plus/routing/JSVoiceRouter.java @@ -28,42 +28,46 @@ public class JSVoiceRouter extends VoiceRouter { // Issue 2377: Play Dest here only if not already previously announced, to avoid repetition if (includeDest == true) { - result.put("toRef", getSpeakablePointName(i.getRef())); - result.put("toStreetName", getSpeakablePointName(i.getStreetName())); - result.put("toDest", getSpeakablePointName(i.getDestinationName())); + 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", getSpeakablePointName(i.getRef())); - result.put("toStreetName", getSpeakablePointName(i.getStreetName())); + 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", getSpeakablePointName(obj.getRef(settings.MAP_PREFERRED_LOCALE.get(), - settings.MAP_TRANSLITERATE_NAMES.get(), currentSegment.isForwardDirection()))); - result.put("fromStreetName", getSpeakablePointName(obj.getName(settings.MAP_PREFERRED_LOCALE.get(), - settings.MAP_TRANSLITERATE_NAMES.get()))); - result.put("fromDest", getSpeakablePointName(obj.getDestinationName(settings.MAP_PREFERRED_LOCALE.get(), - settings.MAP_TRANSLITERATE_NAMES.get(), currentSegment.isForwardDirection()))); + 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", getSpeakablePointName(obj.getRef(settings.MAP_PREFERRED_LOCALE.get(), - settings.MAP_TRANSLITERATE_NAMES.get(), currentSegment.isForwardDirection()))); - result.put("fromStreetName", getSpeakablePointName(obj.getName(settings.MAP_PREFERRED_LOCALE.get(), - settings.MAP_TRANSLITERATE_NAMES.get()))); + 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", getSpeakablePointName(i.getRef())); - result.put("toStreetName", getSpeakablePointName(i.getStreetName())); + 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 @@ -235,10 +239,10 @@ public class JSVoiceRouter extends VoiceRouter { } 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)); + 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)); + play.then().bearRight(getSpeakableJSStreetName(currentSegment, next, false)); } } if (isPlay) { diff --git a/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java b/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java index 60894872ff..84a2e0b04d 100644 --- a/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java +++ b/OsmAnd/src/net/osmand/plus/voice/AbstractJSCommandPlayer.java @@ -1,10 +1,50 @@ 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 { + 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("-")); + } -public abstract class AbstractJSCommandPlayer implements CommandPlayer { @Override public String getCurrentVoice() { return null; @@ -24,7 +64,11 @@ public abstract class AbstractJSCommandPlayer implements CommandPlayer { @Override public void clear() { - + if(ctx != null && ctx.getSettings() != null) { + ctx.getSettings().APPLICATION_MODE.removeListener(this); + } + abandonAudioFocus(); + ctx = null; } @Override @@ -51,4 +95,70 @@ public abstract class AbstractJSCommandPlayer implements CommandPlayer { 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); +// } + } } diff --git a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java index 28d86aed3d..c1805520af 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java @@ -10,7 +10,6 @@ import org.mozilla.javascript.Function; import org.mozilla.javascript.NativeJSON; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.WrapFactory; import java.io.File; import java.io.FileInputStream; @@ -23,12 +22,14 @@ 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 listStruct = new ArrayList<>(); - ScriptableObject jsScope; + private ScriptableObject jsScope; - public JSCommandBuilder(CommandPlayer commandPlayer) { + JSCommandBuilder(CommandPlayer commandPlayer) { super(commandPlayer); } @@ -46,16 +47,16 @@ public class JSCommandBuilder extends CommandBuilder { private String readFileContents(String path) { - FileInputStream fis = null; StringBuilder fileContent = new StringBuilder(""); try { - fis = new FileInputStream(new File(path)); + FileInputStream fis = new FileInputStream(new File(path)); byte[] buffer = new byte[1024]; int n; while ((n = fis.read(buffer)) != -1) { fileContent.append(new String(buffer, 0, n)); } + fis.close(); } catch (IOException e) { log.error(e.getMessage(), e); } @@ -63,18 +64,20 @@ public class JSCommandBuilder extends CommandBuilder { } public void setParameters(String metricCons, boolean tts) { -// jsContext.property("setMode").toFunction().call(jsContext, tts); - Object obj = jsScope.get("setMode", jsScope); + 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}); + } - if (obj instanceof Function) { - Function jsFunction = (Function) obj; - jsFunction.call(jsContext, jsScope, jsScope, new Object[]{metricCons}); - jsFunction.call(jsContext, jsScope, jsScope, new Object[]{tts}); + 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){ - // TODO add JSCore Object obj = jsScope.get(name); if (obj instanceof Function) { Function jsFunction = (Function) obj; @@ -97,7 +100,7 @@ public class JSCommandBuilder extends CommandBuilder { } public JSCommandBuilder makeUT(Map streetName){ - return addCommand(C_MAKE_UT, convertStreetName(streetName)); + return makeUT(-1, streetName); } @Override public JSCommandBuilder speedAlarm(int maxSpeed, float speed){ @@ -126,10 +129,10 @@ public class JSCommandBuilder extends CommandBuilder { public JSCommandBuilder turn(String param, Map streetName) { - return addCommand(C_TURN, param, convertStreetName(streetName)); + return turn(param, -1, streetName); } - public JSCommandBuilder turn(String param, double dist, Map streetName){ + public JSCommandBuilder turn(String param, double dist, Map streetName) { return addCommand(C_TURN, param, dist, convertStreetName(streetName)); } diff --git a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java index b1dc216067..4fbdec31ad 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java @@ -1,6 +1,8 @@ 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; @@ -8,36 +10,210 @@ 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.File; +import java.io.FileInputStream; +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 org.mozilla.javascript.Context jsContext; + 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 VoiceRouter vrt; private String voiceProvider; private HashMap params = new HashMap(); private static int ttsRequests = 0; - public JSTTSCommandPlayerImpl(OsmandApplication ctx, ApplicationMode applicationMode, VoiceRouter vrt, String voiceProvider) { - this.app = ctx; + 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; this.voiceProvider = voiceProvider; - mTts = new TextToSpeech(ctx, null); + 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()); + setJSContext(app.getAppPath(IndexConstants.VOICE_INDEX_DIR).getAbsolutePath() + "/" + voiceProvider + "/" + language + "_tts.js"); + } + + 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 @@ -48,11 +224,36 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { @Override public JSCommandBuilder newCommandBuilder() { JSCommandBuilder commandBuilder = new JSCommandBuilder(this); - commandBuilder.setJSContext(app.getAppPath(IndexConstants.VOICE_INDEX_DIR).getAbsolutePath() + "/" + voiceProvider + "/en_tts.js"); - commandBuilder.setParameters(app.getSettings().METRIC_SYSTEM.get().toHumanString(app), true); + commandBuilder.setJSContext(jsContext, jsScope); + commandBuilder.setParameters(app.getSettings().METRIC_SYSTEM.get().toTTSString(), true); return commandBuilder; } + private String readFileContents(String path) { + StringBuilder fileContent = new StringBuilder(""); + try { + FileInputStream fis = new FileInputStream(new File(path)); + byte[] buffer = new byte[1024]; + int n; + while ((n = fis.read(buffer)) != -1) + { + fileContent.append(new String(buffer, 0, n)); + } + fis.close(); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return fileContent.toString(); + } + + private void setJSContext(String path) { + String script = readFileContents(path); + jsContext = org.mozilla.javascript.Context.enter(); + jsContext.setOptimizationLevel(-1); + jsScope = jsContext.initStandardObjects(); + jsContext.evaluateString(jsScope, script, "JS", 1, null); + } + @Override public void playCommands(CommandBuilder builder) { final List execute = builder.execute(); //list of strings, the speech text, play it @@ -60,7 +261,7 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { for (String s : execute) { bld.append(s).append(' '); } - if (mTts != null && !vrt.isMute()) { + 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) { @@ -83,18 +284,37 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { } @Override - public void clear() { + 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 "en"; + return language; } @Override @@ -102,8 +322,28 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { return true; } - @Override - public void stop() { + private void internalClear() { + ttsRequests = 0; + speechAllowed = false; + if (mTts != null) { + mTts.shutdown(); + mTts = null; + } + abandonAudioFocus(); + mTtsContext = null; + ttsVoiceStatus = ""; + ttsVoiceUsed = ""; + } + + 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; } } From 82078bd9424166067446249045756c6ce1467e5c Mon Sep 17 00:00:00 2001 From: PaulStets Date: Thu, 2 Aug 2018 16:36:48 +0300 Subject: [PATCH 06/28] Added full english JS tts support (needs performance optimization) --- .../plus/voice/JSTTSCommandPlayerImpl.java | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java index 4fbdec31ad..75088576c5 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java @@ -20,9 +20,6 @@ import net.osmand.util.Algorithms; import org.mozilla.javascript.ScriptableObject; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -71,7 +68,7 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { } private OsmandApplication app; private ApplicationMode appMode; - private VoiceRouter vrt; + private VoiceRouter vrt; private String voiceProvider; private HashMap params = new HashMap(); @@ -95,7 +92,6 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { initializeEngine(app, ctx); params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, app.getSettings().AUDIO_STREAM_GUIDANCE .getModeValue(getApplicationMode()).toString()); - setJSContext(app.getAppPath(IndexConstants.VOICE_INDEX_DIR).getAbsolutePath() + "/" + voiceProvider + "/" + language + "_tts.js"); } private void initializeEngine(final Context ctx, final Activity act) { @@ -224,36 +220,11 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { @Override public JSCommandBuilder newCommandBuilder() { JSCommandBuilder commandBuilder = new JSCommandBuilder(this); - commandBuilder.setJSContext(jsContext, jsScope); + commandBuilder.setJSContext(app.getAppPath(IndexConstants.VOICE_INDEX_DIR).getAbsolutePath() + "/" + voiceProvider + "/" + language + "_tts.js"); commandBuilder.setParameters(app.getSettings().METRIC_SYSTEM.get().toTTSString(), true); return commandBuilder; } - private String readFileContents(String path) { - StringBuilder fileContent = new StringBuilder(""); - try { - FileInputStream fis = new FileInputStream(new File(path)); - byte[] buffer = new byte[1024]; - int n; - while ((n = fis.read(buffer)) != -1) - { - fileContent.append(new String(buffer, 0, n)); - } - fis.close(); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - return fileContent.toString(); - } - - private void setJSContext(String path) { - String script = readFileContents(path); - jsContext = org.mozilla.javascript.Context.enter(); - jsContext.setOptimizationLevel(-1); - jsScope = jsContext.initStandardObjects(); - jsContext.evaluateString(jsScope, script, "JS", 1, null); - } - @Override public void playCommands(CommandBuilder builder) { final List execute = builder.execute(); //list of strings, the speech text, play it From c2153c8c28f20680c7ef862d293135e6b2c553fa Mon Sep 17 00:00:00 2001 From: PaulStets Date: Fri, 3 Aug 2018 16:44:51 +0300 Subject: [PATCH 07/28] Attempt to add ability to download js tts --- .../main/java/net/osmand/IndexConstants.java | 8 +--- OsmAnd/assets/bundled_assets.xml | 39 +++++++++++++++++++ OsmAnd/build.gradle | 1 + OsmAnd/res/values/strings.xml | 1 + .../plus/activities/LocalIndexHelper.java | 11 ++++-- .../plus/download/DownloadActivityType.java | 2 + .../download/DownloadOsmandIndexesHelper.java | 7 ++++ .../plus/download/DownloadResourceGroup.java | 7 +++- .../plus/download/DownloadResources.java | 6 +++ .../download/ui/DownloadGroupViewHolder.java | 3 +- .../ui/DownloadResourceGroupFragment.java | 3 +- .../plus/download/ui/ItemViewHolder.java | 4 +- .../download/ui/LocalIndexesFragment.java | 4 +- .../download/ui/SearchDialogFragment.java | 1 + .../controllers/MapDataMenuController.java | 5 ++- .../plus/resources/ResourceManager.java | 7 ++++ .../osmand/plus/voice/JSCommandBuilder.java | 30 +++----------- .../plus/voice/JSTTSCommandPlayerImpl.java | 30 +++++++++++++- 18 files changed, 127 insertions(+), 42 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/IndexConstants.java b/OsmAnd-java/src/main/java/net/osmand/IndexConstants.java index a4f69ecbc7..35f996d87a 100644 --- a/OsmAnd-java/src/main/java/net/osmand/IndexConstants.java +++ b/OsmAnd-java/src/main/java/net/osmand/IndexConstants.java @@ -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"; - - - - - } diff --git a/OsmAnd/assets/bundled_assets.xml b/OsmAnd/assets/bundled_assets.xml index c4f408b34e..5f805aa19a 100644 --- a/OsmAnd/assets/bundled_assets.xml +++ b/OsmAnd/assets/bundled_assets.xml @@ -43,6 +43,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OsmAnd/build.gradle b/OsmAnd/build.gradle index ad430d7c3a..412313be28 100644 --- a/OsmAnd/build.gradle +++ b/OsmAnd/build.gradle @@ -238,6 +238,7 @@ task collectVoiceAssets(type: Sync) { from "../../resources/voice" into "assets/voice" include "**/*.p" + include "**/*.js" } task cleanNoTranslate(type: Delete) { diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 3a72ced33a..ae4193de0c 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -2934,4 +2934,5 @@ Use JS voice guidance Use new voice guidance logic based on JavaScript Tap a button and listen to the corresponding voice prompt to identify missing or faulty propmts. + Voice prompts (JavaScript TTS) diff --git a/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java b/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java index 01822460a5..2110daf2fa 100644 --- a/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java +++ b/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java @@ -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; @@ -87,6 +88,8 @@ public class LocalIndexHelper { info.setDescription(getInstalledDate(f)); } else if (info.getType() == LocalIndexType.TTS_VOICE_DATA) { info.setDescription(getInstalledDate(f)); + } else if (info.getType() == LocalIndexType.TTS_VOICE_DATA_JS) { + info.setDescription(getInstalledDate(f)); } else if (info.getType() == LocalIndexType.DEACTIVATED) { info.setDescription(getInstalledDate(f)); } else if (info.getType() == LocalIndexType.VOICE_DATA) { @@ -216,11 +219,12 @@ public class LocalIndexHelper { private void loadVoiceData(File voiceDir, List result, boolean backup, AbstractLoadLocalIndexTask loadTask) { if (voiceDir.canRead()) { //First list TTS files, they are preferred + boolean useJs = app.getSettings().USE_JS_VOICE_GUIDANCE.get(); 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)) { - info = new LocalIndexInfo(LocalIndexType.TTS_VOICE_DATA, voiceF, backup, app); + if (useJs ? JSTTSCommandPlayerImpl.isMyData(voiceF) : TTSCommandPlayerImpl.isMyData(voiceF)) { + info = new LocalIndexInfo(useJs ? LocalIndexType.TTS_VOICE_DATA_JS : LocalIndexType.TTS_VOICE_DATA, voiceF, backup, app); } if (info != null) { updateDescription(info); @@ -359,6 +363,7 @@ public class LocalIndexHelper { WIKI_DATA(R.string.local_indexes_cat_wiki, R.drawable.ic_plugin_wikipedia, 50), TRAVEL_DATA(R.string.download_maps_travel, R.drawable.ic_plugin_wikipedia, 60), TTS_VOICE_DATA(R.string.local_indexes_cat_tts, R.drawable.ic_action_volume_up, 20), + TTS_VOICE_DATA_JS(R.string.voice_tts_js, R.drawable.ic_action_volume_up, 20), VOICE_DATA(R.string.local_indexes_cat_voice, R.drawable.ic_action_volume_up, 30), FONT_DATA(R.string.fonts_header, R.drawable.ic_action_map_language, 35), DEACTIVATED(R.string.local_indexes_cat_backup, R.drawable.ic_type_archive, 1000); diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 582c18b841..93f5f588ce 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -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); } diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java b/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java index 822bfe91fc..2927a3f579 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java @@ -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(); diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResourceGroup.java b/OsmAnd/src/net/osmand/plus/download/DownloadResourceGroup.java index d95d45acd1..b6a14ba6fc 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResourceGroup.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResourceGroup.java @@ -39,6 +39,7 @@ public class DownloadResourceGroup { NAUTICAL_MAPS_HEADER(R.string.nautical_maps), // headers with voice items VOICE_HEADER_TTS(R.string.index_name_tts_voice), + VOICE_HEADER_TTS_JS(R.string.voice_tts_js), VOICE_HEADER_REC(R.string.index_name_voice), // headers with font items FONTS_HEADER(R.string.fonts_header), @@ -52,6 +53,7 @@ public class DownloadResourceGroup { NAUTICAL_MAPS(R.string.nautical_maps), WIKIVOYAGE_MAPS(R.string.download_maps_travel), VOICE_TTS(R.string.index_name_tts_voice), + VOICE_TTS_JS(R.string.voice_tts_js), FONTS(R.string.fonts_header), VOICE_REC(R.string.index_name_voice), OTHER_MAPS(R.string.download_select_map_types), @@ -65,7 +67,7 @@ public class DownloadResourceGroup { } public boolean isScreen() { - return this == WORLD || this == REGION || this == VOICE_TTS + return this == WORLD || this == REGION || this == VOICE_TTS || this == VOICE_TTS_JS || this == VOICE_REC || this == OTHER_MAPS || this == FONTS || this == NAUTICAL_MAPS || this == WIKIVOYAGE_MAPS; } @@ -83,7 +85,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 == VOICE_HEADER_TTS_JS + || 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 diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java index 62b211e687..2bbd790609 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java @@ -299,8 +299,10 @@ public class DownloadResources extends DownloadResourceGroup { DownloadResourceGroup otherGroup = new DownloadResourceGroup(this, DownloadResourceGroupType.OTHER_GROUP); DownloadResourceGroup voiceScreenTTS = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_TTS); DownloadResourceGroup voiceScreenRec = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_REC); + DownloadResourceGroup voiceScreenTTSJS = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_TTS_JS); DownloadResourceGroup fontScreen = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.FONTS); DownloadResourceGroup voiceTTS = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_HEADER_TTS); + DownloadResourceGroup voiceTTSJS = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_HEADER_TTS_JS); DownloadResourceGroup voiceRec = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_HEADER_REC); DownloadResourceGroup fonts = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.FONTS_HEADER); @@ -320,6 +322,8 @@ public class DownloadResources extends DownloadResourceGroup { if (ii.getType() == DownloadActivityType.VOICE_FILE) { if (ii.getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP)) { voiceTTS.addItem(ii); + } else if (ii.getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)){ + voiceTTSJS.addItem(ii); } else { voiceRec.addItem(ii); } @@ -414,12 +418,14 @@ public class DownloadResources extends DownloadResourceGroup { } voiceScreenTTS.addGroup(voiceTTS); + voiceScreenTTSJS.addGroup(voiceTTSJS); voiceScreenRec.addGroup(voiceRec); if (fonts.getIndividualResources() != null) { fontScreen.addGroup(fonts); } otherGroup.addGroup(voiceScreenTTS); otherGroup.addGroup(voiceScreenRec); + otherGroup.addGroup(voiceScreenTTSJS); if (fonts.getIndividualResources() != null) { diff --git a/OsmAnd/src/net/osmand/plus/download/ui/DownloadGroupViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/DownloadGroupViewHolder.java index 86ef9e6995..f8674dd49c 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/DownloadGroupViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/DownloadGroupViewHolder.java @@ -28,7 +28,8 @@ public class DownloadGroupViewHolder { private Drawable getIconForGroup(DownloadResourceGroup group) { Drawable iconLeft; if (group.getType() == DownloadResourceGroup.DownloadResourceGroupType.VOICE_REC - || group.getType() == DownloadResourceGroup.DownloadResourceGroupType.VOICE_TTS) { + || group.getType() == DownloadResourceGroup.DownloadResourceGroupType.VOICE_TTS + || group.getType() == DownloadResourceGroup.DownloadResourceGroupType.VOICE_TTS_JS) { iconLeft = ctx.getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_action_volume_up); } else if (group.getType() == DownloadResourceGroup.DownloadResourceGroupType.FONTS) { iconLeft = ctx.getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_action_map_language); diff --git a/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupFragment.java index 0e0e49ebd0..c4eabcd9e9 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupFragment.java @@ -531,7 +531,8 @@ public class DownloadResourceGroupFragment extends DialogFragment implements Dow private Drawable getIconForGroup(DownloadResourceGroup group) { Drawable iconLeft; if (group.getType() == DownloadResourceGroupType.VOICE_REC - || group.getType() == DownloadResourceGroupType.VOICE_TTS) { + || group.getType() == DownloadResourceGroupType.VOICE_TTS || group.getType() == + DownloadResourceGroupType.VOICE_TTS_JS) { iconLeft = ctx.getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_action_volume_up); } else if (group.getType() == DownloadResourceGroupType.FONTS) { iconLeft = ctx.getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_action_map_language); diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 72cb9f1414..2db69a358c 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -19,6 +19,7 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; +import net.osmand.IndexConstants; import net.osmand.map.WorldRegion; import net.osmand.plus.R; import net.osmand.plus.Version; @@ -402,7 +403,8 @@ public class ItemViewHolder { } else if (indexItem.getType() == DownloadActivityType.FONT_FILE) { tp = LocalIndexType.FONT_DATA; } else if (indexItem.getType() == DownloadActivityType.VOICE_FILE) { - tp = indexItem.getBasename().contains("tts") ? LocalIndexType.TTS_VOICE_DATA + tp = indexItem.getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS) ? LocalIndexType.TTS_VOICE_DATA_JS : + indexItem.getBasename().contains("tts") ? LocalIndexType.TTS_VOICE_DATA : LocalIndexType.VOICE_DATA; } final LocalIndexInfo info = new LocalIndexInfo(tp, fl, false, context.getMyApplication()); diff --git a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java index f919a82609..81f14ea658 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java @@ -200,7 +200,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement .setPosition(2).createItem()); } if (info.getType() != LocalIndexType.TTS_VOICE_DATA && info.getType() != LocalIndexType.VOICE_DATA - && info.getType() != LocalIndexType.FONT_DATA) { + && info.getType() != LocalIndexType.FONT_DATA && info.getType() != LocalIndexType.TTS_VOICE_DATA_JS) { adapter.addItem(new ContextMenuItem.ItemBuilder() .setTitleId(R.string.shared_string_rename, getContext()) .setListener(listener) @@ -452,6 +452,8 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement parent = getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR); } else if (i.getOriginalType() == LocalIndexType.VOICE_DATA) { parent = getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR); + } else if (i.getOriginalType() == LocalIndexType.TTS_VOICE_DATA_JS) { + parent = getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR); } else if (i.getOriginalType() == LocalIndexType.FONT_DATA) { parent = getMyApplication().getAppPath(IndexConstants.FONT_INDEX_DIR); } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java index 5611a35e71..98a15c6b5f 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java @@ -492,6 +492,7 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven if (group.getType() == DownloadResourceGroupType.OTHER_MAPS_HEADER || group.getType() == DownloadResourceGroupType.VOICE_HEADER_REC || group.getType() == DownloadResourceGroupType.VOICE_HEADER_TTS + || group.getType() == DownloadResourceGroupType.VOICE_HEADER_TTS_JS || group.getType() == DownloadResourceGroupType.FONTS_HEADER) { if (group.getIndividualResources() != null) { for (IndexItem item : group.getIndividualResources()) { diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java index 2b57c67463..de46c93b77 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java @@ -525,7 +525,8 @@ public class MapDataMenuController extends MenuController { } else if (localIndexInfo.getOriginalType() == LocalIndexType.TRAVEL_DATA) { return DownloadActivityType.WIKIVOYAGE_FILE; } else if (localIndexInfo.getOriginalType() == LocalIndexType.TTS_VOICE_DATA - || localIndexInfo.getOriginalType() == LocalIndexType.VOICE_DATA) { + || localIndexInfo.getOriginalType() == LocalIndexType.VOICE_DATA + || localIndexInfo.getOriginalType() == LocalIndexType.TTS_VOICE_DATA_JS) { return DownloadActivityType.VOICE_FILE; } else if (localIndexInfo.getOriginalType() == LocalIndexType.FONT_DATA) { return DownloadActivityType.FONT_FILE; @@ -650,6 +651,8 @@ public class MapDataMenuController extends MenuController { parent = app.getAppPath(IndexConstants.VOICE_INDEX_DIR); } else if (i.getOriginalType() == LocalIndexType.VOICE_DATA) { parent = app.getAppPath(IndexConstants.VOICE_INDEX_DIR); + } else if (i.getOriginalType() == LocalIndexType.TTS_VOICE_DATA_JS) { + parent = app.getAppPath(IndexConstants.VOICE_INDEX_DIR); } return new File(parent, i.getFileName()); } diff --git a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java index cd85fd9d15..10c52477fa 100644 --- a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java @@ -392,6 +392,13 @@ public class ResourceManager { if (conf.exists()) { indexFileNames.put(f.getName(), dateFormat.format(conf.lastModified())); //$NON-NLS-1$ } + if (f.getName().contains("-tts")) { + String lang = f.getName().replace("-tts", ""); + File jsConf = new File(f, lang + "_" + IndexConstants.TTSVOICE_INDEX_EXT_JS); + if (jsConf.exists()) { + indexFileNames.put(f.getName(), dateFormat.format(jsConf.lastModified())); + } + } } } } diff --git a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java index c1805520af..b49829a548 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSCommandBuilder.java @@ -11,8 +11,10 @@ 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; @@ -33,34 +35,14 @@ public class JSCommandBuilder extends CommandBuilder { super(commandPlayer); } - public void setJSContext(String path) { - String script = readFileContents(path); + public void setJSContext(ScriptableObject jsScope) { jsContext = Context.enter(); - jsContext.setOptimizationLevel(-1); - jsScope = jsContext.initStandardObjects(); - jsContext.evaluateString(jsScope, script, "JS", 1, null); + this.jsScope = jsScope; } private Object convertStreetName(Map streetName) { - return NativeJSON.parse(jsContext, jsScope, new JSONObject(streetName).toString(), new NullCallable()); - } - - - private String readFileContents(String path) { - StringBuilder fileContent = new StringBuilder(""); - try { - FileInputStream fis = new FileInputStream(new File(path)); - byte[] buffer = new byte[1024]; - int n; - while ((n = fis.read(buffer)) != -1) - { - fileContent.append(new String(buffer, 0, n)); - } - fis.close(); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - return fileContent.toString(); + return NativeJSON.parse(jsContext, jsScope, new JSONObject(streetName).toString(), + new NullCallable()); } public void setParameters(String metricCons, boolean tts) { diff --git a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java index 75088576c5..124543f7b2 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java @@ -20,6 +20,10 @@ 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; @@ -35,7 +39,6 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { private boolean speechAllowed = false; private Context mTtsContext; - private org.mozilla.javascript.Context jsContext; private ScriptableObject jsScope; private float cSpeechRate = 1; @@ -92,6 +95,20 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { 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) { @@ -220,7 +237,7 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { @Override public JSCommandBuilder newCommandBuilder() { JSCommandBuilder commandBuilder = new JSCommandBuilder(this); - commandBuilder.setJSContext(app.getAppPath(IndexConstants.VOICE_INDEX_DIR).getAbsolutePath() + "/" + voiceProvider + "/" + language + "_tts.js"); + commandBuilder.setJSContext(jsScope); commandBuilder.setParameters(app.getSettings().METRIC_SYSTEM.get().toTTSString(), true); return commandBuilder; } @@ -307,6 +324,15 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { ttsVoiceUsed = ""; } + public static boolean isMyData(File voiceDir) { + for (File f : voiceDir.listFiles()) { + if (f.getName().contains(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); From 65e8b0b7b6fe89e0be348f12de128d80e5e21d69 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Sat, 4 Aug 2018 13:21:32 +0300 Subject: [PATCH 08/28] Added beta JS tts support --- OsmAnd/assets/bundled_assets.xml | 20 +++++++++--------- OsmAnd/res/values/strings.xml | 1 + .../src/net/osmand/plus/AppInitializer.java | 5 ++--- .../plus/activities/LocalIndexHelper.java | 21 ++++++++++++++++--- .../SettingsNavigationActivity.java | 2 +- .../plus/download/DownloadActivityType.java | 5 ++++- .../net/osmand/plus/download/IndexItem.java | 3 +++ .../helpers/FileNameTranslationHelper.java | 9 +++++--- .../other/RoutePreferencesMenu.java | 2 +- .../plus/resources/ResourceManager.java | 6 +++--- .../plus/voice/JSTTSCommandPlayerImpl.java | 7 +------ 11 files changed, 50 insertions(+), 31 deletions(-) diff --git a/OsmAnd/assets/bundled_assets.xml b/OsmAnd/assets/bundled_assets.xml index 5f805aa19a..cf0bcd3506 100644 --- a/OsmAnd/assets/bundled_assets.xml +++ b/OsmAnd/assets/bundled_assets.xml @@ -48,31 +48,31 @@ - + - + - + - + - + - + - - - + + + - + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index ae4193de0c..4ebf6acdf7 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -2935,4 +2935,5 @@ Use new voice guidance logic based on JavaScript Tap a button and listen to the corresponding voice prompt to identify missing or faulty propmts. Voice prompts (JavaScript TTS) + (JavaScript) diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index 0bb92adeaf..c3cca9cf5f 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -578,10 +578,9 @@ public class AppInitializer implements IProgress { if (!voiceDir.exists()) { throw new CommandPlayerException(ctx.getString(R.string.voice_data_unavailable)); } - if (app.getSettings().USE_JS_VOICE_GUIDANCE.get()) { + if (app.getSettings().USE_JS_VOICE_GUIDANCE.get() && JSTTSCommandPlayerImpl.isMyData(voiceDir)) { return new JSTTSCommandPlayerImpl(ctx, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider); - } - if (MediaCommandPlayerImpl.isMyData(voiceDir)) { + } else if (MediaCommandPlayerImpl.isMyData(voiceDir)) { return new MediaCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider); } else if (TTSCommandPlayerImpl.isMyData(voiceDir)) { return new TTSCommandPlayerImpl(ctx, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider); diff --git a/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java b/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java index 2110daf2fa..220f01602e 100644 --- a/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java +++ b/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java @@ -219,12 +219,11 @@ public class LocalIndexHelper { private void loadVoiceData(File voiceDir, List result, boolean backup, AbstractLoadLocalIndexTask loadTask) { if (voiceDir.canRead()) { //First list TTS files, they are preferred - boolean useJs = app.getSettings().USE_JS_VOICE_GUIDANCE.get(); for (File voiceF : listFilesSorted(voiceDir)) { if (voiceF.isDirectory() && !MediaCommandPlayerImpl.isMyData(voiceF)) { LocalIndexInfo info = null; - if (useJs ? JSTTSCommandPlayerImpl.isMyData(voiceF) : TTSCommandPlayerImpl.isMyData(voiceF)) { - info = new LocalIndexInfo(useJs ? LocalIndexType.TTS_VOICE_DATA_JS : LocalIndexType.TTS_VOICE_DATA, voiceF, backup, app); + if (TTSCommandPlayerImpl.isMyData(voiceF)) { + info = new LocalIndexInfo(LocalIndexType.TTS_VOICE_DATA, voiceF, backup, app); } if (info != null) { updateDescription(info); @@ -234,6 +233,22 @@ public class LocalIndexHelper { } } + if (app.getSettings().USE_JS_VOICE_GUIDANCE.get()) { + for (File voiceF : listFilesSorted(voiceDir)) { + if (voiceF.isDirectory() && !MediaCommandPlayerImpl.isMyData(voiceF)) { + LocalIndexInfo info = null; + if (JSTTSCommandPlayerImpl.isMyData(voiceF)) { + info = new LocalIndexInfo(LocalIndexType.TTS_VOICE_DATA_JS, voiceF, backup, app); + } + if (info != null) { + updateDescription(info); + result.add(info); + loadTask.loadFile(info); + } + } + } + } + //Now list recorded voices for (File voiceF : listFilesSorted(voiceDir)) { if (voiceF.isDirectory() && MediaCommandPlayerImpl.isMyData(voiceF)) { diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java index 1942e5f598..e9c0119a7c 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java @@ -253,7 +253,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity { entries[k++] = getString(R.string.shared_string_do_not_use); for (String s : voiceFiles) { entries[k] = (s.contains("tts") ? getString(R.string.ttsvoice) + " " : "") + - FileNameTranslationHelper.getVoiceName(this, s); + FileNameTranslationHelper.getVoiceName(this, s) + (s.contains("-js") ? " " + getString(R.string.js_tts_label) : ""); entrieValues[k] = s; k++; } diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 93f5f588ce..71d7a9a410 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -338,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; + } int l = fileName.lastIndexOf('_'); if (l == -1) { l = fileName.length(); @@ -410,7 +413,7 @@ public class DownloadActivityType { if (l == -1) { l = fileName.length(); } - return fileName.substring(0, l); + return fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS) ? fileName : fileName.substring(0, l); } if (this == FONT_FILE) { int l = fileName.indexOf('.'); diff --git a/OsmAnd/src/net/osmand/plus/download/IndexItem.java b/OsmAnd/src/net/osmand/plus/download/IndexItem.java index 4334ef28f1..06529dc6f0 100644 --- a/OsmAnd/src/net/osmand/plus/download/IndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/IndexItem.java @@ -139,6 +139,9 @@ public class IndexItem implements Comparable { String basename; if (type == DownloadActivityType.HILLSHADE_FILE) { basename = (FileNameTranslationHelper.HILL_SHADE + getBasename()).replace("_", " "); + } else if (type == DownloadActivityType.VOICE_FILE && getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)){ + return new File(type.getDownloadFolder(ctx, this), + getBasename().replaceAll("[_\\.]+", "-") + File.separator + getBasename()); } else { basename = getBasename(); } diff --git a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java index 30d7b2a730..a5a6241e62 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java @@ -25,7 +25,7 @@ public class FileNameTranslationHelper { String basename = getBasename(fileName); if (basename.endsWith(WIKI_NAME)) { //wiki files return getWikiName(ctx, basename); - } else if (fileName.endsWith("tts")) { //tts files + } else if (fileName.endsWith("tts") || fileName.endsWith("tts-js")) { //tts files return getVoiceName(ctx, fileName); } else if (fileName.endsWith(IndexConstants.FONT_INDEX_EXT)) { //otf files return getFontName(ctx, basename); @@ -80,8 +80,11 @@ public class FileNameTranslationHelper { public static String getVoiceName(Context ctx, String fileName) { try { - String nm = fileName.replace('-', '_').replace(' ', '_'); - if (nm.endsWith("_tts") || nm.endsWith("-tts")) { + String nm = fileName.replace('-', '_').replace(' ', '_') + .replace('.', '_'); + if (nm.endsWith("_tts_js")) { + nm = nm.substring(0, nm.indexOf("_tts_js")); + } else if (nm.endsWith("_tts") || nm.endsWith("-tts")) { nm = nm.substring(0, nm.length() - 4); } Field f = R.string.class.getField("lang_"+nm); diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java index 070c0a61c6..e6bd001767 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java @@ -280,7 +280,7 @@ public class RoutePreferencesMenu { k++; for (String s : voiceFiles) { entries[k] = (s.contains("tts") ? mapActivity.getResources().getString(R.string.ttsvoice) + " " : "") + - FileNameTranslationHelper.getVoiceName(mapActivity, s); + FileNameTranslationHelper.getVoiceName(mapActivity, s) + (s.contains("-js") ? " " + mapActivity.getString(R.string.js_tts_label) : ""); entrieValues[k] = s; adapter.addItem(itemBuilder.setTitle(entries[k]).createItem()); if (s.equals(selectedValue)) { diff --git a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java index 10c52477fa..e3ca184e2f 100644 --- a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java @@ -392,11 +392,11 @@ public class ResourceManager { if (conf.exists()) { indexFileNames.put(f.getName(), dateFormat.format(conf.lastModified())); //$NON-NLS-1$ } - if (f.getName().contains("-tts")) { - String lang = f.getName().replace("-tts", ""); + if (f.getName().contains("-tts-js")) { + String lang = f.getName().replace("-tts-js", ""); File jsConf = new File(f, lang + "_" + IndexConstants.TTSVOICE_INDEX_EXT_JS); if (jsConf.exists()) { - indexFileNames.put(f.getName(), dateFormat.format(jsConf.lastModified())); + indexFileNames.put(jsConf.getName(), dateFormat.format(jsConf.lastModified())); } } } diff --git a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java index 124543f7b2..8216b2ec36 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java @@ -325,12 +325,7 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { } public static boolean isMyData(File voiceDir) { - for (File f : voiceDir.listFiles()) { - if (f.getName().contains(IndexConstants.TTSVOICE_INDEX_EXT_JS)) { - return true; - } - } - return false; + return voiceDir.getName().contains("-tts-js"); } private AlertDialog.Builder createAlertDialog(int titleResID, int messageResID, From a1fbee1aa1cf8f149256631f438fd408f8b486f1 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Tue, 7 Aug 2018 15:08:09 +0300 Subject: [PATCH 09/28] Fixed an issue with js files removal --- OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java | 4 ++-- OsmAnd/src/net/osmand/plus/download/IndexItem.java | 3 ++- OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java | 2 +- OsmAnd/src/net/osmand/plus/resources/ResourceManager.java | 2 +- OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java | 3 ++- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 71d7a9a410..2acb463ffa 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -339,7 +339,7 @@ public class DownloadActivityType { // fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP)) { if (this == VOICE_FILE) { if (fileName.contains(".js")) { - return fileName; + return fileName.replace('_', '-').replace('.', '-'); } int l = fileName.lastIndexOf('_'); if (l == -1) { @@ -413,7 +413,7 @@ public class DownloadActivityType { if (l == -1) { l = fileName.length(); } - return fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS) ? fileName : fileName.substring(0, l); + return fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS) ? fileName.replace('_', '-').replace('.', '-') : fileName.substring(0, l); } if (this == FONT_FILE) { int l = fileName.indexOf('.'); diff --git a/OsmAnd/src/net/osmand/plus/download/IndexItem.java b/OsmAnd/src/net/osmand/plus/download/IndexItem.java index 06529dc6f0..0c43ca4cb8 100644 --- a/OsmAnd/src/net/osmand/plus/download/IndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/IndexItem.java @@ -141,7 +141,8 @@ public class IndexItem implements Comparable { basename = (FileNameTranslationHelper.HILL_SHADE + getBasename()).replace("_", " "); } else if (type == DownloadActivityType.VOICE_FILE && getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)){ return new File(type.getDownloadFolder(ctx, this), - getBasename().replaceAll("[_\\.]+", "-") + File.separator + getBasename()); + getBasename().replaceAll("[_\\.]+", "-") + File.separator + getBasename().replaceFirst("-", "_") + .replace('-', '.')); } else { basename = getBasename(); } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 2db69a358c..190ede5b32 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -403,7 +403,7 @@ public class ItemViewHolder { } else if (indexItem.getType() == DownloadActivityType.FONT_FILE) { tp = LocalIndexType.FONT_DATA; } else if (indexItem.getType() == DownloadActivityType.VOICE_FILE) { - tp = indexItem.getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS) ? LocalIndexType.TTS_VOICE_DATA_JS : + tp = indexItem.getFileName().contains("-tts-js") ? LocalIndexType.TTS_VOICE_DATA_JS : indexItem.getBasename().contains("tts") ? LocalIndexType.TTS_VOICE_DATA : LocalIndexType.VOICE_DATA; } diff --git a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java index e3ca184e2f..9798f4100b 100644 --- a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java @@ -396,7 +396,7 @@ public class ResourceManager { String lang = f.getName().replace("-tts-js", ""); File jsConf = new File(f, lang + "_" + IndexConstants.TTSVOICE_INDEX_EXT_JS); if (jsConf.exists()) { - indexFileNames.put(jsConf.getName(), dateFormat.format(jsConf.lastModified())); + indexFileNames.put(f.getName(), dateFormat.format(jsConf.lastModified())); } } } diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java index c34f97f44b..9c103353d3 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java @@ -97,7 +97,8 @@ public class RoutingHelper { public RoutingHelper(OsmandApplication context){ this.app = context; settings = context.getSettings(); - voiceRouter = settings.USE_JS_VOICE_GUIDANCE.get() ?new JSVoiceRouter(this, settings) + boolean useJS = settings.VOICE_PROVIDER.get().contains("-js"); + voiceRouter = useJS ? new JSVoiceRouter(this, settings) : new VoiceRouter(this, settings); provider = new RouteProvider(); setAppMode(settings.APPLICATION_MODE.get()); From 4fe5a2c2d9310749535c3af784c9f5dfbc5d5bbe Mon Sep 17 00:00:00 2001 From: PaulStets Date: Wed, 8 Aug 2018 09:12:02 +0300 Subject: [PATCH 10/28] Added ogg support for en_tts.js --- .../src/net/osmand/plus/AppInitializer.java | 7 +- .../osmand/plus/routing/RoutingHelper.java | 3 +- .../plus/voice/JSMediaCommandPlayerImpl.java | 92 +++++++++++++++++++ .../plus/voice/MediaCommandPlayerImpl.java | 8 +- 4 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/voice/JSMediaCommandPlayerImpl.java diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index c3cca9cf5f..254293298c 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -46,6 +46,7 @@ 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; @@ -578,10 +579,12 @@ public class AppInitializer implements IProgress { if (!voiceDir.exists()) { throw new CommandPlayerException(ctx.getString(R.string.voice_data_unavailable)); } - if (app.getSettings().USE_JS_VOICE_GUIDANCE.get() && JSTTSCommandPlayerImpl.isMyData(voiceDir)) { + 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 new MediaCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider); + return useJs ? 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); } diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java index 9c103353d3..aa05de86c0 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java @@ -97,7 +97,8 @@ public class RoutingHelper { public RoutingHelper(OsmandApplication context){ this.app = context; settings = context.getSettings(); - boolean useJS = settings.VOICE_PROVIDER.get().contains("-js"); + boolean useJS = settings.VOICE_PROVIDER.get().contains("-js") || + (!settings.VOICE_PROVIDER.get().contains("-tts") && settings.USE_JS_VOICE_GUIDANCE.get()); voiceRouter = useJS ? new JSVoiceRouter(this, settings) : new VoiceRouter(this, settings); provider = new RouteProvider(); diff --git a/OsmAnd/src/net/osmand/plus/voice/JSMediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSMediaCommandPlayerImpl.java new file mode 100644 index 0000000000..54d713431b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/voice/JSMediaCommandPlayerImpl.java @@ -0,0 +1,92 @@ +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 splitAnnouncements(List execute) { + List 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; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java index a48a904d98..6e439b2fe0 100644 --- a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java @@ -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 filesToPlay = Collections.synchronizedList(new ArrayList()); - private VoiceRouter vrt; + List filesToPlay = Collections.synchronizedList(new ArrayList()); + 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(); } From 00420753cf09f89b1d0d614219839ce66e715f71 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Wed, 8 Aug 2018 09:14:23 +0300 Subject: [PATCH 11/28] Fixed formatting --- OsmAnd/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OsmAnd/build.gradle b/OsmAnd/build.gradle index 99a23c0abe..9d4e503d8f 100644 --- a/OsmAnd/build.gradle +++ b/OsmAnd/build.gradle @@ -363,7 +363,7 @@ dependencies { implementation 'com.squareup.picasso:picasso:2.71828' // JS core - implementation group: 'org.mozilla', name: 'rhino', version: '1.7.9' + implementation group: 'org.mozilla', name: 'rhino', version: '1.7.9' // size restrictions // implementation 'com.ibm.icu:icu4j:50.1' From 3b64f67c97db236f1c88698b99ef746c698bb03b Mon Sep 17 00:00:00 2001 From: PaulStets Date: Wed, 8 Aug 2018 10:21:35 +0300 Subject: [PATCH 12/28] Fixed issues with js voice deletion --- OsmAnd/src/net/osmand/plus/download/IndexItem.java | 4 ---- OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/download/IndexItem.java b/OsmAnd/src/net/osmand/plus/download/IndexItem.java index 0c43ca4cb8..4334ef28f1 100644 --- a/OsmAnd/src/net/osmand/plus/download/IndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/IndexItem.java @@ -139,10 +139,6 @@ public class IndexItem implements Comparable { String basename; if (type == DownloadActivityType.HILLSHADE_FILE) { basename = (FileNameTranslationHelper.HILL_SHADE + getBasename()).replace("_", " "); - } else if (type == DownloadActivityType.VOICE_FILE && getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)){ - return new File(type.getDownloadFolder(ctx, this), - getBasename().replaceAll("[_\\.]+", "-") + File.separator + getBasename().replaceFirst("-", "_") - .replace('-', '.')); } else { basename = getBasename(); } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 190ede5b32..11b4ba1452 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -19,7 +19,6 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; -import net.osmand.IndexConstants; import net.osmand.map.WorldRegion; import net.osmand.plus.R; import net.osmand.plus.Version; @@ -403,7 +402,7 @@ public class ItemViewHolder { } else if (indexItem.getType() == DownloadActivityType.FONT_FILE) { tp = LocalIndexType.FONT_DATA; } else if (indexItem.getType() == DownloadActivityType.VOICE_FILE) { - tp = indexItem.getFileName().contains("-tts-js") ? LocalIndexType.TTS_VOICE_DATA_JS : + tp = indexItem.getBasename().contains("-tts-js") ? LocalIndexType.TTS_VOICE_DATA_JS : indexItem.getBasename().contains("tts") ? LocalIndexType.TTS_VOICE_DATA : LocalIndexType.VOICE_DATA; } From 8eaedd8d3885a400360aaffa76866602afc9898b Mon Sep 17 00:00:00 2001 From: PaulStets Date: Wed, 8 Aug 2018 10:55:59 +0300 Subject: [PATCH 13/28] Added the ogg routing to voice prompts testing --- .../osmand/plus/development/TestVoiceActivity.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java b/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java index c7988582b7..fded7b1d46 100644 --- a/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java @@ -23,6 +23,7 @@ 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; @@ -220,8 +221,8 @@ public class TestVoiceActivity extends OsmandActionBarActivity { } private void addButtons(final LinearLayout ll, CommandPlayer p) { - if (p instanceof JSTTSCommandPlayerImpl) { - addJSTTSPrompts(ll, (JSTTSCommandPlayerImpl) p); + if (p instanceof JSTTSCommandPlayerImpl || p instanceof JSMediaCommandPlayerImpl) { + addJSTTSPrompts(ll, p); return; } addButton(ll, "Route calculated and number tests:", builder(p)); @@ -317,7 +318,7 @@ public class TestVoiceActivity extends OsmandActionBarActivity { return res; } - private void addJSTTSPrompts(LinearLayout ll, JSTTSCommandPlayerImpl p) { + private void addJSTTSPrompts(LinearLayout ll, CommandPlayer p) { addButton(ll, "Route calculated and number tests:", jsBuilder(p)); addButton(ll, "\u25BA (1.1) New route calculated, 150m, 230sec (00:03:50)", jsBuilder(p).newRouteCalculated(150, 230)); addButton(ll, "\u25BA (1.2) New route calculated, 1350m, 3680sec (01:01:20)", jsBuilder(p).newRouteCalculated(1350, 3680)); @@ -398,8 +399,8 @@ public class TestVoiceActivity extends OsmandActionBarActivity { return p.newCommandBuilder(); } - private JSCommandBuilder jsBuilder(JSTTSCommandPlayerImpl p) { - return p.newCommandBuilder(); + private JSCommandBuilder jsBuilder(CommandPlayer p) { + return (JSCommandBuilder) p.newCommandBuilder(); } public void addButton(ViewGroup layout, final String description, final CommandBuilder builder){ From 72da03870e3e8e2eae8bf4021f3e7e41c97e921d Mon Sep 17 00:00:00 2001 From: PaulStets Date: Wed, 8 Aug 2018 11:53:22 +0300 Subject: [PATCH 14/28] Improved ogg voice guidance initialization --- OsmAnd/src/net/osmand/plus/AppInitializer.java | 2 +- .../net/osmand/plus/voice/JSMediaCommandPlayerImpl.java | 9 +++++++++ .../net/osmand/plus/voice/MediaCommandPlayerImpl.java | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index 254293298c..8c84c0d85d 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -583,7 +583,7 @@ public class AppInitializer implements IProgress { if (useJs && JSTTSCommandPlayerImpl.isMyData(voiceDir)) { return new JSTTSCommandPlayerImpl(ctx, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider); } else if (MediaCommandPlayerImpl.isMyData(voiceDir)) { - return useJs ? new JSMediaCommandPlayerImpl(osmandApplication, applicationMode, osmandApplication.getRoutingHelper().getVoiceRouter(), voiceProvider) + 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); diff --git a/OsmAnd/src/net/osmand/plus/voice/JSMediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSMediaCommandPlayerImpl.java index 54d713431b..b1b0b28dfc 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSMediaCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSMediaCommandPlayerImpl.java @@ -89,4 +89,13 @@ public class JSMediaCommandPlayerImpl extends MediaCommandPlayerImpl { 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; + } + } diff --git a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java index 6e439b2fe0..a3366dec37 100644 --- a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java @@ -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; From 2c0aad5a8e6e9278bfe3b647570fe7a41b84d329 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Wed, 8 Aug 2018 16:49:47 +0300 Subject: [PATCH 15/28] Changed voice guidance according to remarks --- OsmAnd/assets/bundled_assets.xml | 20 ++++++++--------- OsmAnd/res/values/strings.xml | 6 +---- .../src/net/osmand/plus/AppInitializer.java | 1 + .../src/net/osmand/plus/OsmandSettings.java | 1 - .../plus/activities/LocalIndexHelper.java | 22 ++----------------- .../SettingsNavigationActivity.java | 2 +- .../SettingsDevelopmentActivity.java | 11 ++++++++-- .../plus/download/DownloadActivityType.java | 5 +++-- .../plus/download/DownloadResourceGroup.java | 6 ++--- .../plus/download/DownloadResources.java | 13 +++++------ .../download/ui/DownloadGroupViewHolder.java | 3 +-- .../ui/DownloadResourceGroupFragment.java | 3 +-- .../plus/download/ui/ItemViewHolder.java | 3 +-- .../download/ui/LocalIndexesFragment.java | 4 +--- .../download/ui/SearchDialogFragment.java | 1 - .../helpers/FileNameTranslationHelper.java | 9 +++----- .../controllers/MapDataMenuController.java | 5 +---- .../other/RoutePreferencesMenu.java | 2 +- .../plus/resources/ResourceManager.java | 12 ++++------ .../osmand/plus/routing/RoutingHelper.java | 3 +-- .../plus/voice/JSTTSCommandPlayerImpl.java | 11 +++++++++- 21 files changed, 58 insertions(+), 85 deletions(-) diff --git a/OsmAnd/assets/bundled_assets.xml b/OsmAnd/assets/bundled_assets.xml index cf0bcd3506..5f805aa19a 100644 --- a/OsmAnd/assets/bundled_assets.xml +++ b/OsmAnd/assets/bundled_assets.xml @@ -48,31 +48,31 @@ - + - + - + - + - + - + - - - + + + - + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 4ebf6acdf7..d0d6dfce4e 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -2931,9 +2931,5 @@ Searching for the corresponding wiki article Article not found How to open Wikipedia articles? - Use JS voice guidance - Use new voice guidance logic based on JavaScript - Tap a button and listen to the corresponding voice prompt to identify missing or faulty propmts. - Voice prompts (JavaScript TTS) - (JavaScript) + Tap a button and listen to the corresponding voice prompt to identify missing or faulty prompts. diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index 8c84c0d85d..a655b6b60e 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -16,6 +16,7 @@ import android.support.v7.app.AlertDialog; import net.osmand.IProgress; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; +import net.osmand.StateChangedListener; import net.osmand.aidl.OsmandAidlApi; import net.osmand.map.OsmandRegions; import net.osmand.map.OsmandRegions.RegionTranslation; diff --git a/OsmAnd/src/net/osmand/plus/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/OsmandSettings.java index 900a7c792e..3d7cf09ed2 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/OsmandSettings.java @@ -1347,7 +1347,6 @@ public class OsmandSettings { public final OsmandPreference ANIMATE_MY_LOCATION = new BooleanPreference("animate_my_location", true).makeGlobal().cache(); public final OsmandPreference USE_JS_VOICE_GUIDANCE = new BooleanPreference("use_js_voice_guidance", false); - public final OsmandPreference ROUTE_MAP_MARKERS_START_MY_LOC = new BooleanPreference("route_map_markers_start_my_loc", false).makeGlobal().cache(); public final OsmandPreference ROUTE_MAP_MARKERS_ROUND_TRIP = new BooleanPreference("route_map_markers_round_trip", false).makeGlobal().cache(); diff --git a/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java b/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java index 220f01602e..d87f2f76cd 100644 --- a/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java +++ b/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java @@ -88,8 +88,6 @@ public class LocalIndexHelper { info.setDescription(getInstalledDate(f)); } else if (info.getType() == LocalIndexType.TTS_VOICE_DATA) { info.setDescription(getInstalledDate(f)); - } else if (info.getType() == LocalIndexType.TTS_VOICE_DATA_JS) { - info.setDescription(getInstalledDate(f)); } else if (info.getType() == LocalIndexType.DEACTIVATED) { info.setDescription(getInstalledDate(f)); } else if (info.getType() == LocalIndexType.VOICE_DATA) { @@ -218,11 +216,12 @@ public class LocalIndexHelper { private void loadVoiceData(File voiceDir, List 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)) { 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) { @@ -233,22 +232,6 @@ public class LocalIndexHelper { } } - if (app.getSettings().USE_JS_VOICE_GUIDANCE.get()) { - for (File voiceF : listFilesSorted(voiceDir)) { - if (voiceF.isDirectory() && !MediaCommandPlayerImpl.isMyData(voiceF)) { - LocalIndexInfo info = null; - if (JSTTSCommandPlayerImpl.isMyData(voiceF)) { - info = new LocalIndexInfo(LocalIndexType.TTS_VOICE_DATA_JS, voiceF, backup, app); - } - if (info != null) { - updateDescription(info); - result.add(info); - loadTask.loadFile(info); - } - } - } - } - //Now list recorded voices for (File voiceF : listFilesSorted(voiceDir)) { if (voiceF.isDirectory() && MediaCommandPlayerImpl.isMyData(voiceF)) { @@ -378,7 +361,6 @@ public class LocalIndexHelper { WIKI_DATA(R.string.local_indexes_cat_wiki, R.drawable.ic_plugin_wikipedia, 50), TRAVEL_DATA(R.string.download_maps_travel, R.drawable.ic_plugin_wikipedia, 60), TTS_VOICE_DATA(R.string.local_indexes_cat_tts, R.drawable.ic_action_volume_up, 20), - TTS_VOICE_DATA_JS(R.string.voice_tts_js, R.drawable.ic_action_volume_up, 20), VOICE_DATA(R.string.local_indexes_cat_voice, R.drawable.ic_action_volume_up, 30), FONT_DATA(R.string.fonts_header, R.drawable.ic_action_map_language, 35), DEACTIVATED(R.string.local_indexes_cat_backup, R.drawable.ic_type_archive, 1000); diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java index e9c0119a7c..1942e5f598 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java @@ -253,7 +253,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity { entries[k++] = getString(R.string.shared_string_do_not_use); for (String s : voiceFiles) { entries[k] = (s.contains("tts") ? getString(R.string.ttsvoice) + " " : "") + - FileNameTranslationHelper.getVoiceName(this, s) + (s.contains("-js") ? " " + getString(R.string.js_tts_label) : ""); + FileNameTranslationHelper.getVoiceName(this, s); entrieValues[k] = s; k++; } diff --git a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java index be1ae15e49..800001f36a 100644 --- a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java @@ -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; @@ -62,9 +63,15 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { cat.addPreference(createCheckBoxPreference(settings.ANIMATE_MY_LOCATION, R.string.animate_my_location, R.string.animate_my_location_desc)); + cat.addPreference(createCheckBoxPreference(settings.USE_JS_VOICE_GUIDANCE, "Use JS voice guidance", + "Use new voice guidance logic based on JavaScript")); - cat.addPreference(createCheckBoxPreference(settings.USE_JS_VOICE_GUIDANCE, getString(R.string.use_js_voice_guidance), - getString(R.string.use_js_voice_guidance_description))); + getMyApplication().getSettings().USE_JS_VOICE_GUIDANCE.addListener(new StateChangedListener() { + @Override + public void stateChanged(Boolean change) { + getMyApplication().getDownloadThread().runReloadIndexFilesSilent(); + } + }); final Preference firstRunPreference = new Preference(this); firstRunPreference.setTitle(R.string.simulate_initial_startup); diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 2acb463ffa..4576fd654e 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -339,7 +339,7 @@ public class DownloadActivityType { // fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_ZIP)) { if (this == VOICE_FILE) { if (fileName.contains(".js")) { - return fileName.replace('_', '-').replace('.', '-'); + return fileName.replace('_', '-').replaceAll(".js", ""); } int l = fileName.lastIndexOf('_'); if (l == -1) { @@ -413,7 +413,8 @@ public class DownloadActivityType { if (l == -1) { l = fileName.length(); } - return fileName.endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS) ? fileName.replace('_', '-').replace('.', '-') : 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('.'); diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResourceGroup.java b/OsmAnd/src/net/osmand/plus/download/DownloadResourceGroup.java index b6a14ba6fc..fa0125e8e4 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResourceGroup.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResourceGroup.java @@ -39,7 +39,6 @@ public class DownloadResourceGroup { NAUTICAL_MAPS_HEADER(R.string.nautical_maps), // headers with voice items VOICE_HEADER_TTS(R.string.index_name_tts_voice), - VOICE_HEADER_TTS_JS(R.string.voice_tts_js), VOICE_HEADER_REC(R.string.index_name_voice), // headers with font items FONTS_HEADER(R.string.fonts_header), @@ -53,7 +52,6 @@ public class DownloadResourceGroup { NAUTICAL_MAPS(R.string.nautical_maps), WIKIVOYAGE_MAPS(R.string.download_maps_travel), VOICE_TTS(R.string.index_name_tts_voice), - VOICE_TTS_JS(R.string.voice_tts_js), FONTS(R.string.fonts_header), VOICE_REC(R.string.index_name_voice), OTHER_MAPS(R.string.download_select_map_types), @@ -67,7 +65,7 @@ public class DownloadResourceGroup { } public boolean isScreen() { - return this == WORLD || this == REGION || this == VOICE_TTS || this == VOICE_TTS_JS + return this == WORLD || this == REGION || this == VOICE_TTS || this == VOICE_REC || this == OTHER_MAPS || this == FONTS || this == NAUTICAL_MAPS || this == WIKIVOYAGE_MAPS; } @@ -85,7 +83,7 @@ public class DownloadResourceGroup { } public boolean isHeader() { - return this == VOICE_HEADER_REC || this == VOICE_HEADER_TTS || this == VOICE_HEADER_TTS_JS + 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 diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java index 2bbd790609..80d7063e28 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java @@ -299,10 +299,8 @@ public class DownloadResources extends DownloadResourceGroup { DownloadResourceGroup otherGroup = new DownloadResourceGroup(this, DownloadResourceGroupType.OTHER_GROUP); DownloadResourceGroup voiceScreenTTS = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_TTS); DownloadResourceGroup voiceScreenRec = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_REC); - DownloadResourceGroup voiceScreenTTSJS = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_TTS_JS); DownloadResourceGroup fontScreen = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.FONTS); DownloadResourceGroup voiceTTS = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_HEADER_TTS); - DownloadResourceGroup voiceTTSJS = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_HEADER_TTS_JS); DownloadResourceGroup voiceRec = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.VOICE_HEADER_REC); DownloadResourceGroup fonts = new DownloadResourceGroup(otherGroup, DownloadResourceGroupType.FONTS_HEADER); @@ -318,13 +316,14 @@ public class DownloadResources extends DownloadResourceGroup { Map > groupByRegion = new LinkedHashMap>(); 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 if (ii.getFileName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)){ - voiceTTSJS.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; @@ -418,14 +417,12 @@ public class DownloadResources extends DownloadResourceGroup { } voiceScreenTTS.addGroup(voiceTTS); - voiceScreenTTSJS.addGroup(voiceTTSJS); voiceScreenRec.addGroup(voiceRec); if (fonts.getIndividualResources() != null) { fontScreen.addGroup(fonts); } otherGroup.addGroup(voiceScreenTTS); otherGroup.addGroup(voiceScreenRec); - otherGroup.addGroup(voiceScreenTTSJS); if (fonts.getIndividualResources() != null) { diff --git a/OsmAnd/src/net/osmand/plus/download/ui/DownloadGroupViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/DownloadGroupViewHolder.java index f8674dd49c..86ef9e6995 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/DownloadGroupViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/DownloadGroupViewHolder.java @@ -28,8 +28,7 @@ public class DownloadGroupViewHolder { private Drawable getIconForGroup(DownloadResourceGroup group) { Drawable iconLeft; if (group.getType() == DownloadResourceGroup.DownloadResourceGroupType.VOICE_REC - || group.getType() == DownloadResourceGroup.DownloadResourceGroupType.VOICE_TTS - || group.getType() == DownloadResourceGroup.DownloadResourceGroupType.VOICE_TTS_JS) { + || group.getType() == DownloadResourceGroup.DownloadResourceGroupType.VOICE_TTS) { iconLeft = ctx.getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_action_volume_up); } else if (group.getType() == DownloadResourceGroup.DownloadResourceGroupType.FONTS) { iconLeft = ctx.getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_action_map_language); diff --git a/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupFragment.java index c4eabcd9e9..0e0e49ebd0 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupFragment.java @@ -531,8 +531,7 @@ public class DownloadResourceGroupFragment extends DialogFragment implements Dow private Drawable getIconForGroup(DownloadResourceGroup group) { Drawable iconLeft; if (group.getType() == DownloadResourceGroupType.VOICE_REC - || group.getType() == DownloadResourceGroupType.VOICE_TTS || group.getType() == - DownloadResourceGroupType.VOICE_TTS_JS) { + || group.getType() == DownloadResourceGroupType.VOICE_TTS) { iconLeft = ctx.getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_action_volume_up); } else if (group.getType() == DownloadResourceGroupType.FONTS) { iconLeft = ctx.getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_action_map_language); diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 11b4ba1452..72cb9f1414 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -402,8 +402,7 @@ public class ItemViewHolder { } else if (indexItem.getType() == DownloadActivityType.FONT_FILE) { tp = LocalIndexType.FONT_DATA; } else if (indexItem.getType() == DownloadActivityType.VOICE_FILE) { - tp = indexItem.getBasename().contains("-tts-js") ? LocalIndexType.TTS_VOICE_DATA_JS : - indexItem.getBasename().contains("tts") ? LocalIndexType.TTS_VOICE_DATA + tp = indexItem.getBasename().contains("tts") ? LocalIndexType.TTS_VOICE_DATA : LocalIndexType.VOICE_DATA; } final LocalIndexInfo info = new LocalIndexInfo(tp, fl, false, context.getMyApplication()); diff --git a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java index 81f14ea658..f919a82609 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java @@ -200,7 +200,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement .setPosition(2).createItem()); } if (info.getType() != LocalIndexType.TTS_VOICE_DATA && info.getType() != LocalIndexType.VOICE_DATA - && info.getType() != LocalIndexType.FONT_DATA && info.getType() != LocalIndexType.TTS_VOICE_DATA_JS) { + && info.getType() != LocalIndexType.FONT_DATA) { adapter.addItem(new ContextMenuItem.ItemBuilder() .setTitleId(R.string.shared_string_rename, getContext()) .setListener(listener) @@ -452,8 +452,6 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement parent = getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR); } else if (i.getOriginalType() == LocalIndexType.VOICE_DATA) { parent = getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR); - } else if (i.getOriginalType() == LocalIndexType.TTS_VOICE_DATA_JS) { - parent = getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR); } else if (i.getOriginalType() == LocalIndexType.FONT_DATA) { parent = getMyApplication().getAppPath(IndexConstants.FONT_INDEX_DIR); } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java index 98a15c6b5f..5611a35e71 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java @@ -492,7 +492,6 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven if (group.getType() == DownloadResourceGroupType.OTHER_MAPS_HEADER || group.getType() == DownloadResourceGroupType.VOICE_HEADER_REC || group.getType() == DownloadResourceGroupType.VOICE_HEADER_TTS - || group.getType() == DownloadResourceGroupType.VOICE_HEADER_TTS_JS || group.getType() == DownloadResourceGroupType.FONTS_HEADER) { if (group.getIndividualResources() != null) { for (IndexItem item : group.getIndividualResources()) { diff --git a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java index a5a6241e62..a6792b8269 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java @@ -25,7 +25,7 @@ public class FileNameTranslationHelper { String basename = getBasename(fileName); if (basename.endsWith(WIKI_NAME)) { //wiki files return getWikiName(ctx, basename); - } else if (fileName.endsWith("tts") || fileName.endsWith("tts-js")) { //tts files + } else if (fileName.endsWith("tts")) { //tts files return getVoiceName(ctx, fileName); } else if (fileName.endsWith(IndexConstants.FONT_INDEX_EXT)) { //otf files return getFontName(ctx, basename); @@ -80,11 +80,8 @@ public class FileNameTranslationHelper { public static String getVoiceName(Context ctx, String fileName) { try { - String nm = fileName.replace('-', '_').replace(' ', '_') - .replace('.', '_'); - if (nm.endsWith("_tts_js")) { - nm = nm.substring(0, nm.indexOf("_tts_js")); - } else if (nm.endsWith("_tts") || nm.endsWith("-tts")) { + String nm = fileName.replace('-', '_').replace(' ', '_'); + if (nm.endsWith("_tts") || nm.endsWith("-tts")) { nm = nm.substring(0, nm.length() - 4); } Field f = R.string.class.getField("lang_"+nm); diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java index de46c93b77..2b57c67463 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java @@ -525,8 +525,7 @@ public class MapDataMenuController extends MenuController { } else if (localIndexInfo.getOriginalType() == LocalIndexType.TRAVEL_DATA) { return DownloadActivityType.WIKIVOYAGE_FILE; } else if (localIndexInfo.getOriginalType() == LocalIndexType.TTS_VOICE_DATA - || localIndexInfo.getOriginalType() == LocalIndexType.VOICE_DATA - || localIndexInfo.getOriginalType() == LocalIndexType.TTS_VOICE_DATA_JS) { + || localIndexInfo.getOriginalType() == LocalIndexType.VOICE_DATA) { return DownloadActivityType.VOICE_FILE; } else if (localIndexInfo.getOriginalType() == LocalIndexType.FONT_DATA) { return DownloadActivityType.FONT_FILE; @@ -651,8 +650,6 @@ public class MapDataMenuController extends MenuController { parent = app.getAppPath(IndexConstants.VOICE_INDEX_DIR); } else if (i.getOriginalType() == LocalIndexType.VOICE_DATA) { parent = app.getAppPath(IndexConstants.VOICE_INDEX_DIR); - } else if (i.getOriginalType() == LocalIndexType.TTS_VOICE_DATA_JS) { - parent = app.getAppPath(IndexConstants.VOICE_INDEX_DIR); } return new File(parent, i.getFileName()); } diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java index e6bd001767..070c0a61c6 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java @@ -280,7 +280,7 @@ public class RoutePreferencesMenu { k++; for (String s : voiceFiles) { entries[k] = (s.contains("tts") ? mapActivity.getResources().getString(R.string.ttsvoice) + " " : "") + - FileNameTranslationHelper.getVoiceName(mapActivity, s) + (s.contains("-js") ? " " + mapActivity.getString(R.string.js_tts_label) : ""); + FileNameTranslationHelper.getVoiceName(mapActivity, s); entrieValues[k] = s; adapter.addItem(itemBuilder.setTitle(entries[k]).createItem()); if (s.equals(selectedValue)) { diff --git a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java index 9798f4100b..04bb769fcb 100644 --- a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java @@ -386,19 +386,15 @@ 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$ } - if (f.getName().contains("-tts-js")) { - String lang = f.getName().replace("-tts-js", ""); - File jsConf = new File(f, lang + "_" + IndexConstants.TTSVOICE_INDEX_EXT_JS); - if (jsConf.exists()) { - indexFileNames.put(f.getName(), dateFormat.format(jsConf.lastModified())); - } - } } } } diff --git a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java index aa05de86c0..f246cbac77 100644 --- a/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java +++ b/OsmAnd/src/net/osmand/plus/routing/RoutingHelper.java @@ -97,8 +97,7 @@ public class RoutingHelper { public RoutingHelper(OsmandApplication context){ this.app = context; settings = context.getSettings(); - boolean useJS = settings.VOICE_PROVIDER.get().contains("-js") || - (!settings.VOICE_PROVIDER.get().contains("-tts") && settings.USE_JS_VOICE_GUIDANCE.get()); + boolean useJS = settings.USE_JS_VOICE_GUIDANCE.get(); voiceRouter = useJS ? new JSVoiceRouter(this, settings) : new VoiceRouter(this, settings); provider = new RouteProvider(); diff --git a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java index 8216b2ec36..9d8dab29ab 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java @@ -325,7 +325,16 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { } public static boolean isMyData(File voiceDir) { - return voiceDir.getName().contains("-tts-js"); + 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, From 0c6dcab25e990294c7300a6448be9150f2d9f553 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Thu, 9 Aug 2018 10:40:16 +0300 Subject: [PATCH 16/28] Refactoring of the JS tts --- .../SettingsDevelopmentActivity.java | 21 +- .../plus/development/TestVoiceActivity.java | 183 +++++++----------- .../plus/voice/JSTTSCommandPlayerImpl.java | 2 - 3 files changed, 82 insertions(+), 124 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java index 800001f36a..4a8ccd1f74 100644 --- a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java @@ -31,7 +31,7 @@ import java.util.Set; //import net.osmand.plus.development.OsmandDevelopmentPlugin; -public class SettingsDevelopmentActivity extends SettingsBaseActivity { +public class SettingsDevelopmentActivity extends SettingsBaseActivity implements StateChangedListener { @SuppressLint("SimpleDateFormat") @@ -63,15 +63,10 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { cat.addPreference(createCheckBoxPreference(settings.ANIMATE_MY_LOCATION, R.string.animate_my_location, R.string.animate_my_location_desc)); + cat.addPreference(createCheckBoxPreference(settings.USE_JS_VOICE_GUIDANCE, "Use JS voice guidance", "Use new voice guidance logic based on JavaScript")); - - getMyApplication().getSettings().USE_JS_VOICE_GUIDANCE.addListener(new StateChangedListener() { - @Override - public void stateChanged(Boolean change) { - getMyApplication().getDownloadThread().runReloadIndexFilesSilent(); - } - }); + settings.USE_JS_VOICE_GUIDANCE.addListener(this); final Preference firstRunPreference = new Preference(this); firstRunPreference.setTitle(R.string.simulate_initial_startup); @@ -259,6 +254,14 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { b.show(); } + @Override + public void onDestroy() { + super.onDestroy(); + settings.USE_JS_VOICE_GUIDANCE.removeListener(this); + } - + @Override + public void stateChanged(Boolean change) { + getMyApplication().getDownloadThread().runReloadIndexFilesSilent(); + } } diff --git a/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java b/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java index fded7b1d46..4349c0c5a8 100644 --- a/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java @@ -89,15 +89,32 @@ public class TestVoiceActivity extends OsmandActionBarActivity { // read available voice data File extStorage = ((OsmandApplication) getApplication()).getAppPath(IndexConstants.VOICE_INDEX_DIR); Set setFiles = new LinkedHashSet(); + OsmandApplication app = (OsmandApplication) getApplication(); + 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 boolean hasJavaScript(File f) { + for (File file : f.listFiles()) { + if (file.getName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)) { + return true; + } + } + return false; + } + private void selectVoice(final LinearLayout ll) { String[] entries; final String[] entrieValues; @@ -221,55 +238,72 @@ public class TestVoiceActivity extends OsmandActionBarActivity { } private void addButtons(final LinearLayout ll, CommandPlayer p) { - if (p instanceof JSTTSCommandPlayerImpl || p instanceof JSMediaCommandPlayerImpl) { - addJSTTSPrompts(ll, p); - return; - } + 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()).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()).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()).andArriveAtPoi("Museum")); addButton(ll, "Arriving and passing points:", builder(p)); addButton(ll, "\u25BA (8.1) Arrive at your destination 'Home'", builder(p).arrivedAtDestination("Home")); @@ -318,83 +352,6 @@ public class TestVoiceActivity extends OsmandActionBarActivity { return res; } - private void addJSTTSPrompts(LinearLayout ll, CommandPlayer p) { - addButton(ll, "Route calculated and number tests:", jsBuilder(p)); - addButton(ll, "\u25BA (1.1) New route calculated, 150m, 230sec (00:03:50)", jsBuilder(p).newRouteCalculated(150, 230)); - addButton(ll, "\u25BA (1.2) New route calculated, 1350m, 3680sec (01:01:20)", jsBuilder(p).newRouteCalculated(1350, 3680)); - addButton(ll, "\u25BA (1.3) New route calculated 3700m, 7320sec (02:02)", jsBuilder(p).newRouteCalculated(3700, 7320)); - addButton(ll, "\u25BA (1.4) New route calculated 9100m, 10980sec (03:03)", jsBuilder(p).newRouteCalculated(9100, 10980)); - addButton(ll, "\u25BA (2.1) Route recalculated 11500m, 18600sec (05:10)", jsBuilder(p).routeRecalculated(11500, 18600)); - addButton(ll, "\u25BA (2.2) Route recalculated 19633m, 26700sec (07:25)", jsBuilder(p).routeRecalculated(19633, 26700)); - addButton(ll, "\u25BA (2.3) Route recalculated 89750m, 55800sec (15:30)", jsBuilder(p).routeRecalculated(89750, 55800)); - addButton(ll, "\u25BA (2.4) Route recalculated 125900m, 92700sec (25:45)", jsBuilder(p).routeRecalculated(125900, 92700)); - - addButton(ll, "All turn types: prepareTurn, makeTurnIn, turn:", jsBuilder(p)); - addButton(ll, "\u25BA (3.1) After 1520m turn slightly left", 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", 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", 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'", 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 jsStreet', then bear left", 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'", jsBuilder(p).turn(AbstractPrologCommandPlayer.A_RIGHT_SH, jsStreet(p, "Dr.-Quinn-Straße"))); - - addButton(ll, "Keep left/right: prepareTurn, makeTurnIn, turn:", jsBuilder(p)); - addButton(ll, "\u25BA (4.1) After 1810m keep left ' '", 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'", 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'", 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'", - 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:", jsBuilder(p)); - addButton(ll, "\u25BA (5.1) After 1250m enter a roundabout", 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'", 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'", jsBuilder(p).roundAbout(0, 2, jsStreet(p, "Highway 60"))); - - addButton(ll, "U-turns: prepareTurn, makeTurnIn, turn, when possible:", jsBuilder(p)); - addButton(ll, "\u25BA (6.1) After 640m make a U-turn", jsBuilder(p).prepareMakeUT(640, jsStreet(p, ""))); - addButton(ll, "\u25BA (6.2) In 400m make a U-turn", jsBuilder(p).makeUT(400, jsStreet(p, ""))); - addButton(ll, "\u25BA (6.3) Make a U-turn on 'Riviera'", jsBuilder(p).makeUT(jsStreet(p, "Riviera", "", "", "Riviera"))); - addButton(ll, "\u25BA (6.4) When possible, make a U-turn", jsBuilder(p).makeUTwp()); - - addButton(ll, "Go straight, follow the road, approaching:", jsBuilder(p)); - addButton(ll, "\u25BA (7.1) Straight ahead", jsBuilder(p).goAhead()); - addButton(ll, "\u25BA (7.2) Continue for 2350m to ' '", jsBuilder(p).goAhead(2350, jsStreet(p, ""))); - addButton(ll, "\u25BA (7.3) Continue for 360m to 'Broadway' and arrive at your intermediate destination ' '", 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 ' '", 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'", jsBuilder(p).goAhead(200, new HashMap()).andArriveAtWayPoint("Trailhead")); - addButton(ll, "\u25BA (7.6) Continue for 400m and pass favorite 'Brewery'", jsBuilder(p).goAhead(400, new HashMap()).andArriveAtFavorite("Brewery")); - addButton(ll, "\u25BA (7.7) Continue for 600m and pass POI 'Museum'", jsBuilder(p).goAhead(600, new HashMap()).andArriveAtPoi("Museum")); - - addButton(ll, "Arriving and passing points:", jsBuilder(p)); - addButton(ll, "\u25BA (8.1) Arrive at your destination 'Home'", jsBuilder(p).arrivedAtDestination("Home")); - addButton(ll, "\u25BA (8.2) Arrive at your intermediate destination 'Friend'", jsBuilder(p).arrivedAtIntermediatePoint("Friend")); - addButton(ll, "\u25BA (8.3) Passing GPX waypoint 'Trailhead'", jsBuilder(p).arrivedAtWayPoint("Trailhead")); - addButton(ll, "\u25BA (8.4) Passing favorite 'Brewery'", jsBuilder(p).arrivedAtFavorite("Brewery")); - addButton(ll, "\u25BA (8.5) Passing POI 'Museum'", jsBuilder(p).arrivedAtPoi("Museum")); - - addButton(ll, "Attention prompts:", jsBuilder(p)); - addButton(ll, "\u25BA (9.1) You are exceeding the speed limit '50' (18 m/s)", jsBuilder(p).speedAlarm(50, 18f)); - addButton(ll, "\u25BA (9.2) Attention, speed camera", jsBuilder(p).attention("SPEED_CAMERA")); - addButton(ll, "\u25BA (9.3) Attention, border control", jsBuilder(p).attention("BORDER_CONTROL")); - addButton(ll, "\u25BA (9.4) Attention, railroad crossing", jsBuilder(p).attention("RAILWAY")); - addButton(ll, "\u25BA (9.5) Attention, traffic calming", jsBuilder(p).attention("TRAFFIC_CALMING")); - addButton(ll, "\u25BA (9.6) Attention, toll booth", jsBuilder(p).attention("TOLL_BOOTH")); - addButton(ll, "\u25BA (9.7) Attention, stop sign", jsBuilder(p).attention("STOP")); - addButton(ll, "\u25BA (9.8) Attention, pedestrian crosswalk", jsBuilder(p).attention("PEDESTRIAN")); - addButton(ll, "\u25BA (9.9) Attention, tunnel", jsBuilder(p).attention("TUNNEL")); - - addButton(ll, "Other prompts:", jsBuilder(p)); - addButton(ll, "\u25BA (10.1) GPS signal lost", jsBuilder(p).gpsLocationLost()); - addButton(ll, "\u25BA (10.2) GPS signal recovered", jsBuilder(p).gpsLocationRecover()); - addButton(ll, "\u25BA (10.3) You have been off the route for 1050m", jsBuilder(p).offRoute(1050)); - addButton(ll, "\u25BA (10.4) You are back on the route", jsBuilder(p).backOnRoute()); - - addButton(ll, "Voice system info:", jsBuilder(p)); - addButton(ll, "\u25BA (11.1) (Tap to refresh)\n" + getVoiceSystemInfo(), jsBuilder(p).attention("")); - addButton(ll, "\u25BA (11.2) Tap to change Phone call audio delay (if car stereo cuts off prompts). Default is 1500\u00A0ms.", jsBuilder(p).attention("")); - ll.forceLayout(); - } - private CommandBuilder builder(CommandPlayer p){ return p.newCommandBuilder(); } diff --git a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java index 9d8dab29ab..da4ee6e644 100644 --- a/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/JSTTSCommandPlayerImpl.java @@ -72,7 +72,6 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { private OsmandApplication app; private ApplicationMode appMode; private VoiceRouter vrt; - private String voiceProvider; private HashMap params = new HashMap(); @@ -83,7 +82,6 @@ public class JSTTSCommandPlayerImpl extends AbstractJSCommandPlayer { this.app = (OsmandApplication) ctx.getApplicationContext(); this.appMode = applicationMode; this.vrt = vrt; - this.voiceProvider = voiceProvider; if (Algorithms.isEmpty(language)) { throw new CommandPlayerException( ctx.getString(R.string.voice_data_corrupted)); From 6d50e400c77261c9da440775bbefa4e13104f9a5 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Thu, 9 Aug 2018 10:47:54 +0300 Subject: [PATCH 17/28] Formatting fixes --- OsmAnd/src/net/osmand/plus/AppInitializer.java | 1 - .../src/net/osmand/plus/helpers/FileNameTranslationHelper.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index a655b6b60e..8c84c0d85d 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -16,7 +16,6 @@ import android.support.v7.app.AlertDialog; import net.osmand.IProgress; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; -import net.osmand.StateChangedListener; import net.osmand.aidl.OsmandAidlApi; import net.osmand.map.OsmandRegions; import net.osmand.map.OsmandRegions.RegionTranslation; diff --git a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java index a6792b8269..30d7b2a730 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java @@ -80,7 +80,7 @@ public class FileNameTranslationHelper { public static String getVoiceName(Context ctx, String fileName) { try { - String nm = fileName.replace('-', '_').replace(' ', '_'); + String nm = fileName.replace('-', '_').replace(' ', '_'); if (nm.endsWith("_tts") || nm.endsWith("-tts")) { nm = nm.substring(0, nm.length() - 4); } From 1eb9b1a87bb8105822965e2eb2b5bd4cdbcab6cd Mon Sep 17 00:00:00 2001 From: PaulStets Date: Thu, 9 Aug 2018 10:55:53 +0300 Subject: [PATCH 18/28] Display correct voice files in settings --- .../SettingsNavigationActivity.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java index 1942e5f598..1a1302853f 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java @@ -262,21 +262,35 @@ public class SettingsNavigationActivity extends SettingsBaseActivity { registerListPreference(settings.VOICE_PROVIDER, screen, entries, entrieValues); } - private Set getVoiceFiles() { // read available voice data - File extStorage = getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR); + File extStorage = ((OsmandApplication) getApplication()).getAppPath(IndexConstants.VOICE_INDEX_DIR); Set setFiles = new LinkedHashSet(); + boolean addJS = settings.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 boolean hasJavaScript(File f) { + for (File file : f.listFiles()) { + if (file.getName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)) { + return true; + } + } + return false; + } + private void addVoicePrefs(PreferenceGroup cat) { if (!Version.isBlackberry((OsmandApplication) getApplication())) { ListPreference lp = createListPreference( From 87ae02f88c9b138225721bf2b50cfc95f2003633 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Thu, 9 Aug 2018 11:16:15 +0300 Subject: [PATCH 19/28] Removed copy-paste code --- .../SettingsNavigationActivity.java | 32 ++--------------- .../plus/development/TestVoiceActivity.java | 34 ++----------------- .../other/RoutePreferencesMenu.java | 25 +++++++++++--- 3 files changed, 27 insertions(+), 64 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java index 1a1302853f..79c75a4456 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsNavigationActivity.java @@ -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 voiceFiles = getVoiceFiles(); + Set voiceFiles = getVoiceFiles(this); entries = new String[voiceFiles.size() + 2]; entrieValues = new String[voiceFiles.size() + 2]; int k = 0; @@ -262,34 +264,6 @@ public class SettingsNavigationActivity extends SettingsBaseActivity { registerListPreference(settings.VOICE_PROVIDER, screen, entries, entrieValues); } - private Set getVoiceFiles() { - // read available voice data - File extStorage = ((OsmandApplication) getApplication()).getAppPath(IndexConstants.VOICE_INDEX_DIR); - Set setFiles = new LinkedHashSet(); - boolean addJS = settings.USE_JS_VOICE_GUIDANCE.get(); - if (extStorage.exists()) { - for (File f : extStorage.listFiles()) { - if (f.isDirectory()) { - if (addJS && hasJavaScript(f)) { - setFiles.add(f.getName()); - } else if (!addJS) { - setFiles.add(f.getName()); - } - - } - } - } - return setFiles; - } - - private boolean hasJavaScript(File f) { - for (File file : f.listFiles()) { - if (file.getName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)) { - return true; - } - } - return false; - } private void addVoicePrefs(PreferenceGroup cat) { if (!Version.isBlackberry((OsmandApplication) getApplication())) { diff --git a/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java b/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java index 4349c0c5a8..fd5951b010 100644 --- a/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/TestVoiceActivity.java @@ -40,6 +40,8 @@ import java.util.Set; import alice.tuprolog.Struct; import alice.tuprolog.Term; +import static net.osmand.plus.mapcontextmenu.other.RoutePreferencesMenu.getVoiceFiles; + /** * Test Voice activity @@ -84,41 +86,11 @@ public class TestVoiceActivity extends OsmandActionBarActivity { selectVoice(ll); } - - private Set getVoiceFiles() { - // read available voice data - File extStorage = ((OsmandApplication) getApplication()).getAppPath(IndexConstants.VOICE_INDEX_DIR); - Set setFiles = new LinkedHashSet(); - OsmandApplication app = (OsmandApplication) getApplication(); - boolean addJS = app.getSettings().USE_JS_VOICE_GUIDANCE.get(); - if (extStorage.exists()) { - for (File f : extStorage.listFiles()) { - if (f.isDirectory()) { - if (addJS && hasJavaScript(f)) { - setFiles.add(f.getName()); - } else if (!addJS) { - setFiles.add(f.getName()); - } - - } - } - } - return setFiles; - } - - private boolean hasJavaScript(File f) { - for (File file : f.listFiles()) { - if (file.getName().endsWith(IndexConstants.TTSVOICE_INDEX_EXT_JS)) { - return true; - } - } - return false; - } private void selectVoice(final LinearLayout ll) { String[] entries; final String[] entrieValues; - Set voiceFiles = getVoiceFiles(); + Set voiceFiles = getVoiceFiles(this); entries = new String[voiceFiles.size() ]; entrieValues = new String[voiceFiles.size() ]; int k = 0; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java index 070c0a61c6..c40e66a608 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/other/RoutePreferencesMenu.java @@ -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 getVoiceFiles(MapActivity mapActivity) { + public static Set getVoiceFiles(Activity activity) { // read available voice data - File extStorage = mapActivity.getMyApplication().getAppPath(IndexConstants.VOICE_INDEX_DIR); - Set setFiles = new LinkedHashSet<>(); + OsmandApplication app = ((OsmandApplication) activity.getApplication()); + File extStorage = app.getAppPath(IndexConstants.VOICE_INDEX_DIR); + Set setFiles = new LinkedHashSet(); + 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 From ae7d5a7972abda09b923e68783a68d0c0a49f0ec Mon Sep 17 00:00:00 2001 From: PaulStets Date: Thu, 9 Aug 2018 12:13:25 +0300 Subject: [PATCH 20/28] Improved listener --- .../SettingsDevelopmentActivity.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java index 4a8ccd1f74..18d34be754 100644 --- a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java @@ -31,8 +31,9 @@ import java.util.Set; //import net.osmand.plus.development.OsmandDevelopmentPlugin; -public class SettingsDevelopmentActivity extends SettingsBaseActivity implements StateChangedListener { +public class SettingsDevelopmentActivity extends SettingsBaseActivity { + private StateChangedListener jsRoutingListener; @SuppressLint("SimpleDateFormat") @Override @@ -63,10 +64,15 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity implements cat.addPreference(createCheckBoxPreference(settings.ANIMATE_MY_LOCATION, R.string.animate_my_location, R.string.animate_my_location_desc)); - + jsRoutingListener = new StateChangedListener() { + @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(this); + settings.USE_JS_VOICE_GUIDANCE.addListener(jsRoutingListener); final Preference firstRunPreference = new Preference(this); firstRunPreference.setTitle(R.string.simulate_initial_startup); @@ -257,11 +263,6 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity implements @Override public void onDestroy() { super.onDestroy(); - settings.USE_JS_VOICE_GUIDANCE.removeListener(this); - } - - @Override - public void stateChanged(Boolean change) { - getMyApplication().getDownloadThread().runReloadIndexFilesSilent(); + settings.USE_JS_VOICE_GUIDANCE.removeListener(jsRoutingListener); } } From cedb9d89822c530db5af3b488928efe95373c7f2 Mon Sep 17 00:00:00 2001 From: PaulStets Date: Thu, 9 Aug 2018 12:19:25 +0300 Subject: [PATCH 21/28] Changed listener name --- .../plus/development/SettingsDevelopmentActivity.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java index 18d34be754..70665ad117 100644 --- a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java @@ -33,7 +33,7 @@ import java.util.Set; public class SettingsDevelopmentActivity extends SettingsBaseActivity { - private StateChangedListener jsRoutingListener; + private StateChangedListener useJSVoiceGuidanceListener; @SuppressLint("SimpleDateFormat") @Override @@ -64,7 +64,7 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { cat.addPreference(createCheckBoxPreference(settings.ANIMATE_MY_LOCATION, R.string.animate_my_location, R.string.animate_my_location_desc)); - jsRoutingListener = new StateChangedListener() { + useJSVoiceGuidanceListener = new StateChangedListener() { @Override public void stateChanged(Boolean change) { getMyApplication().getDownloadThread().runReloadIndexFilesSilent(); @@ -72,7 +72,7 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { }; 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(jsRoutingListener); + settings.USE_JS_VOICE_GUIDANCE.addListener(useJSVoiceGuidanceListener); final Preference firstRunPreference = new Preference(this); firstRunPreference.setTitle(R.string.simulate_initial_startup); @@ -263,6 +263,6 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity { @Override public void onDestroy() { super.onDestroy(); - settings.USE_JS_VOICE_GUIDANCE.removeListener(jsRoutingListener); + settings.USE_JS_VOICE_GUIDANCE.removeListener(useJSVoiceGuidanceListener); } } From 3a7bf0e030f0698c932fcacca1194e776168ca75 Mon Sep 17 00:00:00 2001 From: Alex Sytnyk Date: Thu, 9 Aug 2018 13:09:08 +0300 Subject: [PATCH 22/28] Replace AndroidUtils.isGooglePlayInstalled with AndroidUtils.isAppInstalled --- .../src/net/osmand/telegram/utils/AndroidUtils.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidUtils.kt b/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidUtils.kt index 297fb05aa6..d9b78ae239 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidUtils.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/AndroidUtils.kt @@ -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" From 92dbe06bca5c5597a5bc7c34b3e065ae40f6332f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sveinn=20=C3=AD=20Felli?= Date: Wed, 8 Aug 2018 10:03:23 +0000 Subject: [PATCH 23/28] Translated using Weblate (Icelandic) Currently translated at 99.9% (2601 of 2602 strings) --- OsmAnd/res/values-is/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/OsmAnd/res/values-is/strings.xml b/OsmAnd/res/values-is/strings.xml index fd967be7dc..3869205f7a 100644 --- a/OsmAnd/res/values-is/strings.xml +++ b/OsmAnd/res/values-is/strings.xml @@ -2956,4 +2956,9 @@ Stendur fyrir svæði: %1$s x %2$s Engar niðurstöður? \nSegðu okkur meira um þetta. Senda leitarfyrirspurn? + Takk fyrir umsögn þína + Hnútur eða leið fannst ekki. + Engar leitarniðurstöður? +\nLáttu okkur vita + Sendi leið inn… From 9c662902c8db56f5af0ac0f261b0926ae82067c6 Mon Sep 17 00:00:00 2001 From: Hinagiku Zeppeki Date: Thu, 9 Aug 2018 08:44:16 +0000 Subject: [PATCH 24/28] Translated using Weblate (Japanese) Currently translated at 69.1% (1798 of 2602 strings) --- OsmAnd/res/values-ja/strings.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OsmAnd/res/values-ja/strings.xml b/OsmAnd/res/values-ja/strings.xml index e8857d45bf..e151b676af 100644 --- a/OsmAnd/res/values-ja/strings.xml +++ b/OsmAnd/res/values-ja/strings.xml @@ -2961,7 +2961,7 @@ OsmAndとOSMをサポートする方法として現状最適な方法である マーカーとして追加する経由地点を経路データから選択してください。(経由地点を含む経路のみリストアップされます) マーカーとして追加するお気に入りのカテゴリを選択してください。 - お気に入り地点や経路データ内の経由地点をまとめてインポートできます。 + お気に入り地点やGPX経由地点をグループとしてインポートできます。 グループでインポート お気に入り地点や経路データ内の経由地点をマーカーとしてまとめて追加できます。 二つ @@ -3016,7 +3016,7 @@ OsmAndとOSMをサポートする方法として現状最適な方法である グループの追加 OSM編集はPOIの作成や更新、注釈の追加などが可能です。記録したGPXファイルを用いてあなたもOSMに貢献することができるでしょう。 通過済みにする - GPX経路に追加 + GPXファイルに追加 現在 その他 @@ -3050,4 +3050,8 @@ OsmAndとOSMをサポートする方法として現状最適な方法である 標準的な汎用スタイルです。人口密集地の場合描写の簡素化がなされます。主な機能としては等高線、ルート、路面品質、通行制限、道路標識、SACスケールの通路描写をサポートし、急流下りなどのウォータースポーツにも使用できます。 Wikipediaをオンライン参照 このリンクを開くとウェブブラウザで閲覧することができます。 + GPXファイルとしてインポート + GPXファイルに追加しよう + GPXファイルまたは記録した経路からインポートします。 + GPXファイルからインポート From 1ce15748ccb24f92a296ca0a393b69c276f7194e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Tich=C3=BD?= Date: Wed, 8 Aug 2018 18:50:41 +0000 Subject: [PATCH 25/28] Translated using Weblate (Czech) Currently translated at 96.8% (3385 of 3494 strings) --- OsmAnd/res/values-cs/phrases.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/OsmAnd/res/values-cs/phrases.xml b/OsmAnd/res/values-cs/phrases.xml index c562363705..c9fedb8c2b 100644 --- a/OsmAnd/res/values-cs/phrases.xml +++ b/OsmAnd/res/values-cs/phrases.xml @@ -3763,6 +3763,8 @@ Platební centrum Služba převodu peněz - Sport: Ano - Sport: Ne + Sportovní horolezectví: Ano + Sportovní horolezectví: Ne + Ledovec typu ledová čepice + Ledovec typu ledové pole From 70a8873e1f0b4d1a604a0c4a646d138d2c3f477b Mon Sep 17 00:00:00 2001 From: Franco Date: Wed, 8 Aug 2018 16:18:59 +0000 Subject: [PATCH 26/28] Translated using Weblate (Spanish (American)) Currently translated at 100.0% (3494 of 3494 strings) --- OsmAnd/res/values-es-rUS/phrases.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OsmAnd/res/values-es-rUS/phrases.xml b/OsmAnd/res/values-es-rUS/phrases.xml index bcd73a8789..5be72a498b 100644 --- a/OsmAnd/res/values-es-rUS/phrases.xml +++ b/OsmAnd/res/values-es-rUS/phrases.xml @@ -3469,7 +3469,7 @@ Piso de oficina Centro de pagos - Transferencia de dinero + Transferencia de dinero; Giros de dinero Metro From e4861b73c4aad40db69240f393859b6c084f19f6 Mon Sep 17 00:00:00 2001 From: Franco Date: Wed, 8 Aug 2018 16:17:18 +0000 Subject: [PATCH 27/28] Translated using Weblate (Spanish (Argentina)) Currently translated at 100.0% (3494 of 3494 strings) --- OsmAnd/res/values-es-rAR/phrases.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OsmAnd/res/values-es-rAR/phrases.xml b/OsmAnd/res/values-es-rAR/phrases.xml index 0b469b467c..e5442b22a1 100644 --- a/OsmAnd/res/values-es-rAR/phrases.xml +++ b/OsmAnd/res/values-es-rAR/phrases.xml @@ -3707,7 +3707,7 @@ Piso de oficina Centro de pagos - Transferencia de dinero + Transferencia de dinero;Giros de dinero Subte From db2b0e431aa69a8734dbbbf40577e0a0f05e5a06 Mon Sep 17 00:00:00 2001 From: Chumva Date: Thu, 9 Aug 2018 13:52:03 +0300 Subject: [PATCH 28/28] remove hardcoded strings --- OsmAnd/res/layout/search_dialog_fragment.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OsmAnd/res/layout/search_dialog_fragment.xml b/OsmAnd/res/layout/search_dialog_fragment.xml index 52de3ba07e..2d8374ba03 100644 --- a/OsmAnd/res/layout/search_dialog_fragment.xml +++ b/OsmAnd/res/layout/search_dialog_fragment.xml @@ -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" />