How to notify MediaStore that Some Items are Deleted? - java

I have a simple gallery app in which user can take or delete photos. For taking photos this works in notifying MediaStore of the newly created file:
File file = new File(storageDir, createImageName());
final Uri uri = Uri.fromFile(file);
Intent scanFileIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
sendBroadcast(scanFileIntent);
I delete photos but local gallery app still shows them as a blank file.
This does not work. I target minimum Android 5.0 :
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "Folder where application stores photos");
final Uri uri = Uri.fromFile(file);
Intent scanFileIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
sendBroadcast(scanFileIntent);
What I'm trying to do is to scan the folder my application creates when a file deleted to inform MediaStore of the images and folders deleted. How can I do this?

Here is a method that deletes any record(s) of a media file from the MediaStore.
Note that the DATA column in the MediaStore refers to the file's full path.
public static boolean deleteFileFromMediaStore(
Context context, String fileFullPath)
{
File file = new File(fileFullPath);
String absolutePath, canonicalPath;
try { absolutePath = file.getAbsolutePath(); }
catch (Exception ex) { absolutePath = null; }
try { canonicalPath = file.getCanonicalPath(); }
catch (Exception ex) { canonicalPath = null; }
ArrayList<String> paths = new ArrayList<>();
if (absolutePath != null) paths.add(absolutePath);
if (canonicalPath != null && !canonicalPath.equalsIgnoreCase(absolutePath))
paths.add(canonicalPath);
if (paths.size() == 0) return false;
ContentResolver resolver = context.getContentResolver();
Uri uri = MediaStore.Files.getContentUri("external");
boolean deleted = false;
for (String path : paths)
{
int result = resolver.delete(uri,
MediaStore.Files.FileColumns.DATA + "=?",
new String[] { path });
if (result != 0) deleted = true;
}
return deleted;
}

Related

How to copy video or image file from one location into another using media store api for android 11

public boolean moveFilesToSentPath(Context context, String type, String filePath, String fileName) {
String folderPath = getFolderPath(type);
File dir;
/* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
dir = new File(Environment.DIRECTORY_MOVIES *//*+ "/" + fileName*//*);
}else {*/
dir = new File(this.context.getFilesDir().toString() + "/" + folderPath); // }
if (!dir.exists()) {
dir.mkdirs();
File nomediaFile = new File(dir.getAbsoluteFile(), ".nomedia");
try {
if (!nomediaFile.exists()) {
nomediaFile.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
}
File from = new File(filePath);
File to = new File(dir + "/" + fileName);
Log.v(TAG, "moveFilesToSentPathFrom= " + from);
Log.v(TAG, "moveFilesToSentPathTo= " + to);
if (from.exists()) {
try {
FileUtils.copyFile(from, to);
return true;
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
I want to copy video file from its original location to my own created destination i.e. like (Environment.DIRECTORY_MOVIES + "/" + "video" + "/"+fileName) and then access the file from destination. And also plz elaborate the media store api usage for saving file at specific location
The above code is for api level lower than 30, but need solution for android 11 api level 30 as we can't access storage due to security reasons but instead they provide with media store api, I don't have much idea with media store api. Thanks in advance for the help.

Can I send files created from bitmaps rather then files from assets folder through Content Provider?

I have assets folder with images in my project structure and I send them to other app through content provider. My task is to create files in runtime from imageviews (so and I cant put them to assets folder in runtime) and send them with content provider.
Content provider code:
...
#Nullable
#Override
public AssetFileDescriptor openAssetFile(#NonNull Uri uri, #NonNull String mode) {
final int matchCode = MATCHER.match(uri);
if (matchCode == STICKERS_ASSET_CODE || matchCode == STICKER_PACK_TRAY_ICON_CODE) {
return getImageAsset(uri);
}
return null;
}
private AssetFileDescriptor getImageAsset(Uri uri) throws IllegalArgumentException {
AssetManager am = Objects.requireNonNull(getContext()).getAssets();
final List<String> pathSegments = uri.getPathSegments();
if (pathSegments.size() != 3) {
throw new IllegalArgumentException("path segments should be 3, uri is: " + uri);
}
String fileName = pathSegments.get(pathSegments.size() - 1);
final String identifier = pathSegments.get(pathSegments.size() - 2);
if (TextUtils.isEmpty(identifier)) {
throw new IllegalArgumentException("identifier is empty, uri: " + uri);
}
if (TextUtils.isEmpty(fileName)) {
throw new IllegalArgumentException("file name is empty, uri: " + uri);
}
//making sure the file that is trying to be fetched is in the list of stickers.
for (StickerPack stickerPack : getStickerPackList()) {
if (identifier.equals(stickerPack.identifier)) {
if (fileName.equals(stickerPack.trayImageFile)) {
return fetchFile(uri, am, fileName, identifier);
} else {
for (Sticker sticker : stickerPack.getStickers()) {
if (fileName.equals(sticker.imageFileName)) {
return fetchFile(uri, am, fileName, identifier);
}
}
}
}
}
return null;
}
private AssetFileDescriptor fetchFile(#NonNull Uri uri, #NonNull AssetManager am, #NonNull String fileName, #NonNull String identifier) {
try {
return am.openFd(identifier + "/" + fileName);
} catch (IOException e) {
Log.e(Objects.requireNonNull(getContext()).getPackageName(), "IOException when getting asset file, uri:" + uri, e);
return null;
}
}
...

Having issue with file path

I am successfully able to select file programmatically. But, when I am getting uri from that file
content://com.android.providers.downloads.documents/document/3356
content://com.android.providers.downloads.documents/document/3331
Unfortunately, wherever the file is my uri is always locating at downloads.documents
I think it doesn't fact. Cause, everyone get the uri by data.getData(). So, I think the uri is correct.
Last year, I was working with Audio, Video, File and Image uploading to server. I was trying that source code to get path.
String mediaPath, mediaPath1;
String[] mediaColumns = {MediaStore.Video.Media._ID};
// Get the file from data
String path = data.getStringExtra(mediaPath);
File file = new File(path);
Uri selectedFile = Uri.fromFile(new File(file.getAbsolutePath()));
String[] filePathColumn = {MediaStore.Files.FileColumns.MEDIA_TYPE};
Cursor cursor = getContentResolver().query(selectedFile, filePathColumn, null, null, null);
assert cursor != null;
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
mediaPath = cursor.getString(columnIndex);
txt.setText(path);
// Set the Image in ImageView for Previewing the Media
cursor.close();
Unfortunately,That is returning null pointerexception. After researching little bit, I found another source code(PathUtils)
public class PathUtils {
public static String getPath(final Context context, final Uri uri) {
// DocumentProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) {
if (isExternalStorageDocument(uri)) {// ExternalStorageProvider
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
String storageDefinition;
if("primary".equalsIgnoreCase(type)){
return Environment.getExternalStorageDirectory() + "/" + split[1];
} else {
if(Environment.isExternalStorageRemovable()){
storageDefinition = "EXTERNAL_STORAGE";
} else{
storageDefinition = "SECONDARY_STORAGE";
}
return System.getenv(storageDefinition) + "/" + split[1];
}
} else if (isDownloadsDocument(uri)) {// DownloadsProvider
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} else if (isMediaDocument(uri)) {// MediaProvider
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {// MediaStore (and general)
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {// File
return uri.getPath();
}
return null;
}
public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
}
I am not getting any error. But, Path is always locating at Downloads file. I don't know why. After looking at uri I noticed my uri is always returning downloads.documents. I am not sure is it the issue.
There is lot of question similar to this question in stackoverflow. To tell everyone none worked for me. So, I will request to not flag it.
PATH: /storage/emulated/0/Download/The Road to Reality ( PDFDrive ).pdf
PATH: /storage/emulated/0/Download/The order of time ( PDFDrive ).pdf
PATH: /storage/emulated/0/Download/pdf_495.pdf
First pdf file is in documents file. Second pdf file is in Download third pdf file is in /storage/emulated/0/
I am parsing pdf texts. Here is my code
try {
String parsedText="";
StringBuilder builder = new StringBuilder();
//Here you can see I need path to load the pdf file
PdfReader reader = new PdfReader(PathUtils.getPathFromUri(getApplicationContext(),PathHolder));
int n = reader.getNumberOfPages();
for (int i = 10; i <n ; i++) {
parsedText = parsedText+ PdfTextExtractor.getTextFromPage(reader, i).trim()+"\n";
Log.d("for_loop", String.valueOf(i));
Log.d("PARSED_TEXT",parsedText+" ");
}
builder.append(parsedText);
reader.close();
runOnUiThread(() -> {
txt.setText(builder.toString());
});
// System.out.println("TEXT FROM PDF : "+builder.toString());
} catch (Exception e) {
System.out.println(e);
}
As you can see I need path to load the pdf file. But, I have already told you I am having issue with path. So, if I wanna do something with uri than how can i do that cause, path required.
I tried another way to get the path also.
Uri PathHolder = data.getData();
Cursor cursor = null;
try {
cursor = this.getContentResolver().query(PathHolder, new String[]{MediaStore.Files.FileColumns.DATA}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String fileName = cursor.getString(0);
String path = Environment.getExternalStorageDirectory().toString()+"/" + fileName;
Log.d("PATH",path);
if (!TextUtils.isEmpty(path)) {
}else{
Toast.makeText(this, "null return", Toast.LENGTH_SHORT).show();
}
}
}catch (Exception e){
e.printStackTrace();
Log.d("EXCEPTION_ERROR",e.toString());
} finally {
if (cursor != null)
cursor.close();
}
Unfortunately, It's not working also.
Like we do for audio, images or, videos.
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Audio.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
assert cursor != null;
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
mediaPath = cursor.getString(columnIndex);
str1.setText(mediaPath);
// Set the Image in ImageView for Previewing the Media
imgView.setImageBitmap(BitmapFactory.decodeFile(mediaPath));
cursor.close();
Edited :
Uri uri = data.getData();
try{
InputStream in = getContentResolver().openInputStream(uri);
BufferedReader r = new BufferedReader(new InputStreamReader(in));
StringBuilder total = new StringBuilder();
for (String line; (line = r.readLine()) != null; ) {
total.append(line).append('\n');
}
String content = total.toString();
Log.d("CONTENT",content);
}catch (Exception e){
e.printStackTrace();
}
The problem I am facing here is.
As you can see the catch is returning no such file or directory found. I am not sure what I am doing wrong here. Cause, I took the source code from somewhere else and it is first time I am working with InputStream. And, I think the problem is on I am unable to get the file by uri.
Catch exception is returning
no such file or directory
Edited :
Uri uri = data.getData();
File file=new File(uri.toString());
InputStream inputStream = getContentResolver().openInputStream(uri);
OutputStream out = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len;
while((len=inputStream.read(buf))>0){
out.write(buf,0,len);
}
out.close();
inputStream.close();
Starting from Android 10 using Scoped Storage is required. You cannot get absolute path of the file and work with file APIs with external Storage. MediaStore.Audio.Media.DATA that is used to take absolute path is depracated. But with uri returned you can copy that file using inputStream to your app-specific directory and do what you want with that file. For more information please refer to this article.
For reading pdf as I said you can copy that file to your app-specific directory or use that uri like this sample.
Copying file from uri in kotlin:
val uri: Uri = data?.data ?: return
val ins = context?.contentResolver?.openInputStream(uri)
val file = File(context?.filesDir, "image.jpg")
val fileOutputStream = FileOutputStream(file)
ins?.copyTo(fileOutputStream)
ins?.close()
fileOutputStream.close()
val absolutePath = file.absolutePath
Log.d("AAA", absolutePath)
If you use java there is no copyTo function so use this function then you can use the file in Files directory in app-specific storage
private void copyInputStreamToFile( InputStream in, File file ) {
try {
OutputStream out = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len;
while((len=in.read(buf))>0){
out.write(buf,0,len);
}
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}}
Copied file in app-specific directory
This is log:
D/AAA: /data/user/0/com.example.pickerapp/files/image.jpg

Android: Store path of an MediaStore image for later use

I succesfully pick an image from gallery or camera and then save it to MediaStore using this code whithin my OnActivityResult:
public static void saveBitmap(#NonNull final Context context, #NonNull final Bitmap bitmap,
#NonNull final Bitmap.CompressFormat format, #NonNull final String mimeType,
#NonNull final String displayName) throws IOException
{
final String relativeLocation = Environment.DIRECTORY_PICTURES + File.separator +"MYAPPNAME";
final ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
final ContentResolver resolver = context.getContentResolver();
OutputStream stream = null;
Uri uri = null;
try
{
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
uri = resolver.insert(contentUri, contentValues);
if (uri == null)
{
throw new IOException("Failed to create new MediaStore record.");
}
stream = resolver.openOutputStream(uri);
if (stream == null)
{
throw new IOException("Failed to get output stream.");
}
if (bitmap.compress(format, 95, stream) == false)
{
throw new IOException("Failed to save bitmap.");
}
}
catch (IOException e)
{
if (uri != null)
{
// Don't leave an orphan entry in the MediaStore
resolver.delete(uri, null, null);
}
throw e;
}
finally
{
if (stream != null)
{
stream.close();
}
}
}
Now lets say, later while using the app, i want to load this specific image for display in an imageview or upload it or whatsoever. How can i adress this specific image programatically? I dont quite understand the path/file system of the mediastore for later access. Any ideas?

Duplicate file on sd card

My application gets a shared image from gallery using
Uri imageUri = (Uri) getIntent().getExtras().get(Intent.EXTRA_STREAM);
I want to be able to duplicate the file(new name) in the same location of the original file.
i am able to do this when the file is located in the internal storage, like this:
String filePath = getRealPathFromURI(this, imageUri);
String filePathNew = filePath;
int index = filePathNew.lastIndexOf('.');
filePathNew = filePathNew.substring(0, index) + "_" + System.currentTimeMillis() + filePathNew.substring(index);
File fOrig = new File(filePath);
File f = new File(filePathNew);
try {
copy(fOrig,f);
return true;
} catch (IOException e) {
MyLogger.log(ShareActivity.this, MyLogger.ERROR, e + "|||" + MyLogger.getStackTrace(e));
e.printStackTrace();
}
public String getRealPathFromURI(Context context, Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = {MediaStore.Images.Media.DATA};
cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} catch (Exception ex) {
MyLogger.log(context, MyLogger.ERROR, "Error getting Real path from URI" + ex + "|||" + MyLogger.getStackTrace(ex));
ex.printStackTrace();
return "NaN";
} finally {
if (cursor != null) {
cursor.close();
}
}
}
when the shared image is from the sd card. i get an exception
java.io.IOException: open failed: EACCES (Permission denied)
java.io.IOException: open failed: EACCES (Permission denied)
at java.io.File.createNewFile(File.java:942)
URI path from EXTRA_STREAM:
/external/images/media/37713
URI Real Path:
/storage/0709-9CE1/raz test/raz.jpg
AndroidManifest permissions:
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
MANAGE_DOCUMENTS
INTERNET
I am testing on Galaxy S7 with android 6.0.1 but the issue was raised from other users also
What am i missing?
It is weird that your code doesn't contain Android permission and you'll using android 6.x, based on the error you need to add to get permission to write on external storage before you save your file.
you need to add something like this:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RESULT);}
else {
//your treatment
}
and check permission is needed to any operation that you'll do which is mentioned as permission on manifest.xml

Categories

Resources