Save image to media picture on Android

link
Android
val GALLERY_PATH = "${Environment.DIRECTORY_PICTURES}/lab"
fun saveToPictures(src: Uri, title: String) {
  save(src, title, "png", GALLERY_PATH)
}

fun saveToDownloads(src: Uri, name: String) {
  save(
    src,
    name.substringBeforeLast("."),
    name.substringAfterLast("."),
    Environment.DIRECTORY_DOWNLOADS
  )
}

private fun save(src: Uri, title: String, ext: String, relativeParent: String) {
  val name = "${title.chunked64().withTimestamp}.$ext"
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    val destRoot = when (relativeParent) {
      GALLERY_PATH -> MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
      else -> MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
    }

    saveToMediaStore(src, destRoot, name, relativeParent)
  } else {
    saveToExternalStorage(src, "${relativeParent}/$name")
  }
}

@RequiresApi(Build.VERSION_CODES.Q)
private fun saveToMediaStore(src: Uri, destRoot: Uri, name: String, relativeParent: String) {
  ContentValues().apply {
    put(MediaStore.MediaColumns.DISPLAY_NAME, name)
    put(MediaStore.MediaColumns.RELATIVE_PATH, relativeParent)
    put(MediaStore.MediaColumns.IS_PENDING, 1)
    if (name.endsWith("png")) {
      put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
    }

    contentResolver.insert(destRoot, this)?.let {
      src.copyTo(it)
      clear()
      put(MediaStore.MediaColumns.IS_PENDING, 0)
      contentResolver.update(it, this, null, null)
    }
  }
}

private fun saveToExternalStorage(from: Uri, relativePath: String) {
  File(Environment.getExternalStorageDirectory(), relativePath).apply {
    parentFile?.mkdirs()
    createNewFile()
    from.copyTo(this.toUri())
  }
}
val String.withTimestamp: String
  get() {
    val suffix = SimpleDateFormat("yyMMddHHmmss", Locale.ENGLISH)
      .format(Date())
    // 12 is suffix's length
    val suffixPattern = Regex(".*-[\\d]{12}$")
    val nameWithoutSuffix = if (suffixPattern.matches(this)) {
      this.substringBeforeLast("-")
    } else {
      this
    }
    return "$nameWithoutSuffix-$suffix"
  }

fun String.chunked64() = chunked(64).firstOrNull() ?: UUID.randomUUID().toString()