Crop Image Located in Internal Storage

2.7k views Asked by At

I am having trouble using the crop intent in Android.

I am storing images in the internal storage allocation for my app and I want to be able to crop them and then set them as the wallpaper.

I can get it to work with an image stored in external storage and using this code:

cropIntent.setDataAndType(Uri.fromFile(new File(uri)), "image/*");

When I execute the code below the intent fails to launch the crop intent and a 0 result code is returned.

Intent cropIntent = new Intent("com.android.camera.action.CROP");

cropIntent.setDataAndType(FileProvider.getUriForFile(activity, "com.example.test.fileprovider", new File(uri)), "image/*");

cropIntent.putExtra("crop", "true");
cropIntent.putExtra("aspectX", 9);
cropIntent.putExtra("aspectY", 16);
cropIntent.putExtra("return-data", true);
cropIntent.putExtra("scale", true);

activity.startActivityForResult(cropIntent, PIC_CROP);
1

There are 1 answers

3
unrulygnu On BEST ANSWER

From Storage Options | Android Developers:

By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user).

ACTION_CROP launches an "other application" that supports cropping, and passes it the URI of the file to crop. If that file is in internal storage, it is not directly accessible to the cropping application.

Using a FileProvider to provide an internal file to other apps requires some configuration. From Setting Up File Sharing | Android Developers:

Specify the FileProvider

Defining a FileProvider for your app requires an entry in your manifest. This entry specifies the authority to use in generating content URIs, as well as the name of an XML file that specifies the directories your app can share.

<snip>

Specify Sharable Directories

Once you have added the FileProvider to your app manifest, you need to specify the directories that contain the files you want to share. To specify the directories, start by creating the file filepaths.xml in the res/xml/ subdirectory of your project.

The following FileProvider sample integrates your code and successfully opens the crop activity on an image in internal storage (tested on a Nexus 5, stock Android 5.1.1):

AndroidManifest.xml

    <application
        ...>
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.test.fileprovider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>
        ...
    </application>

res/xml/filepaths.xml

<paths>
    <files-path path="images/" name="images" />
</paths>

MainActivity.java

            // Manually stored image for testing
            final File imagePath = new File(getFilesDir(), "images");
            final File imageFile = new File(imagePath, "sample.jpg");

            // Provider authority string must match the one declared in AndroidManifest.xml
            final Uri providedUri = FileProvider.getUriForFile(
                    MainActivity.this, "com.example.test.fileprovider", imageFile);

            Intent cropIntent = new Intent("com.android.camera.action.CROP");

            cropIntent.setDataAndType(providedUri, "image/*");

            cropIntent.putExtra("crop", "true");
            cropIntent.putExtra("aspectX", 9);
            cropIntent.putExtra("aspectY", 16);
            cropIntent.putExtra("return-data", true);
            cropIntent.putExtra("scale", true);

            // Exception will be thrown if read permission isn't granted
            cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

            startActivityForResult(cropIntent, PIC_CROP);