diff --git a/DataExtractionOSM/src/com/osmand/ToDoConstants.java b/DataExtractionOSM/src/com/osmand/ToDoConstants.java index c91d0247a7..1ab9f8390d 100644 --- a/DataExtractionOSM/src/com/osmand/ToDoConstants.java +++ b/DataExtractionOSM/src/com/osmand/ToDoConstants.java @@ -20,9 +20,11 @@ public class ToDoConstants { // 68. Implement service to app work with screen offline // (introduce special settings how often update location to monitoring & audio guidance) // Improvement : Show stops in the transport route on the map + // Improvement : redesign poi selecting (show on map ) + // 69. Add phone information to POI // Not clear if it is really needed - // 43. Enable poi filter by name + // 43. Enable poi filter by name (find lake by name or shop) - case sensitive search DB! // 45. Get clear settings. Move that setting on top settings screen. // That setting should rule all activities that use internet. It should ask whenever internet is used // (would you like to use internet for that operation - if using internet is not checked). @@ -31,7 +33,7 @@ public class ToDoConstants { // Unscheduled (complex) // 66. Transport routing (show next stop, total distance, show stop get out) (?). // 64. Traffic information (?) - // 65. Intermediate points - for better control routing, to avoid traffic jam ...(?) + // 65. Intermediate points - for better control routing, to avoid traffic jams ...(?) // 40. Support simple vector road rendering (require new index file) (?) // 63. Support simple offline routing(require new index file) (?) diff --git a/DataExtractionOSM/src/com/osmand/data/index/IndexBatchCreator.java b/DataExtractionOSM/src/com/osmand/data/index/IndexBatchCreator.java index c43a4d86ba..22453df53b 100644 --- a/DataExtractionOSM/src/com/osmand/data/index/IndexBatchCreator.java +++ b/DataExtractionOSM/src/com/osmand/data/index/IndexBatchCreator.java @@ -26,7 +26,7 @@ import com.osmand.impl.ConsoleProgressImplementation; public class IndexBatchCreator { // config params private static final boolean indexPOI = true; - private static final boolean indexAddress = true; + private static final boolean indexAddress = false; private static final boolean indexTransport = true; private static final boolean writeWayNodes = true; @@ -52,30 +52,27 @@ public class IndexBatchCreator { // "czech_republic", "netherlands", // 168, 375, // "great_britain", "italy", // 281, 246, // ADD TO TOTAL : 2449 MB - // TODO transport, poi : "czech_republic", "netherlands", "great_britain", "italy" - // address : "great_britain", "italy" - out of memory + // TODO "great_britain", "italy" (address out of memory, poi, transport) + netherlands }; - // TODO all protected static final String[] franceProvinces = new String[] { - "alsace","aquitaine", "auvergne", "basse-normandie", "bourgogne", "bretagne", "centre", - "champagne-ardenne", "corse", "franche-comte", "haute-normandie", "ile-de-france", - "languedoc-roussillon", "limousin", "lorraine", "midi-pyrenees", "nord-pas-de-calais", - "pays-de-la-loire", "picardie","poitou-charentes", "provence-alpes-cote-d-azur", "rhone-alpes" +// "alsace","aquitaine", "auvergne", "basse-normandie", "bourgogne", "bretagne", "centre", +// "champagne-ardenne", "corse", "franche-comte", "haute-normandie", "ile-de-france", +// "languedoc-roussillon", "limousin", "lorraine", "midi-pyrenees", "nord-pas-de-calais", +// "pays-de-la-loire", "picardie","poitou-charentes", "provence-alpes-cote-d-azur", "rhone-alpes" }; - // TODO all protected static final String[] germanyLands = new String[] { - "baden-wuerttemberg","bayern", "berlin", "brandenburg", "bremen", "hamburg", "hessen", - "mecklenburg-vorpommern", "niedersachsen", "nordrhein-westfalen", "rheinland-pfalz", "saarland", - "sachsen-anhalt", "sachsen", "schleswig-holstein", "thueringen", +// "baden-wuerttemberg","bayern", "berlin", "brandenburg", "bremen", "hamburg", "hessen", +// "mecklenburg-vorpommern", "niedersachsen", "nordrhein-westfalen", "rheinland-pfalz", "saarland", +// "sachsen-anhalt", "sachsen", "schleswig-holstein", "thueringen", }; protected static final String SITE_TO_DOWNLOAD2 = "http://downloads.cloudmade.com/"; //$NON-NLS-1$ // us states - // TODO address + // Address (out of memory) : California ? , Florida ?, Georgia ? protected static final String[] usStates = new String[] { // "Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", // "Delaware", "District_of_Columbia", "Florida", "Georgia", "Guantanamo_Bay", "Hawaii", @@ -255,7 +252,7 @@ public class IndexBatchCreator { uploadIndex(new File(indexDirFiles, fName), alreadyUploadedFiles); } } catch (Exception e) { - log.error("Exception generating indexes for " + f.getName()); //$NON-NLS-1$ + log.error("Exception generating indexes for " + f.getName(), e); //$NON-NLS-1$ } } catch (OutOfMemoryError e) { System.gc(); @@ -323,7 +320,7 @@ public class IndexBatchCreator { is.close(); zout.close(); } catch (IOException e) { - log.error("Exception while zipping file"); + log.error("Exception while zipping file", e); } if(f.delete()){ log.info("Source odb file was deleted"); diff --git a/OsmAnd/.classpath b/OsmAnd/.classpath index 8f4539503f..139125b82b 100644 --- a/OsmAnd/.classpath +++ b/OsmAnd/.classpath @@ -5,5 +5,6 @@ + diff --git a/OsmAnd/lib/tuprolog.jar b/OsmAnd/lib/tuprolog.jar new file mode 100644 index 0000000000..529887697a Binary files /dev/null and b/OsmAnd/lib/tuprolog.jar differ diff --git a/OsmAnd/res/values-ru-rRU/strings.xml b/OsmAnd/res/values-ru-rRU/strings.xml index 6442b1ff6e..e4f0a0c5d8 100644 --- a/OsmAnd/res/values-ru-rRU/strings.xml +++ b/OsmAnd/res/values-ru-rRU/strings.xml @@ -1,6 +1,13 @@ - Stop routing + Не использовать + Выберите голос для маршрутизации + Голосовые данные + Инициализируются голосовые данные... + Голосовые данные не поддерживаются текущей версией приложения + Выбранные голосовые данные не правильного формата + Выбранные голосовые данные не доступны + Отменить маршрут Карточка SD недоступна. \nВы не сможете работать с картой. Карточка SD доступна только для чтения. \nВы не сможете загружать карты из интернета. Файл распаковывается diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 9b09b3db26..fba6f048df 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -1,6 +1,13 @@ - Отменить маршрут + Not use + Choose voice data for routing + Voice data + Initializing voice data... + Voice data has different version that application supports + Specified voice data is corrupted + Current voice data is not available + Stop routing SD card is not accessible. \nYou can\'t see map and find anything. SD card is read-only accessible. \nYou can see only preloaded map and can\'t download from internet. File is unzipping diff --git a/OsmAnd/res/xml/settings_pref.xml b/OsmAnd/res/xml/settings_pref.xml index b0c7c875ab..00cffea30a 100644 --- a/OsmAnd/res/xml/settings_pref.xml +++ b/OsmAnd/res/xml/settings_pref.xml @@ -22,6 +22,7 @@ + diff --git a/OsmAnd/src/com/osmand/OsmandSettings.java b/OsmAnd/src/com/osmand/OsmandSettings.java index a92fd4d306..6bbb5def34 100644 --- a/OsmAnd/src/com/osmand/OsmandSettings.java +++ b/OsmAnd/src/com/osmand/OsmandSettings.java @@ -495,4 +495,28 @@ public class OsmandSettings { } return new PoiFilter(null); } + + + // this value string is synchronized with settings_pref.xml preference name + public static final String VOICE_PROVIDER = "voice_provider"; //$NON-NLS-1$ + + public static String getVoiceProvider(Context ctx){ + SharedPreferences prefs = ctx.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_WORLD_READABLE); + return prefs.getString(VOICE_PROVIDER, null); + } + + public static final String VOICE_MUTE = "voice_mute"; //$NON-NLS-1$ + public static final boolean VOICE_MUTE_DEF = false; + + public static boolean isVoiceMute(Context ctx){ + SharedPreferences prefs = ctx.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_WORLD_READABLE); + return prefs.getBoolean(VOICE_MUTE, VOICE_MUTE_DEF); + } + + public static boolean setVoiceMute(Context ctx, boolean mute){ + SharedPreferences prefs = ctx.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_WORLD_READABLE); + return prefs.edit().putBoolean(VOICE_MUTE, mute).commit(); + } + + } diff --git a/OsmAnd/src/com/osmand/activities/MainMenuActivity.java b/OsmAnd/src/com/osmand/activities/MainMenuActivity.java index b4d1e5e897..6952b743f5 100644 --- a/OsmAnd/src/com/osmand/activities/MainMenuActivity.java +++ b/OsmAnd/src/com/osmand/activities/MainMenuActivity.java @@ -32,6 +32,7 @@ import com.osmand.ProgressDialogImplementation; import com.osmand.R; import com.osmand.ResourceManager; import com.osmand.activities.search.SearchActivity; +import com.osmand.voice.CommandPlayer; public class MainMenuActivity extends Activity { @@ -57,7 +58,14 @@ public class MainMenuActivity extends Activity { @Override public void run() { try { + // initializing voice prolog subsystem + List warnings = ResourceManager.getResourceManager().reloadIndexes(impl); + impl.startTask(getString(R.string.voice_data_initializing), -1); + String w = CommandPlayer.init(MainMenuActivity.this); + if(w != null){ + warnings.add(w); + } SavingTrackHelper helper = new SavingTrackHelper(MainMenuActivity.this); if (helper.hasDataToSave()) { impl.startTask(getString(R.string.saving_gpx_tracks), -1); @@ -100,9 +108,6 @@ public class MainMenuActivity extends Activity { requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.menu); - - - showMap = (Button) findViewById(R.id.MapButton); showMap.setOnClickListener(new OnClickListener() { @@ -110,7 +115,6 @@ public class MainMenuActivity extends Activity { public void onClick(View v) { final Intent mapIndent = new Intent(MainMenuActivity.this, MapActivity.class); startActivityForResult(mapIndent, 0); - } }); settingsButton = (Button) findViewById(R.id.SettingsButton); diff --git a/OsmAnd/src/com/osmand/activities/SettingsActivity.java b/OsmAnd/src/com/osmand/activities/SettingsActivity.java index 30696d0a44..55220a611e 100644 --- a/OsmAnd/src/com/osmand/activities/SettingsActivity.java +++ b/OsmAnd/src/com/osmand/activities/SettingsActivity.java @@ -1,6 +1,9 @@ package com.osmand.activities; +import java.io.File; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import android.app.ProgressDialog; import android.content.Context; @@ -9,6 +12,7 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.ActivityInfo; import android.content.res.Resources; +import android.os.Environment; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; @@ -29,6 +33,7 @@ import com.osmand.OsmandSettings.ApplicationMode; import com.osmand.activities.RouteProvider.RouteService; import com.osmand.map.TileSourceManager; import com.osmand.map.TileSourceManager.TileSourceTemplate; +import com.osmand.voice.CommandPlayer; public class SettingsActivity extends PreferenceActivity implements OnPreferenceChangeListener, OnPreferenceClickListener { @@ -72,7 +77,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference private ListPreference routerPreference; private ListPreference maxLevelToDownload; private ListPreference mapScreenOrientation; - + private ListPreference voicePreference; private BooleanPreference[] booleanPreferences = new BooleanPreference[]{ @@ -87,6 +92,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference new BooleanPreference(OsmandSettings.SAVE_TRACK_TO_GPX, OsmandSettings.SAVE_TRACK_TO_GPX_DEF), }; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -127,6 +133,8 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference tileSourcePreference.setOnPreferenceChangeListener(this); routerPreference =(ListPreference) screen.findPreference(OsmandSettings.ROUTER_SERVICE); routerPreference.setOnPreferenceChangeListener(this); + voicePreference =(ListPreference) screen.findPreference(OsmandSettings.VOICE_PROVIDER); + voicePreference.setOnPreferenceChangeListener(this); } @@ -194,6 +202,31 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference routerPreference.setEntries(entries); routerPreference.setEntryValues(entries); routerPreference.setValue(entry); + + // read available voice data + File extStorage = new File(Environment.getExternalStorageDirectory(), CommandPlayer.VOICE_DIR); + Set setFiles = new LinkedHashSet(); + if (extStorage.exists()) { + for (File f : extStorage.listFiles()) { + if (f.isDirectory()) { + setFiles.add(f.getName()); + } + } + } + String provider = OsmandSettings.getVoiceProvider(this); + entries = new String[setFiles.size() + 1]; + int k = 0; + entries[k++] = getString(R.string.voice_not_use); + for(String s : setFiles){ + entries[k++] = s; + } + voicePreference.setEntries(entries); + voicePreference.setEntryValues(entries); + if(setFiles.contains(provider)){ + voicePreference.setValue(provider); + } else { + voicePreference.setValueIndex(0); + } int startZoom = 12; int endZoom = 19; @@ -280,6 +313,15 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference edit.putInt(OsmandSettings.ROUTER_SERVICE, s.ordinal()); } edit.commit(); + } else if (preference == voicePreference) { + int i = voicePreference.findIndexOfValue((String) newValue); + if(i==0){ + edit.putString(OsmandSettings.VOICE_PROVIDER, null); + } else { + edit.putString(OsmandSettings.VOICE_PROVIDER, (String) newValue); + } + edit.commit(); + CommandPlayer.init(this); } else if (preference == tileSourcePreference) { edit.putString(OsmandSettings.MAP_TILE_SOURCES, (String) newValue); edit.commit(); diff --git a/OsmAnd/src/com/osmand/activities/ShowRouteInfoActivity.java b/OsmAnd/src/com/osmand/activities/ShowRouteInfoActivity.java index 26a886eaf1..c69251beaf 100644 --- a/OsmAnd/src/com/osmand/activities/ShowRouteInfoActivity.java +++ b/OsmAnd/src/com/osmand/activities/ShowRouteInfoActivity.java @@ -34,6 +34,7 @@ import com.osmand.activities.RoutingHelper.RouteDirectionInfo; import com.osmand.activities.RoutingHelper.TurnType; import com.osmand.osm.MapUtils; import com.osmand.views.MapInfoLayer; +import com.osmand.voice.CommandPlayer; /** * @@ -62,6 +63,11 @@ public class ShowRouteInfoActivity extends ListActivity { RouteDirectionInfo item = ((RouteInfoAdapter)getListAdapter()).getItem(position - 1); RoutingHelper inst = RoutingHelper.getInstance(this); Location loc = inst.getLocationFromRouteDirection(item); + CommandPlayer player = CommandPlayer.getInstance(this); + if(player != null){ + // TODO temp solution + player.newCommandBuilder().prepareTurnLeft(300).play(); + } if(loc != null){ OsmandSettings.setMapLocationToShow(this, loc.getLatitude(),loc.getLongitude()); startActivity(new Intent(this, MapActivity.class)); diff --git a/OsmAnd/src/com/osmand/views/OsmBugsLayer.java b/OsmAnd/src/com/osmand/views/OsmBugsLayer.java index ce5fb62be4..173eb26148 100644 --- a/OsmAnd/src/com/osmand/views/OsmBugsLayer.java +++ b/OsmAnd/src/com/osmand/views/OsmBugsLayer.java @@ -190,60 +190,59 @@ public class OsmBugsLayer implements OsmandMapLayer { @Override public boolean onLongPressEvent(PointF point) { - if (objects != null && !objects.isEmpty()) { + final OpenStreetBug bug = getBugFromPoint(point); + if(bug != null){ + Builder builder = new AlertDialog.Builder(view.getContext()); + Resources resources = view.getContext().getResources(); + builder.setItems(new String[]{ + resources.getString(R.string.osb_comment_menu_item), + resources.getString(R.string.osb_close_menu_item) + }, new DialogInterface.OnClickListener(){ + @Override + public void onClick(DialogInterface dialog, int which) { + if(which == 0){ + commentBug(view.getContext(), activity.getLayoutInflater(), bug); + } else if(which == 1){ + closeBug(view.getContext(), activity.getLayoutInflater(), bug); + } + } + }); + builder.create().show(); + return true; + } + return false; + } + + public OpenStreetBug getBugFromPoint(PointF point){ + OpenStreetBug result = null; + if (objects != null) { int ex = (int) point.x; int ey = (int) point.y; int radius = getRadiusBug(view.getZoom()) * 3 / 2; try { - for (final OpenStreetBug n : objects) { + for (int i = 0; i < objects.size(); i++) { + OpenStreetBug n = objects.get(i); int x = view.getRotatedMapXForPoint(n.getLatitude(), n.getLongitude()); int y = view.getRotatedMapYForPoint(n.getLatitude(), n.getLongitude()); if (Math.abs(x - ex) <= radius && Math.abs(y - ey) <= radius) { - Builder builder = new AlertDialog.Builder(view.getContext()); - Resources resources = view.getContext().getResources(); - builder.setItems(new String[]{ - resources.getString(R.string.osb_comment_menu_item), - resources.getString(R.string.osb_close_menu_item) - }, new DialogInterface.OnClickListener(){ - @Override - public void onClick(DialogInterface dialog, int which) { - if(which == 0){ - commentBug(view.getContext(), activity.getLayoutInflater(), n); - } else if(which == 1){ - closeBug(view.getContext(), activity.getLayoutInflater(), n); - } - } - }); - builder.create().show(); - return true; + radius = Math.max(Math.abs(x - ex), Math.abs(y - ey)); + result = n; } } } catch (IndexOutOfBoundsException e) { // that's really rare case, but is much efficient than introduce synchronized block } } - return false; + return result; } @Override public boolean onTouchEvent(PointF point) { - if (objects != null && !objects.isEmpty()) { - int ex = (int) point.x; - int ey = (int) point.y; - int radius = getRadiusBug(view.getZoom()) * 3 / 2; - try { - for (OpenStreetBug n : objects) { - int x = view.getRotatedMapXForPoint(n.getLatitude(), n.getLongitude()); - int y = view.getRotatedMapYForPoint(n.getLatitude(), n.getLongitude()); - if (Math.abs(x - ex) <= radius && Math.abs(y - ey) <= radius) { - String format = "Bug : " + n.getName(); //$NON-NLS-1$ - Toast.makeText(view.getContext(), format, Toast.LENGTH_LONG).show(); - return true; - } - } - } catch (IndexOutOfBoundsException e) { - // that's really rare case, but is much efficient than introduce synchronized block - } + OpenStreetBug bug = getBugFromPoint(point); + if(bug != null){ + String format = "Bug : " + bug.getName(); //$NON-NLS-1$ + Toast.makeText(view.getContext(), format, Toast.LENGTH_LONG).show(); + return true; } return false; } diff --git a/OsmAnd/src/com/osmand/views/OsmandMapTileView.java b/OsmAnd/src/com/osmand/views/OsmandMapTileView.java index 2549c13c46..0ece57d6a0 100644 --- a/OsmAnd/src/com/osmand/views/OsmandMapTileView.java +++ b/OsmAnd/src/com/osmand/views/OsmandMapTileView.java @@ -694,7 +694,8 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall @Override public boolean onDown(MotionEvent e) { - animatedDraggingThread.stopAnimating(); + // enable double tap animation +// animatedDraggingThread.stopAnimating(); return false; } @@ -792,8 +793,8 @@ public class OsmandMapTileView extends SurfaceView implements IMapDownloaderCall @Override public boolean onDoubleTap(MotionEvent e) { LatLon l = getLatLonFromScreenPoint(e.getX(), e.getY()); - setLatLon(l.getLatitude(), l.getLongitude()); - setZoom(zoom + 1); + getAnimatedDraggingThread().startMoving(getLatitude(), getLongitude(), + l.getLatitude(), l.getLongitude(), getZoom(), getZoom() + 1, getSourceTileSize(), getRotate(), true); return true; } diff --git a/OsmAnd/src/com/osmand/views/POIMapLayer.java b/OsmAnd/src/com/osmand/views/POIMapLayer.java index 8d7605a825..2464e7195a 100644 --- a/OsmAnd/src/com/osmand/views/POIMapLayer.java +++ b/OsmAnd/src/com/osmand/views/POIMapLayer.java @@ -74,6 +74,7 @@ public class POIMapLayer implements OsmandMapLayer { } public Amenity getAmenityFromPoint(PointF point){ + Amenity result = null; if (objects != null) { int ex = (int) point.x; int ey = (int) point.y; @@ -84,14 +85,15 @@ public class POIMapLayer implements OsmandMapLayer { int x = view.getRotatedMapXForPoint(n.getLocation().getLatitude(), n.getLocation().getLongitude()); int y = view.getRotatedMapYForPoint(n.getLocation().getLatitude(), n.getLocation().getLongitude()); if (Math.abs(x - ex) <= radius && Math.abs(y - ey) <= radius) { - return n; + radius = Math.max(Math.abs(x - ex), Math.abs(y - ey)); + result = n; } } } catch (IndexOutOfBoundsException e) { // that's really rare case, but is much efficient than introduce synchronized block } } - return null; + return result; } diff --git a/OsmAnd/src/com/osmand/views/TransportStopsLayer.java b/OsmAnd/src/com/osmand/views/TransportStopsLayer.java index 7a4a2beb75..92b0bd0581 100644 --- a/OsmAnd/src/com/osmand/views/TransportStopsLayer.java +++ b/OsmAnd/src/com/osmand/views/TransportStopsLayer.java @@ -44,6 +44,7 @@ public class TransportStopsLayer implements OsmandMapLayer { } public TransportStop getFromPoint(PointF point){ + TransportStop result = null; if (objects != null) { int ex = (int) point.x; int ey = (int) point.y; @@ -54,14 +55,15 @@ public class TransportStopsLayer implements OsmandMapLayer { int x = view.getRotatedMapXForPoint(n.getLocation().getLatitude(), n.getLocation().getLongitude()); int y = view.getRotatedMapYForPoint(n.getLocation().getLatitude(), n.getLocation().getLongitude()); if (Math.abs(x - ex) <= radius && Math.abs(y - ey) <= radius) { - return n; + radius = Math.max(Math.abs(x - ex), Math.abs(y - ey)); + result = n; } } } catch (IndexOutOfBoundsException e) { // that's really rare case, but is much efficient than introduce synchronized block } } - return null; + return result; } diff --git a/OsmAnd/src/com/osmand/voice/CommandPlayer.java b/OsmAnd/src/com/osmand/voice/CommandPlayer.java new file mode 100644 index 0000000000..1b63d27bcf --- /dev/null +++ b/OsmAnd/src/com/osmand/voice/CommandPlayer.java @@ -0,0 +1,312 @@ +package com.osmand.voice; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +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; +import android.os.Environment; + +import com.osmand.Algoritms; +import com.osmand.LogUtil; +import com.osmand.OsmandSettings; +import com.osmand.R; +import com.osmand.ResourceManager; + +public class CommandPlayer { + + public static final String VOICE_DIR = ResourceManager.APP_DIR + "/voice"; //$NON-NLS-1$ + public static final int VOICE_VERSION = 0; + private static final Log log = LogUtil.getLog(CommandPlayer.class); + + private static CommandPlayer instance = null; + + protected Context ctx; + private File voiceDir; + + // resolving commands to play + private Prolog prologSystem; + + // playing media + private MediaPlayer mediaPlayer; + private List filesToPlay = new ArrayList(); + + /** + * @param ctx + * @return null could be returned it means there is no available voice config + */ + public static CommandPlayer getInstance(Context ctx) { + init(ctx); + return instance; + } + + + public static String init(Context ctx){ + if(OsmandSettings.getVoiceProvider(ctx) == null && instance == null){ + return null; + } + if(instance == null){ + long time = System.currentTimeMillis(); + instance = new CommandPlayer(ctx); + if (log.isInfoEnabled()) { + log.info("Initializing prolog system : " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ + } + } + instance.ctx = ctx; + if(!Algoritms.objectEquals(OsmandSettings.getVoiceProvider(ctx), instance.getCurrentVoice())){ + return instance.init(); + } + + return null; + } + + protected CommandPlayer(Context ctx){ + 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(); + } + + public String getCurrentVoice(){ + if(voiceDir == null){ + return null; + } + return voiceDir.getName(); + } + + protected String init(){ + String voiceProvider = OsmandSettings.getVoiceProvider(ctx); + prologSystem.clearTheory(); + voiceDir = null; + if(voiceProvider != null){ + File parent = new File(Environment.getExternalStorageDirectory(), VOICE_DIR); + voiceDir = new File(parent, voiceProvider); + if(!voiceDir.exists()){ + voiceDir = null; + return ctx.getString(R.string.voice_data_unavailable); + } + } + if(voiceDir != null) { + long time = System.currentTimeMillis(); + File config = new File(voiceDir, "_config.p"); //$NON-NLS-1$ + boolean wrong = !config.exists(); + + if (!wrong) { + + try { + prologSystem.setTheory(new Theory(new FileInputStream(config))); + } catch (InvalidTheoryException e) { + log.error("Loading voice config exception " + voiceProvider, e); //$NON-NLS-1$ + } catch (IOException e) { + log.error("Loading voice config exception " + voiceProvider, e); //$NON-NLS-1$ + } + } + 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() == 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 static final String P_VERSION = "version"; //$NON-NLS-1$ + protected static final String P_RESOLVE = "resolve"; //$NON-NLS-1$ + + protected static final String С_PREPARE_TURN_LEFT = "prepare_turn_left"; //$NON-NLS-1$ + protected static final String С_PREPARE_TURN_RIGHT = "prepare_turn_right"; //$NON-NLS-1$ + protected static final String С_PREAMBLE = "preamble"; //$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(С_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); + listStruct.add(struct); + return this; + } + + public CommandBuilder prepareTurnLeft(double dist){ + return addCommand(С_PREPARE_TURN_LEFT, dist); + } + + public CommandBuilder prepareTurnRight(double dist){ + return addCommand(С_PREPARE_TURN_LEFT, dist); + } + + public void play(){ + CommandPlayer.this.playCommands(this); + } + + protected List execute(){ + alreadyExecuted = true; + return CommandPlayer.this.execute(listStruct); + } + + } + + 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 void playQueue() { + boolean playNext = true; + while (!filesToPlay.isEmpty() && playNext) { + String f = filesToPlay.remove(0); + if (f != null && voiceDir != null) { + File file = new File(voiceDir, f); + if (file.exists()) { + try { + mediaPlayer.setDataSource(file.getAbsolutePath()); + mediaPlayer.prepare(); + mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + public void onCompletion(MediaPlayer mp) { + 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) { + } + playQueue(); + } + }); + playNext = false; + mediaPlayer.start(); + } catch (Exception e) { + log.error("Error while playing voice command", e); //$NON-NLS-1$ + playNext = true; + + } + } + } + } + } + +} + + +