-1

I am new to Android. I want to know why my app crashes on Android 7.1.1 while setting the image to imageView using explicit intent, as it totally runs fine on other versions of Android. I have tried to understand and solve the issue but unable to do.

Can anyone please help me to understand why this is happening and what's the reason behind this?

Thanks.

This is what it showing :

eAnimators on 0x97c5ee80 (RippleDrawable) with handle 0x9affd880
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: com.example.himanshu.imageviewintent, PID: 5049
                  java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { dat=content://media/external/images/media/41 }} to activity {com.example.himanshu.imageviewintent/com.example.himanshu.imageviewintent.MainActivity}: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/images/media/41 from pid=5049, uid=10073 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
                      at android.app.ActivityThread.deliverResults(ActivityThread.java:4089)
                      at android.app.ActivityThread.handleSendResult(ActivityThread.java:4132)
                      at android.app.ActivityThread.-wrap20(ActivityThread.java)
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1533)
                      at android.os.Handler.dispatchMessage(Handler.java:102)
                      at android.os.Looper.loop(Looper.java:154)
                      at android.app.ActivityThread.main(ActivityThread.java:6119)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
                   Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/images/media/41 from pid=5049, uid=10073 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
                      at android.os.Parcel.readException(Parcel.java:1684)
                      at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
                      at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
                      at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
                      at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1147)
                      at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:984)
                      at android.content.ContentResolver.openInputStream(ContentResolver.java:704)
                      at com.example.himanshu.imageviewintent.MainActivity.onActivityResult(MainActivity.java:44)
                      at android.app.Activity.dispatchActivityResult(Activity.java:6932)
                      at android.app.ActivityThread.deliverResults(ActivityThread.java:4085)
                      at android.app.ActivityThread.handleSendResult(ActivityThread.java:4132) 
                      at android.app.ActivityThread.-wrap20(ActivityThread.java) 
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1533) 
                      at android.os.Handler.dispatchMessage(Handler.java:102) 
                      at android.os.Looper.loop(Looper.java:154) 
                      at android.app.ActivityThread.main(ActivityThread.java:6119) 
                      at java.lang.reflect.Method.invoke(Native Method) 
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
Application terminated.

And this is my code :

Java :

public class MainActivity extends Activity {

    final static int ImageIntentRequest = 1;

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

    public void setImage(View view) {

        Intent intent = new Intent(Intent.ACTION_PICK);
        File imageViewPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        String path =  imageViewPath.getPath();
        intent.setDataAndType(Uri.parse(path),"image/*");
        startActivityForResult(intent,ImageIntentRequest);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if(resultCode == RESULT_OK && requestCode == ImageIntentRequest) {

            InputStream stream;
            ImageView imageView = (ImageView)findViewById(R.id.imageView);
            try {
             stream = getContentResolver().openInputStream(data.getData());
                Bitmap bitmap = BitmapFactory.decodeStream(stream);
                imageView.setImageBitmap(bitmap);

            } catch (FileNotFoundException e) {
                Toast.makeText(this,"There is no File Present",Toast.LENGTH_SHORT).show();;
                e.printStackTrace();
            }
        }
    }
}

XML :

   <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.himanshu.imageviewintent.MainActivity"
>

<Button
    android:text="@string/set_image_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/btn_set_image"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true"
    android:onClick="setImage"
    />

<ImageView
    android:src="@mipmap/ic_launcher"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/imageView"
    android:contentDescription="@null"
    android:layout_above="@+id/btn_set_image"
    android:layout_alignParentStart="true"
    android:layout_marginBottom="10dp"
    />
</RelativeLayout>

I found an easy fix for this :

I just replaced,

Intent.ACTION_PICK by -

Intent intent = Intent(Intent.ACTION_GET_CONTENT);

and now the app runs totally fine on old and latest releases of Android.

But still have doubts:

Why ACTION_PICK works on old versions of Android but not on new Releases? Why ACTION_GET_CONTENT works on all releases?

I will be thankful if someone clarify this?

Himanshu Verma
  • 131
  • 1
  • 10

3 Answers3

1

If you look at the documentation on the changes introduced in Android Nougat, you'll find this -

For apps targeting Android 7.0, the Android framework enforces the StrictMode API policy that prohibits exposing file:// URIs outside your app. If an intent containing a file URI leaves your app, the app fails with a FileUriExposedException exception.

To share files between applications, you should send a content:// URI and grant a temporary access permission on the URI. The easiest way to grant this permission is by using the FileProvider class.

Here, your intent

 Intent intent = new Intent(Intent.ACTION_PICK);
    File imageViewPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    String path =  imageViewPath.getPath();
    intent.setDataAndType(Uri.parse(path),"image/*");

is leaving your app to open the Gallery/Camera to pick an image, which results into a crash, as it does not follow Android Nougat Protocol.

To resolve your problem, change the targetApi in your gradle file to 23 (marshmallow) down from 24/25/26 (whichever you may be having as of now).

Or, you can read more on File Sharing and do it in a way that does not violate StrictMode policy.

In case you are short on time, you can use EasyImage library to solve your purpose.

Kunal Chawla
  • 1,236
  • 2
  • 11
  • 24
  • I just replaced ACTION_PICK by ACTION_GET_CONTENT and it solved my problem but I don't know why? Please clarify this. – Himanshu Verma Jun 13 '17 at 08:08
  • Take a look at the description given in the document [here](https://developer.android.com/reference/android/content/Intent.html#ACTION_GET_CONTENT). "Allow the user to select a particular kind of data and return it. This is different than ACTION_PICK in that here we just say what kind of data is desired, not a URI of existing data from which the user can pick." – Kunal Chawla Jun 13 '17 at 08:12
  • Thanks, Kunal Chawla for the clarification. I understand the difference now. – Himanshu Verma Jun 13 '17 at 09:15
  • @HimanshuVerma you're most welcome. Glad I could help. Thanks for accepting the answer. :) – Kunal Chawla Jun 13 '17 at 09:16
0

You need to take runtime permission from the user to read external storage.

You can use following code for asking permission from user.

Hope it will help you.

if(ActivityCompat.checkSelfPermission(this,Manifest.permission.READ_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){
        requestStoragePermission();
    } else { 
        loadImageInImageview();
    }


    private void requestStoragePermission() {
        final String[] permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
        if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
            ActivityCompat.requestPermissions(this, permissions, 1000);
            return;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 1000) {
            if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                loadImageInImageview();
            } else {
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("Allow permission.")
                        .setMessage("Please....")
                        .setCancelable(false)
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int id) {
                                Intent intent = new Intent();
                                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                Uri uri = Uri.fromParts("package", getPackageName(), null);
                                intent.setData(uri);
                                startActivity(intent);
                            }
                        })
                        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                finish();
                            }
                        }).show();
            }
        }
    }
Chetan Ashtivkar
  • 194
  • 2
  • 15
0

You have either not added Storage permissions to the manifest or you do not ask the user to accept them before using them.

<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ...
</manifest>

You can ask user for permission using this function

    public  boolean isStoragePermissionGranted() {
        if (Build.VERSION.SDK_INT >= 23) {
            if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    == PackageManager.PERMISSION_GRANTED) {
                Log.v(TAG,"Permission is granted");
                return true;
            } else {

                Log.v(TAG,"Permission is revoked");
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                return false;
            }
        }
        else { //permission is automatically granted on sdk<23 upon installation
            Log.v(TAG,"Permission is granted");
            return true;
        }

}
Karishnu Poddar
  • 313
  • 2
  • 11