title: How to properly let the user pick videos and images (targetSdkVersion 30)
body: <p>This is a somewhat broad question that covers a few topics that are all related to the same goal: Understanding how to <em>properly</em> load multimedia content e.g. videos and images with the standard Android &quot;picker&quot;/&quot;gallery&quot;, not using any third party library.</p>
  <p>Before I upgraded to targetSdkVersion 30, this code worked properly on every known device:</p>
  <pre><code>val intent = Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
  intent.type = &quot;video/*&quot;
  intent.action = Intent.ACTION_GET_CONTENT
  intent.putExtra(
      MediaStore.EXTRA_OUTPUT,
      Uri.fromFile(File(filesDir, &quot;chosenVideo&quot;))
  )
  
  if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {
      requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), PICK_VIDEO_PERMISSION)
  } else {
      startActivityForResult(Intent.createChooser(intent, &quot;Select Video&quot;), PICK_VIDEO)
  }
  </code></pre>
  <p>However, not only does this not work on some devices, <code>startActivityForResult</code> is also deprecated.</p>
  <p>The result is processed like this:</p>
  <pre><code>override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
      super.onActivityResult(requestCode, resultCode, data)
      //...
      val dataSource = PathUtils.getPath(this, data!!.data) /* Code for getPath further blow */
  </code></pre>
  <p><code>dataSource</code> is the file path in form of a string and passed to the next activity, where the error occurs:</p>
  <pre><code>override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
          Log.d(&quot;VIDEOPLAYER&quot;, &quot;SURFACE&quot;)
          mediaPlayer.setSurface(Surface(surface))
          mediaPlayer.isLooping = true
          mediaPlayer.setDataSource(fullFilePath) // line 428
  </code></pre>
  <p>Sidenote about the error: The error occurred on e.g. a Samsung Galaxy S9, but sadly at a somewhat unrelated part of the code, where I basically try to load the chosen video and it crashes because I am not allowed to. Can't reproduce the error on my device, so I have yet to debug it properly, but from what it looks like, it's a permission issue. But as I said, it works on my Pixel 4a, but not on a Samsung Galaxy S9, which is odd.</p>
  <p>Screenshot from Firebase:</p>
  <p><a href="https://i.stack.imgur.com/sJrEk.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/sJrEk.png" alt="enter image description here" /></a></p>
  <p>Full error:</p>
  <pre><code>libcore.io.Linux.open (Linux.java)
  libcore.io.ForwardingOs.open (ForwardingOs.java:167)
  libcore.io.BlockGuardOs.open (BlockGuardOs.java:252)
  libcore.io.ForwardingOs.open (ForwardingOs.java:167)
  android.app.ActivityThread$AndroidOs.open (ActivityThread.java:8044)
  libcore.io.IoBridge.open (IoBridge.java:482)
  java.io.FileInputStream.&lt;init&gt; (FileInputStream.java:159)
  android.media.MediaPlayer.setDataSource (MediaPlayer.java:1268)
  android.media.MediaPlayer.setDataSource (MediaPlayer.java:1239)
  android.media.MediaPlayer.setDataSource (MediaPlayer.java:1204)
  com.mycompany.app.camera.VideoEditActivity.onSurfaceTextureAvailable (VideoEditActivity.kt:428)
  android.view.TextureView.getTextureLayer (TextureView.java:400)
  android.view.TextureView.draw (TextureView.java:349)
  android.view.View.updateDisplayListIfDirty (View.java:22062)
  android.view.View.draw (View.java:22917)
  android.view.ViewGroup.drawChild (ViewGroup.java:5230)
  android.view.ViewGroup.dispatchDraw (ViewGroup.java:4987)
  androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw (ConstraintLayout.java:1994)
  android.view.View.draw (View.java:23190)
  android.view.View.updateDisplayListIfDirty (View.java:22062)
  android.view.View.draw (View.java:22917)
  android.view.ViewGroup.drawChild (ViewGroup.java:5230)
  android.view.ViewGroup.dispatchDraw (ViewGroup.java:4987)
  androidx.constraintlayout.widget.ConstraintLayout.dispatchDraw (ConstraintLayout.java:1994)
  android.view.View.draw (View.java:23190)
  android.view.View.updateDisplayListIfDirty (View.java:22062)
  android.view.View.draw (View.java:22917)
  android.view.ViewGroup.drawChild (ViewGroup.java:5230)
  android.view.ViewGroup.dispatchDraw (ViewGroup.java:4987)
  android.view.View.updateDisplayListIfDirty (View.java:22048)
  android.view.View.draw (View.java:22917)
  android.view.ViewGroup.drawChild (ViewGroup.java:5230)
  android.view.ViewGroup.dispatchDraw (ViewGroup.java:4987)
  android.view.View.updateDisplayListIfDirty (View.java:22048)
  android.view.View.draw (View.java:22917)
  android.view.ViewGroup.drawChild (ViewGroup.java:5230)
  android.view.ViewGroup.dispatchDraw (ViewGroup.java:4987)
  android.view.View.updateDisplayListIfDirty (View.java:22048)
  android.view.View.draw (View.java:22917)
  android.view.ViewGroup.drawChild (ViewGroup.java:5230)
  android.view.ViewGroup.dispatchDraw (ViewGroup.java:4987)
  android.view.View.updateDisplayListIfDirty (View.java:22048)
  android.view.View.draw (View.java:22917)
  android.view.ViewGroup.drawChild (ViewGroup.java:5230)
  android.view.ViewGroup.dispatchDraw (ViewGroup.java:4987)
  android.view.View.draw (View.java:23190)
  com.android.internal.policy.DecorView.draw (DecorView.java:1154)
  android.view.View.updateDisplayListIfDirty (View.java:22062)
  android.view.ThreadedRenderer.updateViewTreeDisplayList (ThreadedRenderer.java:588)
  android.view.ThreadedRenderer.updateRootDisplayList (ThreadedRenderer.java:594)
  android.view.ThreadedRenderer.draw (ThreadedRenderer.java:667)
  android.view.ViewRootImpl.draw (ViewRootImpl.java:4293)
  android.view.ViewRootImpl.performDraw (ViewRootImpl.java:4077)
  android.view.ViewRootImpl.performTraversals (ViewRootImpl.java:3345)
  android.view.ViewRootImpl.doTraversal (ViewRootImpl.java:2222)
  android.view.ViewRootImpl$TraversalRunnable.run (ViewRootImpl.java:9123)
  android.view.Choreographer$CallbackRecord.run (Choreographer.java:999)
  android.view.Choreographer.doCallbacks (Choreographer.java:797)
  android.view.Choreographer.doFrame (Choreographer.java:732)
  android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:984)
  android.os.Handler.handleCallback (Handler.java:883)
  android.os.Handler.dispatchMessage (Handler.java:100)
  android.os.Looper.loop (Looper.java:237)
  android.app.ActivityThread.main (ActivityThread.java:8167)
  java.lang.reflect.Method.invoke (Method.java)
  com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:496)
  com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1100)
  </code></pre>
  <p>So, my question is: is there a &quot;right&quot; way to let a person pick a video or image and get the file on all devices, without any permission issues.</p>
  <p>I looked at some documentations:<br />
  <a href="https://developer.android.com/reference/android/database/Cursor" rel="nofollow noreferrer">https://developer.android.com/reference/android/database/Cursor</a>
  <a href="https://developer.android.com/reference/androidx/loader/content/CursorLoader" rel="nofollow noreferrer">https://developer.android.com/reference/androidx/loader/content/CursorLoader</a>
  <a href="https://developer.android.com/reference/android/provider/MediaStore.Images.Media.html" rel="nofollow noreferrer">https://developer.android.com/reference/android/provider/MediaStore.Images.Media.html</a>
  <a href="https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts" rel="nofollow noreferrer">https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts</a><br />
  And some more.</p>
  <p>In addition to that, I am using code that I took from other SO questions, which I can't find at the moment, like the one above to let the user pick an image/video and this one I generically use and which I just copy-pasted to get the file in the format I need it:</p>
  <pre><code>public class PathUtils {
  
      public static String getPath(final Context context, final Uri uri)
      {
          // DocumentProvider
          if (DocumentsContract.isDocumentUri(context, uri)) {
  
              if (isDownloadsDocument(uri)) {// DownloadsProvider
  
                  final String id = DocumentsContract.getDocumentId(uri);
                  final Uri contentUri = ContentUris.withAppendedId(
                          Uri.parse(&quot;content://downloads/public_downloads&quot;), Long.parseLong(id)
                  );
  
                  return getDataColumn(context, contentUri, null, null);
  
              } else if (isMediaDocument(uri)) {// MediaProvider
                  final String docId = DocumentsContract.getDocumentId(uri);
                  final String [] split = docId . split (&quot;:&quot;);
                  final String type = split[0];
  
                  Uri contentUri = null;
                  if (&quot;image&quot;.equals(type)) {
                      contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                  } else if (&quot;video&quot;.equals(type)) {
                      contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                  } else if (&quot;audio&quot;.equals(type)) {
                      contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                  }
  
                  final String selection = &quot;_id=?&quot;;
                  final String [] selectionArgs = new String[]{
                          split[1]
                  };
  
                  return getDataColumn(context, contentUri, selection, selectionArgs);
              }
  
          } else if (&quot;content&quot;.equalsIgnoreCase(uri.getScheme())) {// MediaStore (and general)
  
              // Return the remote address
              if (isGooglePhotosUri(uri))
                  return uri.getLastPathSegment();
  
              return getDataColumn(context, uri, null, null);
  
          } else if (&quot;file&quot;.equalsIgnoreCase(uri.getScheme())) {// File
              return uri.getPath();
          }
  
          return null;
      }
  
      public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs)
      {
  
          Cursor cursor = null;
          final String column = &quot;_data&quot;;
          final String [] projection = {
                  column
          };
  
          try {
              cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
              if (cursor != null &amp;&amp; cursor.moveToFirst()) {
                  final int column_index = cursor.getColumnIndexOrThrow(column);
                  return cursor.getString(column_index);
              }
          } finally {
              if (cursor != null)
                  cursor.close();
          }
          return null;
      }
  
      public static boolean isDownloadsDocument(Uri uri)
      {
          return &quot;com.android.providers.downloads.documents&quot;.equals(uri.getAuthority());
      }
  
      public static boolean isMediaDocument(Uri uri)
      {
          return &quot;com.android.providers.media.documents&quot;.equals(uri.getAuthority());
      }
  
      public static boolean isGooglePhotosUri(Uri uri)
      {
          return &quot;com.google.android.apps.photos.content&quot;.equals(uri.getAuthority());
      }
  }
  </code></pre>
  <p>Which I quite honestly don't fully understand.</p>
  <p>Please note: I am not looking for &quot;opinions&quot; about what is better or worse. I am looking for specific flaws in my approach and a reliable and correct way of doing this, that is supported with good arguments, references to documentations and official statements.</p>