2 * Navit, a modular navigation system.
\r
3 * Copyright (C) 2005-2008 Navit Team
\r
5 * This program is free software; you can redistribute it and/or
\r
6 * modify it under the terms of the GNU General Public License
\r
7 * version 2 as published by the Free Software Foundation.
\r
9 * This program is distributed in the hope that it will be useful,
\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 * GNU General Public License for more details.
\r
14 * You should have received a copy of the GNU General Public License
\r
15 * along with this program; if not, write to the
\r
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
\r
17 * Boston, MA 02110-1301, USA.
\r
20 package org.navitproject.navit;
\r
22 import java.io.File;
\r
23 import java.io.FileOutputStream;
\r
24 import java.io.InputStream;
\r
25 import java.util.HashMap;
\r
26 import java.util.Locale;
\r
27 import java.util.Map;
\r
28 import java.util.regex.Matcher;
\r
29 import java.util.regex.Pattern;
\r
31 import android.app.Activity;
\r
32 import android.app.AlertDialog;
\r
33 import android.app.Dialog;
\r
34 import android.app.Notification;
\r
35 import android.app.NotificationManager;
\r
36 import android.app.PendingIntent;
\r
37 import android.content.Context;
\r
38 import android.content.DialogInterface;
\r
39 import android.content.Intent;
\r
40 import android.content.SharedPreferences;
\r
41 import android.content.pm.ApplicationInfo;
\r
42 import android.content.pm.PackageManager;
\r
43 import android.content.pm.PackageManager.NameNotFoundException;
\r
44 import android.content.res.Resources;
\r
45 import android.media.AudioManager;
\r
46 import android.net.Uri;
\r
47 import android.os.Bundle;
\r
48 import android.os.Message;
\r
49 import android.os.PowerManager;
\r
50 import android.text.SpannableString;
\r
51 import android.text.method.LinkMovementMethod;
\r
52 import android.text.util.Linkify;
\r
53 import android.util.DisplayMetrics;
\r
54 import android.util.Log;
\r
55 import android.view.Display;
\r
56 import android.view.Menu;
\r
57 import android.view.MenuItem;
\r
58 import android.view.inputmethod.InputMethodManager;
\r
59 import android.widget.RelativeLayout;
\r
60 import android.widget.TextView;
\r
61 import android.widget.Toast;
\r
64 public class Navit extends Activity
\r
67 public NavitDialogs dialogs;
\r
68 private PowerManager.WakeLock wl;
\r
69 private NavitActivityResult ActivityResults[];
\r
70 public static InputMethodManager mgr = null;
\r
71 public static DisplayMetrics metrics = null;
\r
72 public static Boolean show_soft_keyboard = false;
\r
73 public static Boolean show_soft_keyboard_now_showing = false;
\r
74 public static long last_pressed_menu_key = 0L;
\r
75 public static long time_pressed_menu_key = 0L;
\r
76 private static Intent startup_intent = null;
\r
77 private static long startup_intent_timestamp = 0L;
\r
78 public static String my_display_density = "mdpi";
\r
79 public static final int NavitDownloaderSelectMap_id = 967;
\r
80 public static final int MAP_NUM_PRIMARY = 11;
\r
81 public static final int NavitAddressSearch_id = 70;
\r
82 public static String NavitLanguage;
\r
83 public static Resources NavitResources = null;
\r
85 public static final int MAP_NUM_SECONDARY = 12;
\r
86 static final String NAVIT_PACKAGE_NAME = "org.navitproject.navit";
\r
87 static final String TAG = "Navit";
\r
88 static final String MAP_FILENAME_PATH = "/sdcard/navit/";
\r
89 static final String NAVIT_DATA_DIR = "/data/data/" + NAVIT_PACKAGE_NAME;
\r
90 static final String NAVIT_DATA_SHARE_DIR = NAVIT_DATA_DIR + "/share";
\r
91 static final String FIRST_STARTUP_FILE = NAVIT_DATA_SHARE_DIR + "/has_run_once.txt";
\r
92 public static final String NAVIT_PREFS = "NavitPrefs";
\r
94 public static String get_text(String in)
\r
96 return NavitTextTranslations.get_text(in);
\r
99 private boolean extractRes(String resname, String result) {
\r
100 boolean needs_update = false;
\r
101 Log.e(TAG, "Res Name " + resname + ", result " + result);
\r
102 int id = NavitResources.getIdentifier(resname, "raw", NAVIT_PACKAGE_NAME);
\r
103 Log.e(TAG, "Res ID " + id);
\r
107 File resultfile = new File(result);
\r
108 if (!resultfile.exists()) {
\r
109 needs_update = true;
\r
110 File path = resultfile.getParentFile();
\r
111 if ( !path.exists() && !resultfile.getParentFile().mkdirs())
\r
114 PackageManager pm = getPackageManager();
\r
115 ApplicationInfo appInfo;
\r
116 long apkUpdateTime = 0;
\r
118 appInfo = pm.getApplicationInfo(NAVIT_PACKAGE_NAME, 0);
\r
119 apkUpdateTime = new File(appInfo.sourceDir).lastModified();
\r
120 } catch (NameNotFoundException e) {
\r
121 Log.e(TAG, "Could not read package infos");
\r
122 e.printStackTrace();
\r
124 if (apkUpdateTime > resultfile.lastModified())
\r
125 needs_update = true;
\r
128 if (needs_update) {
\r
129 Log.e(TAG, "Extracting resource");
\r
132 InputStream resourcestream = NavitResources.openRawResource(id);
\r
133 FileOutputStream resultfilestream = new FileOutputStream(resultfile);
\r
134 byte[] buf = new byte[1024];
\r
136 while ((i = resourcestream.read(buf)) != -1) {
\r
137 resultfilestream.write(buf, 0, i);
\r
139 } catch (Exception e) {
\r
140 Log.e(TAG, "Exception " + e.getMessage());
\r
147 private void showInfos()
\r
149 SharedPreferences settings = getSharedPreferences(NAVIT_PREFS, MODE_PRIVATE);
\r
150 boolean firstStart = settings.getBoolean("firstStart", true);
\r
154 AlertDialog.Builder infobox = new AlertDialog.Builder(this);
\r
155 infobox.setTitle(getString(R.string.initial_info_box_title)); // TRANS
\r
156 infobox.setCancelable(false);
\r
157 final TextView message = new TextView(this);
\r
158 message.setFadingEdgeLength(20);
\r
159 message.setVerticalFadingEdgeEnabled(true);
\r
160 // message.setVerticalScrollBarEnabled(true);
\r
161 RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
\r
163 message.setLayoutParams(rlp);
\r
164 final SpannableString s = new SpannableString(getString(R.string.initial_info_box_message)); // TRANS
\r
165 Linkify.addLinks(s, Linkify.WEB_URLS);
\r
166 message.setText(s);
\r
167 message.setMovementMethod(LinkMovementMethod.getInstance());
\r
168 infobox.setView(message);
\r
171 infobox.setPositiveButton(getString(R.string.initial_info_box_OK), new DialogInterface.OnClickListener() {
\r
172 public void onClick(DialogInterface arg0, int arg1) {
\r
173 Log.e("Navit", "Ok, user saw the infobox");
\r
178 infobox.setNeutralButton(getString(R.string.initial_info_box_more_info), new DialogInterface.OnClickListener() {
\r
179 public void onClick(DialogInterface arg0, int arg1) {
\r
180 Log.e("Navit", "user wants more info, show the website");
\r
181 String url = "http://wiki.navit-project.org/index.php/Navit_on_Android";
\r
182 Intent i = new Intent(Intent.ACTION_VIEW);
\r
183 i.setData(Uri.parse(url));
\r
188 SharedPreferences.Editor edit_settings = settings.edit();
\r
189 edit_settings.putBoolean("firstStart", false);
\r
190 edit_settings.commit();
\r
194 /** Called when the activity is first created. */
\r
196 public void onCreate(Bundle savedInstanceState)
\r
198 super.onCreate(savedInstanceState);
\r
200 dialogs = new NavitDialogs(this);
\r
202 NavitResources = getResources();
\r
204 // only take arguments here, onResume gets called all the time (e.g. when screenblanks, etc.)
\r
205 Navit.startup_intent = this.getIntent();
\r
206 // hack! Remember time stamps, and only allow 4 secs. later in onResume to set target!
\r
207 Navit.startup_intent_timestamp = System.currentTimeMillis();
\r
208 Log.e("Navit", "**1**A " + startup_intent.getAction());
\r
209 Log.e("Navit", "**1**D " + startup_intent.getDataString());
\r
211 // init translated text
\r
212 NavitTextTranslations.init();
\r
215 // Setup the status bar notification
\r
216 // This notification is removed in the exit() function
\r
217 NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // Grab a handle to the NotificationManager
\r
218 Notification NavitNotification = new Notification(R.drawable.icon,"Navit started",System.currentTimeMillis()); // Create a new notification, with the text string to show when the notification first appears
\r
219 PendingIntent appIntent = PendingIntent.getActivity(getApplicationContext(), 0, getIntent(), 0);
\r
220 NavitNotification.setLatestEventInfo(getApplicationContext(), "Navit", "Navit running", appIntent); // Set the text in the notification
\r
221 NavitNotification.flags|=Notification.FLAG_ONGOING_EVENT; // Ensure that the notification appears in Ongoing
\r
222 nm.notify(R.string.app_name, NavitNotification); // Set the notification
\r
224 // get the local language -------------
\r
225 Locale locale = java.util.Locale.getDefault();
\r
226 String lang = locale.getLanguage();
\r
227 String langu = lang;
\r
228 String langc = lang;
\r
229 Log.e("Navit", "lang=" + lang);
\r
230 int pos = langu.indexOf('_');
\r
233 langc = langu.substring(0, pos);
\r
234 NavitLanguage = langc + langu.substring(pos).toUpperCase(locale);
\r
235 Log.e("Navit", "substring lang " + NavitLanguage.substring(pos).toUpperCase(locale));
\r
236 // set lang. for translation
\r
237 NavitTextTranslations.main_language = langc;
\r
238 NavitTextTranslations.sub_language = NavitLanguage.substring(pos).toUpperCase(locale);
\r
242 String country = locale.getCountry();
\r
243 Log.e("Navit", "Country1 " + country);
\r
244 Log.e("Navit", "Country2 " + country.toUpperCase(locale));
\r
245 NavitLanguage = langc + "_" + country.toUpperCase(locale);
\r
246 // set lang. for translation
\r
247 NavitTextTranslations.main_language = langc;
\r
248 NavitTextTranslations.sub_language = country.toUpperCase(locale);
\r
250 Log.e("Navit", "Language " + lang);
\r
252 // make sure the new path for the navitmap.bin file(s) exist!!
\r
253 File navit_maps_dir = new File(MAP_FILENAME_PATH);
\r
254 navit_maps_dir.mkdirs();
\r
256 // make sure the share dir exists
\r
257 File navit_data_share_dir = new File(NAVIT_DATA_SHARE_DIR);
\r
258 navit_data_share_dir.mkdirs();
\r
260 Display display_ = getWindowManager().getDefaultDisplay();
\r
261 int width_ = display_.getWidth();
\r
262 int height_ = display_.getHeight();
\r
263 metrics = new DisplayMetrics();
\r
264 display_.getMetrics(Navit.metrics);
\r
265 int densityDpi = (int)(( Navit.metrics.density*160)+.5f);
\r
266 Log.e("Navit", "Navit -> pixels x=" + width_ + " pixels y=" + height_);
\r
267 Log.e("Navit", "Navit -> dpi=" + densityDpi);
\r
268 Log.e("Navit", "Navit -> density=" + Navit.metrics.density);
\r
269 Log.e("Navit", "Navit -> scaledDensity=" + Navit.metrics.scaledDensity);
\r
271 ActivityResults = new NavitActivityResult[16];
\r
272 setVolumeControlStream(AudioManager.STREAM_MUSIC);
\r
273 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
\r
274 wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,"NavitDoNotDimScreen");
\r
276 if (!extractRes(langc, NAVIT_DATA_DIR + "/locale/" + langc + "/LC_MESSAGES/navit.mo"))
\r
278 Log.e("Navit", "Failed to extract language resource " + langc);
\r
281 if (densityDpi <= 120)
\r
283 my_display_density = "ldpi";
\r
285 else if (densityDpi <= 160)
\r
287 my_display_density = "mdpi";
\r
289 else if (densityDpi < 320)
\r
291 my_display_density = "hdpi";
\r
295 Log.e("Navit", "found xhdpi device, this is not fully supported!!");
\r
296 Log.e("Navit", "using hdpi values");
\r
297 my_display_density = "hdpi";
\r
300 if (!extractRes("navit" + my_display_density, NAVIT_DATA_DIR + "/share/navit.xml"))
\r
302 Log.e("Navit", "Failed to extract navit.xml for " + my_display_density);
\r
305 // --> dont use android.os.Build.VERSION.SDK_INT, needs API >= 4
\r
306 Log.e("Navit", "android.os.Build.VERSION.SDK_INT=" + Integer.valueOf(android.os.Build.VERSION.SDK));
\r
307 NavitMain(this, NavitLanguage, Integer.valueOf(android.os.Build.VERSION.SDK), my_display_density, NAVIT_DATA_DIR+"/bin/navit");
\r
311 Navit.mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
\r
315 public void onResume()
\r
318 Log.e("Navit", "OnResume");
\r
319 //InputMethodManager mgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
\r
321 // intent_data = "google.navigation:q=Wien Burggasse 27";
\r
322 // intent_data = "google.navigation:q=48.25676,16.643";
\r
323 // intent_data = "google.navigation:ll=48.25676,16.643&q=blabla-strasse";
\r
324 // intent_data = "google.navigation:ll=48.25676,16.643";
\r
325 if (startup_intent != null)
\r
327 if (System.currentTimeMillis() <= Navit.startup_intent_timestamp + 4000L)
\r
329 Log.e("Navit", "**2**A " + startup_intent.getAction());
\r
330 Log.e("Navit", "**2**D " + startup_intent.getDataString());
\r
331 String navi_scheme = startup_intent.getScheme();
\r
332 if ( navi_scheme != null && navi_scheme.equals("google.navigation")) {
\r
333 parseNavigationURI(startup_intent.getData().getSchemeSpecificPart());
\r
337 Log.e("Navit", "timestamp for navigate_to expired! not using data");
\r
342 private void parseNavigationURI(String schemeSpecificPart) {
\r
343 String naviData[]= schemeSpecificPart.split("&");
\r
344 Pattern p = Pattern.compile("(.*)=(.*)");
\r
345 Map<String,String> params = new HashMap<String,String>();
\r
346 for (int count=0; count < naviData.length; count++) {
\r
347 Matcher m = p.matcher(naviData[count]);
\r
350 params.put(m.group(1), m.group(2));
\r
354 // d: google.navigation:q=blabla-strasse # (this happens when you are offline, or from contacts)
\r
355 // a: google.navigation:ll=48.25676,16.643&q=blabla-strasse
\r
356 // c: google.navigation:ll=48.25676,16.643
\r
357 // b: google.navigation:q=48.25676,16.643
\r
361 Bundle b = new Bundle();
\r
363 String geoString = params.get("ll");
\r
364 if (geoString != null) {
\r
365 String address = params.get("q");
\r
366 if (address != null) b.putString("q", address);
\r
369 geoString = params.get("q");
\r
372 if ( geoString != null) {
\r
373 if (geoString.matches("^[+-]{0,1}\\d+(|\\.\\d*),[+-]{0,1}\\d+(|\\.\\d*)$")) {
\r
374 String geo[] = geoString.split(",");
\r
375 if (geo.length == 2) {
\r
377 lat = Float.valueOf(geo[0]);
\r
378 lon = Float.valueOf(geo[1]);
\r
379 b.putFloat("lat", lat);
\r
380 b.putFloat("lon", lon);
\r
381 Message msg = Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_SET_DESTINATION.ordinal());
\r
384 msg.sendToTarget();
\r
385 Log.e("Navit", "target found (b): " + geoString);
\r
386 } catch (NumberFormatException e) { } // nothing to do here
\r
390 start_targetsearch_from_intent(geoString);
\r
395 public void setActivityResult(int requestCode, NavitActivityResult ActivityResult)
\r
397 //Log.e("Navit", "setActivityResult " + requestCode);
\r
398 ActivityResults[requestCode] = ActivityResult;
\r
402 public boolean onPrepareOptionsMenu(Menu menu)
\r
404 super.onPrepareOptionsMenu(menu);
\r
405 //Log.e("Navit","onPrepareOptionsMenu");
\r
406 // this gets called every time the menu is opened!!
\r
407 // change menu items here!
\r
410 // group-id,item-id,sort order number
\r
411 menu.add(1, 1, 100, getString(R.string.optionsmenu_zoom_in)); //TRANS
\r
412 menu.add(1, 2, 200, getString(R.string.optionsmenu_zoom_out)); //TRANS
\r
414 menu.add(1, 3, 300, getString(R.string.optionsmenu_download_maps)); //TRANS
\r
415 menu.add(1, 5, 400, getString(R.string.optionsmenu_toggle_poi)); //TRANS
\r
417 menu.add(1, 6, 500, getString(R.string.optionsmenu_address_search)); //TRANS
\r
419 menu.add(1, 99, 900, getString(R.string.optionsmenu_exit_navit)); //TRANS
\r
423 // define callback id here
\r
424 public static NavitGraphics N_NavitGraphics = null;
\r
426 // callback id gets set here when called from NavitGraphics
\r
427 public static void setKeypressCallback(int kp_cb_id, NavitGraphics ng)
\r
429 //Log.e("Navit", "setKeypressCallback -> id1=" + kp_cb_id);
\r
430 //Log.e("Navit", "setKeypressCallback -> ng=" + String.valueOf(ng));
\r
431 //N_KeypressCallbackID = kp_cb_id;
\r
432 N_NavitGraphics = ng;
\r
435 public static void setMotionCallback(int mo_cb_id, NavitGraphics ng)
\r
437 //Log.e("Navit", "setKeypressCallback -> id2=" + mo_cb_id);
\r
438 //Log.e("Navit", "setKeypressCallback -> ng=" + String.valueOf(ng));
\r
439 //N_MotionCallbackID = mo_cb_id;
\r
440 N_NavitGraphics = ng;
\r
443 public void start_targetsearch_from_intent(String target_address)
\r
445 if (target_address == null || target_address.equals(""))
\r
447 // empty search string entered
\r
448 Toast.makeText(getApplicationContext(), getString(R.string.address_search_not_found), Toast.LENGTH_LONG).show(); //TRANS
\r
452 Intent search_intent = new Intent(this, NavitAddressSearchActivity.class);
\r
453 search_intent.putExtra("search_string", target_address);
\r
454 this.startActivityForResult(search_intent, NavitAddressSearch_id);
\r
459 public boolean onOptionsItemSelected(MenuItem item)
\r
461 // Handle item selection
\r
462 switch (item.getItemId())
\r
466 Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_ZOOM_IN.ordinal()).sendToTarget();
\r
467 // if we zoom, hide the bubble
\r
468 Log.e("Navit", "onOptionsItemSelected -> zoom in");
\r
472 Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_ZOOM_OUT.ordinal()).sendToTarget();
\r
473 // if we zoom, hide the bubble
\r
474 Log.e("Navit", "onOptionsItemSelected -> zoom out");
\r
477 // map download menu
\r
478 Intent map_download_list_activity = new Intent(this, NavitDownloadSelectMapActivity.class);
\r
479 startActivityForResult(map_download_list_activity, Navit.NavitDownloaderSelectMap_id);
\r
482 // toggle the normal POI layers (to avoid double POIs)
\r
483 Message msg = Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_CALL_CMD.ordinal());
\r
484 Bundle b = new Bundle();
\r
485 b.putString("cmd", "toggle_layer(\"POI Symbols\");");
\r
487 msg.sendToTarget();
\r
489 // toggle full POI icons on/off
\r
490 msg = Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_CALL_CMD.ordinal());
\r
492 b.putString("cmd", "toggle_layer(\"Android-POI-Icons-full\");");
\r
494 msg.sendToTarget();
\r
498 // ok startup address search activity
\r
499 Intent search_intent = new Intent(this, NavitAddressSearchActivity.class);
\r
500 this.startActivityForResult(search_intent, NavitAddressSearch_id);
\r
511 void setDestination(float latitude, float longitude, String address) {
\r
512 Toast.makeText( getApplicationContext(),getString(R.string.address_search_set_destination) + "\n" + address, Toast.LENGTH_LONG).show(); //TRANS
\r
514 Message msg = Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_SET_DESTINATION.ordinal());
\r
515 Bundle b = new Bundle();
\r
516 b.putFloat("lat", latitude);
\r
517 b.putFloat("lon", longitude);
\r
518 b.putString("q", address);
\r
520 msg.sendToTarget();
\r
523 protected void onActivityResult(int requestCode, int resultCode, Intent data)
\r
525 switch (requestCode)
\r
527 case Navit.NavitDownloaderSelectMap_id :
\r
528 if (resultCode == Activity.RESULT_OK)
\r
530 Message msg = dialogs.obtainMessage(NavitDialogs.MSG_START_MAP_DOWNLOAD
\r
531 , data.getIntExtra("map_index", -1), 0);
\r
532 msg.sendToTarget();
\r
535 case NavitAddressSearch_id :
\r
536 if (resultCode == Activity.RESULT_OK) {
\r
537 Bundle destination = data.getExtras();
\r
538 Toast.makeText( getApplicationContext(),getString(R.string.address_search_set_destination) + "\n" + destination.getString("q"), Toast.LENGTH_LONG).show(); //TRANS
\r
540 Message msg = Message.obtain(N_NavitGraphics.callback_handler, NavitGraphics.msg_type.CLB_SET_DESTINATION.ordinal());
\r
541 msg.setData(destination);
\r
542 msg.sendToTarget();
\r
546 //Log.e("Navit", "onActivityResult " + requestCode + " " + resultCode);
\r
547 ActivityResults[requestCode].onActivityResult(requestCode, resultCode, data);
\r
552 protected Dialog onCreateDialog(int id)
\r
554 return dialogs.createDialog(id);
\r
558 public void onDestroy()
\r
561 Log.e("Navit", "OnDestroy");
\r
562 // TODO next call will kill our app the hard way. This should not be necessary, but ensures navit is
\r
563 // properly restarted and no resources are wasted with navit in background. Remove this call after
\r
568 public void disableSuspend()
\r
576 NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
\r
577 nm.cancel(R.string.app_name);
\r
578 NavitVehicle.removeListener();
\r
582 public native void NavitMain(Navit x, String lang, int version, String display_density_string, String path);
\r
583 public native void NavitDestroy();
\r
586 * this is used to load the 'navit' native library on
\r
587 * application startup. The library has already been unpacked at
\r
588 * installation time by the package manager.
\r
592 System.loadLibrary("navit");
\r