The mission is what the title says. It was quite hard to define the problem to search for a solution. I ended up asking for help at StackOverflow and as I was suggested, I implemented a way to list some known apps and let the user select a preferred nav app.
So, let's say, somewhere on Activity there's a button, which on click is supposed to call the Navigation application. The way I did it, on tap, the activity shows a custom dialog letting the user select one of the known applications or prompt Android to find suitable activities implicitly.
Here's the source code of the payload of the main method invoked from the OnClickListener - showNavigation():
Here are few utils needed only to follow up the last call in the showNavigation() method - showTableDialog():
These are the resource files:
* this goes to /res/values/themes.xml
* this goes to /res/values/styles.xml
* this goes to /res/layout/table_dialog.xml
* this goes to /res/drawable as menu_gps.png

And here is the result dialog you are supposed to see:
On click on each row it will start up the respective activity.
That's it, I hope it helps!
So, let's say, somewhere on Activity there's a button, which on click is supposed to call the Navigation application. The way I did it, on tap, the activity shows a custom dialog letting the user select one of the known applications or prompt Android to find suitable activities implicitly.
Here's the source code of the payload of the main method invoked from the OnClickListener - showNavigation():
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
final int defaultFlag = PackageManager.MATCH_DEFAULT_ONLY; | |
Intent[] explicitIntents; | |
//see an example here http://stackoverflow.com/questions/2662531/launching-google-maps-directions-via-an-intent-on-android | |
private Intent[] getExplicitIntents() { | |
if(explicitIntents == null) { | |
PackageManager currentPM = getPackageManager(); | |
explicitIntents = new Intent[]{ | |
new Intent("android.intent.action.navigon.START_PUBLIC"), //navigon with public intent | |
currentPM.getLaunchIntentForPackage("com.navigon.navigator"), //navigon without public intent | |
currentPM.getLaunchIntentForPackage("hr.mireo.dp"), //ginius driver dont panic | |
currentPM.getLaunchIntentForPackage("com.ndrive.android"), //ndrive | |
currentPM.getLaunchIntentForPackage("com.sygic.aura"), //aura | |
currentPM.getLaunchIntentForPackage("org.microemu.android.se.appello.lp.Lightpilot") // wisepilot | |
}; | |
} | |
return explicitIntents; | |
} | |
/** | |
* Following the suggestion at http://stackoverflow.com/questions/5801684/intent-to-start-a-navigation-activity/5809637#5809637 | |
* Will query up the system for desired applications and then let the user decide which one they would like to launch. | |
*/ | |
protected void showNavigation() { | |
//explicit activities (known apps) | |
//build the list and show it in a dialog | |
int titleColor = Color.rgb(28, 97, 211); | |
int headerColor = Color.rgb(98, 151, 251); | |
HashMap<String, ArrayList<TableRow>> rows = new HashMap<String, ArrayList<TableRow>>(); | |
ArrayList<TableRow> tableRows = new ArrayList<TableRow>(); | |
TableRow row = new TableRow(this); | |
row.setLayoutParams(new TableRow.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); | |
row.setGravity(Gravity.CENTER); | |
row.setBackgroundColor(headerColor); | |
TextView recyclableTextView = new TextView(this); | |
recyclableTextView.setText("Explicitly known"); | |
recyclableTextView.setTextColor(Color.BLACK); | |
recyclableTextView.setTextSize(20); | |
int screenWidth = getResources().getDisplayMetrics().widthPixels; | |
recyclableTextView.setWidth(100 * screenWidth / 100); //take up 100% of the width | |
recyclableTextView.setPadding(5, 5, 5, 5); | |
row.addView(recyclableTextView); | |
tableRows.add(row); | |
PackageManager currentPM = getPackageManager(); | |
for(int i = 0; i < getExplicitIntents().length; i++) { | |
Intent navigationAppIntent = explicitIntents[i]; | |
try { | |
for(ResolveInfo explicitActivityInfo : currentPM.queryIntentActivities(navigationAppIntent, defaultFlag)) { | |
try { | |
row = new TableRow(this); | |
row.setLayoutParams(new TableRow.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); | |
row.setGravity(Gravity.CENTER); | |
recyclableTextView = new TextView(this); | |
recyclableTextView.setText(explicitActivityInfo.loadLabel(currentPM) + " (" + explicitActivityInfo.activityInfo.applicationInfo.packageName + ")"); | |
recyclableTextView.setTypeface(Typeface.DEFAULT_BOLD); | |
recyclableTextView.setTextColor(Color.BLACK); | |
recyclableTextView.setTextSize(20); | |
recyclableTextView.setWidth(100 * screenWidth / 100); | |
recyclableTextView.setPadding(5, 5, 5, 5); | |
row.addView(recyclableTextView); | |
row.setContentDescription("" + i); //this descriptor will be used in the following listener to find out which row was selected | |
//set onclick listener to each row (it already has a descriptor pointing the intent index it represents from the array) | |
row.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
String desc = "" + v.getContentDescription(); | |
if(desc != null) { | |
try { | |
startActivity(getExplicitIntents()[Integer.parseInt(desc)]); | |
} | |
catch(Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
}); | |
row.setMinimumHeight(100); | |
tableRows.add(row); | |
} | |
catch(Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
catch(Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
//add implicit apps option | |
row = new TableRow(this); | |
row.setLayoutParams(new TableRow.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); | |
row.setGravity(Gravity.CENTER); | |
row.addView(makePoiTableRowWithText("Find Implicitly", true, 100)); | |
row.setMinimumHeight(150); | |
//add a listener to this view to let the OS find matching applications | |
row.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
Intent implicitIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:q="); //NOTE: google navigation can't be launched without destination | |
startActivity(implicitIntent); | |
} | |
}); | |
tableRows.add(row); | |
rows.put("Compatible apps", tableRows); | |
showTableDialog(rows, "Launch GPS Navigator", titleColor, R.drawable.menu_gps); | |
} |
Here are few utils needed only to follow up the last call in the showNavigation() method - showTableDialog():
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static final int TABLE_DIALOG = 0; | |
ArrayList<TableRow> rows; | |
String tableDialogTime; | |
int tableDialogTableHeaderColor; | |
int tableDialogTableHeaderIcon; | |
Dialog dialog; | |
/** | |
* Pops up a dialog to show a table. | |
* @param rows | |
*/ | |
public void showTableDialog(ArrayList<TableRow> rows, String time, int headerColor, int headerIcon) { | |
this.rows = rows; | |
this.tableDialogTableHeaderColor = headerColor; | |
this.tableDialogTableHeaderIcon = headerIcon; | |
activityHandler.post(new Runnable() { | |
@Override | |
public void run() { | |
showDialog(TABLE_DIALOG); | |
} | |
}); | |
} | |
public Dialog onCreateDialog(int id) { | |
switch(id) { | |
case TABLE_DIALOG: | |
dialog = new Dialog(this, R.style.TableDialog); | |
dialog.setContentView(R.layout.table_dialog); | |
break; | |
default: | |
dialog = null; | |
} | |
return dialog; | |
} | |
public void onPrepareDialog(int id, Dialog dialog) { | |
if(id == TABLE_DIALOG) { | |
//set icon | |
((ImageView) dialog.findViewById(R.id.table_dialog_icon)).setBackgroundDrawable(getResources().getDrawable(tableDialogTableHeaderIcon)); | |
//set background for the whole header | |
dialog.findViewById(R.id.table_dialog_header).setBackgroundColor(tableDialogTableHeaderColor); | |
//set title | |
TextView titleField = (TextView) dialog.findViewById(R.id.table_dialog_title); | |
titleField.setText(title); | |
//set up the header for the table on a separate TableLayout (it's a separate TableLayout, so that it doesn't scroll along with the rest of the scrollable area) | |
TableLayout tableHeader = (TableLayout) dialog.findViewById(R.id.table_header); | |
tableHeader.removeAllViews(); | |
tableHeader.addView(rows.get(0)); | |
//set up the table | |
TableLayout table = (TableLayout) dialog.findViewById(R.id.table_dialog_layout); | |
//clean up old entries (on re-invoke) | |
table.removeAllViews(); | |
//populate table rows: add rows with separator inbetween | |
for(int i = 1; i < rows.size(); i++) { | |
int colorAmount = i % 2 == 0? 230 : 200; | |
TableRow row = rows.get(i); | |
row.setBackgroundColor(Color.rgb(colorAmount, colorAmount, colorAmount)); | |
table.addView(row); | |
//add a separator after each line | |
View separator = new View(dialog.getContext()); | |
separator.setBackgroundColor(Color.rgb(77, 77, 77)); | |
separator.setMinimumHeight(2); | |
table.addView(separator); | |
} | |
} | |
} | |
private TextView recyclableTextView; | |
public TextView makePoiTableRowWithText(CharSequence text, boolean bold, int widthInPercentOfScreenWidth) { | |
recyclableTextView = new TextView(this); | |
recyclableTextView.setText(text); | |
recyclableTextView.setTypeface(bold? Typeface.DEFAULT_BOLD : Typeface.DEFAULT); | |
recyclableTextView.setTextColor(Color.BLACK); | |
recyclableTextView.setTextSize(20); | |
recyclableTextView.setWidth(widthInPercentOfScreenWidth * screenWidth / 100); | |
recyclableTextView.setPadding(5, 5, 5, 5); | |
return recyclableTextView; | |
} |
These are the resource files:
* this goes to /res/values/themes.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<style name="TableDialog" parent="android:style/Theme.Dialog"> | |
<item name="android:windowBackground">@null</item> | |
<item name="android:windowNoTitle">true</item> | |
<item name="android:windowIsFloating">true</item> | |
</style> | |
</resources> |
* this goes to /res/values/styles.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<style name="DialogText"> | |
<item name="android:textColor">#FF231f20</item> | |
<item name="android:textSize">25sp</item> | |
<item name="android:textStyle">bold</item> | |
</style> | |
<style name="DialogText.Title"> | |
<item name="android:textColor">#FF364945</item> | |
<item name="android:textSize">24sp</item> | |
<item name="android:textStyle">bold</item> | |
</style> | |
</resources> |
* this goes to /res/layout/table_dialog.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
android:layout_width="fill_parent" | |
android:layout_height="fill_parent" | |
android:orientation="vertical"> | |
<LinearLayout android:orientation="horizontal" | |
android:layout_height="wrap_content" | |
android:layout_width="fill_parent" | |
android:gravity="center_horizontal" | |
android:id="@+id/table_dialog_header"> | |
<ImageView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_margin="5dp" | |
android:id="@+id/table_dialog_icon"/> | |
<TextView | |
android:id="@+id/table_dialog_title" | |
android:layout_width="0dp" | |
android:layout_height="wrap_content" | |
android:layout_weight="1" | |
android:gravity="center_horizontal" | |
android:textSize="30sp" | |
android:textColor="#fff" | |
android:textStyle="bold"/> | |
</LinearLayout> | |
<LinearLayout android:orientation="vertical" | |
android:layout_width="fill_parent" | |
android:layout_height="fill_parent" | |
android:gravity="center_horizontal" | |
android:id="@+id/fillable_area"> | |
<TableLayout | |
android:id="@+id/table_header" | |
android:layout_width="fill_parent" | |
android:layout_height="wrap_content"/> | |
<ScrollView | |
android:id="@+id/scrollable_table" | |
android:layout_width="fill_parent" | |
android:layout_height="wrap_content"> | |
<TableLayout | |
android:id="@+id/table_dialog_layout" | |
android:layout_width="fill_parent" | |
android:layout_height="fill_parent"/> | |
</ScrollView> | |
</LinearLayout> | |
</LinearLayout> |
* this goes to /res/drawable as menu_gps.png

And here is the result dialog you are supposed to see:
On click on each row it will start up the respective activity.
That's it, I hope it helps!
Very nice!
ReplyDeleteBut your code will serve to start a turn by turn Navigator withour any destination set, correct?
Do you know any way to start turn-by-turn Navigation to a specified lat/long coordinates? I wonder if this is possible with NDrive for example.
@Fede, that's right, without destination. The only navigator I know of which supports destination (it supports a public Intent and accepts parameters) is Navigon. That's the reason why it's the only one with 'new Intent' statement in the list of navigators.
ReplyDelete