I am using ActivityResultLauncher to fetch uri of a file. Now I want to modify that file using fileoutputStream but can't, since fileoutputstream need path string. Here is a code that I am using to get File Uri:
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
// There are no request codes
Intent data = result.getData();
Uri uri = data.getData();//this gets me URI
}
}
});
public void openFileChooser()
{
Intent data = new Intent(Intent.ACTION_GET_CONTENT);
data.addCategory(Intent.CATEGORY_OPENABLE);
data.setType("*/*");
data = Intent.createChooser(data, "Choose a file");
Intent intent = Intent.createChooser(data, "Choose a file");
someActivityResultLauncher.launch(intent);
}
The code works perfect for picking and reading file. But I am not able to use below mentioned function to write same file using:
static void writeToFile(String path,String data,Context context) {
try {
FileOutputStream output = null;
output = new FileOutputStream(path, false);
output.write(data.getBytes());
output.close();
Toast.makeText(getApplicationContext(),"SAVED",Toast.LENGTH_LONG).show();
}
catch (IOException e) {
Toast.makeText(getApplicationContext(),"File write failed: " + e.toString(),Toast.LENGTH_LONG).show();
}
}
I have tried uri.getPath() but it is returning /document/msf:21. That is some complicated uri string and can't be used as file path. I have also been trying to use realPath functions mentioned in stackflow and other forums but do not find it working (they may work but they are depending upon API levels that making them not reliable). So, what actually want to ask:
1- Is there any other way to pick file to get actual path?
2- If there is no way to pick path, how can I write to File using direct Uri? and not using getpath or path string.
3- Is there a way to use fileoutstream with Uri in this scenario where Uri is only available?
Thanks
OutputStream os = getContentResolver().openOutputStream(data.getData());
You need to pass it through input stream amd buffer reader with output stream.
Try this on start intent
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, "JWI.pdf");
startActivityForResult(intent, CREATE_FILE);
Try this on activity result
try {
uri = _data.getData();
muri = uri.toString();
t.edit().putString("Uri", muri).commit();
final int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
getContentResolver().takePersistableUriPermission(uri, takeFlags);
DocumentFile doc = DocumentFile.fromSingleUri(this, uri);
boolean doo = doc.exists();
if (!doo) {
DocumentFile f = doc.createFile("pdf/plain", "JAMZ.pdf");}
ContentResolver cr = getContentResolver(); OutputStream output =
cr.openOutputStream(uri);
java.io.InputStream asset = getAssets().open("JWI.pdf");
final byte[] buffer = new byte[1024];
int size;
while ((size = asset.read(buffer)) != -1) {
output.write(buffer, 0, size);
}
asset.close();
output.close();
} catch (Exception e) {}}}
Related
I was wondering, if I launch the following Intent.ACTION_GET_CONTENT
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("application/zip");
startActivityForResult(intent, RequestCode.REQUEST_CHOOSE_BACKUP_FILE);
and try to read the returned Uri from intent in the following way.
Uri uri = data.getData();
// Figure out extension
ContentResolver contentResolver = getContext().getContentResolver();
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
final String extension = mimeTypeMap.getExtensionFromMimeType(contentResolver.getType(uri));
File temp = null;
try {
temp = File.createTempFile(Utils.getJStockUUID(), "." + extension);
} catch (IOException e) {
e.printStackTrace();
}
// Delete temp file when program exits.
temp.deleteOnExit();
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = getContext().getContentResolver().openInputStream(uri);
outputStream = new FileOutputStream(temp);
byte[] buffer = new byte[8 * 1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
Log.e(TAG, "", e);
} finally {
close(outputStream);
close(inputStream);
}
Is READ_EXTERNAL_STORAGE permission ever required?
I tested a few round. To my surprise, I can perform success read without request for READ_EXTERNAL_STORAGE.
I just would like to confirm READ_EXTERNAL_STORAGE isn't really required to read Uri from Intent.ACTION_GET_CONTENT, in all type of situation.
I have had instances where a user had a third-party file manager installed (File Manager+) and in those cases reading from the Uri returned by ACTION_GET_CONTENT would fail with a permission error if the READ_EXTERNAL_STORAGE permission was not first granted (only if they used the third-party app to select the file, if they used Google Drive or the normal system selection it worked fine without the permission).
I was able to replicate the behavior by installing File Manager+ on one of my emulators with the Play Store and trying it out.
I am trying to create an app that will store some PDf files as base64 encoded Strings in a database and then later decode them and dispay them (with an Intent to open other PDF reader).
But something doesn't work properly. I know that the byte array is the same before and after storage as encoded String, so that isn't the problem.
I think the problem is somewhere in the process of creating a File to open with the intent, but I'm not sure.
Creating the String:
byte[] b = Files.toByteArray(pdf);
String encodedFile = Base64.encodeToString(b, Base64.DEFAULT);
pdf is the File I get from this:
else if (requestCode == PICK_PDF_REQUEST && resultCode == RESULT_OK && data != null && data.getData() != null)
{
Uri uri = data.getData();
try {
String fileName = uri.toString();
fileName = fileName.substring(fileName.length()-10);
service.addPDF(order, fileName, new File(uri.getPath()));
} catch (IOException e) {
e.printStackTrace();
}
updateFileList();
}
Getting File from String:
case PDF:
try {
byte[] pdfAsBytes = Base64.decode(file.getContent(), Base64.DEFAULT);
File dir = getStorageDir();
File pdffile = new File(dir, file.getName());
if(!pdffile.exists())
{
pdffile.getParentFile().mkdirs();
pdffile.createNewFile();
}
Files.write(pdfAsBytes, pdffile);
Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
pdfIntent.setDataAndType(Uri.fromFile(pdffile), "application/pdf");
pdfIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(pdfIntent);
} catch (IOException e) {
e.printStackTrace();
}
break;
This code runs with no errors, but the PDF viewer cannot display the file. I have tried with several viewers. I suspect the resulting file
Turns out I needed to save to external storage instead of the dir.
File dir = getStorageDir();
Should be
File dir = Environment.getExternalStorageDirectory();
Then it works.
I have a problem. I'd like to share my MP3 File to Whatsapp but it don't work! Here my Share Intent:
public void shareAchieve() {
Intent shareAchievement = new Intent();
shareAchievement.setType("audio/*");
shareAchievement.putExtra(Intent.EXTRA_STREAM, Uri.parse("android.resource://de.logtainment.ungesoundboard/" + R.raw.achieve + ".mp3"));
startActivity(Intent.createChooser(shareAchievement, "Teile Längstes Achievement"));
}
But it doesnt work! Sorry for my bad english i'm from germany.
As #CommonsWare Said Very few apps support the android:resource scheme
To make your app compatible with all apps.
You have to copy your raw resource to Internal Storage by this method
private String CopyRAWtoSDCard(int raw_id,String sharePath) throws IOException {
InputStream in = getResources().openRawResource(raw_id);
FileOutputStream out = new FileOutputStream(sharePath);
byte[] buff = new byte[1024];
int read = 0;
try {
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
} finally {
in.close();
out.close();
}
return sharePath;
}
Replace your method by below code
public void shareAchieve() {
Intent shareAchievement = new Intent(Intent.ACTION_SEND);
shareAchievement.setType("audio/*");
String sPath= null;
try {
sPath = CopyRAWtoSDCard(R.raw.filename, Environment.getExternalStorageDirectory()+"/filename.mp3");
} catch (IOException e) {
e.printStackTrace();
}
Uri uri = Uri.parse(sPath);
shareAchievement.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareAchievement.putExtra(Intent.EXTRA_STREAM,uri);
startActivity(Intent.createChooser(shareAchievement, "Teile Längstes Achievement"));
}
Also add
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
You have missed adding /raw/ to the path and change your Intent like
change
Intent shareAchievement= new Intent();
shareAchievement.putExtra(Intent.EXTRA_STREAM, Uri.parse("android.resource://de.logtainment.ungesoundboard/" + R.raw.achieve + ".mp3"));
to
Intent shareAchievement= new Intent(Intent.ACTION_SEND);
shareAchievement.putExtra(Intent.EXTRA_STREAM, Uri.parse("android.resource://de.logtainment.ungesoundboard/raw/achieve.mp3"));
I'm trying to share a downloaded bitmap via Android's ShareActionProvider, and I'm having an issue actually passing the bitmap to appropriate apps (such as Messenger, Google+, Gmail). When I pass the intent with the uri, nothing happens (image isn't populated in the 3rd party app).
Here is my Intent:
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
writeToDirectory(bitmap, "cached-image.png"); // write bitmap to file system in order to share
intent.putExtra(Intent.EXTRA_STREAM, getFileUri("cached-image.png"))
mShareActionProvider.setShareIntent(intent);
I'm currently saving the bitmap to file using the following:
public String writeToDirectory(Bitmap bitmap, String filename) {
assert context != null;
assert bitmap != null;
assert filename != null;
// Ensure directory exists and create if not
String path = Environment.getExternalStorageDirectory() + File.separator + "myApp";
File f = new File(path);
boolean dirExists = f.isDirectory();
if (!f.isDirectory() && !f.exists()) {
dirExists = f.mkdirs();
}
if (dirExists) {
File file = new File(sharedPrivateExternalPath, filename);
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.close();
} catch (IOException ex) {
e(TAG, "Write failed!");
e(TAG, ex.getMessage());
ex.printStackTrace();
}
return file.toString();
} else {
return "";
}
}
and I'm retrieving the Uri with this method
public Uri getFileUri(String filename) {
assert context != null;
assert filename != null;
String path = Environment.getExternalStorageDirectory() + File.separator + "myApp";
File file = new File(path, filename);
return Uri.fromFile(file);
}
I've checked that the file gets written to the appropriate place (Uri is file:///storage/emulated/0/myApp/cached-image.png) and was able to view the image there (did an adb pull from the device), though the image doesn't get passed. I don't see any errors in the log (no FileNotFoundException or anything of the sort). Is this a file permission issue? Am I not able to share to a "non-public" location?
If I change getExternalStorageDirectory() + File.separator + "myApp"; to plain getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) it works fine... but it's bothering me it doesn't work the other way :).
Any help would be great!
Turns out it had to do with code I didn't post (surprise, surprise). I was attempting to set the shareIntent in the setOnShareTargetSelectedListener... which apparently you can't do.
My problem was with this code snippet
mShareActionProvider = (ShareActionProvider)
MenuItemCompat.getActionProvider(shareItem);
mShareActionProvider.setOnShareTargetSelectedListener(new ShareActionProvider.OnShareTargetSelectedListener() {
#Override
public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) {
String urlKey = mImageUrlList.get(mImageGallery.getCurrentItem()) + "\n";
// This is the problem line
mShareActionProvider.setShareIntent(getImageIntent(app.getImageCache().get(urlKey)));
return false;
}
})
In my onCreate() I do this check:
//
// check if we have a PDF viewer, else bad things happen
//
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setType("application/pdf");
List<ResolveInfo> intents = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (intents == null || intents.size() == 0) {
// display message then...
finish();
}
On my HTC Desire, this doesn't return a match, even though I have Adobe's PDF viewer. An answer to this question android: open a pdf from my app using the built in pdf viewer mentions that Adobe may not have any public Intents, so the above check will obviously return nothing.
Can anyone verify whether you should be able launch Acrobat from an intent, or is there some other method or PDF viewer to use.
The actual use case is downloading copies of invoices and storing them on local storage using code such as:
URL url = new URL(data);
InputStream myInput = url.openConnection().getInputStream();
FileOutputStream fos = openFileOutput(fname, Context.MODE_WORLD_READABLE);
// transfer bytes from the input file to the output file
byte[] buffer = new byte[8192];
int length;
while ((length = myInput.read(buffer)) > 0) {
fos.write(buffer, 0, length);
progressDialog.setProgress(i++);
}
fos.close();
and then to show
// read from disk, and call intent
openFileInput(fname); // will throw FileNotFoundException
File dir = getFilesDir(); // where files are stored
File file = new File(dir, fname); // new file with our name
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.fromFile(file));
intent.setType("application/pdf");
startActivity(intent);
Connect your phone to you PC, start Eclipse and open the LogCat. Then download a PDF file with the browser and open it. You should see a line such as (I used the HTC desire):
09-14 17:45:58.152: INFO/ActivityManager(79): Starting activity: Intent { act=android.intent.action.VIEW dat=file:///sdcard/download/FILENAME.pdf typ=application/pdf flg=0x4000000 cmp=com.htc.pdfreader/.ActPDFReader }
Have a go with an explicit intent using the component information. Docs say here:
>
component -- Specifies an explicit name of a component class to use for the intent. Normally this is determined by looking at the other information in the intent (the action, data/type, and categories) and matching that with a component that can handle it. If this attribute is set then none of the evaluation is performed, and this component is used exactly as is. By specifying this attribute, all of the other Intent attributes become optional.
Downside is you will be bound to the htc reader. But you could try an implicit intent first and if that fails try the explicit intent as a fallback.
-Copy the following code in your activity. Call the function CopyReadAssets("File_name.pdf") from onCreate() function. Place the File_name.pdf file in assets folder.
private void CopyReadAssets(String pdfname)
{
AssetManager assetManager = getAssets();
InputStream in = null;
OutputStream out = null;
File file = new File(getFilesDir(), pdfname);
try
{
in = assetManager.open(pdfname);
out = openFileOutput(file.getName(), Context.MODE_WORLD_READABLE);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e)
{
Toast.makeText(getApplicationContext(), "Pdf Viewer not installed", Toast.LENGTH_SHORT).show();
}
try
{
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(
Uri.parse("file://" + getFilesDir() + "/"+pdfname),
"application/pdf");
startActivity(intent);
}catch (Exception e) {
// TODO: handle exception
Toast.makeText(getApplicationContext(), "Pdf Viewer not installed" ,Toast.LENGTH_SHORT).show();
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException
{
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
}