A'm working on Android project.
I need to provide ContentProvider to provide access to some directory.
FileProvider it is good solution for me.
Is it possible to retrieve list of files in directory using FileProvider?
User ACTION_SEND_MULTIPLE to send multiple URI
// Set up an Intent to send back to apps that request files
mResultIntent = new Intent("com.yourpacakgename.ACTION_SEND_MULTIPLE");
// Get the files/res subdirectory;
File mResDir = new File(getFilesDir(), "res");
// Get the files in the res subdirectory
File[] mResFiles = mResDir.listFiles();
// Uri list
ArrayList<Uri> uriArrayList = new ArrayList<Uri>();
// Set the Activity's result to null to begin with
setResult(Activity.RESULT_CANCELED, null);
Uri fileUri = null;
for (int i = 0; i < mResFiles.length; i++) {
Log.i(TAG, mResFiles[i].getName());
// Use the FileProvider to get a content URI
try {
fileUri = FileProvider.getUriForFile(this, this.getPackageName() + ".fileprovider", mResFiles[i]);
// add current file uri to the list
uriArrayList.add(fileUri);
} catch (Exception e) {
Log.e(TAG, "The selected file can't be shared: " + mResFiles[i].getPath());
fileUri = null;
}
}
if (uriArrayList.size() != 0) {
// Put the UriList Intent
mResultIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriArrayList);
mResultIntent.setType("application/*");
// Grant temporary read permission to all apps
mResultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// if you want send this to a specific app
//grantUriPermission("pacakgename of client app", fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Set the result
this.setResult(Activity.RESULT_OK, mResultIntent);
} else {
// Set the result to failed
mResultIntent.setDataAndType(null, "");
this.setResult(RESULT_CANCELED, mResultIntent);
}
// Finish Activity and return Result to Caller
finish();
Related
I am writing a new Application on Android 11 (SDK Version 30) and I simply cannot find an example on how to save a file to the external storage.
I read their documentation and now know that they basicly ignore Manifest Permissions (READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE). They also ignore the android:requestLegacyExternalStorage="true" in the manifest.xml application tag.
In their documentation https://developer.android.com/about/versions/11/privacy/storage they write you need to enable the DEFAULT_SCOPED_STORAGE and FORCE_ENABLE_SCOPED_STORAGE flags to enable scoped storage in your app.
Where do I have to enable those?
And when I've done that how and when do I get the actual permission to write to the external storage? Can someone provide working code? I want to save .gif, .png and .mp3 files. So I don't want to write to the gallery.
Thanks in advance.
Corresponding To All Api, included Api 30, Android 11 :
public static File commonDocumentDirPath(String FolderName)
{
File dir = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/" + FolderName);
}
else
{
dir = new File(Environment.getExternalStorageDirectory() + "/" + FolderName);
}
// Make sure the path directory exists.
if (!dir.exists())
{
// Make it, if it doesn't exit
boolean success = dir.mkdirs();
if (!success)
{
dir = null;
}
}
return dir;
}
Now, use this commonDocumentDirPath for saving file.
A side note from comments, getExternalStoragePublicDirectory with certain scopes are now working with Api 30, Android 11. Cheers! Thanks to CommonsWare hints.
You can save files to the public directories on external storage.
Like Documents, Download, DCIM, Pictures and so on.
In the usual way like before version 10.
**Simplest Answer and Tested ( Java ) **
private void createFile(String title) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/html");
intent.putExtra(Intent.EXTRA_TITLE, title);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Uri.parse("/Documents"));
}
createInvoiceActivityResultLauncher.launch(intent);
}
private void createInvoice(Uri uri) {
try {
ParcelFileDescriptor pfd = getContentResolver().
openFileDescriptor(uri, "w");
if (pfd != null) {
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
fileOutputStream.write(invoice_html.getBytes());
fileOutputStream.close();
pfd.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/////////////////////////////////////////////////////
// You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed
String invoice_html;
ActivityResultLauncher<Intent> createInvoiceActivityResultLauncher;
#Override
protected void onCreate(Bundle savedInstanceState) {
invoice_html = "<h1>Just for testing received...</h1>";
createInvoiceActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
// There are no request codes
Uri uri = null;
if (result.getData() != null) {
uri = result.getData().getData();
createInvoice(uri);
// Perform operations on the document using its URI.
}
}
});
I'm using this method and it really worked for me
I hope I can help you. Feel free to ask me if something is not clear to you
Bitmap imageBitmap;
OutputStream outputStream ;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME,"Image_"+".jpg");
contentValues.put(MediaStore.MediaColumns.MIME_TYPE,"image/jpeg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH,Environment.DIRECTORY_PICTURES + File.separator+"TestFolder");
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues);
try {
outputStream = resolver.openOutputStream(Objects.requireNonNull(imageUri) );
imageBitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
Objects.requireNonNull(outputStream);
Toast.makeText(context, "Image Saved", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(context, "Image Not Not Saved: \n "+e, Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
manifest file (Add Permission)
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
i'm getting a pdf file from an api, and i got something like that http://x/docs/document1
In my android project, i have like this:
try{
Android.Content.Intent activity = new Android.Content.Intent(this, typeof(WebViewPDF));
activity.AddFlags(Android.Content.ActivityFlags.GrantReadUriPermission);
activity.AddFlags(Android.Content.ActivityFlags.NoHistory);
string uriAndroid = "http://x/docs/document1";
activity.PutExtra("url", JsonConvert.SerializeObject(uriAndroid));
StartActivity(activity);
}catch (Exception){
...
}
The main problem is, i cannot modify the api, so the endpoint is http://x/docs/document1, but if i try another uri, with the .pdf extension, for example https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf it works fine.
I don't know if i need to get that info from the API in a different way,
How can i show the pdf in the webView or external app without download first the doc?
The solution was download first and then open from local.
void PrintPdf(Uri uri)
{
var webClient = new WebClient();
webClient.Proxy = WebRequest.DefaultWebProxy;
webClient.Credentials = new NetworkCredential("UserName", "Pass");
webClient.Encoding = Encoding.UTF8;
var bytes = webClient.DownloadData(uri);
var text = bytes; // get the downloaded text
string localFilename = "NameforPdf.PDF";
string localPath = System.IO.Path.Combine(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).ToString(), localFilename);
File.WriteAllBytes(localPath, text); // writes to local storage
bool exists = File.Exists(localPath);
if (exists)
{
Java.IO.File file = new Java.IO.File(localPath);
file.SetReadable(true);
//That's the important part, notice the content://
Android.Net.Uri uriLocal = Android.Net.Uri.Parse("content://" + localPath);
Intent intent = new Intent(Intent.ActionView);
intent.SetFlags(ActivityFlags.NewTask);
intent.SetDataAndType(uriLocal, "application/pdf");
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
try
{
StartActivity(intent);
}
catch (Exception)
{
Toast.MakeText(Xamarin.Forms.Forms.Context, "pdf reader not installed", ToastLength.Short).Show();
}
}
}
Just want to confirm that is it possible that when i click on file of any extension will open up with its compatible software in android phone or display me the list of software’s present in mobile which can open the file and if it didn't found any software it will indicate user to first download the software to open that particular file (All this thing need to be done pro grammatically).
Thanks.
Any help will be appreciated.
In order to open the file you can use the following method, If there is no application that can handle given file, it simply shows a Toast saying no application found.
private void viewFile(String filePath, String title, int fileType) {
Uri uri = Uri.parse("file://" + filePath);
Intent intent = new Intent(Intent.ACTION_VIEW);
String dataAndType = getIntentDataAndType(filePath);
intent.setDataAndType(uri, dataAndType);
intent.putExtra(Intent.EXTRA_TITLE, title);
// Verify that the intent will resolve to an activity
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
startActivity(intent);
} else {
Toast.makeText(getActivity(), "No Application found", Toast.LENGTH_SHORT).show();
}
}
UPDATED :
For finding the mime type of the file.
private String getIntentDataAndType(String filePath) {
String exten = "";
int i = filePath.lastIndexOf('.');
// If the index position is greater than zero then get the substring.
if (i > 0) {
exten = filePath.substring(i + 1);
}
String mimeType = android.webkit.MimeTypeMap.getSingleton().getMimeTypeFromExtension(exten);
mimeType = (mimeType == null) ? "*/*" : mimeType;
return mimeType;
}
I've coded my own file explorer and now I want to start to Implement capabilities to open various files. I have implemented Mime Types and all other things but how can I parse specific selected file to specific "viewer activity"? I know by using intents but how to receive it in that viewer app and use it. Many thanks guys :)
Something like this should work. Change file to whatever you want.
File file = new File(Environment.getExternalStorageDirectory(), "movie.mp4");
int index = file.getName().lastIndexOf(".") + 1;
String extension = null;
if (index > 0) {
extension = file.getName().substring(index);
}
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
Uri u = Uri.parse("file://" + file.getAbsolutePath());
String mimeType = null;
if (extension != null) {
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
if (mimeType == null) {
mimeType = "*/*";
}
intent.setDataAndType(u, mimeType);
try {
startActivity(Intent.createChooser(intent, "Open with..."));
} catch (ActivityNotFoundException e) {
// handle the exception
}
I am Working email attachment .I am facing one problem while attachment .Problem Is that i want to sent a mail with attachment .I have one file on this path sdcard0 then fgg then hh.html.When I debug
on this File file = new File(attachments.getString(i));
it show file:/storage/sdcard0/fgg/hh.html
But After this it not go to if condition why ?
File file = new File(attachments.getString(i));
if (file.exists()) {
Uri uri = Uri.fromFile(file);
uris.add(uri);
}
Here is my hole code
JSONArray attachments = parameters.getJSONArray("attachments");
if (attachments != null && attachments.length() > 0) {
ArrayList<Uri> uris = new ArrayList<Uri>();
//convert from paths to Android friendly Parcelable Uri's
for (int i=0; i<attachments.length(); i++) {
try {
File file = new File(attachments.getString(i));
if (file.exists()) {
Uri uri = Uri.fromFile(file);
uris.add(uri);
}
} catch (Exception e) {
LOG.e("EmailComposer", "Error adding an attachment: " + e.toString());
}
}
if (uris.size() > 0) {
emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
}
}
} catch (Exception e) {
LOG.e("EmailComposer", "Error handling attachments param: " + e.toString());
}
Uri to file will need triple slashes :
file:///storage/sdcard0/fgg/hh.html
As you can see here.
But i twill probably not work, so try to remove the "file:" part of your string :
File file = new File(attachments.getString(i).replace("file:","");
in a URI, you have different parts.
First the scheme:
http://
file://
ftp://
Then the path you want to access:
myfile.txt
videos/myvideo.avi
/storage/sdcard0/fgg/hh.html
So your complete URI should be:
file:///storage/sdcard0/fgg/hh.html
More information here:
http://developer.android.com/reference/java/net/URI.html
You can then build your File and check if it exist with this snippet
Uri.Builder builder = new Uri.Builder();
builder.scheme("file");
builder.path(myFilePath);
Uri uri = builder.build();
File file = new File(uri.getPath());
if (file.exists()) {
uris.add(uri);
// do whatever you want
}
EDIT:
If your JSON send you complete URI path, use this code instead:
Uri uri = Uri.parse(attachments.getString(i));
File file = new File(uri.getPath());
if (file.exists()) {
uris.add(uri);
// do whatever you want
}
Try two forward slashes after :
file://storage/sdcard0/fgg/hh.html
edit:
should be 3 forward slashes