How to open downloaded file using .openFileDescriptor() - android

I used to open a downloaded file using the following code :
final String filePath = cursor.getString(
cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME));
if (TextUtils2.isNotEmpty(filePath)) {
apkFile = Uri.fromFile(new File(filePath));
}
startActivity(new Intent(Intent.ACTION_VIEW)
.setDataAndType(apkFile, UPDATE_APK_FILE_MIME_TYPE)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
But from API 24 DownloadManager.COLUMN_LOCAL_FILENAME is deprecated and I need to use openFileDescriptor (Uri uri,
String mode) method instead.
But I don't know how i can get the file path from the Fd to use it as data for the intent.
Does anyone know how to do it or whats the alternative to open downloaded file ?

To retrieve the good old absolute path of the downloaded file just :
String absolutePath = Uri.parse(cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))).getPath();
And of course :
File file = new File(absolutePath);

instead of
cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME)
you can use
cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI)
which return the uri for apk file you need

Related

Append data to text file in android 10 (API 29)

I am working on an application where app writes a log file with the currentdate as filename
Ex: 20200710.txt
The earlier was working fine before android 10 but from android 10 the code is no longer writing the file in the external storage.
So I have modified the code a bit for android 10 especially
string logDir = "Documents/MyApp_Data/logs/";
Context context = MyApplication.Context;
ContentValues values = new ContentValues();
values.Put(MediaStore.MediaColumns.DisplayName, filename);
values.Put(MediaStore.MediaColumns.MimeType, "text/plain"); //file extension, will automatically add to file
values.Put(MediaStore.MediaColumns.RelativePath, logDir);
var uri = context.ContentResolver.Insert(MediaStore.Files.GetContentUri("external"), values);
Stream outputStream = context.ContentResolver.OpenOutputStream(uri, "rw");
outputStream.Write(Encoding.UTF8.GetBytes(message));
outputStream.Close();
The above code is working for android 10 but it is creating multiple log files instead I want to update the file if the file already exists. I am not getting a way to check if the file exists then append new data in the existing file. Can someone please let me know? The above code is in Xamarin android but if you have any suggestion that will work in android then I will convert that code to Xamarin android
Thanks in advance
This code corrects (especially words' upper/lower cases) vaibhav ones and use blackapps suggestion to include text append. Can write txt or json.
Good to write text in persistent folders (e.g. /storage/self/Downloads) without user interaction on Android 10+ (actually not tested on 11, but should work).
// filename can be a String for a new file, or an Uri to append it
fun saveTextQ(ctx: Context,
relpathOrUri: Any,
text: String,
dir: String = Environment.DIRECTORY_DOWNLOADS):Uri?{
val fileUri = when (relpathOrUri) {
is String -> {
// create new file
val mime = if (relpathOrUri.endsWith("json")) "application/json"
else "text/plain"
val values = ContentValues()
values.put(MediaStore.MediaColumns.DISPLAY_NAME, relpathOrUri)
values.put(MediaStore.MediaColumns.MIME_TYPE, mime) //file extension, will automatically add to file
values.put(MediaStore.MediaColumns.RELATIVE_PATH, dir)
ctx.contentResolver.insert(MediaStore.Files.getContentUri("external"), values) ?: return null
}
is Uri -> relpathOrUri // use given Uri to append existing file
else -> return null
}
val outputStream = ctx.contentResolver.openOutputStream(fileUri, "wa") ?: return null
outputStream.write(text.toByteArray(charset("UTF-8")))
outputStream.close()
return fileUri // return Uri to then allow append
}

How to open Context.MODE_PRIVATE files with external apps

I have a string (called comments) that contains some text that I want to display using an external app. I initially create the file like so:
String end = "rtf";
FileOutputStream outputStream;
try {
outputStream = openFileOutput("document." + end, Context.MODE_PRIVATE);
outputStream.write(comments.getBytes());
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
However I am unable to open the file with an external application when I try the following:
String type = "text/rtf";
Intent intent = new Intent (Intent.ACTION_VIEW);
File file = new File(getFilesDir() + "/document." + end);
Uri fileUri = Uri.fromFile(file);
intent.setDataAndType(fileUri,type);
startActivityForResult(intent, Intent.FLAG_GRANT_READ_URI_PERMISSION);
The message that I receive when I open try to the document with the external app is:
"open failed: EACCESS (Permission denied)."
Please advise. Thanks.
However I am unable to open the file with an external application when I try the following:
Correct. Intent.FLAG_GRANT_READ_URI_PERMISSION is for use with a ContentProvider, not for bare file:// Uri values, such as you are using. Use FileProvider to add such a ContentProvider to your app. See also the "Sharing Files" training module and this sample app.
Bear in mind that there's a good chance that your next problem will be an ActivityNotFoundException, as relatively few Android devices will have an app that will support the text/rtf MIME type.

Get a Content URI from a File URI?

I am using the DownloadManager to download an image to the system's gallery and then in the Broadcast receiver (once the download succeeds) using an Intent to set the image as the wallpaper.
Everything was working fine but then recently on 4.4 I started to get an exception in the Photos/Google+ app because it is expecting a content URI and not a file URI.
So my question is if anyone knows how to convert a full file path/URI (file://) into a content style URI (content://)?
Sorry for the lack of source code, I am away from the computer that has the source, but I hope the question makes sense without it, get a content style uri from a full path.
EDIT:
The image is copied into the system's gallery or media gallery, not saved within my apps internal storeage.
Here is an example of what I want to convert:
file:///storage/emulated/0/Pictures/Rockstar/image.jpg
to
content://media/internal/images/media/445
EDIT 2:
Here is the error that I get from the Google+ app:
04-21 10:50:35.090: E/AndroidRuntime(7220): FATAL EXCEPTION: main
04-21 10:50:35.090: E/AndroidRuntime(7220): Process: com.google.android.apps.plus, PID: 7220
04-21 10:50:35.090: E/AndroidRuntime(7220): java.lang.RuntimeException: Unable to resume activity
{com.google.android.apps.plus/com.google.android.apps.photos.phone.SetWallpaperActivity}:
java.lang.IllegalArgumentException: Image URI must be of the content scheme type
Here is the code that I use to let the user set the wallpaper:
String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
Uri u = Uri.parse(uriString);
Intent wall_intent = new Intent(Intent.ACTION_ATTACH_DATA);
wall_intent.setDataAndType(u, "image/*");
wall_intent.putExtra("mimeType", "image/*");
Intent chooserIntent = Intent.createChooser(wall_intent,
"Set As");
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(chooserIntent);
}
Where uriString is:
file:///storage/emulated/0/Pictures/Rockstar/image.jpg
I was able to figure it out. It was a combination of the code found here: Converting android image URI and scanning the media file after downloading.
So after the file finished downloading I get the path and do the following:
String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
//Update the System
Uri u = Uri.parse(uriString);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, u));
//Get the abs path using a file, this is important
File wallpaper_file = new File(u.getPath());
Uri contentURI = getImageContentUri(context, wallpaper_file.getAbsolutePath());
For some reason starting the media scanner, newing the file, and getting the absolute path are important, I'm not exactly sure why but I can't spend any more time on this!
The way to convert from a file URI to a content URI is as follows (taken from the linked StackOver flow post:
public static Uri getImageContentUri(Context context, String absPath) {
Log.v(TAG, "getImageContentUri: " + absPath);
Cursor cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
, new String[] { MediaStore.Images.Media._ID }
, MediaStore.Images.Media.DATA + "=? "
, new String[] { absPath }, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
return Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI , Integer.toString(id));
} else if (!absPath.isEmpty()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, absPath);
return context.getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
return null;
}
}
Maybe this will help someone in the future.
So my question is if anyone knows how to convert a full file path/URI (file://) into a content style URI (content://)?
Implement a ContentProvider. FileProvider offers an out-of-the-box solution for serving up local files.
I'm not sure about the technique you are using to set the wallpaper but the easiest way is probably to use WallpaperManager.setStream() which doesn't require any URI.
Also note that a file URI only works between apps if the file is publicly accessible so a content URI is a more general solution.
Using a content URI implies that a ContentProvider will serve the file. Which one depends on where your file is located.
If your app has a direct read access to the file, you can implement a content provider in your app by using for example the FileProvider class of the support library, but this should really only be used if the file is located in the private data storage of your app.
If the image is added to the system media gallery, you should probably use the URI provided by the MediaStore.

Intent.ACTION_VIEW returning wrong file path

My app intends to launch a file using ACTION_VIEW.
The following code returns the file path of the selected file
if(Intent.ACTION_VIEW.equals(action)){
String Path = intent.getDataString();
//file processing code
}
It works fine when the selected file has no spaces in it. e.g Path becomes "/mnt/sdcard/sample.pdf" , but when i select a file with spaces in it's name such as "/mnt/sdcard/4C 1099 + 2 WOOO6.pdf" Path becomes "/mnt/sdcard/4C%20%20%201099%20%20%20%2B%20%202%20W0006.pdf"
Any help?
if(Intent.ACTION_VIEW.equals(action)){
Uri uri = intent.getData();
path = uri.getPath();
path = path.replace("%20", " ");
}

ACTION_GET_CONTENT : How to get file-ending when searching for multiple file types?

I am using an Intent to let the user select a file, and after the user has done that I want to know what kind of file-type the selected file is.
The intent:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
In my onActivityResult I want to extract the path from the intent through intent.getData() and all of it's "submethods" (getScheme(), getLastPathSegment() etc). However, I only get the Uri for the selected file.
Example of Uris':
Uri: content://media/external/audio/media/15185 //This is an audiofile
Uri: content://media/external/audio/media/20 //Another audiofile
Uri: content://media/external/images/media/20577 //this is a picture
Uri: file:///storage/emulated/0/testWed%20Apr%2017%2011%3A10%3A34%20CEST%202013 //This is a file
I've seen solutions of how to get the absolute path when the user is only allowed to chose images or audios. But how do I do I get the absolutePath (the real path with the name and file-ending, e.g. MyPicture.jpeg) if I want to allow the user to select from different file types?
The code I've been twiggling with to try to get the path-name in onActivityResult(int requestCode, int resultCode
String fileName = data.getData().getLastPathSegment().toString();
System.out.println("Uri: " +data.getData().toString());
File f = new File(fileName);
System.out.println("TrYinG wIWThA Da FiLE: " +f.getAbsolutePath());
System.out.println("FileNAME!!!: "+fileName);
By file-ending I'm assuming you mean the file extension? (i.e. mp4)
If so you can use the ContentResolver to get the mimetype:
String mimeType = getContentResolver().getType(sourceUri);
This will give you the mimetype assuming they are within the MediaStore which they should if you have the content Uri. so you'll get something like "image/png", "video/mp4", etc.
Then you can use MimeTypeMap to get the file extension from the mimetype:
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
Note that you should never substring the mimetype after "/" to get the extension as some mimetype does not use its extension such as ".mov" files which has the mimetype "video/quicktime"

Categories

Resources