Open image from external public directory through intent - android

Recently I am developing a file sharing application and I created a GridView, where the downloaded files are being shown. From this View, I would like to be able to open the default application through an intent, to open the whole file. Currently I am testing the app with only image files. All the files are downloaded to the external public directory this way:
File externalFolder = new File(Environment.getExternalStorageDirectory(), "My application");
if(!externalFolder.exists()){
externalFolder.mkdir();
}
...
File folder = new File(externalFolder, "Images");
if(!folder.exists()){
folder.mkdir();
}
...
String filename = folder.getAbsolutePath() + "/" + fileToDownload.getName() + "." + fileToDownload.getExtension();
FileOutputStream fos = new FileOutputStream(filename);
When the file is downloaded, I scan it with MediaScannerConnection.scanFile. The scan is successful, the picture is visible among other files in Photos app.
After the file is downloaded, I am able to extract a thumbnail in the adapter of the GridView, so I surely have a valid path to the file.
And where the fun begins: I tried to set an onClickListener to the GridView items in the adapter to be able to open the picture in Photos app this way:
listItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
if(new File(current.getPath()).exists()){
Log.e("path", "valid");
}else{
Log.e("path", "invalid");
}
Log.e("path", Uri.parse("content://"+current.getPath()).toString());
intent.setDataAndType(Uri.parse("content://"+current.getPath()), "image/*");
getContext().startActivity(intent);
}
});
The Intent is created successfully, I get the following in the log:
E/path: valid
E/path: content:///storage/emulated/0/My application/Images/best_picture_ever.jpeg
I have the option to choose among apps to open. When I select the app, it fails to open the image, like when it does not exist. All the 5 applications.
I tested this on my device with Oreo, and on two emulated devices with Nougat and Lollipop, all of them behaves the same way.
What am I doing wrong?

What am I doing wrong?
You are not creating a valid Uri. You cannot put content:// in front of arbitrary things and have a useful Uri, any more than you can put https:// in front of arbitrary things and have a usable URL.
Use FileProvider to serve up this file.

Related

How to delete a file using the path

I'm building an app that allows the user to save the bitmap or share it without saving it. The 2nd functionality doesn't quite work. I understand that the app needs to save the file to the device before sharing it on a social media app so my idea was, immediately after the file was successfully shared, to automatically delete the file from the device. I've build a delete method trying 2 different approaches and neither have worked:
First approach:
public void deleteFile(String path){
File file = new File(path);
try {
file.getCanonicalFile().delete();
} catch (IOException e) {
e.printStackTrace();
}
}
Second approach:
public void deleteFile(String path){
File file = new File(path);
boolean deleted = file.delete();
}
And I'm calling deleteFile(String) from the sharing method:
public void shareMeme(Bitmap bitmap) {
String path = MediaStore.Images.Media.insertImage(Objects.requireNonNull(getContext()).getContentResolver(), bitmap, "Meme", null);
Uri uri = Uri.parse(path);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
share.putExtra(Intent.EXTRA_TEXT, "This is my Meme");
getContext().startActivity(Intent.createChooser(share, "Share Your Meme!"));
deleteFile(path);
}
With respect to your stated problem, insertImage() returns a string representation of a Uri. That Uri is not a file. Calling getPath() on it is pointless, and you cannot delete anything based on that path.
More broadly, if your intention is to delete the content right away:
Do not put it in the MediaStore
Do not share it, as you will be deleting it before the other app has a chance to do anything with it
If you want to share it, but then delete it:
Do not put it in the MediaStore
Delete it the next day, or in a few hours, or something, as you have no good way of knowing when the other app is done with the content
To share an image with another app without using the MediaStore:
Save the image to a file in getCacheDir() (call that on a Context, such as an Activity or Service)
Use FileProvider to make that file available to other apps
Beyond that:
Do not use wildcard MIME types in ACTION_SEND. You are the one who is supplying the content to send. You know the actual MIME type. Use it.
Note that there is no requirement for an ACTION_SEND activity to honor both EXTRA_TEXT and EXTRA_STREAM. Most seem to do so, but that behavior is outside of the ACTION_SEND specification.
Note that insertImage() is deprecated on Android Q.
First, you need to check if your file exists, (maybe you set the wrong path?). Then delete the file
File file = new File(path);
if (file.exists()){
if (file.delete()) {
Toast.makeText(this, "file Deleted :" + path, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "file not Deleted :" + path, Toast.LENGTH_SHORT).show();
}
}

Rescan older files with mediascannerconnection in local storage. files get not updated on windows

My android-application writes a bunch of csv-files and jpg-files to the internal storage of the device. I am using MediaScannerConnection.scanFile() to make the files accessable from my windows-system without rebooting the android-device.
private void scanFiles() {
File targetDirectory = new File(Environment.getExternalStorageDirectory(), "DIR_OF_MY_APP");
if (targetDirectory.exists()) {
List<File> filesToScan = getFiles(targetDirectory);
List<String> filePathsToScan = new ArrayList<>();
for(File file : filesToScan) {
filePathsToScan.add(file.getPath());
}
MediaScannerConnection.scanFile(this, filePathsToScan.toArray(new String[0]), null, new MediaScannerConnection.OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
Log.d("OK", "Path: " + path);
Log.d("OK", "Uri : " + uri);
}
});
}
}
In my Logcat i can see every file is getting scanned. The new ones and the old ones.
My problem is when my app is adding new lines to an existing csv-file and the file is getting scanned, The new lines do not appear in the csv-file when its opend from my pc. How can i fix this problem?
I already tried to rename all the files from filename to tmp_filename, rescann all the files and rename them back from tmp_filename to filename and rescann them again. After this, i have can see the oldfilename-file and the tmp_oldfilename-file on my windows-computer. The tmp_oldfilename-file can not be opend (Unknown error on [memory-adress]). The oldfilename-file shows the not updated csv-file.
I also tried to use a intent to scan the files, since some questions on so say its going to update them:
for(File file : filesToScan) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
Uri contentUri = Uri.fromFile(file);
mediaScanIntent.setData(contentUri);
sendBroadcast(mediaScanIntent);
Log.d("OK", "File: " + file.getName() + " scanned...");
}
here i can see the files getting scanned too, but they do not show up updated on my windows-computer.
Okay the only solution i came up with, is to set the usb-mode to load-only (this must be done by hand from the user) before performing the MediaScannerConnection.scanFile();. After this is done, the user can set the usb-mode back to mtp and than the csv-files will show up with the new added lines.
This is a really bad workarround, but still better than rebooting the device. If someone has an better solution, pls share.

Android - Select file from internal storage folder

I have saved a bunch of videos in an internal storage folder. Afterwards, I want the user to be able to select one of these videos in this specific folder. I tried using ACTION_GET_CONTENT in an attempt to let another app do this for me, without any success, as it just opens up a file browser in some other directory.
What I have now is:
public static File getOwnVideosDirectory(Context context) {
String ownVideosDirPath =
context.getFilesDir().getAbsolutePath() + File.separator + "OwnVideos";
File ownVideosDir = new File(ownVideosDirPath);
if (!ownVideosDir.exists()) {
ownVideosDir.mkdirs();
}
return ownVideosDir;
}
private void dispatchExistingVideo() {
Log.d(LOG_TAG, "> dispatchExistingVideo");
Intent videoPicker = new Intent(Intent.ACTION_GET_CONTENT);
File ownVideosDir = Utility.getOwnVideosDirectory(getContext());
videoPicker.setDataAndType(Uri.fromFile(ownVideosDir), "video/*");
if (videoPicker.resolveActivity(getContext().getPackageManager()) != null) {
startActivityForResult(videoPicker, REQUEST_EXISTING_VIDEO);
}
}
So I'm wondering, am I doing something wrong or is it impossible like this. If impossible: is there any library,... available that would allow me to do what I want, or any direction on how I could implement this myself as a last resort?
Thanks in advance
Please take a look at that library - Material File Picker
It allows to show a dialog with the specified path using .withPath(Utility.getOwnVideosDirectory(getContext()).getAbsolutePath()).
The whole creation code:
new MaterialFilePicker()
.withActivity(this)
.withRequestCode(1)
.withFilter(Pattern.compile(".*\\.txt$")) // Filtering files and directories by file name using regexp
.withFilterDirectories(true) // Set directories filterable (false by default)
.withHiddenFiles(true) // Show hidden files and folders
.withPath(Utility.getOwnVideosDirectory(getContext()).getAbsolutePath())
.start();

Open PDF in Android App

First I would like to say that I have tried several solutions, methods, suggestions and referred to several links on here to open a PDF. You can call me slow, but I have at least tried. I would like to open a PDF in my Android Application. I am on a Nexus 10 tablet. I cannot use a web view. I want to open this pdf via my OnClickListener in one of my fragments. I think my biggest problem is I am unsure where to save my PDF. I have tried res and assets folders. Many example use /sdcard/ - is that just saving it on my device? If so where / how to get path? I have saved a .pdf file in adobe reader on my tablet can I access that path? I am using API min 16 target API 19.
I have tried many variations of this
public void onClick(View v)
{
switch(v.getId())
{
case R.id.bizbro3:
File pdfFile = new File( // I don't know what to put here / where to save pdf. Have tried /sdcard/ , getresrouces, absolutepath, ect.);
if(pdfFile.exists())
{
Uri path = Uri.fromFile(pdfFile);
Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
pdfIntent.setDataAndType(path, "application/pdf");
pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try
{
startActivity(pdfIntent);
}
catch(ActivityNotFoundException e)
{
Toast.makeText(v.getContext(), "Something went wrong. Returning to the Main Menu",
Toast.LENGTH_LONG).show();
fragment = new FragmentThree();
fragment.setArguments(args);
FragmentManager frgManager = getFragmentManager();
frgManager.beginTransaction().replace(R.id.content_frame, fragment)
.commit();
}
}
}
first declare permissions in manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
then try this
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/yourfolder";
File file = new File(path,"file.pdf");
The reason your code fails is that the extracted APK folders, assets/ included, are application-private,
and cannot be viewed by an external app. The fact that you have "invited" such an external app to view your data
by issuing an intent, makes no difference.
You will need to copy the file to a non-private location (typically: sdcard) and
things will start working:
void coptAssetToSdcard() {
InputStream input = getAssets().open("myFile.pdf");
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File (sdCard.getAbsolutePath() + "/myDir/");
dir.mkdirs();
File outFile = new File(dir, "destFile.pdf");
OutputStream out = new FileOutputStream(outFile);
byte[] buffer = new byte[10*1024];
int nBytes;
while ((nBytes = input.read(buffer)) != -1) {
out.write(buffer, 0, nBytes);
}
out.flush();
out.close();
input.close();
}
Remember to place this func in an AsyncTask or something.
I also see that your code has a fall through (catch ActivityNotFoundException) for
the case device has no pdf viewer, which is the correct thing to do.

About opening .PDF in an Android App (locally stored)

So I have these buttons in my app that I want to open .PDF files from within the application once pressed.
I've stored my files in the /res/raw directory. Let's call them file1.PDF, file2.PDF etc.
public void onClick(View v) {
switch (v.getId()) {
case R.id.file1:
Log.i(TAG, "Button one pressed");
openPDF(Uri.parse("android:resource://test.pdf.files/raw/res/file1.PDF"));
break;
}}
Above is my code for the onClick. Underneath is the code for the openPDF function.
public void openPDF(Uri url)
{
File file = new File(url.toString());
Log.i(TAG, url.toString());
if (file.exists())
{
Log.i(TAG, "File exists");
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(file), "application/pdf");
try
{
getApplicationContext().startActivity(intent);
}
catch (ActivityNotFoundException e)
{
Log.e(TAG,"Activity not found exception");
}
}else{
Log.i(TAG,"404: File not found!");
}
}
My problem is as follows, I can never get past the if (file.exists()) check. I've tried making the File file take both Uri, URI and String, I have also tried more or less every variation of the filepath, like "res/raw/file1.PDF", "raw/file.PDF", "test.pdf.files/raw/res/file1.PDF". But in any of these cases I just can't find the file I want to open, can anyone see why this won't work?
I have been trying to fix this for hours and hours and I just can't seem to wrap my head around it.
I can never get past the if (file.exists()) check
Of course. Your Uri does not point to a file. Your Uri points to a resource. Resources are not files. Files are not resources.
You can try startActivity() with an Intent that uses your resource Uri. If you find that PDF viewers refuse to work with that Uri, you will need to copy the resource to a local world-readable file (e.g., on external storage) and use that copy with Uri.fromFile() to launch your PDF viewer.

Categories

Resources