Alex Cai
Alex Cai

Reputation: 33

Stored image URI for later use, but it throws SecurityException

In the SecondActivity, it has an ImageViewand a Button

public class SecondActivity extends AppCompatActivity
{
    private ImageView galleryImage;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        galleryImage = findViewById(R.id.galleryImage);
        Button openGalleryButton = findViewById(R.id.openGalleryButton);
        openGalleryButton.setOnClickListener(view ->
        {
            Intent intent = new Intent(Intent.ACTION_PICK);
            intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            activityResultLauncher.launch(intent);
        });
        /*...*/
    }
}

for the activityResultLauncher, it is an ActivityResultLauncher that I used to open gallery and pick an Image.

ActivityResultLauncher<Intent> activityResultLauncher =
    registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
    result ->
    {
        if (result.getResultCode() == Activity.RESULT_OK)
        {
            Intent data = result.getData();
            if (data != null)
            {
                galleryImage.setImageURI(data.getData());
                try
                {
                    openFileOutput("uri", MODE_PRIVATE).write((data.getData() + "\n").getBytes());
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
    });

As you can see, this not only shows the image that users pick, but also stores the uri string into a file for later use. But when I change to ThirdActivity, there is always a SecurityException stop me.

/*SecondActivity.java*/
Button showPickedButton = findViewById(R.id.showPickedButton);
showPickedButton.setOnClickListener(view ->
{
    Intent thirdActivity = new Intent(SecondActivity.this, ThirdActivity.class);
    thirdActivity.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivity(thirdActivity);
});

/*ThirdActivity.java*/
public class ThirdActivity extends AppCompatActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_third);

        LinkedList<String> uriStrings = new LinkedList<>();
        String readImageURI;
        try
        {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(openFileInput("uri"), StandardCharsets.UTF_8));
            while (true)
            {
                readImageURI = bufferedReader.readLine();
                if (readImageURI != null)
                    uriStrings.add(readImageURI);
                else
                    break;
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        LinearLayout imagesList = findViewById(R.id.imagesList);
        uriStrings.forEach(imageURIString ->
        {
            ImageView imageView = new ImageView(this);
            imageView.setImageURI(Uri.parse(imageURIString));
            imageView.setAdjustViewBounds(true);
            imageView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
            imagesList.addView(imageView);
        });
    }
}

The exception message: exception message

Upvotes: 1

Views: 72

Answers (1)

CommonsWare
CommonsWare

Reputation: 1007474

As you can see, this not only shows the image that users pick, but also stores the uri string into a file for later use

That will not work with ACTION_PICK.

But when I change to ThirdActivity, there is always a SecurityException stop me.

For the purposes of using the Uri in the same process invocation as when you got it, you can pass the Uri along in the Intent:

Intent thirdActivity = new Intent(SecondActivity.this, ThirdActivity.class);
thirdActivity.setData(theImageUriThatWeAreTalkingAbout);
thirdActivity.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(thirdActivity);

However, that will not help for durable access via your persisted Uri value. Either get rid of that, or:

  • Switch to ACTION_OPEN_DOCUMENT instead of ACTION_PICK, and
  • Call takePersistableUriPermission() on a ContentResolver when you get the image Uri value

See this blog post for more.

Upvotes: 0

Related Questions