I am experimenting with Android development. I am making an app that will allow the user to browse files in a web service and view them. These files could be anything: text, pdf, pictures, etc.
Previously, I would download the file to external storage and then call Intent.SetDataAndType() and pass it the URL to the file. That would cause the Android device to bring up an app picker and let the user choose the appropriate method to look at the file.
But since I do not want the user to edit the file, only to look at it, it seemed silly to download a file to storage; a file that I didn't want to hang around. Since the file can be obtained by a URL, why don't I pass that as a parameter to the Intent.SetDataAndType()?
I tried that. The first problem was that the file name was assumed to be the name of the web service call, and that seemed to be more important than the mime-type. I changed the web service to be the same name as whatever file was attempting to be downloaded. That solved that issue.
So now, the file is being opened. But it is always being opened in a web browser. I get to choose the web browser, but I would rather have another app open it.
My code looks like this:
Intent i = new Intent(Intent.ActionView);
i.SetDataAndType(Android.Net.Uri.Parse(GetUrlToFile(fileref, fileName)), mimeType);
i.SetFlags(ActivityFlags.GrantReadUriPermission);
i.SetFlags(ActivityFlags.NewTask);
i.SetFlags(ActivityFlags.ClearWhenTaskReset); // so if the app is relaunched, we don't show the display application.
StartActivity(i);
The code is in C# because I'm using Xamarin, but I don't believe that should make a difference.
I tried using StartActivity(Intent.CreateChooser(i, "Open me")); but that didn't give me any more options for choosing.
Does anyone have any ideas as to how to do this?
I have not found a way to do this yet, so I have gone through a workaround.
Instead of using a URL, I changed my app to be a Content Provider as well. Now, when I want the file opened, I create a URI that refers to the file within my app and pass that off to an Intent. When my app is contacted by this Intent, I download the file locally to my cache directory and return that.
My code has changed to this:
Intent i = new Intent(Intent.ActionView);
i.SetDataAndType(Android.Net.Uri.Parse("content://com.sample.erik.provider/files/" + id), mimeType);
i.SetFlags(ActivityFlags.GrantReadUriPermission);
i.SetFlags(ActivityFlags.NewTask);
i.SetFlags(ActivityFlags.ClearWhenTaskReset); // so if the app is relaunched, we don't show the display application.
StartActivity(i);
Then, I have my own content provider which does most of the work in OpenFile()
public override ParcelFileDescriptor OpenFile(Android.Net.Uri uri, string mode)
{
switch (sUriMatcher.Match(uri))
{
case FILE_ID:
if (mode != "r")
throw new Java.Lang.UnsupportedOperationException("Do not support write access: " + uri);
String id = uri.LastPathSegment;
Java.IO.File file = new Java.IO.File(Application.Context.CacheDir, id);
DownloadToFile(file, id);
return ParcelFileDescriptor.Open(file, ParcelFileMode.ReadOnly);
default:
throw new Java.Lang.IllegalArgumentException("Unknown Uri: " + uri);
}
}
It is not my original plan, but this way seems to work quite well and meets my needs.
Related
DONT mark this as duplicate, before reading what I need.
I have seen many similar topics, but in none of them I've found solution.
I need the simplest thing: In my application I have button "View Media Files". After clicking that button, i need to be opened (with built-in File Explorer) this directory - SD_CARD/my_folder where are media files (and I want to click any of them and they should be opened in default Media player)..
I have used all suggested answers on SO , like this:
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri mydir = Uri.parse("/sdcard/Recorder_Videos");
intent.setDataAndType(mydir, "*/*");
startActivity(intent);
but all they do: after clicking button, it opens "Choose File" menu:
(Where I cant still play media files when clicking)
The solution (not complete) I have found, was that I was missing file:// prefix. Here is my solution (however, it shows all kinds of applications on first view):
public void openFolder(String location)
{
// location = "/sdcard/my_folder";
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri mydir = Uri.parse("file://"+location);
intent.setDataAndType(mydir,"application/*"); // or use */*
startActivity(intent);
}
p.s. Strange and surprising, but there doesnt exist the standard definition of "File Browser" in stock Android systems (unless you install 3rd party "File Explorer")..
That's why "resource/folder" mime-type doesnt work by default..
However, let's say a simple truth. File-Browser is a SIMPLE and ESSENTIAL part of any OS system. And it's quite disrespectful from Android, saying that it's not their job, and throwing the responsiblity to 3rd party apps.
You can use type DocumentsContract.Document.MIME_TYPE_DIR which works on several devices and launches File Explorer. You can refer this SO for more details.
I have this code:
protected void pickFile(View view){
///Codigo que abre la galeria de imagenes y carga la imagen en displayedImage
Intent intent = new Intent();
intent.setType("file/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Choose File to Upload"), 1);
}
//It's executed when leaving file system
#Override
protected void onActivityResult(int reqCode, int resCode, Intent data){
super.onActivityResult(reqCode, resCode, data);
if (reqCode == 1 && resCode == RESULT_OK && data != null) {
Uri selectedFile = data.getData();
RequestMaker.uploadFile(this, selectedFile, "this is a file");
}
}
What i want to do is to be able to select any file from my phone and send it.
The code works, it opens the chooser and lets me search for any file. However, there are a few problems i am having:
When i try to access via "Internal Storage" option, i cannot select any item. They are all disabled. I fixed that installing a file manager and it lets me choose the files i want, but maybe there is a quick fix for that.
When i select the file and run Uri.getPath(), sometimes the path is valid, others, and in general when i am selecting some image file, there is an error with the path i get in return. Is not the actual one.
I saw some fixes online but they are all for selecting images from the galery, i want the general one.
How can i fix this?
The code works
No, it does not.
First, file/* is not a valid MIME type, or even a wildcard MIME type. There is no MIME type that begins with file/. If you want any MIME type, try */*.
Second, ACTION_GET_CONTENT does not allow the user to "select any file". It allow the user to pick a piece of content, from any app on the device that implements an ACTION_GET_CONTENT activity that elects to honor your MIME type. What is returned by that activity is a Uri pointing to the content. This does not have to be a local file, let alone one that you have direct filesystem access to.
When i select the file and run Uri.getPath(), sometimes the path is valid
No, the path is always valid (at least, for a while). It just is not what you think it is. A Uri is not a file.
For example, presumably you are viewing this Web page in a Web browser. If you look in the address bar of that Web browser, you will see the following URL:
https://stackoverflow.com/questions/33575449/how-to-get-any-type-of-file-with-intent-createchooser-android
By your way of thinking, this is referring to a file, on your hard drive, located at /questions/33575449/how-to-get-any-type-of-file-with-intent-createchooser-android.
That is not the case. Part of the URL indicates a location where the path is relevant; in this case, it refers to a Web server.
A Uri is the same thing. In particular, if the Uri has a scheme other than file:, the Uri is simply an address, one that does not necessarily map to anything you can get to directly. Just as Web browser developers use HTTP to get a stream on the contents of this Web page, so you must use ContentResolver and openInputStream() to get at the contents of content: Uri values.
How can i fix this?
Either:
Use the Uri as a Uri, with openInputStream(), getType(), and similar methods on ContentResolver, or
Do not use ACTION_GET_CONTENT, but instead build your own UI for browsing files that your app happens to be able to reach. This will be a subset of all the files on the device, as not everything is in a location that your app has access to (e.g., files on removable media will be missed). But, it synchronizes your code with your mental model (i.e., that you want files, not content).
what is the correct way how I should form the intent to show content from my app in 3rd party viewers? I need to show images in gallery (or any other image viewer), pdfs in some pdf reader,..
Data gets server through a content provider which implements the openFile() method and returns a output pipe..
ParcelFileDescriptor[] pipe=ParcelFileDescriptor.createPipe();
...
ParcelFileDescriptor.AutoCloseOutputStream stream = new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]);
PipeThread pipeThread = new PipeThread(fileContents, stream);
pipeThread.start();
return pipe[0];
For images I use this:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM, uri);
I'm creating then a chooser for this intent as usual that's not the issue..
My problem is that although I see for example the Photos app in the chooser, I just cannot open the file in it..It just only opens the gallery with my images.
It's working when I use the action send, apps like gmail, drive, dropbox,..all of them are able to correctly read the image from the provider.
Also Skitch seems to be the only one app which I have tested it on that is able to open the image also using the Intent.ACTION_VIEW action..
Please don't tell me I should just send the URI, I really need to provide the file as a stream, or somehow as a serie of bytes (IPC limitations would be probably against this). I can't save the file to a public directory.
So the issue was that have been setting Intent type and data in two separate method calls..
What I didn't know is that Intent.setType() clears its data and Intent.setData() clears its type..
When I set both data and type through the Intent.setDataAndType() method call, it works even for URI pointing to a stream.
Unfortunately the final implementation is still not working flawlessly everywhere.
It works in default android gallery app, in G+ Photos app, in QuickPic, in Sony gallery app, but it does not work in default HTC gallery neither in default Samsung gallery.
Its just a pity, that its actually not that much dependent on my implementation as on how is it implemented in the 3rd party viewer app.
I am currently trying to create an app which when taking a picture using the camera, when saving the image, it saves to a specific folder location and if the folder doesn't currently exist on the phone, it creates the folder and saves the file to that location. My code does not currently work, although I have tried. Could you please look at my code and advise me on what I need to do.
Here is the code:
else if(v==camera){
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File newDirectory = new File(Environment.getExternalStorageDirectory() + "App_Pictures/");
String filename = new String("image1.jpeg");
File outputFile = new File(newDirectory, filename);
outputFile.mkdirs();
startActivity(intent);
}
It looks like you're trying to get an external app to take a picture for you. If this is the case, you need to use startActivityForResult, not startActivity, because you want to receive the resulting photo. Then you will receive the result in the onActivityResult method of your activity. This process is described in detail here.
If you actually want your own app to take the picture, instead of an external app, you'll need to use a completely different approach. Here is an app that starts recording a video right when it is launched, and saves it to a directory on the SD card, so perhaps it's a useful starting point if you want to do it this way.
I have a programmatically generated image that I want to send as an attachment via the ACTION_SEND and EXTRA_STREAM method.
But how do i do this?
My first attempt (writing to my context.getCacheDir() based file path) appeared to work in the Gmail preview (no image preview, but attached file name and icon was visible), but the attachment never arrived on the recipient side. I guess this has something to do with permissions on the generated file, but how to avoid this? Do I need to set more permissive settings on these generated files (so that the Gmail activity can access)? Is that even possible for the app's cache folder?
Is there another file location that would be more suitable to write my files to? I considered the downloads folder, but think it would be an awkward location for something that only needs to exist until it has been emailed.
I have even tried encoding my image purely in a data:image/png;base64,ABCD... style URI. This, too, showed up in Gmail preview (attachment icon, but no file name), but did not result in a recipient-side attachment.
Has anyone been able to attach a one-shot generated image to an email intent by any means? What options may I have overlooked?
My problem really consisted of two parts:
context.getCacheDir() is private to your app. You can't put something there and expect another app to be able to access it.
I misunderstood what MIME type I should have been using. Even though I was sending email text, I really needed to specify image/png for the sake of my attachment.
Additionally, research indicated that putting (potentially large) images on the primary memory was not a good idea, even if you were going to immediately clean it up.
Once I did these things and wrote my generated images to a public location on the SD Card, it worked just fine.
So, in overview:
Request SD Card Access in your manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Make sure SD Card is available
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
{
//Bail gracefully
}
Create a directory on the SD Card
File pngDir = new File(
Environment.getExternalStorageDirectory(),
//Loose convention inferred from app examples
"Android/data/com.somedomain.someapp/flotsam");
if (!pngDir.exists())
pngDir.mkdirs();
Write your file to that directory and capture the Uri
File pngFile = new File(pngDir, "jetsam.png");
//Save file encoded as PNG
Uri pngUri = Uri.fromFile(pngFile);
Build an ACTION_SEND intent
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("image/png"); //
intent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { "someone#somewhere.com" });
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Portable Network Graphics");
intent.putExtra(android.content.Intent.EXTRA_CC, new String[] { "carbon#somewhere.com" });
intent.putExtra(Intent.EXTRA_TEXT, "Something textual");
intent.putExtra(Intent.EXTRA_STREAM, pngUri);
And then start the activity
context.startActivity(Intent.createChooser(intent, "Something Pithy"));
And then make sure you clean everything up...
Caveat 1
There appears to be more support coming for app-specific SD Card directories, but alas, not in my required SDK version.
Caveat 2
This is an overview of the solution that eventually worked for me. It is not necessarily a "best practice" approach.
Caveat 3
This does mean that the application has to have an SD Card mounted in order to have the image attachments feature available, but this was totally acceptable for my use case. Your mileage may vary. If the SD Card is not available, I append a friendly note to the email explaining why the images could not be attached and how to rectify the situation.
I've just run into exactly the same issue (wanting to attach a text file in my case). If you look in the Android log, the reason for it is:
02-28 21:01:28.434: E/Gmail(19673): file:// attachment paths must point to file:///mnt/sdcard. Ignoring attachment file:///data/data/com.stephendnicholas.gmailattach/cache/Test.txt
As a workaround (as mentioned by HRJ), you can use a ContentProvider to provide access to files in your application's internal cache so that Gmail can attach them. I've just written up a blog post on how to do it.
Hopefully that's of some help :)
tableLayout.buildDrawingCache();
Bitmap test = Bitmap.createBitmap(tableLayout.getDrawingCache());
tableLayout.destroyDrawingCache();
Log.d("Image", test.toString());
String path = Environment.getExternalStorageDirectory().toString();
Log.d("Path", path);
File file = new File(path,"mail_image.png");
Uri pngUri = Uri.fromFile(file);
Log.d("Real Image Path", pngUri.toString());
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("image/png");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, "email to");
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,"Subject");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, "From My App");
emailIntent.putExtra(android.content.Intent.EXTRA_STREAM, pngUri );
startActivity(Intent.createChooser(emailIntent, "Send mail..."));