Given an absolute Uri and a relative Uri or relative path, how do you get the absolute Uri pointing to the relative location?
For example, suppose we have the Uri for file:///android_asset/dir, pointing to a location in our assets. Further suppose that elsewhere, we have a relative path of /foo. The absolute Uri for that relative path should be file:///android_asset/foo. Is there something on Uri, or elsewhere in the Android SDK, that I am missing that give me that result Uri?
Uri.withAppendedPath() is not the answer, as all it seems to do is handle trailing directory separators:
Uri abs=Uri.parse("file:///android_asset/");
Uri rel=Uri.withAppendedPath(abs, "/foo");
Assert.assertEquals("file:///android_asset/foo", rel.toString());
// actually returns file:///android_asset//foo
Uri abs2=Uri.parse("file:///android_asset/dir");
Uri rel2=Uri.withAppendedPath(abs2, "/foo");
Assert.assertEquals("file:///android_asset/foo", rel2.toString());
// actually returns file:///android_asset/dir//foo
Uri.Builder, via buildUpon() on Uri, is not an improvement:
Uri rel3=abs.buildUpon().appendPath("/foo").build();
Assert.assertEquals("file:///android_asset/foo", rel3.toString());
// actually returns file:///android_asset/%2Ffoo
Uri rel4=abs.buildUpon().appendEncodedPath("/foo").build();
Assert.assertEquals("file:///android_asset/foo", rel4.toString());
// actually returns file:///android_asset//foo
In a pinch I can try using java.net.URL and its URL(URL context, String spec) constructor, or just roll some code for it, but I was hoping to stay in the realm of Android Uri values if possible, just for any quirks differentiating URL and Uri.
Android doesn't make this easy.
In my case, I had to take a base url that may or may not have an included path:
http://www.myurl.com/myapi/
...and append a REST API method path, like:
api/where/agencies-with-coverage.json
...to produce the entire url:
http://www.myurl.com/myapi/api/where/agencies-with-coverage.json
Here's how I did it (compiled from various methods within the app - there may be a simpler way of doing this):
String baseUrlString = "http://www.myurl.com/myapi/";
String pathString = "api/where/agencies-with-coverage.json";
Uri.Builder builder = new Uri.Builder();
builder.path(pathString);
Uri baseUrl = Uri.parse(baseUrlString);
// Copy partial path (if one exists) from the base URL
Uri.Builder path = new Uri.Builder();
path.encodedPath(baseUrl.getEncodedPath());
// Then, tack on the rest of the REST API method path
path.appendEncodedPath(builder.build().getPath());
// Finally, overwrite builder with the full URL
builder.scheme(baseUrl.getScheme());
builder.encodedAuthority(baseUrl.getEncodedAuthority());
builder.encodedPath(path.build().getEncodedPath());
// Final Uri
Uri finalUri = builder.build();
In my case, the Builder classes for the API client code assembled the path prior to combining it with the baseURL, so that explains the order of things above.
If I've pulled together the above code correctly, it should handle port numbers as well as spaces in the URL string.
I pulled this source code from the OneBusAway Android app, specifically the ObaContext class. Note that this code on Github also handles the additional case where the user typed in a baseUrl (String serverName = Application.get().getCustomApiUrl() in the above code) that should override the region base URL (mRegion.getObaBaseUrl()), and the user-entered URL may not have http:// in front of it.
The unit tests that pass for the above code on Github, including cases where port numbers and spaces are included in the baseUrl and path, and the leading/trailing / may or may not be included, are here on Github. Related issues on Github where I was banging my head on the wall to try and get this all to work - 72, 126, 132.
I haven't tried this with non-HTTP URIs, but I believe it may work more generally.
There is an equivalent to urllib.parse.urljoin (Python) in Android URI.create(baseUrl).resolve(path).
import java.net.URI
URI.create("https://dmn92m25mtw4z.cloudfront.net/helpvids/f3_4/hls_480/480.m3u8")
.resolve("0.ts")
// output:
// https://dmn92m25mtw4z.cloudfront.net/helpvids/f3_4/hls_480/0.ts
Sean Barbeau answer returns wrong URL, it's just appending the 0.ts to the url.
Related
I'm creating an Intent for Android, to send e-mails.
And I'm getting confused about the behavior of Uri.fromParts.
Mi code:
This works fine!
uri=Uri.parse(
"mailto:" + toAddress +
(subject != null ?
("?" + "subject=" + Uri.encode(subject)) :
"")
The previous work fine, and create an Uri in the form mailto:john#doe.com?subject=Test
But if I try to use Uri.from parts, with this sample:
uriBuilder=Uri.fromParts("mailto",toAddress,null).buildUpon();
if (subject!=null) {
uriBuilder.appendQueryParameter("subject",subject);
}
uri=uriBuilder.build();
I get an error. The final uri is mailto:?subject=Test
The intermediate is correct, but when I use appendQueryParameter, it removes the content after the mailto scheme.
Do you know why? Which is the canonical way to do this?
Uri#fromParts()
Creates an opaque Uri from the given components. Encodes the ssp which means this method cannot be used to create hierarchical URIs.
When you call buildUpon() on this, the Builder contains the scheme, scheme-specific part (ssp) and the fragment (null in your case).
appendQueryParameter() then turns the Builder to a hierarchical one, deleting the opaque ssp data.
I don't think there's a "canonical" way. Just don't mix hierarchical and opaque builders.
For details on what happens under the hood, read the source.
I'm trying to make an HTTPGET request to a REST server, the URL i need to send contains many parameters:
This is the URI :
http://darate.free.fr/rest/api.php?rquest=addUser&&login=samuel&&password=0757bed3d74ccc8fc8e67a13983fc95dca209407&&firstname=samuel&&lastname=barbier
I need to get the Login,password,first, name and last name that the user types, then produce an URI like the once above.
Is there any easy way to create the URI, without concatenate the first part of the URI http://darate.free.fr/rest/api.php?rquest=addUser with every &¶meter:value
I prefer to use Uri.Builder for building Uris. It makes sure everything is escaped properly.
My typical code:
Uri.Builder builder = Uri.parse(BASE_URI).buildUpon();
builder.appendPath(REQUEST_PATH);
builder.builder.appendQueryParameter("param1", value);
Uri builtUri = builder.build();
I hope you can use webview.posturl shown below
webview.postUrl("http://5.39.186.164/SEBC.php?user="+username));
It also worked fine for me to get the username from the database. I hope it will help you.
In order to send an image to a server from my android application, I have to get mime type of the image (or, at least, its extension).
I get the image from a ACTION_GET_CONTENT intent. Some applications, like Dropbox, send a file:// scheme data, so I can guess the extension using MimeTypeMap.getFileExtensionFromUrl(), but some others, like Google Drive, send a content:// scheme, which not permit to do so.
I've tried many things before posting here, like this:
AssetFileDescriptor asset = getContentResolver().openAssetFileDescriptor(getIntent().getData(), "r");
FileInputStream stream = asset.createInputStream();
String mime = URLConnection.guessContentTypeFromStream(stream); // returns null
The only workaround I think is to decode the asset into a Bitmap, then generate a new compressed image (using Bitmap.compress()), but it will add some work to the phone, and it could change the format/quality of original image, so I reserve it only if there is no other solution.
Does anyone have an idea about my problem? Thanks a lot for help ;)
As suggested by #sh3psheep on Twitter, I've tried this, and it works for content:// schemes:
Uri data = getIntent().getData();
String mime = getContentResolver().getType(data); // returns correct MIME type, such as "image/png"
The only con I found is that it does not support file:// scheme, but we can handle it using method I described in question.
do you know a method or the method used by the evernote widget to retrieve the thumbnails that we see in a very convenient way (before retrieving the entire note) in the main interface or in the widget?
I saw the post method via http request, but it seems complicated when not sharing notes and perhaps there are more straightforward methods via a direct evernote API call or via reading files stored by the application(s)?
The widget pulls the thumbnails from the Evernote app's Content Provider.
Something like this should work.
In your manifest :
<permission android:name="evernote.permission.READ_DATA" android:protectionLevel="normal" />
In your java code:
final Uri AUTHORITY_URI = Uri.parse("content://com.evernote.evernoteprovider");
final Uri NOTE_URI = Uri.withAppendedPath(AUTHORITY_URI, "notes");
private FileDescriptor getNoteThumbnail(Context context, String noteGuid) throws FileNotFoundException {
Uri thumbnailUri = NOTE_URI.buildUpon().appendEncodedPath(noteGuid).appendPath("thumbnail").appendPath("data").build();
ContentResolver cr = context.getContentResolver();
return cr.openFileDescriptor(thumbnailUri, "r").getFileDescriptor();
}
The HTTP Post method is not too complex. I'm not familiar with Java but this is an example in python that should be pretty straightforward to port to android:
import requests
ACCESS_TOKEN="INSERT YOUR AUTH TOKEN OR DEV TOKEN HERE"
payload={'auth':ACCESS_TOKEN} #map auth token to param "auth"
r=requests.post('https://sandbox.evernote.com//shard/s1/thm/note/e679c010-d8b2-4644-9eag-56bd31c84be7.jpg?size=75',data=payload, stream=True) #returns a binary of the picture type in header
f=open('thumbnail.jpg','wb') #open file for binary writing
f.write(r.content) #write binary contents of request to a file
f.close() #close the file
The only parameter of the POST request is "auth" and it should contain your auth token (or dev token). The rest of the information comes from the URL itself and is of the form:
[domain].evernote.com/shard/[shard]/thm/note/[guid]
where
[domain] is sandbox (for the sandbox) and www (for production)
[shard] is the shard the account is on (should be something like s1)
[guid] is the notebook guid
with the optional parameters appended at the end of .jpg, .gif, .bmp or .png as well as the optional parameter at the end of the URL ?size=[1 to 299] (default is 300px square)
for example on the sandbox with shard s1, note guid "e669c090-d8b2-4324-9eae-56bd31c64af7" to return a jpg of size 150px square the URL would look like this:
https://sandbox.evernote.com/shard/s1/thm/note/e669c090-d8b2-4324-9eae-56bd31c64af7.jpg?size=75
I am having a curious problem that perhaps someone has insight into. I encode a query string into a URL on Android using the following code:
request = REQUEST_BASE + "?action=loadauthor&author=" + URLEncoder.encode(author, "UTF-8");
I then add a few other parameters to the string and create a URI like this:
uri = new URI(request);
At a certain point, I pull out the query string to make a checksum:
uri.getRawQuery().getBytes();
Then I send it on its way with:
HttpGet get = new HttpGet(uri);
On the Appengine server, I then retrieve the string and try to match the checksum:
String query = req.getQueryString();
Normally, this works fine. However, there are a few characters that seem to get unencoded on the way to the server. For example,
action=loadauthor&author=Charles+Alexander+%28Ohiyesa%29+Eastman×tamp=1343261225838&user=1479845600
shows up in the server logs (and in the GAE app) as:
action=loadauthor&author=Charles+Alexander+(Ohiyesa)+Eastman×tamp=1343261226837&user=1479845600
This only happens to a few characters (like parentheses). Other characters remain encoded all the way through. Does anyone have a thought about what I might be doing wrong? Any feedback is appreciated.
I never did find a solution for this problem. I worked around it by unencoding certain characters on the client before sending things to the server:
request = request.replace("%28", "(");
request = request.replace("%29", ")");
request = request.replace("%27", "'");
If anyone has a better solution, I am sure that I (and others) would be interested!
URLEncoder does not encode parentheses and certain other characters, as they are supposed to be "safe" for most servers. See URLEncoder. You will have to replace these yourself if necessary.
Example:
URI uri = new URI(request.replace("(","%28"));
If a lot of replacements are needed, you can try request.replaceAll(String regularExpression, String replacement). This, of course, requires knowledge of regular expressions.