diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 44345e30d0..94f46bc242 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -1,5 +1,10 @@ + Language unsupported + The selected language is not supported by the installed TTS engine. Do you want to go to market and search for other TTS engine? Else preset TTS language will be used. + Missing data + Data for selected language are not installed. Do you want to go market to install them? + Reverse GPX route Use current destination point Pass whole track diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java index cce994d1a5..a285565794 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java @@ -6,9 +6,9 @@ import java.util.List; import net.osmand.Algoritms; import net.osmand.CallbackWithObject; +import net.osmand.GPXUtilities.GPXFileResult; import net.osmand.LogUtil; import net.osmand.Version; -import net.osmand.GPXUtilities.GPXFileResult; import net.osmand.data.MapTileDownloader; import net.osmand.data.MapTileDownloader.DownloadRequest; import net.osmand.data.MapTileDownloader.IMapDownloaderCallback; @@ -27,17 +27,17 @@ import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.PointLocationLayer; import android.app.Activity; import android.app.AlertDialog; +import android.app.AlertDialog.Builder; import android.app.Dialog; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.ProgressDialog; -import android.app.AlertDialog.Builder; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.DialogInterface; -import android.content.Intent; import android.content.DialogInterface.OnMultiChoiceClickListener; +import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Color; @@ -66,8 +66,8 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.Window; import android.view.View.OnClickListener; +import android.view.Window; import android.view.animation.AccelerateInterpolator; import android.view.animation.Animation; import android.view.animation.Transformation; @@ -127,7 +127,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso private OsmandSettings settings; // Store previous map rotation settings for rotate button private Integer previousMapRotate = null; - + private boolean isMapLinkedToLocation = false; private ProgressDialog startProgressDialog; @@ -150,7 +150,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - settings = ((OsmandApplication) getApplication()).getSettings(); + settings = getMyApplication().getSettings(); requestWindowFeature(Window.FEATURE_NO_TITLE); // Full screen is not used here // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); @@ -183,7 +183,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso @Override public void tileDownloaded(DownloadRequest request) { if(request != null && !request.error && request.fileToSave != null){ - ResourceManager mgr = ((OsmandApplication)getApplication()).getResourceManager(); + ResourceManager mgr = getMyApplication().getResourceManager(); mgr.tileDownloaded(request); } mapView.tileDownloaded(request); @@ -193,7 +193,8 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso savingTrackHelper = new SavingTrackHelper(this); - routingHelper = ((OsmandApplication) getApplication()).getRoutingHelper(); + routingHelper = getMyApplication().getRoutingHelper(); + routingHelper.getVoiceRouter().onActivityInit(this); LatLon pointToNavigate = settings.getPointToNavigate(); // This situtation could be when navigation suddenly crashed and after restarting @@ -213,7 +214,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso @Override public void onClick(DialogInterface dialog, int which) { routingHelper.setFollowingMode(true); - ((OsmandApplication)getApplication()).showDialogInitializingCommandPlayer(MapActivity.this); + getMyApplication().showDialogInitializingCommandPlayer(MapActivity.this); } }); builder.setNegativeButton(R.string.default_buttons_no, new DialogInterface.OnClickListener(){ @@ -268,6 +269,10 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso }); } + + private OsmandApplication getMyApplication() { + return ((OsmandApplication) getApplication()); + } @Override protected Dialog onCreateDialog(int id) { @@ -334,7 +339,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso public void onClick(View v) { dlg.dismiss(); - ((OsmandApplication) getApplication()).closeApplication(); + getMyApplication().closeApplication(); // 1. Work for almost all cases when user open apps from main menu Intent newIntent = new Intent(MapActivity.this, MainMenuActivity.class); newIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -427,6 +432,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso mNotificationManager.cancel(APP_NOTIFICATION_ID); } MapTileDownloader.getInstance().removeDownloaderCallback(mapView); + routingHelper.getVoiceRouter().onActivityStop(this); } @@ -684,9 +690,6 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso } }; - - - @Override protected void onPause() { super.onPause(); @@ -699,8 +702,9 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso sensorRegistered = false; currentLocationProvider = null; routingHelper.setUiActivity(null); + routingHelper.getVoiceRouter().onActivityStop(this); - ((OsmandApplication)getApplication()).getDaynightHelper().onMapPause(); + getMyApplication().getDaynightHelper().onMapPause(); settings.setLastKnownMapLocation((float) mapView.getLatitude(), (float) mapView.getLongitude()); AnimateDraggingMapThread animatedThread = mapView.getAnimatedDraggingThread(); @@ -715,8 +719,8 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso wakeLock = null; } settings.MAP_ACTIVITY_ENABLED.set(false); - ((OsmandApplication)getApplication()).getResourceManager().interruptRendering(); - ((OsmandApplication)getApplication()).getResourceManager().setBusyIndicator(null); + getMyApplication().getResourceManager().interruptRendering(); + getMyApplication().getResourceManager().setBusyIndicator(null); } private void updateApplicationModeSettings(){ @@ -781,7 +785,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso } routingHelper.setUiActivity(this); - + routingHelper.getVoiceRouter().onActivityInit(this); LocationManager service = (LocationManager) getSystemService(LOCATION_SERVICE); try { @@ -831,10 +835,10 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso View progress = findViewById(R.id.ProgressBar); if(progress != null){ - ((OsmandApplication) getApplication()).getResourceManager().setBusyIndicator(new BusyIndicator(this, progress)); + getMyApplication().getResourceManager().setBusyIndicator(new BusyIndicator(this, progress)); } - ((OsmandApplication)getApplication()).getDaynightHelper().onMapResume(); + getMyApplication().getDaynightHelper().onMapResume(); } @@ -937,8 +941,8 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso boolean val = super.onPrepareOptionsMenu(menu); MenuItem navigateToPointMenu = menu.findItem(R.id.map_navigate_to_point); if (navigateToPointMenu != null) { - navigateToPointMenu.setTitle(routingHelper.isRouteCalculated() ? R.string.stop_routing : R.string.stop_navigation); if (settings.getPointToNavigate() != null) { + navigateToPointMenu.setTitle(routingHelper.isRouteCalculated() ? R.string.stop_routing : R.string.stop_navigation); navigateToPointMenu.setVisible(true); } else { navigateToPointMenu.setVisible(false); @@ -946,64 +950,73 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso } MenuItem muteMenu = menu.findItem(R.id.map_mute); if(muteMenu != null){ - muteMenu.setTitle(routingHelper.getVoiceRouter().isMute() ? R.string.menu_mute_on : R.string.menu_mute_off); if (routingHelper.getFinalLocation() != null && routingHelper.isFollowingMode()) { + muteMenu.setTitle(routingHelper.getVoiceRouter().isMute() ? R.string.menu_mute_on : R.string.menu_mute_off); muteMenu.setVisible(true); } else { muteMenu.setVisible(false); } } - return val; } @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.map_show_settings) { - final Intent settings = new Intent(MapActivity.this, SettingsActivity.class); - startActivity(settings); - return true; - } else if (item.getItemId() == R.id.map_where_am_i) { + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.map_show_settings: + final Intent intentSettings = new Intent(MapActivity.this, + SettingsActivity.class); + startActivity(intentSettings); + return true; + case R.id.map_where_am_i: backToLocationImpl(); - return true; - } else if (item.getItemId() == R.id.map_show_gps_status) { + return true; + case R.id.map_show_gps_status: Intent intent = new Intent(); - intent.setComponent(new ComponentName(GPS_STATUS_COMPONENT, GPS_STATUS_ACTIVITY)); - ResolveInfo resolved = getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); - if(resolved != null){ + intent.setComponent(new ComponentName(GPS_STATUS_COMPONENT, + GPS_STATUS_ACTIVITY)); + ResolveInfo resolved = getPackageManager().resolveActivity(intent, + PackageManager.MATCH_DEFAULT_ONLY); + if (resolved != null) { startActivity(intent); } else { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(getString(R.string.gps_status_app_not_found)); - builder.setPositiveButton(getString(R.string.default_buttons_yes), + builder.setPositiveButton( + getString(R.string.default_buttons_yes), new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialog, int which) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:" + GPS_STATUS_COMPONENT)); + public void onClick(DialogInterface dialog, + int which) { + Intent intent = new Intent(Intent.ACTION_VIEW, + Uri.parse("market://search?q=pname:" + + GPS_STATUS_COMPONENT)); try { startActivity(intent); } catch (ActivityNotFoundException e) { } } }); - builder.setNegativeButton(getString(R.string.default_buttons_no), null); + builder.setNegativeButton( + getString(R.string.default_buttons_no), null); builder.show(); } return true; - } else if (item.getItemId() == R.id.map_get_directions) { + case R.id.map_get_directions: getDirections(mapView.getLatitude(), mapView.getLongitude(), true); return true; - } else if (item.getItemId() == R.id.map_layers) { + case R.id.map_layers: mapLayers.openLayerSelectionDialog(mapView); return true; - } else if (item.getItemId() == R.id.map_specify_point) { + case R.id.map_specify_point: NavigatePointActivity dlg = new NavigatePointActivity(this); dlg.showDialog(); return true; - } else if (item.getItemId() == R.id.map_mute) { - routingHelper.getVoiceRouter().setMute(!routingHelper.getVoiceRouter().isMute()); + case R.id.map_mute: + routingHelper.getVoiceRouter().setMute( + !routingHelper.getVoiceRouter().isMute()); return true; - } else if (item.getItemId() == R.id.map_navigate_to_point) { + case R.id.map_navigate_to_point: if(mapLayers.getNavigationLayer().getPointToNavigate() != null){ if(routingHelper.isRouteCalculated()){ routingHelper.setFinalAndCurrentLocation(null, null); @@ -1016,16 +1029,18 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso navigateToPoint(new LatLon(mapView.getLatitude(), mapView.getLongitude())); } mapView.refreshMap(); - } else if (item.getItemId() == R.id.map_gpx_routing) { + return true; + case R.id.map_gpx_routing: useGPXRouting(); return true; - } else if (item.getItemId() == R.id.map_show_point_options) { + case R.id.map_show_point_options: contextMenuPoint(mapView.getLatitude(), mapView.getLongitude()); - return true; - } - return super.onOptionsItemSelected(item); - } - + return true; + default: + return super.onOptionsItemSelected(item); + } + } + private ApplicationMode getAppMode(ToggleButton[] buttons){ for(int i=0; i b : booleanPreferences.values()){ CheckBoxPreference pref = (CheckBoxPreference) screenPreferences.get(b.getId()); @@ -396,7 +401,8 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference values[ki] = MORE_VALUE; fill(tileSourcePreference, entries, values, value); } - + + private void fill(ListPreference component, String[] list, String[] values, String selected) { component.setEntries(list); component.setEntryValues(values); @@ -435,7 +441,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference // Specific actions after list preference changed if (changed) { if (listPref.getId().equals(osmandSettings.VOICE_PROVIDER.getId())) { - getMyApplication().initCommandPlayer(); + getMyApplication().showDialogInitializingCommandPlayer(this); } else if (listPref.getId().equals(osmandSettings.APPLICATION_MODE.getId())) { updateAllSettings(); } else if (listPref.getId().equals(osmandSettings.PREFERRED_LOCALE.getId())) { diff --git a/OsmAnd/src/net/osmand/plus/activities/VoiceRouter.java b/OsmAnd/src/net/osmand/plus/activities/VoiceRouter.java index 845014f4f5..9e4cb8c186 100644 --- a/OsmAnd/src/net/osmand/plus/activities/VoiceRouter.java +++ b/OsmAnd/src/net/osmand/plus/activities/VoiceRouter.java @@ -2,8 +2,10 @@ package net.osmand.plus.activities; import net.osmand.plus.activities.RoutingHelper.RouteDirectionInfo; import net.osmand.plus.activities.RoutingHelper.TurnType; +import net.osmand.plus.voice.AbstractPrologCommandPlayer; +import net.osmand.plus.voice.CommandBuilder; import net.osmand.plus.voice.CommandPlayer; -import net.osmand.plus.voice.CommandPlayer.CommandBuilder; +import android.app.Activity; public class VoiceRouter { @@ -227,17 +229,17 @@ public class VoiceRouter { private String getTurnType(TurnType t){ if(TurnType.TL.equals(t.getValue())){ - return CommandPlayer.A_LEFT; + return AbstractPrologCommandPlayer.A_LEFT; } else if(TurnType.TSHL.equals(t.getValue())){ - return CommandPlayer.A_LEFT_SH; + return AbstractPrologCommandPlayer.A_LEFT_SH; } else if(TurnType.TSLL.equals(t.getValue())){ - return CommandPlayer.A_LEFT_SL; + return AbstractPrologCommandPlayer.A_LEFT_SL; } else if(TurnType.TR.equals(t.getValue())){ - return CommandPlayer.A_RIGHT; + return AbstractPrologCommandPlayer.A_RIGHT; } else if(TurnType.TSHR.equals(t.getValue())){ - return CommandPlayer.A_RIGHT_SH; + return AbstractPrologCommandPlayer.A_RIGHT_SH; } else if(TurnType.TSLR.equals(t.getValue())){ - return CommandPlayer.A_RIGHT_SL; + return AbstractPrologCommandPlayer.A_RIGHT_SL; } return null; } @@ -262,4 +264,16 @@ public class VoiceRouter { } } + public void onActivityInit(Activity activity) { + if (player != null) { + player.onActivityInit(activity); + } + } + + public void onActivityStop(Activity activity) { + if (player != null) { + player.onActvitiyStop(activity); + } + } + } diff --git a/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java b/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java new file mode 100644 index 0000000000..a7d34a0769 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/voice/AbstractPrologCommandPlayer.java @@ -0,0 +1,179 @@ +package net.osmand.plus.voice; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import net.osmand.LogUtil; +import net.osmand.plus.OsmandSettings; +import net.osmand.plus.R; +import net.osmand.plus.ResourceManager; + +import org.apache.commons.logging.Log; + +import alice.tuprolog.InvalidLibraryException; +import alice.tuprolog.InvalidTheoryException; +import alice.tuprolog.NoSolutionException; +import alice.tuprolog.Number; +import alice.tuprolog.Prolog; +import alice.tuprolog.SolveInfo; +import alice.tuprolog.Struct; +import alice.tuprolog.Term; +import alice.tuprolog.Theory; +import alice.tuprolog.Var; +import android.content.Context; + +public abstract class AbstractPrologCommandPlayer implements CommandPlayer { + + private static final Log log = LogUtil + .getLog(AbstractPrologCommandPlayer.class); + + protected Context ctx; + protected File voiceDir; + protected Prolog prologSystem; + protected static final String P_VERSION = "version"; + protected static final String P_RESOLVE = "resolve"; + public static final String A_LEFT = "left"; + public static final String A_LEFT_SH = "left_sh"; + public static final String A_LEFT_SL = "left_sl"; + public static final String A_RIGHT = "right"; + public static final String A_RIGHT_SH = "right_sh"; + public static final String A_RIGHT_SL = "right_sl"; + protected static final String DELAY_CONST = "delay_"; + private final int voiceVersion; + + protected AbstractPrologCommandPlayer(Context ctx, String voiceProvider, String configFile, int voiceVersion) + throws CommandPlayerException + { + this.voiceVersion = voiceVersion; + long time = System.currentTimeMillis(); + try { + this.ctx = ctx; + prologSystem = new Prolog( + new String[] { "alice.tuprolog.lib.BasicLibrary" }); //$NON-NLS-1$ + } catch (InvalidLibraryException e) { + log.error("Initializing error", e); //$NON-NLS-1$ + throw new RuntimeException(e); + } + if (log.isInfoEnabled()) { + log.info("Initializing prolog system : " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ + } + init(voiceProvider, configFile); + } + + private void init(String voiceProvider, String configFile) throws CommandPlayerException { + prologSystem.clearTheory(); + voiceDir = null; + if (voiceProvider != null) { + File parent = OsmandSettings.getOsmandSettings(ctx).extendOsmandPath(ResourceManager.VOICE_PATH); + voiceDir = new File(parent, voiceProvider); + if (!voiceDir.exists()) { + voiceDir = null; + throw new CommandPlayerException( + ctx.getString(R.string.voice_data_unavailable)); + } + } + + // see comments below why it is impossible to read from zip (don't know + // how to play file from zip) + // voiceZipFile = null; + if (voiceDir != null) { + long time = System.currentTimeMillis(); + boolean wrong = false; + try { + InputStream config; + // if (voiceDir.getName().endsWith(".zip")) { //$NON-NLS-1$ + // voiceZipFile = new ZipFile(voiceDir); + // config = voiceZipFile.getInputStream(voiceZipFile.getEntry("_config.p")); //$NON-NLS-1$ + // } else { + config = new FileInputStream(new File(voiceDir, configFile)); //$NON-NLS-1$ + // } + if (!wrong) { + prologSystem.setTheory(new Theory(config)); + } + } catch (InvalidTheoryException e) { + log.error("Loading voice config exception " + voiceProvider, e); //$NON-NLS-1$ + wrong = true; + } catch (IOException e) { + log.error("Loading voice config exception " + voiceProvider, e); //$NON-NLS-1$ + wrong = true; + } + if (wrong) { + throw new CommandPlayerException(ctx.getString(R.string.voice_data_corrupted)); + } else { + Term val = solveSimplePredicate(P_VERSION); + if (!(val instanceof Number) || ((Number)val).intValue() != voiceVersion) { + throw new CommandPlayerException(ctx.getString(R.string.voice_data_not_supported)); + } + } + if (log.isInfoEnabled()) { + log.info("Initializing voice subsystem " + voiceProvider + " : " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ //$NON-NLS-2$ + } + + } + } + + protected Term solveSimplePredicate(String predicate) { + Term val = null; + Var v = new Var("MyVariable"); //$NON-NLS-1$ + SolveInfo s = prologSystem.solve(new Struct(predicate, v)); + if (s.isSuccess()) { + prologSystem.solveEnd(); + try { + val = s.getVarValue(v.getName()); + } catch (NoSolutionException e) { + } + } + return val; + } + + @Override + public List execute(List listCmd){ + Struct list = new Struct(listCmd.toArray(new Term[listCmd.size()])); + Var result = new Var("RESULT"); //$NON-NLS-1$ + List files = new ArrayList(); + SolveInfo res = prologSystem.solve(new Struct(P_RESOLVE, list, result)); + + if (res.isSuccess()) { + try { + prologSystem.solveEnd(); + Term solution = res.getVarValue(result.getName()); + + Iterator listIterator = ((Struct) solution).listIterator(); + while(listIterator.hasNext()){ + Object term = listIterator.next(); + if(term instanceof Struct){ + files.add(((Struct) term).getName()); + } + } + + } catch (NoSolutionException e) { + } + } + return files; + } + + @Override + public String getCurrentVoice() { + if (voiceDir == null) { + return null; + } + return voiceDir.getName(); + } + + @Override + public CommandBuilder newCommandBuilder() { + return new CommandBuilder(this); + } + + @Override + public void clear() { + ctx = null; + prologSystem = null; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java b/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java new file mode 100644 index 0000000000..bf976e7616 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/voice/CommandBuilder.java @@ -0,0 +1,179 @@ +package net.osmand.plus.voice; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import net.osmand.LogUtil; + +import org.apache.commons.logging.Log; + +import alice.tuprolog.Struct; +import alice.tuprolog.Term; + +public class CommandBuilder { + + private static final Log log = LogUtil.getLog(CommandBuilder.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_PREAMBLE = "preamble"; //$NON-NLS-1$ + protected static final String C_AND_ARRIVE_DESTINATION = "and_arrive_destination"; //$NON-NLS-1$ + protected static final String C_THEN = "then"; //$NON-NLS-1$ + protected static final String C_REACHED_DESTINATION = "reached_destination"; //$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$ + + /** + * + */ + private final CommandPlayer commandPlayer; + private boolean alreadyExecuted = false; + private List listStruct = new ArrayList(); + + public CommandBuilder(CommandPlayer commandPlayer){ + this(commandPlayer, true); + } + + public CommandBuilder(CommandPlayer commandPlayer, boolean preamble) { + this.commandPlayer = commandPlayer; + if (preamble) { + addCommand(C_PREAMBLE); + } + } + + private void checkState(){ + if(alreadyExecuted){ + throw new IllegalArgumentException(); + } + } + + private CommandBuilder addCommand(String name, Object... args){ + checkState(); + Term[] list = new Term[args.length]; + for (int i = 0; i < args.length; i++) { + Object o = args[i]; + if(o instanceof java.lang.Number){ + if(o instanceof java.lang.Double){ + list[i] = new alice.tuprolog.Double((Double) o); + } else if(o instanceof java.lang.Float){ + list[i] = new alice.tuprolog.Float((Float) o); + } else if(o instanceof java.lang.Long){ + list[i] = new alice.tuprolog.Long((Long) o); + } else { + list[i] = new alice.tuprolog.Int(((java.lang.Number)o).intValue()); + } + } else if(o instanceof String){ + list[i] = new Struct((String) o); + } + if(list[i]== null){ + throw new NullPointerException(name +" " + o); //$NON-NLS-1$ + } + } + Struct struct = new Struct(name, list); + if(log.isDebugEnabled()){ + log.debug("Adding command : " + name + " " + Arrays.toString(args)); //$NON-NLS-1$ //$NON-NLS-2$ + } + listStruct.add(struct); + return this; + } + + + public CommandBuilder goAhead(){ + return addCommand(C_GO_AHEAD); + } + + public CommandBuilder goAhead(double dist){ + return addCommand(C_GO_AHEAD, dist); + } + + public CommandBuilder makeUT(){ + return addCommand(C_MAKE_UT); + } + + public CommandBuilder makeUT(double dist){ + return addCommand(C_MAKE_UT, dist); + } + + public CommandBuilder prepareMakeUT(double dist){ + return addCommand(C_PREPARE_MAKE_UT, dist); + } + + + public CommandBuilder turn(String param){ + return addCommand(C_TURN, param); + } + + public CommandBuilder turn(String param, double dist){ + return addCommand(C_TURN, param, dist); + } + + /** + * + * @param param A_LEFT, A_RIGHT, ... + * @param dist + * @return + */ + public CommandBuilder prepareTurn(String param, double dist){ + return addCommand(C_PREPARE_TURN, param, dist); + } + + public CommandBuilder prepareRoundAbout(double dist){ + return addCommand(C_PREPARE_ROUNDABOUT, dist); + } + + public CommandBuilder roundAbout(double dist, double angle, int exit){ + return addCommand(C_ROUNDABOUT, dist, angle, exit); + } + + public CommandBuilder roundAbout(double angle, int exit){ + return addCommand(C_ROUNDABOUT, angle, exit); + } + + public CommandBuilder andArriveAtDestination(){ + return addCommand(C_AND_ARRIVE_DESTINATION); + } + + public CommandBuilder arrivedAtDestination(){ + return addCommand(C_REACHED_DESTINATION); + } + + public CommandBuilder bearLeft(){ + return addCommand(C_BEAR_LEFT); + } + + public CommandBuilder bearRight(){ + return addCommand(C_BEAR_RIGHT); + } + + public CommandBuilder then(){ + return addCommand(C_THEN); + } + + public CommandBuilder newRouteCalculated(double dist){ + return addCommand(C_ROUTE_NEW_CALC, dist); + } + + public CommandBuilder routeRecalculated(double dist){ + return addCommand(C_ROUTE_RECALC, dist); + } + + + + public void play(){ + this.commandPlayer.playCommands(this); + } + + protected List execute(){ + alreadyExecuted = true; + return this.commandPlayer.execute(listStruct); + } + +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/voice/CommandPlayer.java b/OsmAnd/src/net/osmand/plus/voice/CommandPlayer.java index 373aab180e..2979ca6069 100644 --- a/OsmAnd/src/net/osmand/plus/voice/CommandPlayer.java +++ b/OsmAnd/src/net/osmand/plus/voice/CommandPlayer.java @@ -1,420 +1,24 @@ -package net.osmand.plus.voice; - - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import net.osmand.LogUtil; -import net.osmand.data.IndexConstants; -import net.osmand.plus.OsmandSettings; -import net.osmand.plus.R; -import net.osmand.plus.ResourceManager; - -import org.apache.commons.logging.Log; - -import alice.tuprolog.InvalidLibraryException; -import alice.tuprolog.InvalidTheoryException; -import alice.tuprolog.NoSolutionException; -import alice.tuprolog.Number; -import alice.tuprolog.Prolog; -import alice.tuprolog.SolveInfo; -import alice.tuprolog.Struct; -import alice.tuprolog.Term; -import alice.tuprolog.Theory; -import alice.tuprolog.Var; -import android.content.Context; -import android.media.MediaPlayer; - -/** - * That class represents command player. - * It gets commands from input, analyze what files should be played and play - * them using media player - */ -public class CommandPlayer { - - private static final Log log = LogUtil.getLog(CommandPlayer.class); - - protected Context ctx; - // or zip file - private File voiceDir; -// private ZipFile voiceZipFile; - - // resolving commands to play - private Prolog prologSystem; - - // playing media - private MediaPlayer mediaPlayer; - // indicates that player is ready to play first file - private boolean playNext = true; - private List filesToPlay = Collections.synchronizedList(new ArrayList()); - - - public CommandPlayer(Context ctx){ - long time = System.currentTimeMillis(); - try { - this.ctx = ctx; - prologSystem = new Prolog(new String[]{"alice.tuprolog.lib.BasicLibrary"}); //$NON-NLS-1$ - } catch (InvalidLibraryException e) { - log.error("Initializing error", e); //$NON-NLS-1$ - throw new RuntimeException(e); - } - mediaPlayer = new MediaPlayer(); - if (log.isInfoEnabled()) { - log.info("Initializing prolog system : " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ - } - } - - - public String getCurrentVoice(){ - if(voiceDir == null){ - return null; - } - return voiceDir.getName(); - } - - public String init(String voiceProvider){ - prologSystem.clearTheory(); - voiceDir = null; - if(voiceProvider != null){ - File parent = OsmandSettings.getOsmandSettings(ctx).extendOsmandPath(ResourceManager.VOICE_PATH); - voiceDir = new File(parent, voiceProvider); - if(!voiceDir.exists()){ - voiceDir = null; - return ctx.getString(R.string.voice_data_unavailable); - } - } - - // see comments below why it is impossible to read from zip (don't know how to play file from zip) -// voiceZipFile = null; - if(voiceDir != null) { - long time = System.currentTimeMillis(); - boolean wrong = false; - try { - InputStream config; -// if (voiceDir.getName().endsWith(".zip")) { //$NON-NLS-1$ -// voiceZipFile = new ZipFile(voiceDir); -// config = voiceZipFile.getInputStream(voiceZipFile.getEntry("_config.p")); //$NON-NLS-1$ -// } else { - config = new FileInputStream(new File(voiceDir, "_config.p")); //$NON-NLS-1$ -// } - if (!wrong) { - prologSystem.setTheory(new Theory(config)); - } - } catch (InvalidTheoryException e) { - log.error("Loading voice config exception " + voiceProvider, e); //$NON-NLS-1$ - wrong = true; - } catch (IOException e) { - log.error("Loading voice config exception " + voiceProvider, e); //$NON-NLS-1$ - wrong = true; - } - if(wrong){ - return ctx.getString(R.string.voice_data_corrupted); - } else { - boolean versionSupported = false; - Var v = new Var("VERSION"); //$NON-NLS-1$ - SolveInfo s = prologSystem.solve(new Struct(P_VERSION, v)); - if(s.isSuccess()){ - prologSystem.solveEnd(); - try { - Term val = s.getVarValue(v.getName()); - if(val instanceof Number){ - versionSupported = ((Number) val).intValue() == IndexConstants.VOICE_VERSION; - } - } catch (NoSolutionException e) { - } - } - if(!versionSupported){ - return ctx.getString(R.string.voice_data_not_supported); - } - } - - if (log.isInfoEnabled()) { - log.info("Initializing voice subsystem " + voiceProvider + " : " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ //$NON-NLS-2$ - } - - } - return null; - } - - public CommandBuilder newCommandBuilder(){ - return new CommandBuilder(); - } - - - protected List execute(List listCmd){ - Struct list = new Struct(listCmd.toArray(new Term[listCmd.size()])); - Var result = new Var("RESULT"); //$NON-NLS-1$ - List files = new ArrayList(); - SolveInfo res = prologSystem.solve(new Struct(P_RESOLVE, list, result)); - - if (res.isSuccess()) { - try { - prologSystem.solveEnd(); - Term solution = res.getVarValue(result.getName()); - - Iterator listIterator = ((Struct) solution).listIterator(); - while(listIterator.hasNext()){ - Object term = listIterator.next(); - if(term instanceof Struct){ - files.add(((Struct) term).getName()); - } - } - - } catch (NoSolutionException e) { - } - } - return files; - } - - public void playCommands(CommandBuilder builder){ - filesToPlay.addAll(builder.execute()); - playQueue(); - } - - private synchronized void playQueue() { - while (!filesToPlay.isEmpty() && playNext) { - String f = filesToPlay.remove(0); - if (f != null && voiceDir != null) { - boolean exists = false; -// if(voiceZipFile != null){ -// ZipEntry entry = voiceZipFile.getEntry(f); -// exists = entry != null; -// voiceZipFile.getInputStream(entry); -// -// } else { - File file = new File(voiceDir, f); - exists = file.exists(); -// } - if (exists) { - log.debug("Playing file : " + f); //$NON-NLS-1$ - playNext = false; - try { - // Can't play sound file from zip it seams to be impossible only unpack and play!!! - mediaPlayer.setDataSource(file.getAbsolutePath()); - mediaPlayer.prepare(); - mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - public void onCompletion(MediaPlayer mp) { - mp.release(); - mediaPlayer = new MediaPlayer(); - int sleep = 60; - boolean delay = true; - while (!filesToPlay.isEmpty() && delay) { - delay = filesToPlay.get(0).startsWith(DELAY_CONST); - if (delay) { - String s = filesToPlay.remove(0).substring(DELAY_CONST.length()); - try { - sleep += Integer.parseInt(s); - } catch (NumberFormatException e) { - } - } - } - try { - Thread.sleep(sleep); - } catch (InterruptedException e) { - } - playNext = true; - playQueue(); - } - }); - - mediaPlayer.start(); - } catch (Exception e) { - log.error("Error while playing voice command", e); //$NON-NLS-1$ - playNext = true; - - } - } else { - log.info("Play file not found : " + f); //$NON-NLS-1$ - } - } - } - } - - - - protected static final String P_VERSION = "version"; //$NON-NLS-1$ - protected static final String P_RESOLVE = "resolve"; //$NON-NLS-1$ - - public static final String A_LEFT = "left"; //$NON-NLS-1$ - public static final String A_LEFT_SH = "left_sh"; //$NON-NLS-1$ - public static final String A_LEFT_SL = "left_sl"; //$NON-NLS-1$ - public static final String A_RIGHT = "right"; //$NON-NLS-1$ - public static final String A_RIGHT_SH = "right_sh"; //$NON-NLS-1$ - public static final String A_RIGHT_SL = "right_sl"; //$NON-NLS-1$ - - 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_PREAMBLE = "preamble"; //$NON-NLS-1$ - protected static final String C_AND_ARRIVE_DESTINATION = "and_arrive_destination"; //$NON-NLS-1$ - protected static final String C_THEN = "then"; //$NON-NLS-1$ - protected static final String C_REACHED_DESTINATION = "reached_destination"; //$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 DELAY_CONST = "delay_"; //$NON-NLS-1$ - - public class CommandBuilder { - - - private boolean alreadyExecuted = false; - private List listStruct = new ArrayList(); - - public CommandBuilder(){ - this(true); - } - - public CommandBuilder(boolean preamble) { - if (preamble) { - addCommand(C_PREAMBLE); - } - } - - private void checkState(){ - if(alreadyExecuted){ - throw new IllegalArgumentException(); - } - } - - private CommandBuilder addCommand(String name, Object... args){ - checkState(); - Term[] list = new Term[args.length]; - for (int i = 0; i < args.length; i++) { - Object o = args[i]; - if(o instanceof java.lang.Number){ - if(o instanceof java.lang.Double){ - list[i] = new alice.tuprolog.Double((Double) o); - } else if(o instanceof java.lang.Float){ - list[i] = new alice.tuprolog.Float((Float) o); - } else if(o instanceof java.lang.Long){ - list[i] = new alice.tuprolog.Long((Long) o); - } else { - list[i] = new alice.tuprolog.Int(((java.lang.Number)o).intValue()); - } - } else if(o instanceof String){ - list[i] = new Struct((String) o); - } - if(list[i]== null){ - throw new NullPointerException(name +" " + o); //$NON-NLS-1$ - } - } - Struct struct = new Struct(name, list); - if(log.isDebugEnabled()){ - log.debug("Adding command : " + name + " " + Arrays.toString(args)); //$NON-NLS-1$ //$NON-NLS-2$ - } - listStruct.add(struct); - return this; - } - - - public CommandBuilder goAhead(){ - return addCommand(C_GO_AHEAD); - } - - public CommandBuilder goAhead(double dist){ - return addCommand(C_GO_AHEAD, dist); - } - - public CommandBuilder makeUT(){ - return addCommand(C_MAKE_UT); - } - - public CommandBuilder makeUT(double dist){ - return addCommand(C_MAKE_UT, dist); - } - - public CommandBuilder prepareMakeUT(double dist){ - return addCommand(C_PREPARE_MAKE_UT, dist); - } - - - public CommandBuilder turn(String param){ - return addCommand(C_TURN, param); - } - - public CommandBuilder turn(String param, double dist){ - return addCommand(C_TURN, param, dist); - } - - /** - * - * @param param A_LEFT, A_RIGHT, ... - * @param dist - * @return - */ - public CommandBuilder prepareTurn(String param, double dist){ - return addCommand(C_PREPARE_TURN, param, dist); - } - - public CommandBuilder prepareRoundAbout(double dist){ - return addCommand(C_PREPARE_ROUNDABOUT, dist); - } - - public CommandBuilder roundAbout(double dist, double angle, int exit){ - return addCommand(C_ROUNDABOUT, dist, angle, exit); - } - - public CommandBuilder roundAbout(double angle, int exit){ - return addCommand(C_ROUNDABOUT, angle, exit); - } - - public CommandBuilder andArriveAtDestination(){ - return addCommand(C_AND_ARRIVE_DESTINATION); - } - - public CommandBuilder arrivedAtDestination(){ - return addCommand(C_REACHED_DESTINATION); - } - - public CommandBuilder bearLeft(){ - return addCommand(C_BEAR_LEFT); - } - - public CommandBuilder bearRight(){ - return addCommand(C_BEAR_RIGHT); - } - - public CommandBuilder then(){ - return addCommand(C_THEN); - } - - public CommandBuilder newRouteCalculated(double dist){ - return addCommand(C_ROUTE_NEW_CALC, dist); - } - - public CommandBuilder routeRecalculated(double dist){ - return addCommand(C_ROUTE_RECALC, dist); - } - - - - public void play(){ - CommandPlayer.this.playCommands(this); - } - - protected List execute(){ - alreadyExecuted = true; - return CommandPlayer.this.execute(listStruct); - } - - } - - -} - - - +package net.osmand.plus.voice; + +import java.util.List; + +import alice.tuprolog.Struct; +import android.app.Activity; +import android.content.Context; + +public interface CommandPlayer { + + public abstract String getCurrentVoice(); + + public abstract CommandBuilder newCommandBuilder(); + + public abstract void playCommands(CommandBuilder builder); + + public abstract void clear(); + + public abstract List execute(List listStruct); + + public void onActivityInit(Activity ctx); + + public void onActvitiyStop(Context ctx); +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/voice/CommandPlayerException.java b/OsmAnd/src/net/osmand/plus/voice/CommandPlayerException.java new file mode 100644 index 0000000000..82b6f8d129 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/voice/CommandPlayerException.java @@ -0,0 +1,22 @@ +package net.osmand.plus.voice; + + +/** + * Exception on CommandPlayer + * + * @author Pavol Zibrita + */ +public class CommandPlayerException extends Exception { + + private static final long serialVersionUID = 8413902962574061832L; + private final String error; + + public CommandPlayerException(String error) { + this.error = error; + } + + public String getError() { + return error; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/voice/CommandPlayerFactory.java b/OsmAnd/src/net/osmand/plus/voice/CommandPlayerFactory.java new file mode 100644 index 0000000000..2b3bd1078b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/voice/CommandPlayerFactory.java @@ -0,0 +1,34 @@ +package net.osmand.plus.voice; + +import java.io.File; + +import net.osmand.plus.OsmandSettings; +import net.osmand.plus.R; +import net.osmand.plus.ResourceManager; +import net.osmand.plus.activities.OsmandApplication; +import android.app.Activity; +import android.os.Build; + +public class CommandPlayerFactory +{ + public static CommandPlayer createCommandPlayer(String voiceProvider, OsmandApplication osmandApplication, Activity ctx) + throws CommandPlayerException + { + if (voiceProvider != null){ + File parent = OsmandSettings.getOsmandSettings(ctx).extendOsmandPath(ResourceManager.VOICE_PATH); + File voiceDir = new File(parent, voiceProvider); + if(!voiceDir.exists()){ + throw new CommandPlayerException(ctx.getString(R.string.voice_data_unavailable)); + } + if (MediaCommandPlayerImpl.isMyData(voiceDir)) { + return new MediaCommandPlayerImpl(osmandApplication, voiceProvider); + } else if (Integer.parseInt(Build.VERSION.SDK) >= 4) { + if (TTSCommandPlayerImpl.isMyData(voiceDir)) { + return new TTSCommandPlayerImpl(ctx, voiceProvider); + } + } + throw new CommandPlayerException(ctx.getString(R.string.voice_data_not_supported)); + } + return null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java new file mode 100644 index 0000000000..d813d33f89 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java @@ -0,0 +1,127 @@ +package net.osmand.plus.voice; + + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.osmand.LogUtil; + +import org.apache.commons.logging.Log; + +import android.app.Activity; +import android.content.Context; +import android.media.MediaPlayer; + +/** + * That class represents command player. + * It gets commands from input, analyze what files should be played and play + * them using media player + */ +public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer { + + private static final String CONFIG_FILE = "_config.p"; + private static final int MEDIA_VOICE_VERSION = 0; + + private static final Log log = LogUtil.getLog(MediaCommandPlayerImpl.class); + + // playing media + private MediaPlayer mediaPlayer; + // indicates that player is ready to play first file + private boolean playNext = true; + private List filesToPlay = Collections.synchronizedList(new ArrayList()); + + + public MediaCommandPlayerImpl(Context ctx, String voiceProvider) + throws CommandPlayerException + { + super(ctx, voiceProvider, CONFIG_FILE, MEDIA_VOICE_VERSION); + mediaPlayer = new MediaPlayer(); + } + + @Override + public void onActivityInit(Activity ctx) { + //do nothing here + } + + @Override + public void onActvitiyStop(Context ctx) { + //do nothing here + } + + @Override + public void clear() { + super.clear(); + mediaPlayer = null; + } + + @Override + public void playCommands(CommandBuilder builder){ + filesToPlay.addAll(builder.execute()); + playQueue(); + } + + private synchronized void playQueue() { + while (!filesToPlay.isEmpty() && playNext) { + String f = filesToPlay.remove(0); + if (f != null && voiceDir != null) { + boolean exists = false; +// if(voiceZipFile != null){ +// ZipEntry entry = voiceZipFile.getEntry(f); +// exists = entry != null; +// voiceZipFile.getInputStream(entry); +// +// } else { + File file = new File(voiceDir, f); + exists = file.exists(); +// } + if (exists) { + log.debug("Playing file : " + f); //$NON-NLS-1$ + playNext = false; + try { + // Can't play sound file from zip it seams to be impossible only unpack and play!!! + mediaPlayer.setDataSource(file.getAbsolutePath()); + mediaPlayer.prepare(); + mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + public void onCompletion(MediaPlayer mp) { + mp.release(); + mediaPlayer = new MediaPlayer(); + int sleep = 60; + boolean delay = true; + while (!filesToPlay.isEmpty() && delay) { + delay = filesToPlay.get(0).startsWith(DELAY_CONST); + if (delay) { + String s = filesToPlay.remove(0).substring(DELAY_CONST.length()); + try { + sleep += Integer.parseInt(s); + } catch (NumberFormatException e) { + } + } + } + try { + Thread.sleep(sleep); + } catch (InterruptedException e) { + } + playNext = true; + playQueue(); + } + }); + + mediaPlayer.start(); + } catch (Exception e) { + log.error("Error while playing voice command", e); //$NON-NLS-1$ + playNext = true; + + } + } else { + log.info("Play file not found : " + f); //$NON-NLS-1$ + } + } + } + } + + public static boolean isMyData(File voiceDir) { + return new File(voiceDir, CONFIG_FILE).exists(); + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java new file mode 100644 index 0000000000..cdffab5305 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/voice/TTSCommandPlayerImpl.java @@ -0,0 +1,175 @@ +package net.osmand.plus.voice; + +import java.io.File; +import java.util.List; +import java.util.Locale; + +import net.osmand.Algoritms; +import net.osmand.plus.R; +import net.osmand.plus.activities.SettingsActivity; +import alice.tuprolog.Struct; +import alice.tuprolog.Term; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.speech.tts.TextToSpeech; +import android.speech.tts.TextToSpeech.OnInitListener; + +public class TTSCommandPlayerImpl extends AbstractPrologCommandPlayer { + + private final class IntentStarter implements + DialogInterface.OnClickListener { + private final Activity ctx; + private final String intentAction; + private final Uri intentData; + + private IntentStarter(Activity ctx, String intentAction) { + this(ctx,intentAction, null); + } + + private IntentStarter(Activity 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 static final String CONFIG_FILE = "_ttsconfig.p"; + private static final int TTS_VOICE_VERSION = 100; + private TextToSpeech mTts; + private Context mTtsContext; + private String language; + + protected TTSCommandPlayerImpl(Activity ctx, String voiceProvider) + throws CommandPlayerException { + super(ctx, voiceProvider, CONFIG_FILE, TTS_VOICE_VERSION); + final Term langVal = solveSimplePredicate("language"); + if (langVal instanceof Struct) { + language = ((Struct) langVal).getName(); + } + if (Algoritms.isEmpty(language)) { + throw new CommandPlayerException( + ctx.getString(R.string.voice_data_corrupted)); + } + onActivityInit(ctx); + } + + @Override + public void playCommands(CommandBuilder builder) { + if (mTts != null) { + 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(' '); + } + mTts.speak(bld.toString(), TextToSpeech.QUEUE_ADD, null); + } + } + + @Override + public void onActivityInit(final Activity ctx) { + if (mTts != null && mTtsContext != ctx) { + //clear only, if the mTts was initialized in another context. + //Unfortunately, for example from settings to map first the map is initialized than + //the settingsactivity is destroyed... + internalClear(); + } + if (mTts == null) { + mTtsContext = ctx; + mTts = new TextToSpeech(ctx, new OnInitListener() { + @Override + public void onInit(int status) { + if (status != TextToSpeech.SUCCESS) { + internalClear(); + } else { + switch (mTts.isLanguageAvailable(new Locale(language))) + { + case TextToSpeech.LANG_MISSING_DATA: + internalClear(); + Builder builder = createAlertDialog( + R.string.tts_missing_language_data_title, + R.string.tts_missing_language_data, + new IntentStarter( + ctx, + TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA), + ctx); + builder.show(); + break; + case TextToSpeech.LANG_AVAILABLE: + mTts.setLanguage(new Locale(language)); + 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 (ctx instanceof SettingsActivity) { + builder = createAlertDialog( + R.string.tts_language_not_supported_title, + R.string.tts_language_not_supported, + new IntentStarter( + ctx, + Intent.ACTION_VIEW, Uri.parse("market://search?q=text to speech engine" + )), + ctx); + builder.show(); + } + break; + } + } + } + + + }); + } + } + + private Builder createAlertDialog(int titleResID, int messageResID, IntentStarter intentStarter, final Activity ctx) { + Builder builder = new AlertDialog.Builder(ctx); + builder.setCancelable(true); + builder.setNegativeButton(R.string.default_buttons_no, null); + builder.setPositiveButton(R.string.default_buttons_yes, intentStarter); + builder.setTitle(titleResID); + builder.setMessage(messageResID); + return builder; + } + + @Override + public void onActvitiyStop(Context ctx) { + //stop only when the context is the same + if (mTtsContext == ctx) { + internalClear(); + } + } + + private void internalClear() { + if (mTts != null) { + mTts.shutdown(); + mTtsContext = null; + mTts = null; + } + } + + @Override + public void clear() { + super.clear(); + internalClear(); + } + + public static boolean isMyData(File voiceDir) throws CommandPlayerException { + return new File(voiceDir, CONFIG_FILE).exists(); + } + +} diff --git a/OsmAnd/voice/en-tts/_ttsconfig.p b/OsmAnd/voice/en-tts/_ttsconfig.p new file mode 100644 index 0000000000..a8a63f76d7 --- /dev/null +++ b/OsmAnd/voice/en-tts/_ttsconfig.p @@ -0,0 +1,120 @@ +:- op('==', xfy, 500). +version(100). +language(en). + +% before each announcement (beep) +preamble - []. + + +%% TURNS +turn('left', ['turn left ']). +turn('left_sh', ['sharp left ']). +turn('left_sl', ['turn slightly left ']). +turn('right', ['turn right ']). +turn('right_sh', ['sharp right ']). +turn('right_sl', ['turn slightly right ']). + +prepare_turn(Turn, Dist) == ['Prepare to ', M, ' after ', D] :- + distance(Dist) == D, turn(Turn, M). +turn(Turn, Dist) == ['After ', D, M] :- + distance(Dist) == D, turn(Turn, M). +turn(Turn) == M :- turn(Turn, M). + + +prepare_make_ut(Dist) == ['Prepare to turn after ', D, ' turn back'] :- + distance(Dist) == D. + +prepare_roundabout(Dist) == ['Prepare to enter roundabout after ', D] :- + distance(Dist) == D. + +make_ut(Dist) == ['After ', D, ' turn back '] :- + distance(Dist) == D. +make_ut == ['Turn back ']. + +roundabout(Dist, _Angle, Exit) == ['After ', D, ' enter the roundabout, and take the ', E, 'exit'] :- distance(Dist) == D, nth(Exit, E). +roundabout(_Angle, Exit) == ['taking the ', E, 'exit'] :- nth(Exit, E). + +and_arrive_destination == ['and arrive at your destination ']. % Miss and? +then == ['then ']. +reached_destination == ['you have reached your destination ']. +bear_right == ['keep right ']. +bear_left == ['keep left ']. +route_recalc(_Dist) == []. % ['recalculating route ']. %nothing to said possibly beep? +route_new_calc(Dist) == ['The trip is ', D] :- distance(Dist) == D. % nothing to said possibly beep? + +go_ahead(Dist) == ['Drive for ', D]:- distance(Dist) == D. +go_ahead == ['Continue straight ahead ']. + +%% +nth(1, '1st '). +nth(2, '2nd '). +nth(3, '3rd '). +nth(4, '4th '). +nth(5, '5th '). +nth(6, '6th '). +nth(7, '7th '). +nth(8, '8th '). +nth(9, '9th '). +nth(10, '10th '). +nth(11, '11th '). +nth(12, '12th '). +nth(13, '13th '). +nth(14, '14th '). +nth(15, '15th '). +nth(16, '16th '). +nth(17, '17th '). + + +%%% distance measure +distance(Dist) == T :- Dist < 1000, dist(Dist, F), append(F, ' meters',T). +dist(D, ['10 ']) :- D < 20, !. +dist(D, ['20 ']) :- D < 30, !. +dist(D, ['30 ']) :- D < 40, !. +dist(D, ['40 ']) :- D < 50, !. +dist(D, ['50 ']) :- D < 60, !. +dist(D, ['60 ']) :- D < 70, !. +dist(D, ['70 ']) :- D < 80, !. +dist(D, ['80 ']) :- D < 90, !. +dist(D, ['90 ']) :- D < 100, !. +dist(D, ['100 ']) :- D < 150, !. +dist(D, ['150 ']) :- D < 200, !. +dist(D, ['200 ']) :- D < 250, !. +dist(D, ['250 ']) :- D < 300, !. +dist(D, ['300 ']) :- D < 350, !. +dist(D, ['350 ']) :- D < 400, !. +dist(D, ['400 ']) :- D < 450, !. +dist(D, ['450 ']) :- D < 500, !. +dist(D, ['500 ']) :- D < 550, !. +dist(D, ['550 ']) :- D < 600, !. +dist(D, ['600 ']) :- D < 650, !. +dist(D, ['650 ']) :- D < 700, !. +dist(D, ['700 ']) :- D < 750, !. +dist(D, ['750 ']) :- D < 800, !. +dist(D, ['800 ']) :- D < 850, !. +dist(D, ['850 ']) :- D < 900, !. +dist(D, ['900 ']) :- D < 950, !. +dist(D, ['950 ']) :- !. + +distance(Dist) == ['more than 1 kilometer '] :- Dist < 1500. +distance(Dist) == ['more than 2 kilometers '] :- Dist < 3000. +distance(Dist) == ['more than 3 kilometers '] :- Dist < 4000. +distance(Dist) == ['more than 4 kilometers '] :- Dist < 5000. +distance(Dist) == ['more than 5 kilometers '] :- Dist < 6000. +distance(Dist) == ['more than 6 kilometers '] :- Dist < 7000. +distance(Dist) == ['more than 7 kilometers '] :- Dist < 8000. +distance(Dist) == ['more than 8 kilometers '] :- Dist < 9000. +distance(Dist) == ['more than 9 kilometers '] :- Dist < 10000. +distance(Dist) == ['more than ', X, ' kilometers '] :- D is Dist/1000, dist(D, X). + +%% resolve command main method +%% if you are familar with Prolog you can input specific to the whole mechanism, +%% by adding exception cases. +flatten(X, Y) :- flatten(X, [], Y), !. +flatten([], Acc, Acc). +flatten([X|Y], Acc, Res):- + flatten(Y, Acc, R), flatten(X, R, Res). +flatten(X, Acc, [X|Acc]). + +resolve(X, Y) :- resolve_impl(X,Z), flatten(Z, Y). +resolve_impl([],[]). +resolve_impl([X|Rest], List) :- resolve_impl(Rest, Tail), ((X == L) -> append(L, Tail, List); List = Tail). \ No newline at end of file