So I am working on a project where if a manager registers a user he gets an email with a QR code (bitmap). The QR code is saved in cache. I want the QR code removed after the QR code is sent to the user, but a "cache" folder gets created (also shows up in gallery), and the image itself gets deleted but it remains there ( you cant see it, but its there as a grey square).
Any idea how to remove the created folder and the created bitmap compeletely?
My code:
BitmapSaver(Context mContext){
this.mContext=mContext;
this.cache = new DiskBasedCache(mContext.getCacheDir(), 1024 * 1024);
}
public static File saveImageToExternalStorage(Context context, Bitmap finalBitmap) {
destFolder =context.getCacheDir().getAbsolutePath();
String root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
// myDir = new File(root + "/saved_images");
// myDir.mkdirs();
long n = System.currentTimeMillis() / 1000L;
fname = "Image-" + n + ".jpg";
//file2 = new File(destFolder);
file = new File(destFolder+"/"+fname);
if (file.exists())
file.delete();
try {
Log.i("path",destFolder+"/"+fname);
FileOutputStream out = new FileOutputStream(destFolder+"/"+fname);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
MediaScannerConnection.scanFile(context, new String[]{file.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
url = Uri.parse(path);
Log.i("External",url.toString());
}
});
return file;
}
In Activity after email is sent:
BitmapSaver bms = new BitmapSaver(RegisterActivity.this);
bms.saveImageToExternalStorage(RegisterActivity.this, bitmap);
bms.file.delete();
First of all you need to know what MediaScannerConnection.scanFile does. It will update the file information to the current device, so other applications like gallery, file explorer, etc, can show the correct file information and content.
From your code, when you are saving the temporary file, you are also scanning it, because your app has changed the corresponding file, which is creating the file to be exact. So the file will be available to the other applications right away. But, since the file location is in your application cache directory, it will not accessible by other applications. Usually you must restart your device to update the file information if you don't call MediaScanner.scanFile. If you are creating a temporary file, I think you don't need to call MediaScanner.scanFile, since you will delete it right away.
Then after delete, you also need to re-scan the file again, so other applications will know that the file has been deleted.
Also, despite of using MediaScannerConnection.scanFile directly, if you are supporting android version < KitKat, you should broadcast with intent action Intent.ACTION_MEDIA_MOUNTED instead. And I also recommend that you are broadcasting the data directly, because from my experience MediaScannerConnection.scanFile failed from one of my test devices.
Intent mediaScanIntent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(Uri.fromFile(new File(imagePath)));
} else {
Uri fileUri = Uri.parse("file://" + imagePath);
mediaScanIntent = new Intent(Intent.ACTION_MEDIA_MOUNTED, fileUri);
}
context.sendBroadcast(mediaScanIntent);
Related
I have a method to start the camera and take a photo (working with API 24 and Higher) :
public void invokeCamera()
{
// create the image Uri
Uri pictureUri = FileProvider.getUriForFile(getContext(),getContext().getPackageName() + ".provider",createImageFile());
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// tell the camera where to save
intent.putExtra(MediaStore.EXTRA_OUTPUT,pictureUri);
// permission for saving the image
intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent,CAPTURE_REQ_CODE);
}
creating the image File:
private File createImageFile() {
File picturesDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String imgName = "myImageName_0X0Y02_test.jpg";
return new File(picturesDirectory.getPath(),"picture" + imgName );
}
the problem is :
this code is working without errors but sometimes i can't see the image in the Gallery , sometimes when i open the gallery after about 10 mins i see it there ! this is weird and i'm confused , am i missing something ?
All permissions are granted (Camera and full access to Storage)
Since you work with API 24 and higher, I will provide the code for it only. Basically, you need to tell the media scanner that a file was added so it can scan and add it straight away:
public static void scanMediaForChanges(Context context, File file){
MediaScannerConnection.scanFile(context,
new String[]{file.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
}
You are number #### with this problem. You should tell the media store about your new file.
To do that invoke the media scanner for your file.
Code has been posted a ### times. So google.
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.
According to user reviews, my app dosn't save on their phones (LG4, oneplus phones, android 5.1, Android 6.0)
For Android 6.0 I have solved the problem by using the new permission system.
But how can I be sure that the code actually works 100% on all devices?
Is there any improvment that can be made?
This is the onClick method that is run, when the user clicks the save button
But also ask for permission for Android 6 devices
public void saveQuote(View v) {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
//check if we have permissoin to WRITE_EXTERNAL_STORAGE
if (PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
//This method just create a bitmap of my edittext
saveBitmap();
} else {
//if permission is not granted, then we ask for it
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_WRITE_EXTERNAL_STORAGE);
}
}
}
This is the code that makes the saving operation:
private void saveImageToExternalStorage(Bitmap finalBitmap) {
String filename = "#" + pref_fileID.getInt(SAVE_ID, 0) + " Quote.JPEG";
//The directory in the gallery where the bitmaps are saved
File myDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString() + "/QuoteCreator");
//The directory in the gallery where the bitmaps are saved
File myDir = new File(root + "/QuoteCreator");
//creates the directory myDir.
myDir.mkdirs();
File file = new File(myDir, filename);
try {
FileOutputStream out = new FileOutputStream(file);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
Toast.makeText(getApplicationContext(), R.string.savedToast, Toast.LENGTH_LONG).show();
} catch (Exception e) {
e.printStackTrace();
}
/*
Tell the media scanner about the new file so that it is
immediately available to the user.
*/
MediaScannerConnection.scanFile(this, new String[]{file.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
}
Replace:
//The directory in the gallery where the bitmaps are saved
File myDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString() + "/QuoteCreator");
//The directory in the gallery where the bitmaps are saved
File myDir = new File(root + "/QuoteCreator");
with:
File root=
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File myDir=new File(root, "QuoteCreator");
(note: no /, no toString(), no +)
This ensures that File can handle the cases where root has or does not have a trailing /.
Replace:
out.flush();
out.close();
with:
out.flush();
out.getFD().sync();
out.close();
This ensures all bytes are written to disk before proceeding, particularly before having the file be indexed by the MediaStore.
And replace:
e.printStackTrace();
with something that will be useful for you in production. This statement logs something to LogCat. It's not even the preferred way of logging something to LogCat (which is using methods on android.util.Log, like e()). While you can see LogCat on your development machine for your own devices and emulators, you cannot see LogCat on user devices. There may be useful information in these exceptions that you are lacking. Personally, I use ACRA and its "silent exception" option for recording these sorts of exceptions that I am handling in-app but still want to know about. However, there are plenty of crash-reporting services, and presumably some of them offer an equivalent feature.
Requirements of app:
Saved files visible in stock device's file browser. (for user manipulation)
Files are deleted when app is uninstalled.
I came across this post (How can I let users access the internal storage directory of my app?) where the OP seemed to be wanting the same behavior I do and seemed to have it solved by using getExternalFilesDir() [last post] but when I use that method my files are not visible to the user outside of my app. So how do I get both functionalities in my app?
externalDir = mContext.getExternalFilesDir(null);
File myDir = new File(externalDir.toString() + "/my_snapshots");
myDir.mkdirs()
String fname = "Image-" + n + ".jpg";
File file = new File(myDir, fname);
FileOutputStream out = new FileOutputStream(file);
MediaScannerConnection.scanFile(mContext, new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
seemed to have it solved by using getExternalFilesDir()
That is the correct solution.
when I use that method my files are not visible to the user outside of my app
That is because you did not index the files using MediaScannerConnection and its scanFile() method. Once you do that, MTP clients and any on-device apps that use MediaStore will see your files.
I'm deleting a file as such
File fileToDelete = new File("filepath");
Boolean fileDeleted = fileToDelete.delete();
The fileDeleted is true and when I check the DDMS the file is not there but if I click on the gallery it still shows the image that was just deleted. I have to restart the emulator to see the change.
Is there any way to see the changes without having to restart the emaulator? I'm using eclipse
The gallery is using Android's media database to display the list of media. Deleting the file will not be reflected in the database until it scans the filesystem again. That is for example done after rebooting.
You can either delete the file directly through the database or force it to scan the file or folder you just deleted.
File fileToDelete = new File("filepath");
boolean fileDeleted = fileToDelete.delete();
// request scan
Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
scanIntent.setData(Uri.fromFile(fileToDelete));
sendBroadcast(scanIntent);
It is to do with how Gallery shows the Image files. Image file's Thumbnails are cached in the MediaStore and all the details are present in the Mediastore contentProvider.
Deleting the file will not update this database. But when you restart the emulator, Mediascanning is done by android. If MediaScanning can be triggered , gallery will stop showing the files
I use the following code (which is also much like my code for creating a video which also tells the media system about file changes and correctly updates the Gallery):
private void deleteVideo(String videoUrl)
{
File videoFile = new File(videoUrl);
if (!videoFile.delete())
{
Log.e(TAG, "Failed to delete " + videoUrl);
}
else
{
MediaScannerConnection.scanFile(mContext,new String[] { videoUrl }, null, new MediaScannerConnection.OnScanCompletedListener()
{
public void onScanCompleted(String path, Uri uri)
{
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
}
}