Khuê Nguyễn
Khuê Nguyễn

Reputation: 51

Photo Picker with READ_MEDIA_VISUAL_USER_SELECTED permission on Android 14 still shows all media content

In the Android 14 release, there is a new permission:

This new permission has a new dialog with the following options:

https://developer.android.com/about/versions/14/changes/partial-photo-video-access

Note: If your app already uses the photo picker, you don't need to take any action to support this change. Otherwise, consider using the photo picker instead of adopting this change.

When I try to implement the photo picker and test myapp.

Step 1: Request permission: "READ_MEDIA_IMAGES", "READ_MEDIA_VISUAL_USER_SELECTED"

Step 2: I select "Select photos and videos"

Step 3: Select a few photos -> Allow

Result: The PhotoPicker still displays all media content on the device instead of only displaying media content that has been granted permission.

Am I misunderstanding or is there a problem with my code?

If I don't understand correctly, what is the actual behavior? What should I do with it?

Everyone please let me know. Thanks.

I implemented the photo picker in two cases and got the same results. Here is my code:

Case 1 uses Intent:

public class MainActivity extends AppCompatActivity {

    ImageView imageView;
    Button takePictureButton;
    int RESULT_CODE = 0;
    int REQUEST_CODE = 1;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = findViewById(R.id.img);
        takePictureButton = findViewById(R.id.btn_take_picture);

        takePictureButton.setOnClickListener(view -> {
            if (android.os.Build.VERSION.SDK_INT == 34) {
                int result14 = this.checkSelfPermission(READ_MEDIA_VISUAL_USER_SELECTED);
                boolean hasReadMediaPermissionAndroid14 = PackageManager.PERMISSION_GRANTED == result14;

                if (hasReadMediaPermissionAndroid14) {
                    Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
                    intent.setType("image/*");
                    startActivityForResult(intent, RESULT_CODE);
                } else {
                    requestPermissions(new String[] {READ_MEDIA_IMAGES, READ_MEDIA_VISUAL_USER_SELECTED}, REQUEST_CODE);
                }
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (data != null) {
            imageView.setImageURI(data.getData());
        }
    }
}

Case 2 follows the instructions in the documentation Photo picker:

public class MainActivity extends AppCompatActivity {
    ImageView imageView;
    Button takePictureButton;
    int REQUEST_CODE = 1;

    ActivityResultLauncher<PickVisualMediaRequest> pickMedia =
            registerForActivityResult(new ActivityResultContracts.PickVisualMedia(), uri -> {
                if (uri != null) {
                    imageView.setImageURI(uri);
                    Log.d("PhotoPicker", "Selected URI: " + uri);
                } else {
                    Log.d("PhotoPicker", "No media selected");
                }
            });
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = findViewById(R.id.img);
        takePictureButton = findViewById(R.id.btn_take_picture);

        takePictureButton.setOnClickListener(view -> {
                    if (android.os.Build.VERSION.SDK_INT == 34) {
                        int result14 = this.checkSelfPermission(READ_MEDIA_VISUAL_USER_SELECTED);
                        boolean hasReadMediaPermissionAndroid14 = PackageManager.PERMISSION_GRANTED == result14;

                        if (hasReadMediaPermissionAndroid14) {
                            pickMedia.launch(new PickVisualMediaRequest.Builder()
                                    .setMediaType(ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE)
                                    .build());
                        } else {
                            requestPermissions(new String[]{READ_MEDIA_IMAGES, READ_MEDIA_VISUAL_USER_SELECTED}, REQUEST_CODE);
                        }
                    }
                }
        );
    }
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/img"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHeight_percent="0.5"
        app:layout_constraintVertical_bias="0"
        android:src="@drawable/img"
        tools:ignore="ContentDescription" />

    <Button
        android:id="@+id/btn_take_picture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="take picture"
        app:layout_constraintTop_toBottomOf="@id/img"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="20dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Upvotes: 5

Views: 5343

Answers (3)

Paolo Mazza
Paolo Mazza

Reputation: 87

So, I had the same issue, tried to use READ_MEDIA_VISUAL_USER_SELECTED and Photopicker together and it didn't work properly.

Turned out you can use the Photopicker and still omit to use the READ_MEDIA_VISUAL_USER_SELECTED permission: in this case, the Photopicker will still ask grant automatically this permission as you can select only few photos as the photopicker will "hide" all photos informations from the app, and show it to the app only when the photos will be selected.

If you are planning to use a custom photopicker than you will be required to manage manually the READ_MEDIA_VISUAL_USER_SELECTED permission.

Upvotes: 0

chenheihei
chenheihei

Reputation: 11

'READ_MEDIA_VISUAL_USER_SELECTED' need requested alongside READ_MEDIA_IMAGES and/or READ_MEDIA_VIDEO. And if you choose 'select a few photos', the result 'READ_MEDIA_IMAGES' returns denied, and 'READ_MEDIA_VISUAL_USER_SELECTED' granted. Maybe you can check this first.

Upvotes: 1

blackapps
blackapps

Reputation: 9292

My five cents:

If your app requests READ_MEDIA_IMAGES and the user confirms then your app can use the classic File class to list files in for instance the DCIM/Camera directory and see all files of the default Camera app.

This is true for Android 14 too.

But if it directly goes for READ_MEDIA_IMAGES and READ_MEDIA_VISUAL_USER_SELECTED and the user selects four files in DCIM/Camera then after that the File.listFiles() function will only list those four files.

In my experience the new permission has nothing to do with PickVisualMedia() or ACTION_GET_CONTENT or ACTION_PICK_IMAGES only with the classic File class.

Well my five cents. I find the documentation pretty obscure too and messed a long while with this.

Upvotes: 0

Related Questions