2

My goal is to:

  • Save media file to External Storage (in my case it's photo).
  • Get file path or URI of saved data.
  • Save it to SQLite (either file path or content URI or smth else).
  • Be able to get correct URI to this content at any point in the future.

It's very similar to what other very popular application do - they create their directory in 'Pictures' folder and store there photos and use them in their applications while they're also available for viewing using gallery/file explorer etc.

As I understand recommended way to save media content (image, f.e.) is to use MediaStore API and as a result I get content URI, which I can use later. But then I read that these content URIs might be changed after re-scan of Media happens, so it looks it's not reliable. (For example if SD card is used and it's taken out and inserted again)

At the same time usage of absolute file paths is not recommended and there's tendency to deprecate APIs which use absolute file paths to work with External Storage. So it doesn't look reliable either.

I can only imagine the following solution:

  • Use unique auto-generated file name while saving (like UUID).
  • When I need to get content URI (f.e. want to render photo in ImageView) - I can use ContentResolver and search for content URI using file name filter.

Problem with this approach is that I have a lot of photos (gallery) and querying it using ContentResolver can affect performance significantly.

I feel like I'm over complicating things and missing something.

XZen
  • 225
  • 5
  • 27
  • 49

3 Answers3

3

You are indeed overcomplicating things. Store file to the needed folder in the filesystem(it is better to name the folder under your app name)

Store this path or URI path - whatever you like. (Do not hardcode passes though in your app - device vendors may have different base paths in their devices)

As long as the folder is named the same and files in it named the same(as in your db) - you will be able to access them even if the sdcard was taken out and then put back in.

There are possible complications after reindexing - but for the eight years I work as Android dev I encountered it only once, thus you can easily ignore this stuff.

If you want to have more control over what you store and want to limit access to it - store data into the inner storage of your app - this way you will be 100% sure of where the data is and that it is not tampered with.

Starting from Android 10 you have scoped storage - it is like internal storage but it may be even on an external sdcard.

Here is a small overview of possible storage locations.

And don't overthink it too much - it is a default usecase of the phone and it works just as you would expect - pretty ok and pretty stable.

Pavlo Ostasha
  • 14,527
  • 11
  • 35
  • Hi Pavlo, thank you for your clarification. Saving URIs in db was my first attempt and it worked, but after that I started thinking about possibility of reindexing issues. You mentioned they're very rare though, I'll try to search docs about that. Inner storage is not an option for my app, because I'd like my files be present in 'gallery' and in general in file explorer. Anyway your answer is very helpful and you understood the point of my question. (it's also rare unfortunately) – XZen Mar 06 '21 at 09:06
-1

I would recommend using Intent.ACTION_OPEN_DOCUMENT for your demand.

1. Create Photo Picking Intent:

val REQUEST_CODE_PICK_PHOTO = 1

fun pickAndSavePhoto(requestCode: Int) {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)

    intent.type = "image/*"

    startActivityForResult(intent, requestCode)
}

2. Handle Result:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    if (requestCode == REQUEST_CODE_PICK_PHOTO && resultCode == RESULT_OK) {
        val imageUri = data!!.data!!

        //save this uri to your database as String -> imageUri.toString()
    }
}

3. Get Image back and Display on ImageView:

fun getBitmapFromUri(context: Context, imageUri: Uri): Bitmap? {  //uri is just an address, image may be deleted any time, if so returns null
    val bitmap: Bitmap

    return try {
        val inputStream = context.contentResolver.openInputStream(imageUri)

        inputStream.use {
            bitmap = BitmapFactory.decodeStream(it)
        }

        bitmap
    } catch (e: Exception) {
        Log.e("getBitmapFromUri()", "Image not found.")

        null
    }
}

val bitmap = getBitmapFromUri(context, imageUri)  //get uri String from database and convert it to uri -> uriString.toUri()

if (bitmap != null) {
    imageView.setImageBitmap(bitmap)
}
Sam Chen
  • 7,597
  • 2
  • 40
  • 73
-1

first, you have to apply for external storage permission in manifest and Runtime Permission Also.

after creating a directory for saving an image in this directory.

you have to also add file provider in XML and code side because it's required.

now it's time to code check my code for saving an image in the folder also these image in the gallery and get the path from a file path.

    • convert URI to bitmap

http://prntscr.com/10dpvjj

    • save image function from getting bitmap

    private String save(Bitmap bitmap) {

     File save_path = null;
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
            try {
                File sdCard = Environment.getExternalStorageDirectory();
                File dir = new File(sdCard.getAbsolutePath() + "/SaveDirectory");
                dir.mkdirs();
                File file = new File(dir, "DirName_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime()) + ".png");
                save_path = file;
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
                FileOutputStream f = null;
                f = new FileOutputStream(file);
                MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null);
                if (f != null) {
                    f.write(baos.toByteArray());
                    f.flush();
                    f.close();
                }
             } catch (Exception e) {
                 // TODO: handle exception
             }
             Share(save_path); // call your Function Store into database
             Log.e("PathOFExec----", "save: " + save_path);
         }
    
    • get store image location into your database if you wish

    private void Share(File savePath) {

     if (savePath != null) {
    
         Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", savePath);
    
         Intent share = new Intent(Intent.ACTION_SEND);
         share.setType("image/*");
         share.putExtra(Intent.EXTRA_TEXT, "TextDetail");
         share.putExtra(Intent.EXTRA_STREAM, uri);
         context.startActivity(Intent.createChooser(share, "Share Image!"));
    
     //after getting URI you can store the image into SQLite databse for get uri
    
     }
    

    }

axar
  • 539
  • 2
  • 17