title: How to save an image in Android Q using MediaStore?
body: <p>Here is a link to the new Android Q <a href="https://developer.android.com/preview/privacy/scoped-storage" rel="noreferrer">Scoped Storage</a>.</p>
  
  <p>According to <a href="https://android-developers.googleblog.com/2019/04/android-q-scoped-storage-best-practices.html" rel="noreferrer">this Android Developers Best Practices Blog</a>, <code>storing shared media files</code> (which is my case) should be done using the <a href="https://developer.android.com/reference/android/provider/MediaStore" rel="noreferrer">MediaStore</a> API.</p>
  
  <p>Digging into the docs and I cannot find a relevant function.</p>
  
  <p>Here is my trial in Kotlin:</p>
  
  <pre><code>val bitmap = getImageBitmap() // I have a bitmap from a function or callback or whatever
  val name = "example.png" // I have a name
  
  val picturesDirectory = getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!
  
  // Make sure the directory "Android/data/com.mypackage.etc/files/Pictures" exists
  if (!picturesDirectory.exists()) {
      picturesDirectory.mkdirs()
  }
  
  try {
      val out = FileOutputStream(File(picturesDirectory, name))
      bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
  
      out.flush()
      out.close()
  
  } catch(e: Exception) {
      // handle the error
  }
  </code></pre>
  
  <p>The result is that my image is saved here <code>Android/data/com.mypackage.etc/files/Pictures/example.png</code> as described in the Best Practices Blog as <code>Storing app-internal files</code></p>
  
  <hr>
  
  <p>My question is:</p>
  
  <p>How to save an image using the MediaStore API?</p>
  
  <hr>
  
  <p>Answers in Java are equally acceptable.</p>
  
  <p>Thanks in Advance!</p>
  
  <hr>
  
  <p><strong>EDIT</strong></p>
  
  <p>Thanks to PerracoLabs</p>
  
  <p>That really helped!</p>
  
  <p>But there are 3 more points.</p>
  
  <p>Here is my code:</p>
  
  <pre><code>val name = "Myimage"
  val relativeLocation = Environment.DIRECTORY_PICTURES + File.pathSeparator + "AppName"
  
  val contentValues  = ContentValues().apply {
      put(MediaStore.Images.ImageColumns.DISPLAY_NAME, name)
      put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
  
      // without this part causes "Failed to create new MediaStore record" exception to be invoked (uri is null below)
      if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.Q) {
          put(MediaStore.Images.ImageColumns.RELATIVE_PATH, relativeLocation)
      }
  }
  
  val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
  var stream: OutputStream? = null
  var uri: Uri? = null
  
  try {
      uri = contentResolver.insert(contentUri, contentValues)
      if (uri == null)
      {
          throw IOException("Failed to create new MediaStore record.")
      }
  
      stream = contentResolver.openOutputStream(uri)
  
      if (stream == null)
      {
          throw IOException("Failed to get output stream.")
      }
  
      if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream))
      {
          throw IOException("Failed to save bitmap.")
      }
  
  
      Snackbar.make(mCoordinator, R.string.image_saved_success, Snackbar.LENGTH_INDEFINITE).setAction("Open") {
          val intent = Intent()
          intent.type = "image/*"
          intent.action = Intent.ACTION_VIEW
          intent.data = contentUri
          startActivity(Intent.createChooser(intent, "Select Gallery App"))
      }.show()
  
  } catch(e: IOException) {
      if (uri != null)
      {
          contentResolver.delete(uri, null, null)
      }
  
      throw IOException(e)
  
  }
  finally {
      stream?.close()
  }
  </code></pre>
  
  <p>1- The image saved doesn't get its correct name "Myimage.png"</p>
  
  <p>I tried using "Myimage" and "Myimage.PNG" but neither worked.</p>
  
  <p>The image always gets a name made up of numbers like:</p>
  
  <p><code>1563468625314.jpg</code></p>
  
  <p>Which bring us to the second problem:</p>
  
  <p>2- The image is saved as <code>jpg</code> even though I compress the bitmap in the format of <code>png</code>.</p>
  
  <p>Not a big issue. Just curious why.</p>
  
  <p>3- The relativeLocation bit causes an exception on Devices less than Android Q.
  After surrounding with the "Android Version Check" if statement, the images are saved directly in the root of the <code>Pictures</code> folder.</p>
  
  <p>Another Thank you.</p>
  
  <hr>
  
  <p><strong>EDIT 2</strong></p>
  
  <p>Changed to:</p>
  
  <pre><code>uri = contentResolver.insert(contentUri, contentValues)
  if (uri == null)
  {
      throw IOException("Failed to create new MediaStore record.")
  }
  
  val cursor = contentResolver.query(uri, null, null, null, null)
  DatabaseUtils.dumpCursor(cursor)
  cursor!!.close()
  
  stream = contentResolver.openOutputStream(uri)
  </code></pre>
  
  <p>Here are the logs</p>
  
  <pre><code>I/System.out: &gt;&gt;&gt;&gt;&gt; Dumping cursor android.content.ContentResolver$CursorWrapperInner@76da9d1
  I/System.out: 0 {
  I/System.out:    _id=25417
  I/System.out:    _data=/storage/emulated/0/Pictures/1563640732667.jpg
  I/System.out:    _size=null
  I/System.out:    _display_name=Myimage
  I/System.out:    mime_type=image/png
  I/System.out:    title=1563640732667
  I/System.out:    date_added=1563640732
  I/System.out:    is_hdr=null
  I/System.out:    date_modified=null
  I/System.out:    description=null
  I/System.out:    picasa_id=null
  I/System.out:    isprivate=null
  I/System.out:    latitude=null
  I/System.out:    longitude=null
  I/System.out:    datetaken=null
  I/System.out:    orientation=null
  I/System.out:    mini_thumb_magic=null
  I/System.out:    bucket_id=-1617409521
  I/System.out:    bucket_display_name=Pictures
  I/System.out:    width=null
  I/System.out:    height=null
  I/System.out:    is_hw_privacy=null
  I/System.out:    hw_voice_offset=null
  I/System.out:    is_hw_favorite=null
  I/System.out:    hw_image_refocus=null
  I/System.out:    album_sort_index=null
  I/System.out:    bucket_display_name_alias=null
  I/System.out:    is_hw_burst=0
  I/System.out:    hw_rectify_offset=null
  I/System.out:    special_file_type=0
  I/System.out:    special_file_offset=null
  I/System.out:    cam_perception=null
  I/System.out:    cam_exif_flag=null
  I/System.out: }
  I/System.out: &lt;&lt;&lt;&lt;&lt;
  </code></pre>
  
  <p>I noticed the <code>title</code> to be matching the name so I tried adding:</p>
  
  <p><code>put(MediaStore.Images.ImageColumns.TITLE, name)</code></p>
  
  <p>It still didn't work and here are the new logs:</p>
  
  <pre><code>I/System.out: &gt;&gt;&gt;&gt;&gt; Dumping cursor android.content.ContentResolver$CursorWrapperInner@51021a5
  I/System.out: 0 {
  I/System.out:    _id=25418
  I/System.out:    _data=/storage/emulated/0/Pictures/1563640934803.jpg
  I/System.out:    _size=null
  I/System.out:    _display_name=Myimage
  I/System.out:    mime_type=image/png
  I/System.out:    title=Myimage
  I/System.out:    date_added=1563640934
  I/System.out:    is_hdr=null
  I/System.out:    date_modified=null
  I/System.out:    description=null
  I/System.out:    picasa_id=null
  I/System.out:    isprivate=null
  I/System.out:    latitude=null
  I/System.out:    longitude=null
  I/System.out:    datetaken=null
  I/System.out:    orientation=null
  I/System.out:    mini_thumb_magic=null
  I/System.out:    bucket_id=-1617409521
  I/System.out:    bucket_display_name=Pictures
  I/System.out:    width=null
  I/System.out:    height=null
  I/System.out:    is_hw_privacy=null
  I/System.out:    hw_voice_offset=null
  I/System.out:    is_hw_favorite=null
  I/System.out:    hw_image_refocus=null
  I/System.out:    album_sort_index=null
  I/System.out:    bucket_display_name_alias=null
  I/System.out:    is_hw_burst=0
  I/System.out:    hw_rectify_offset=null
  I/System.out:    special_file_type=0
  I/System.out:    special_file_offset=null
  I/System.out:    cam_perception=null
  I/System.out:    cam_exif_flag=null
  I/System.out: }
  I/System.out: &lt;&lt;&lt;&lt;&lt;
  </code></pre>
  
  <p>And I can't change <code>date_added</code> to a name.</p>
  
  <p>And <code>MediaStore.MediaColumns.DATA</code> is deprecated.</p>
  
  <p>Thanks in Advance!</p>