Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to save image to camera folder in Android Q?

I need to save an image to camera folder, but as Android Q getExternalStoragePublicDirectory is deprecated, I do it in another way. What I have (this method receive bitmap and its name):

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        ContentResolver resolver = mContext.getContentResolver();
        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
        contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/" + IMAGES_FOLDER_NAME);
        Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
        OutputStream fos = resolver.openOutputStream(imageUri);
        saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
    } else {
        String imagesDir = Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_DCIM).toString() + File.separator + IMAGES_FOLDER_NAME;

        File file = new File(imagesDir);

        if (!file.exists()) {
            file.mkdir();
        }

        File image = new File(
                imagesDir,
                name + ".png"
        );

        final long fileHashCode = image.hashCode();
        Logger.d(TAG, "saveImage, saving image file, hashCode = " + fileHashCode);

        FileOutputStream fos = new FileOutputStream(image);
        saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.flush();
        fos.close();
    }

That perfectly works for all needed OS versions, but it looks inaccurate and I'd like to find a more common way. I tried to play around with content values or try some similar way as for Q, but it doesn't work. I've seen many questions here, but neither of them can help me.

The question is how can I optimize saving for OS lower than Q?

like image 276
VolodymyrH Avatar asked Aug 05 '19 10:08

VolodymyrH


People also ask

How do I save a picture to my gallery?

Touch and hold the image. Select a save option (e.g., Save attachment, Save to SD card, etc.). Unless otherwise specified, the image is saved to the default picture/video location (e.g., Gallery, Photos, etc.).

How do I get a list of images in a specific folder on Android?

You can use below code to get all images from specific folder. 1) First you need to define File object to get the storage and appened the name of the folder you want to read. File folder = new File(Environment. getExternalStorageDirectory().


2 Answers

The most generalized version I was able to write is:

private Uri saveImage(Context context, Bitmap bitmap, @NonNull String folderName, @NonNull String fileName) throws IOException {
    OutputStream fos = null;
    File imageFile = null;
    Uri imageUri = null;

    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            ContentResolver resolver = context.getContentResolver();
            ContentValues contentValues = new ContentValues();
            contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
            contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
            contentValues.put(
                    MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + folderName);
            imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

            if (imageUri == null)
                throw new IOException("Failed to create new MediaStore record.");

            fos = resolver.openOutputStream(imageUri);
        } else {
            File imagesDir = new File(Environment.getExternalStoragePublicDirectory(
                    Environment.DIRECTORY_PICTURES).toString() + File.separator + folderName);

            if (!imagesDir.exists())
                imagesDir.mkdir();

            imageFile = new File(imagesDir, fileName + ".png");
            fos = new FileOutputStream(imageFile);
        }


        if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos))
            throw new IOException("Failed to save bitmap.");
        fos.flush();
    } finally {
        if (fos != null)
            fos.close();
    }
    
    if (imageFile != null) {//pre Q
        MediaScannerConnection.scanFile(context, new String[]{imageFile.toString()}, null, null);
        imageUri = Uri.fromFile(imageFile);
    }
    return imageUri;
}

If you've found a better way, post here, I'll mark it as answer.

like image 116
VolodymyrH Avatar answered Sep 21 '22 00:09

VolodymyrH


Using this document : https://developer.android.com/training/data-storage

Create temp file first

val mTempFileRandom = Random()

fun createTempFile(ext:String, context:Context):String {
  val path = File(context.getExternalCacheDir(), "AppFolderName")
  if (!path.exists() && !path.mkdirs())
  {
    path = context.getExternalCacheDir()
  }
  val result:File
  do
  {
    val value = Math.abs(mTempFileRandom.nextInt())
    result = File(path, "AppFolderName-" + value + "-" + ext)
  }
  while (result.exists())
  return result.getAbsolutePath()
}   

Send file from path

copyFileToDownloads(this@CameraNewActivity, File(savedUri.path))

Copy data to storage

MAIN_DIR: Main folder name in which you want to store image (Like App Name) IMAGE_DIR: Sub folder if you want to create.

    fun copyFileToDownloads(context: Context, downloadedFile: File): Uri? {

    // Create an image file name
    val timeStamp = SimpleDateFormat(DATE_FORMAT_SAVE_IMAGE).format(Date())
    val imageFileName = "JPEG_$timeStamp.jpg"
    val resolver = context.contentResolver

    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val values = ContentValues().apply {
            put(MediaStore.Images.Media.DISPLAY_NAME, imageFileName)
            put(MediaStore.Images.Media.MIME_TYPE, IMAGE_MIME_TYPE)
            put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + File.separator + MAIN_DIR + File.separator + IMAGE_DIR + File.separator)
        }

        resolver.run {
            val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)

            uri
        }
    } else {
        val authority = "${context.packageName}.provider"
        val imagePath =
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)?.absolutePath
        val destinyFile = File(imagePath, imageFileName)
        val uri = FileProvider.getUriForFile(context, authority, destinyFile)
        FileUtils.scanFile(context, destinyFile.absolutePath)

        uri
    }?.also { uri ->
        var writtenValue = 0L
        // Opening an outputstream with the Uri that we got
        resolver.openOutputStream(uri)?.use { outputStream ->
            downloadedFile.inputStream().use { inputStream ->
                writtenValue = inputStream.copyTo(outputStream)
                Log.d("Copy Written flag", " = $writtenValue")
            }
        }
    }
}

ScanFile : ( More detail : https://developer.android.com/reference/android/media/MediaScannerConnection )

fun scanFile(context:Context, path:String) {
  MediaScannerConnection.scanFile(context,
                                  arrayOf<String>(path), null,
                                  { newPath, uri-> if (BuildConfig.DEBUG)
                                   Log.e("TAG", "Finished scanning " + newPath) })
}
like image 30
Shweta Chauhan Avatar answered Sep 23 '22 00:09

Shweta Chauhan