summaryrefslogtreecommitdiff
path: root/navit/android
diff options
context:
space:
mode:
authorlains <lains@caramail.com>2019-10-23 21:38:52 +0200
committerPierre GRANDIN <pgrandin@users.noreply.github.com>2019-10-23 13:38:52 -0600
commit7021575af7578fc54c46c61e13835941183b652f (patch)
tree91fc8eb36f65fd708e0486e490447d7b91dc8bb5 /navit/android
parent9ae8f5770b0fe310cec8f0da7b6bdf2a3920588c (diff)
downloadnavit-7021575af7578fc54c46c61e13835941183b652f.tar.gz
Add/Android Supporting sending & receiving geo: intents (#812)
Diffstat (limited to 'navit/android')
-rw-r--r--navit/android/AndroidManifest.xml7
-rw-r--r--navit/android/res/values/strings.xml1
-rw-r--r--navit/android/src/org/navitproject/navit/Navit.java190
-rw-r--r--navit/android/src/org/navitproject/navit/NavitGraphics.java96
4 files changed, 230 insertions, 64 deletions
diff --git a/navit/android/AndroidManifest.xml b/navit/android/AndroidManifest.xml
index e8f153fd4..2bcdb4e19 100644
--- a/navit/android/AndroidManifest.xml
+++ b/navit/android/AndroidManifest.xml
@@ -16,6 +16,8 @@
android:theme="@style/NavitTheme">
<activity android:name="Navit"
android:label="@string/app_name"
+ android:launchMode="singleTask"
+ android:taskAffinity=""
android:configChanges="screenLayout|smallestScreenSize|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|fontScale|screenSize"
android:windowSoftInputMode="adjustResize">
<intent-filter>
@@ -23,6 +25,11 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="geo"/>
+ </intent-filter>
+ <intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="google.navigation" />
diff --git a/navit/android/res/values/strings.xml b/navit/android/res/values/strings.xml
index 44ed1483b..5ce99e52a 100644
--- a/navit/android/res/values/strings.xml
+++ b/navit/android/res/values/strings.xml
@@ -31,6 +31,7 @@
<string name="position_popup_title">Position</string>
<string name="position_popup_drive_here">Route to here</string>
<string name="position_popup_view">View</string>
+ <string name="use_position_with">Use position with</string>
<!-- MAP DOWNLOAD -->
<string name="map_delete">Delete this map?</string>
diff --git a/navit/android/src/org/navitproject/navit/Navit.java b/navit/android/src/org/navitproject/navit/Navit.java
index 87ad5d9a8..2fa2f312d 100644
--- a/navit/android/src/org/navitproject/navit/Navit.java
+++ b/navit/android/src/org/navitproject/navit/Navit.java
@@ -71,14 +71,86 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
-
public class Navit extends Activity {
+ /**
+ * Nested class storing the intent that was sent to the main navit activity at startup.
+ **/
+ private class StartupIntent {
+ /**
+ * Constructor.
+ *
+ * @param intent The intent to store in this object
+ **/
+ public StartupIntent(Intent intent) {
+ mStartupIntent = intent;
+ mStartupIntentTimestamp = System.currentTimeMillis();
+ }
+
+ /**
+ * Check if the encapsulated intent still valid or too old.
+ *
+ * @return true if the encapsulated intent is recent enough
+ **/
+ public boolean isRecentEnough() {
+ if (mStartupIntent == null) {
+ return false;
+ }
+ /* We consider the intent is valid for 4s */
+ return (System.currentTimeMillis() <= getExpirationTimeMillis());
+ }
+
+ /**
+ * Compute the system time when the stored intent will become invalid.
+ *
+ * @return The system time for invalidation (in ms)
+ **/
+ private long getExpirationTimeMillis() {
+ if (mStartupIntent == null) {
+ return 0;
+ }
+ /* We give 4s to navit to process the intent */
+ return mStartupIntentTimestamp + 4000L;
+ }
+
+ /**
+ * Getter for the encapsulated intent.
+ *
+ * @return The encapsulated intent
+ **/
+ public Intent getIntent() {
+ return mStartupIntent;
+ }
+
+ /**
+ * Represent this object as a string.
+ *
+ * @return A string containing the summary of the data we store here
+ **/
+ public String toString() {
+ if (mStartupIntent == null) {
+ return "{null}";
+ } else {
+ String validForStr;
+ long remainingValidity = getExpirationTimeMillis() - System.currentTimeMillis();
+ if (remainingValidity < 0) {
+ validForStr = "(expired since " + -remainingValidity + "ms)";
+ } else {
+ validForStr = "(valid for " + remainingValidity + "ms)";
+ }
+ return "{ act=" + mStartupIntent.getAction() + " data=" + mStartupIntent.getDataString()
+ + " " + validForStr + " }";
+ }
+ }
+
+ private Intent mStartupIntent; /*!< The intent we store */
+ private long mStartupIntentTimestamp; /*!< A timestamp (in ms) for when mStartupIntent was recorded */
+ }
+
public static DisplayMetrics sMetrics;
public static boolean sShowSoftKeyboardShowing;
- private static Intent sStartupIntent;
- private static long sStartupIntentTimestamp;
+ private static StartupIntent sStartupIntent;
private static final int MY_PERMISSIONS_REQ_FINE_LOC = 103;
private static final int NavitDownloaderSelectMap_id = 967;
private static final int NavitAddressSearch_id = 70;
@@ -214,6 +286,9 @@ public class Navit extends Activity {
return true;
}
+ /**
+ * Show the first start infoxbox (presentation of navit and link website for more info).
+ **/
private void showInfos() {
SharedPreferences settings = getSharedPreferences(NavitAppConfig.NAVIT_PREFS, MODE_PRIVATE);
boolean firstStart = settings.getBoolean("firstStart", true);
@@ -260,22 +335,21 @@ public class Navit extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
+ Log.d(TAG, "OnCreate");
super.onCreate(savedInstanceState);
windowSetup();
mDialogs = new NavitDialogs(this);
- // only take arguments here, onResume gets called all the time (e.g. when screenblanks, etc.)
- Navit.sStartupIntent = this.getIntent();
- // hack! Remember time stamps, and only allow 4 secs. later in onResume to set target!
- Navit.sStartupIntentTimestamp = System.currentTimeMillis();
- Log.d(TAG, "**1**A " + sStartupIntent.getAction());
- Log.d(TAG, "**1**D " + sStartupIntent.getDataString());
+ /* Only store the startup intent, onResume() gets called all the time (e.g. when screenblanks, etc.) and
+ will process this intent later on if needed */
+ sStartupIntent = new StartupIntent(this.getIntent());
+ Log.d(TAG, "Recording intent " + sStartupIntent.toString());
createNotificationChannel();
buildNotification();
verifyPermissions();
- // get the local language -------------
+ // get the local language
Locale locale = Locale.getDefault();
String lang = locale.getLanguage();
String langc = lang;
@@ -426,6 +500,13 @@ public class Navit extends Activity {
}
@Override
+ public void onNewIntent(Intent intent) {
+ Log.d(TAG, "OnNewIntent");
+ sStartupIntent = new StartupIntent(intent);
+ Log.d(TAG, "Recording intent " + sStartupIntent.toString());
+ }
+
+ @Override
public void onResume() {
super.onResume();
Log.d(TAG, "OnResume");
@@ -435,24 +516,33 @@ public class Navit extends Activity {
// intent_data = "google.navigation:q=48.25676,16.643";
// intent_data = "google.navigation:ll=48.25676,16.643&q=blabla-strasse";
// intent_data = "google.navigation:ll=48.25676,16.643";
+ // intent_data = "geo:48.25676,16.643";
if (sStartupIntent != null) {
- if (System.currentTimeMillis() <= Navit.sStartupIntentTimestamp + 4000L) {
- Log.d(TAG, "**2**A " + sStartupIntent.getAction());
- Log.d(TAG, "**2**D " + sStartupIntent.getDataString());
- String naviScheme = sStartupIntent.getScheme();
- if (naviScheme != null && naviScheme.equals("google.navigation")) {
- parseNavigationURI(sStartupIntent.getData().getSchemeSpecificPart());
+ Log.d(TAG, "Using stored startup intent " + sStartupIntent.toString());
+ if (sStartupIntent.isRecentEnough()) {
+ Intent startupIntent = sStartupIntent.getIntent();
+ String naviScheme = startupIntent.getScheme();
+ if (naviScheme != null) {
+ if (naviScheme.equals("google.navigation")) {
+ parseNavigationURI(startupIntent.getData().getSchemeSpecificPart());
+ } else if (naviScheme.equals("geo")
+ && startupIntent.getAction().equals("android.intent.action.VIEW")) {
+ invokeCallbackOnGeo(startupIntent.getData().getSchemeSpecificPart(),
+ NavitGraphics.MsgType.CLB_SET_DESTINATION,
+ "");
+ }
}
} else {
- Log.e(TAG, "timestamp for navigate_to expired! not using data");
+ Log.e(TAG, "timestamp for startup intent expired! not using data");
}
+ sStartupIntent = null;
}
}
@Override
public void onPause() {
super.onPause();
- Log.d(TAG, "onPause");
+ Log.d(TAG, "OnPause");
}
@Override
@@ -476,6 +566,42 @@ public class Navit extends Activity {
}
}
+ /**
+ * Invoke NavitGraphics.sCallbackHandler on a geographical position
+ *
+ * @param geoString A string containing the target geographical position with a format like "48.25676,16.643"
+ * @param msgType The type of message to send to the callback (see NavitGraphics.MsgType for possible values)
+ * @param name The name/label to associate to the geographical position
+ **/
+ private void invokeCallbackOnGeo(String geoString, NavitGraphics.MsgType msgType, String name) {
+ String[] geo = geoString.split(",");
+ if (geo.length == 2) {
+ try {
+ Bundle b = new Bundle();
+ Float lat = Float.valueOf(geo[0]);
+ Float lon = Float.valueOf(geo[1]);
+ b.putFloat("lat", lat);
+ b.putFloat("lon", lon);
+ b.putString("q", name);
+ Message msg = Message.obtain(NavitGraphics.sCallbackHandler,
+ msgType.ordinal());
+
+ msg.setData(b);
+ msg.sendToTarget();
+ Log.d(TAG, "target found (b): " + geoString);
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ }
+ } else {
+ Log.w(TAG, "Ignoring invalid geo string: " + geoString);
+ }
+ }
+
+ /**
+ * Parse google navigation URIs (usually starting with "google.navigation:") and take the appropriate actions
+ *
+ * @param schemeSpecificPart A string containing the URI scheme, for example "ll=48.25676,16.643&q=blabla-strasse"
+ **/
private void parseNavigationURI(String schemeSpecificPart) {
String[] naviData = schemeSpecificPart.split("&");
Pattern p = Pattern.compile("(.*)=(.*)");
@@ -492,39 +618,17 @@ public class Navit extends Activity {
// c: google.navigation:ll=48.25676,16.643
// b: google.navigation:q=48.25676,16.643
- float lat;
- float lon;
- Bundle b = new Bundle();
-
String geoString = params.get("ll");
+ String address = null;
if (geoString != null) {
- String address = params.get("q");
- if (address != null) {
- b.putString("q", address);
- }
+ address = params.get("q");
} else {
geoString = params.get("q");
}
if (geoString != null) {
if (geoString.matches("^[+-]{0,1}\\d+(|\\.\\d*),[+-]{0,1}\\d+(|\\.\\d*)$")) {
- String[] geo = geoString.split(",");
- if (geo.length == 2) {
- try {
- lat = Float.valueOf(geo[0]);
- lon = Float.valueOf(geo[1]);
- b.putFloat("lat", lat);
- b.putFloat("lon", lon);
- Message msg = Message.obtain(NavitGraphics.sCallbackHandler,
- NavitGraphics.MsgType.CLB_SET_DESTINATION.ordinal());
-
- msg.setData(b);
- msg.sendToTarget();
- Log.i(TAG, "target found (b): " + geoString);
- } catch (NumberFormatException e) {
- e.printStackTrace();
- }
- }
+ invokeCallbackOnGeo(geoString, NavitGraphics.MsgType.CLB_SET_DESTINATION, address);
} else {
start_targetsearch_from_intent(geoString);
}
diff --git a/navit/android/src/org/navitproject/navit/NavitGraphics.java b/navit/android/src/org/navitproject/navit/NavitGraphics.java
index 6aee53cf5..cd6ce6944 100644
--- a/navit/android/src/org/navitproject/navit/NavitGraphics.java
+++ b/navit/android/src/org/navitproject/navit/NavitGraphics.java
@@ -41,6 +41,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
+import android.os.Parcelable;
import android.support.annotation.RequiresApi;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.Log;
@@ -151,6 +152,9 @@ class NavitGraphics {
static final int ZOOM = 2;
static final int PRESSED = 3;
PointF mPressedPosition = null;
+ // mContextMenuMapViewIntent is the ACTION_VIEW intent for a geo coordinates.
+ // it is used when clicking on view in the map contextual menu
+ Intent mContextMenuMapViewIntent = null;
NavitView(Context context) {
super(context);
@@ -197,6 +201,56 @@ class NavitGraphics {
return insets;
}
+ /**
+ * Create an intent for a view action of a point provided by its x and y position on the display.
+ *
+ * @param x The x coordinates of the point on the display
+ * @param y The y coordinates of the point on the display
+ *
+ * @return An intent to start to view the specified point on a third-party app on Android (can be null if a
+ * view action is not possible)
+ **/
+ protected Intent getViewIntentForDisplayPoint(int x, int y) {
+ Intent result = null;
+
+ /* Check if there is at least one application that can process a geo intent... */
+ String selectedPointCoord = getCoordForPoint(x, y, true);
+ Uri intentUri = Uri.parse("geo:" + selectedPointCoord);
+ Intent defaultShareIntent = new Intent(Intent.ACTION_VIEW, intentUri);
+
+ List<Intent> customShareIntentList = new ArrayList<Intent>();
+ List<ResolveInfo> intentTargetAppList;
+ intentTargetAppList = this.getContext().getPackageManager().queryIntentActivities(defaultShareIntent, 0);
+
+ String selfPackageName = this.getContext().getPackageName(); /* aka: "org.navitproject.navit" */
+
+ if (!intentTargetAppList.isEmpty()) {
+ for (ResolveInfo resolveInfo : intentTargetAppList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ Intent copiedIntent = new Intent(Intent.ACTION_VIEW, intentUri);
+ if (!packageName.equals(selfPackageName)) {
+ Log.d(TAG, "Adding package \"" + packageName + "\" to app chooser");
+ copiedIntent.setPackage(packageName);
+ copiedIntent.setClassName(
+ resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name);
+ customShareIntentList.add(copiedIntent);
+ } else {
+ Log.d(TAG, "Excluding ourselves (package " + packageName + ") from intent targets");
+ }
+ }
+ if (customShareIntentList.size() > 0) {
+ result = Intent.createChooser(customShareIntentList.remove(customShareIntentList.size() - 1),
+ NavitAppConfig.getTstring(R.string.use_position_with));
+ result.putExtra(Intent.EXTRA_INITIAL_INTENTS,
+ customShareIntentList.toArray(new Parcelable[customShareIntentList.size()]));
+ Log.d(TAG, "Preparing action intent (" + customShareIntentList.size() + 1
+ + " candidate apps) to view selected coord: " + selectedPointCoord);
+ }
+ }
+ return result;
+ }
+
private static final int MENU_DRIVE_HERE = 1;
private static final int MENU_VIEW = 2;
private static final int MENU_CANCEL = 3;
@@ -208,39 +262,39 @@ class NavitGraphics {
menu.setHeaderTitle(NavitAppConfig.getTstring(R.string.position_popup_title) + " " + clickCoord);
menu.add(1, MENU_DRIVE_HERE, NONE, NavitAppConfig.getTstring(R.string.position_popup_drive_here))
.setOnMenuItemClickListener(this);
- Uri intentUri = Uri.parse("geo:" + getCoordForPoint((int)mPressedPosition.x,
- (int)mPressedPosition.y, true));
- Intent mContextMenuMapViewIntent = new Intent(Intent.ACTION_VIEW, intentUri);
-
- PackageManager packageManager = this.getContext().getPackageManager();
- List<ResolveInfo> activities = packageManager.queryIntentActivities(mContextMenuMapViewIntent,
- PackageManager.MATCH_DEFAULT_ONLY);
- boolean isIntentSafe = (activities.size() > 0); // at least one candidate receiver
- if (isIntentSafe) { // add view with external app option
- menu.add(1, MENU_VIEW, NONE, NavitAppConfig.getTstring(R.string.position_popup_view))
- .setOnMenuItemClickListener(this);
+ mContextMenuMapViewIntent = getViewIntentForDisplayPoint((int)mPressedPosition.x, (int)mPressedPosition.y);
+ if (mContextMenuMapViewIntent != null) {
+ menu.add(1, MENU_VIEW, NONE,
+ NavitAppConfig.getTstring(R.string.position_popup_view)).setOnMenuItemClickListener(this);
} else {
- Log.w(TAG, "No application available to handle ACTION_VIEW intent, option not displayed");
+ Log.w(TAG, "No application available to handle ACTION_VIEW intent, view option not displayed");
}
- menu.add(1, MENU_CANCEL, NONE, getTstring(R.string.cancel)).setOnMenuItemClickListener(this);
+ menu.add(1, MENU_CANCEL, NONE,
+ NavitAppConfig.getTstring(R.string.cancel)).setOnMenuItemClickListener(this);
}
@Override
public boolean onMenuItemClick(MenuItem item) {
int itemId = item.getItemId();
+ if (itemId != MENU_VIEW) {
+ /* Destroy any previous map view intent if the user didn't select the MENU_VIEW action */
+ mContextMenuMapViewIntent = null;
+ }
if (itemId == MENU_DRIVE_HERE) {
Message msg = Message.obtain(sCallbackHandler, MsgType.CLB_SET_DISPLAY_DESTINATION.ordinal(),
(int) mPressedPosition.x, (int) mPressedPosition.y);
msg.sendToTarget();
} else if (itemId == MENU_VIEW) {
- Uri intentUri = Uri.parse("geo:" + getCoordForPoint((int) mPressedPosition.x,
- (int) mPressedPosition.y, true));
- Intent mContextMenuMapViewIntent = new Intent(Intent.ACTION_VIEW, intentUri);
- mContextMenuMapViewIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- if (mContextMenuMapViewIntent.resolveActivity(this.getContext().getPackageManager()) != null) {
- this.getContext().startActivity(mContextMenuMapViewIntent);
+ if (mContextMenuMapViewIntent != null) {
+ mContextMenuMapViewIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ if (mContextMenuMapViewIntent.resolveActivity(this.getContext().getPackageManager()) != null) {
+ this.getContext().startActivity(mContextMenuMapViewIntent);
+ } else {
+ Log.w(TAG, "View menu selected but intent is not handled by any application. Ignoring...");
+ }
+ mContextMenuMapViewIntent = null; /* Destoy the intent once it has been used */
} else {
- Log.w(TAG, "ACTION_VIEW intent is not handled by any application, discarding...");
+ Log.e(TAG, "User clicked on view on menu but intent was null. Discarding...");
}
}
return true;
@@ -669,7 +723,7 @@ class NavitGraphics {
private native void motionCallback(long id, int x, int y);
- private native String getCoordForPoint(int x, int y, boolean absolutCoord);
+ private native String getCoordForPoint(int x, int y, boolean absoluteCoord);
static native String[][] getAllCountries();