Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build Uri of the format X:Y

Tags:

android

uri

I'm trying to build a Uri the most correct way for an intent to query a location on a map. In the documentation for a Maps intent, it is states that Uris should be of the form: geo:0,0?q=my+street+address. I tried using Uri.Builder but found no method to specify the "0,0" part of the uri as Uri.Builder doesn't have a function to specify the path without prepending a '/'. Currently I'm stuck using the following code:

uri = new Uri.Builder()
         .scheme(URL_SCHEME_MAP)
         .encodedOpaquePart("0,0?q=" + query)
         .build();

Which is OK, but not as nice as I'd like to have it. So I'm wondering if anyone knows of a better/nicer way to do this.

like image 886
idunnololz Avatar asked Feb 10 '14 23:02

idunnololz


5 Answers

As @providencemac points out, the Udacity Android development course shows how to do this, but it actually uses Uri.Builder now. However, their code is wrong:

Uri.Builder builder = new Uri.Builder();
builder.scheme("geo")
        .path("0,0")
        .query(streetAddr);
Uri uri = builder.build();

If you use Google's address as the query location, as the course does, the emulator will still go there, because that's the default. But there's no location marker, and other addresses won't work. Instead, it should be like this:

Uri.Builder builder = new Uri.Builder();
builder.scheme("geo")
        .appendPath("0,0")
        .appendQueryParameter("q", addressString);
Uri addressUri = builder.build();
like image 152
orodbhen Avatar answered Nov 18 '22 21:11

orodbhen


You can accomplish this by using a combination of the parse and buildUpon methods:

Uri geoLocation = Uri.parse("geo:0,0?").buildUpon()
                .appendQueryParameter("q", location)
                .build();

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(geoLocation);

I learned this approach from the Udacity class "Developing Android Apps" although they do not fully explain it.

like image 43
providencemac Avatar answered Nov 18 '22 20:11

providencemac


Just to build upon @providencemac answer and provide a bit more context:

Java distinguishes between hierarchical and opaque URI. What you're trying to create here is an opaque URI. Such URI consist of three parts: scheme, opaque part and fragment. The opaque part is basically everything between the scheme separator (:) and the fragment separator (#). So what buildUpon actually does under the hood for opaque URI is the following:

return new Builder()
    .scheme(getScheme())
    .opaquePart(getSsp())
    .fragment(getFragmentPart());

getScheme gets everything from the string set by parse up until :, getSsp (SSP means scheme specific part) gets everything in between the : and possible #, and getFragmentPart gets everything after #.

So @providencemac's answer without using parse and builtUpon would look as follows:

uri = new Uri.Builder()
     .scheme(URL_SCHEME_MAP)
     .encodedOpaquePart("0,0")
     .appendQueryParameter("q", query)
     .build();

Sadly though while researching this a bit in the sources I noticed a flaw with this as well as the original answer (and thus a possible issue with the mentioned Udacity course). The first two lines in appendQueryParameter as of SDK 23rev3 look as follows:

// This URI will be hierarchical.
this.opaquePart = null;

So our formerly opaque URI gets transformed to a hierarchical one, without actually being hierarchical as it's neither relative nor does it contain the "://" part. So the result would look something like "geo:?q=1600+Amphitheatre+Parkway%2C+CA", which does work when no specific longitude and latitude are given. Although if you are trying to create the second sample from the documentation like this

uri = new Uri.Builder()
     .scheme("geo")
     .encodedOpaquePart("47.6,-122.3")
     .appendQueryParameter("z", "11")
     .build();

you'd end up with "geo:?z=zoom", which apparently results in showing the current device's location.

So long story short: I think you should actually stick to the way you initially proposed in your question, as it does produce what you want. Although you should probably use Uri.encode(query).

like image 20
Boris Avatar answered Nov 18 '22 21:11

Boris


For those who are still having problem with this. I figured this is the closest to Udacity course:

builder.scheme("geo")
       .encodedPath("0,0")
       .appendQueryParameter("q",streetAddress);

You just have to change Path() to encodedPath().

like image 36
anishtain4 Avatar answered Nov 18 '22 21:11

anishtain4


I read documentation and try all answers and so mine. As you see in documentation there is 4 data uri schema to show location on map.

  1. simple
  2. zoom
  3. label
  4. address

here is my suggestion for all options:

private Uri getGeoUri(double lat, double log) {
    return Uri.parse("geo:" + lat + "," + log);
}

private Uri getGeoUri(double lat, double log, int zoom) {
    return Uri.parse("geo:" + lat + "," + log + "?z=" + zoom);
}

private Uri getGeoUri(double lat, double log, String lable) {
    return Uri.parse("geo:0,0?q=" + lat + "," + log + "(" + lable + ")");
}

private Uri getGeoUri(String address) {
    return Uri.parse("geo:0,0?q=" + address);
}

and this is how to use:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //Uri geoLocation = getGeoUri(47.6,-122.3);                     //1. simple
    //Uri geoLocation = getGeoUri(47.6,-122.3, 11);                 //2. zoom
    //Uri geoLocation = getGeoUri(34.99,-106.61, "Treasure");       //3. label
    Uri geoLocation = getGeoUri("1600 Amphitheatre Parkway ,CA");   //4. address
    openMap(geoLocation);
}

private void openMap(Uri geoLocation) {
    Intent i = new Intent(Intent.ACTION_VIEW);
    i.setData(geoLocation);
    if (i.resolveActivity(getPackageManager()) != null) {
        startActivity(i);
    }
}

All in one MainActivity.

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //Uri geoLocation = getGeoUri(47.6,-122.3);                     //1. simple
    //Uri geoLocation = getGeoUri(47.6,-122.3, 11);                 //2. zoom
    //Uri geoLocation = getGeoUri(34.99,-106.61, "Treasure");       //3. label
    Uri geoLocation = getGeoUri("1600 Amphitheatre Parkway ,CA");   //4. address
    openMap(geoLocation);
}

private void openMap(Uri geoLocation) {
    Intent i = new Intent(Intent.ACTION_VIEW);
    i.setData(geoLocation);
    if (i.resolveActivity(getPackageManager()) != null) {
        startActivity(i);
    }
}

private Uri getGeoUri(double lat, double log) {
    return Uri.parse("geo:" + lat + "," + log);
}

private Uri getGeoUri(double lat, double log, int zoom) {
    return Uri.parse("geo:" + lat + "," + log + "?z=" + zoom);
}

private Uri getGeoUri(double lat, double log, String lable) {
    return Uri.parse("geo:0,0?q=" + lat + "," + log + "(" + lable + ")");
}

private Uri getGeoUri(String address) {
    return Uri.parse("geo:0,0?q=" + address);
}
}

Enjoy it.

like image 37
Mohammad Reza Khahani Avatar answered Nov 18 '22 19:11

Mohammad Reza Khahani