1

I have a chat application. If a user sends an image i save that image to internal storage of the app under

data/data/package_name/....

So if the user clicks on that image i send an intent to the system to open it

val uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, File(it.localUri))
       val intent = Intent(Intent.ACTION_VIEW).apply {
       flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
       setDataAndType(uri, "image/*")
    }
    startActivity(intent)

The problem is that in the image viewer there is no choice to save the file in the public storage of the phone, in that case in the gallery. Is there any way to do that without changing the internal storage default save location of the images in my app?

james04
  • 1,580
  • 2
  • 20
  • 46

1 Answers1

0

If this is something you are interested in, you can use MediaStore to programmatically save in the gallery a picture from your internal storage.

Code

You could use the following code, which I successfully use in my app.

ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "MyPicture.jpg");
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
   contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
}

ContentResolver resolver = myContext.getContentResolver();
Bitmap bitmap;
Uri uri;
try {
   // Requires permission WRITE_EXTERNAL_STORAGE
   bitmap = BitmapFactory.decodeFile(currentPhotoPath);
   uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
} catch (Exception e) {
   Log.e(LOG_TAG, "Error inserting picture in MediaStore: " + e.getMessage());
   return;
}

try (OutputStream stream = resolver.openOutputStream(uri)) {
   if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream) {
      throw new IOException("Error compressing the picture.");
   }
} catch (Exception e) {
   if (uri != null) {
      resolver.delete(uri, null, null);
   }
   Log.e(LOG_TAG, "Error adding picture to gallery: " + e.getMessage());       
}

Credits to this answer for the conversion of File to Bitmap and to this answer for the usage of  MediaStore.

Notes

  • BitmapFactory.decodeFile() requires the runtime permission WRITE_EXTERNAL_STORAGE
  • With the introduction of scoped memory in Android 10 (which will become mandatory in Android 11, see this article) MediaStore is probably the only reliable way to save pictures to the gallery
  • A consequence of scoped storage is that, yes, you should keep using the internal storage for the cache copy of your picture.

Tests

I have tested this code with with targetSdkVersion 29 on the following devices / OS combinations

  • Samsung Galaxy S10 / API 29
  • Samsung Galaxy S9 / API 29
  • Huawei Nexus 6P / API 27
MathMax
  • 571
  • 7
  • 22
  • Well that a very good answer. However i think you didnt understand my problem. In my case when i click on the photo i dont use a Custom activity for ImageViewer. Instead i open the photo with the default image viewer of the Android phone. So where do i suppose to write the above code..? – james04 Feb 14 '20 at 16:42
  • @james04 Thank you! I understand that, which is why I said "If this is something you are interested in". If you could put a small button next to each picture in your chat [like this](https://www.ghacks.net/wp-content/uploads/2019/05/whatsapp-broadcast-receive-fix.png), then my answer might be of use. Unfortunately, I cannot help you with the default image viewer if you must stick with that. – MathMax Feb 14 '20 at 16:54
  • @james04 AFAIK since the system decides which app will handle `Intent.ACTION_VIEW` (or lets you pick one if there's more) you cannot expect the same behaviour on every platform. You can use `Intent.ACTION_MEDIA_SCANNER_SCAN_FILE` if you want to save the picture to the gallery without wiewing it (deprecated in Android Q though). But I think that no `Intent` is guaranteed to launch an app that consistently performs both actions on all platforms. – MathMax Feb 14 '20 at 17:51