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.
Related
I get an Uri and I do:
String s = Uri.decode(uri.toString());
But when I encode the string again I haven't got the same result:
Uri uri = Uri.parse(Uri.encode(s));
Uri received without decoding (just called toString()):
content://com.android.externalstorage.documents/document/primary%3APictures%2FScreenshots%2FScreenshot_20190807-153556.png
Uri created via parse/encode method:
content%3A%2F%2Fcom.android.externalstorage.documents%2Fdocument%2Fprimary%3APictures%2FScreenshots%2FScreenshot_20190807-153556.png
Is there a way to re-parse correctly the Uri?
Is there a way to re-parse correctly the Uri?
No. Hold onto the original Uri. This is not significantly different than converting an image to monochrome, then wondering how to get the original color image back. The decode() and encode() methods are not designed to decode and encode Uri values, but rather specific pieces (e.g., query parameter values).
I'm attempting to take a very generic approach in providing sharing options for sharing images from my app's private storage, but I've encountered a problem that appears to be specific to sharing images to the Facebook app (com.facebook.katana):
I launch an intent with EXTRA_STREAM containing a Uri to an image inside the private directory of my app.
Through a ContentProvider, I provide access to the desired file by returning the following in openFile():
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
When sharing to Facebook, the image doesn't appear. Every other app, however, does work. Debugging calls into my ContentProvider, I see that Facebook does indeed query the ContentProvider, and openFile() is hit. Nevertheless, the image doesn't appear.
After much head scratching, I realized I was returning null as the MIME type. I changed the result of getType() to return "image/png", and that was it: Facebook accepted my image:
#Nullable
#Override
public String getType(#NonNull Uri uri) {
// It's absolutely imperative that we provide the MIME type, otherwise some apps like
// Facebook will simply ignore the file
return "image/png";
}
I would point out that you should return the actual MIME type of the file associated with the Uri; I'm returning PNG here because I'm lazy, and I know that all my images are of that type.
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.
Can someone please explain to me what's an image uri? I have an android app with a listview that can attach an image, and that listview displays the imag uri. thanks
URI is an address like: http://www.google.com/image.png it refers to the image somewhere.
It can also be a local address: file:////something.png
So you can attach a file that you don't have on your device and you don't want to download it.
URI or Uniform Resource Identifier is a compact sequence of characters that identifies an abstract or physical resource. It can be further classified as a locator, a name, or both.
Basically URI (in some cases URL or URN) will point to the image location, or will be the name of the image (why not both?).
Let's take a look at some URI examples:
https://stackoverflow.com/ (a URL because of the HTTPS protocol)
ftp://ftp.is.co.za/rfc/rfc1808.txt (also a URL because of the
FTP protocol)
http://www.ietf.org/rfc/rfc2396.txt (also a URL because of the
HTTP protocol)
ldap://[2001:db8::7]/c=GB?objectClass?one (also a URL because of the
protocol)
mailto:John.Doe#example.com
tel:+1-816-555-1212
telnet://192.0.2.16:80/ (also a URL because of the protocol)
Basically it's just a string in which identifies some-type of web resource.
I have yet another pesky question for people who understand how Google Drive SDK works. On Android platform, I am creating my own custom thumbnails for JPEG image files ( thumbnail is a reduced JPG of the most important detail of the parent image ), The size is a bit non-standard - 384 x 128px, but well within limits stated in the documentation. So, the code goes like this:
// thumbnail
String myThumb = "test.tnl";
Thumbnail tn = new Thumbnail();
tn.setMimeType("image/jpeg");
tn.setImage(Base64.encodeBase64String(myThumb.getBytes()));
// define meta-data
File body = new File();
body.setTitle("test.jpg");
body.setDescription("bla bla");
body.setMimeType("image/jpeg");
body.setThumbnail(tn);
File gooFl = drvSvc.files()
.insert(body, new FileContent("image/jpeg", new java.io.File(test.jpg)))
.execute();
and executes flawlessly (there are more 'body' elements I don't list here) and everything works like a charm. But when I download the image, my thumbnail is gone, replaced by standard Google thumbnail - s220 type.
I did notice the documentation statement:
As with indexable text, Drive automatically generates thumbnails for many common file types. For shortcuts and other file types Drive can not render, you can provide a thumbnail image generated by your application.
Reading it ambiguously, I was hoping that by supplying my own thumbnail to a known MIME type, I will keep Google Drive from generating its standard one, but it probably is not the case. So the question remains. Is there a solution to my problem? Having custom thumbnails for standard "image/jpeg" MIME types? Or is there a work-around, perhaps another custom field I can stick some 10Kb of binary data in? I need the thumbnails in my Android viewer - another app.
Thank you, sean
Google Drive will only use custom thumbnails for non-standard MIME types, so you can't override the one for jpeg. Your app can use a custom file property to store and read proprietary data:
https://developers.google.com/drive/properties