Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Android Build a Self-Destructing Message Android App Capturing Photos and Videos Setting Where Photos are Saved

Niyamat Almass
Niyamat Almass
8,176 Points

There was a problem accessing your device's external storage?

My code is match with the video but When I choose to take a picture a toast is appear and say "There was a problem accessing your device's external storage".But I set my emulator sdcard and I declare necessary method in the menifast.But still I get the toast.The toast is only appear when mMediaUri is null.

MainActivity.java
    public static final String TAG = MainActivity.class.getSimpleName();
    public static final int TAKE_PHOTO_REQUEST = 0;
    public static final int TAKE_VIDEO_REQUEST = 1;
    public static final int PICK_PHOTO_REQUEST = 2;
    public static final int PICK_VIDEO_REQUEST = 3;
    public static final int MEDIA_TYPE_IMAGE = 4;
    public static final int MEDIA_TYPE_VIDEO = 5;

    private Uri mMediaUri;

    private DialogInterface.OnClickListener mDialogListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            switch (which){
                case 0:
                    Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                    mMediaUri = getOutPutMediaFileUri(MEDIA_TYPE_IMAGE);
                    if (mMediaUri == null) {
                        //display an error
                        Toast.makeText(MainActivity.this, R.string.error_external_storage, Toast.LENGTH_LONG).show();
                    } else {
                        takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mMediaUri);
                        startActivityForResult(takePhotoIntent, TAKE_PHOTO_REQUEST);
                    }
                    break;


                case 1:
                    Intent videoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                    mMediaUri = getOutPutMediaFileUri(MEDIA_TYPE_VIDEO);
                    if (mMediaUri == null) {
                        //display an error
                        Toast.makeText(MainActivity.this, R.string.error_external_storage, Toast.LENGTH_LONG).show();
                    } else {
                        videoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mMediaUri);
                        videoIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10);
                        videoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0); //0 for low quality
                        startActivityForResult(videoIntent, TAKE_VIDEO_REQUEST);
                    }
                    break;


                case 2:
                    break;
                case 3:
                    break;
            }
        }





        private Uri getOutPutMediaFileUri(int mediaType) {
                        if (isExternalStorageAvailable()) {
            //Get the external storage directory
                String appName = MainActivity.this.getString(R.string.app_name);
                File mediaStorageDir = new File(
                        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appName);
            //Create our subdirectory
                if (! mediaStorageDir.exists()){
                    if (! mediaStorageDir.mkdirs()){
                        Log.d(TAG, "failed to create directory");
                        return null;
                    }
                }
//Create file name
//Create the file
                            File mediaFile;
                            Date now = new Date();
                            String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(now);
                            String path = mediaStorageDir.getPath() + File.separator;
                            if (mediaType == MEDIA_TYPE_IMAGE) {
                            mediaFile = new File(path + "IMG_" + timestamp + ".jpg");
                            } else if (mediaType == MEDIA_TYPE_VIDEO) {
                            mediaFile = new File(path + "VID_" + timestamp + ".mp4");
                            } else {
                            return null;
                            }
                            Log.d(TAG, "File: " + Uri.fromFile(mediaFile));
                            //Return the file's uri
                            return Uri.fromFile(mediaFile);
                            } else {
                            return null;
                            }
                            }

                            private boolean isExternalStorageAvailable() {
                            String state = Environment.getExternalStorageState();
                            if (state.equals(Environment.MEDIA_MOUNTED)) {
                            return true;
                            } else {
                            return false;
                            }
                            }
    };
manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.niyamat.ribbit" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-feature android:name="android.hardware.camera2" android:required="true"/>



    <application
        android:name=".RibbitApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".LoginActivity"
            android:label="@string/title_activity_login"
            android:screenOrientation="portrait" >
        </activity>
        <activity
            android:name=".SignUpActivity"
            android:label="@string/title_activity_sign_up"
            android:parentActivityName=".LoginActivity"
            android:screenOrientation="portrait" >
        </activity>
        <activity
            android:name=".EditFriendsActivity"
            android:label="@string/title_activity_edit_friends"
            android:parentActivityName=".MainActivity"
            android:screenOrientation="portrait">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.example.niyamat.ribbit.MainActivity" />
        </activity>
    </application>

</manifest>

So why mMediaUri return null. Can someone solve this problem?

Harry James
Harry James
14,780 Points

Hey Niyamat!

I haven't had the time to take a look at this code yet but if like Steve is saying everything looks OK then there's a few things we need to check to see if it's a problem with the device storing (In which case we need to use another device or find a way around it) or with the code itself.

I put together a piece of debug code for students who have issues with writing to external storage for this exact purpose, so go ahead and plug it in at the top of your getOutPutMediaFileUri() method.

Log.d("MainActivity", ">> Let's debug why this directory isn't being created: ");
Log.d("MainActivity", "Is it working?: " + mediaStorageDir.mkdirs());
Log.d("MainActivity", "Is the external storage mounted?: " + Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED));
Log.d("MainActivity", "Does the directory exist?: " + mediaStorageDir.exists());
Log.d("MainActivity", "What is the full URI?: " + mediaStorageDir.toURI());
Log.d("MainActivity", "--");
Log.d("MainActivity", "Can we write to this file?: " + mediaStorageDir.canWrite());
if (!mediaStorageDir.canWrite()) {
    Log.d("MainActivity", ">> We can't write! Do we have WRITE_EXTERNAL_STORAGE permission?");
    if (getBaseContext().checkCallingOrSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE") == PackageManager.PERMISSION_DENIED) {
        Log.d("MainActivity", ">> We don't have permission to write - please add it.");
    } else {
        Log.d("MainActivity", "We do have permission - the problem lies elsewhere.");
    }
}
Log.d("MainActivity", "Are we even allowed to read this file?: " + mediaStorageDir.canRead());
Log.d("MainActivity", "--");
Log.d("MainActivity", ">> End of debugging.");

After that, run your app and post the logcat results over here. I'll get back to you tomorrow when I've got more time :)

Nice test. Like that a lot!

Niyamat Almass
Niyamat Almass
8,176 Points

Hi Harry James

I added your debug code at top of the getOutPutMediaFileUri() like that

getoutputmediauri.java
        Log.d("MainActivity", ">> Let's debug why this directory isn't being created: ");
        Log.d("MainActivity", "Is it working?: " + mediaStorageDir.mkdirs());
        Log.d("MainActivity", "Is it available?: " + isExternalStorageAvailable());
        Log.d("MainActivity", "Does it exist?: " + mediaStorageDir.exists());
        Log.d("MainActivity", "What is the full URI?: " + mediaStorageDir.toURI());
        Log.d("MainActivity", "--");
        Log.d("MainActivity", "Can we write to this file?: " + mediaStorageDir.canWrite());
        if (!mediaStorageDir.canWrite()) {
            Log.d("MainActivity", ">> We can't write! Do we have WRITE_EXTERNAL_STORAGE permission?");
            if (getBaseContext().checkCallingOrSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE") == PackageManager.PERMISSION_DENIED) {
                Log.d("MainActivity", ">> We don't have permission to write - please add it.");
            } else {
                Log.d("MainActivity", "We do have permission - the problem lies elsewhere.");
            }
        }
        Log.d("MainActivity", "Are we even allowed to read this file?: " + mediaStorageDir.canRead());
        Log.d("MainActivity", "--");
        Log.d("MainActivity", ">> End of debugging.");
        private Uri getOutPutMediaFileUri(int mediaType) {

                        if (isExternalStorageAvailable()) {
            //Get the external storage directory
                String appName = MainActivity.this.getString(R.string.app_name);
                File mediaStorageDir = new File(
                        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appName);
            //Create our subdirectory
                if (! mediaStorageDir.exists()){
                    if (! mediaStorageDir.mkdirs()){
                        Log.d(TAG, "failed to create directory");
                        return null;
                    }
                }
//Create file name
//Create the file
                            File mediaFile;
                            Date now = new Date();
                            String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(now);
                            String path = mediaStorageDir.getPath() + File.separator;
                            if (mediaType == MEDIA_TYPE_IMAGE) {
                            mediaFile = new File(path + "IMG_" + timestamp + ".jpg");
                            } else if (mediaType == MEDIA_TYPE_VIDEO) {
                            mediaFile = new File(path + "VID_" + timestamp + ".mp4");
                            } else {
                            return null;
                            }
                            Log.d(TAG, "File: " + Uri.fromFile(mediaFile));
                            //Return the file's uri
                            return Uri.fromFile(mediaFile);
                            } else {
                            return null;
                            }
                            }

But it gives me many error that cannot resolve mediaStorageDir.Here is a picture https://drive.google.com/file/d/0Bze-_k4O9A53dXlUb2tGX3gxZnc/view?usp=sharing

When I added the debug code in the getOutPutMediaUri it gives me that https://drive.google.com/file/d/0Bze-_k4O9A53anY4UmIzR1JyQmc/view?usp=sharing

But when I added your debug code below of mediaStorageDir that there was no error. Here is a picture https://drive.google.com/file/d/0Bze-_k4O9A53N1hmbl82bjI3YzQ/view?usp=sharing

But unfortunately when I run the app and choose to take a picture there was nothing shown in the logcat.

I am really confused!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Niyamat Almass
Niyamat Almass
8,176 Points

where I placed that debug code?

Am I do something wrong to placed that debug code?

It needs to be inside the method getOutPutMediaFile - at the top means "the first lines of". So, paste it in right after the method header.

[EDIT: I was wrong! add it after the declaration of mediaStorageDir ]

Harry James
Harry James
14,780 Points

Yep, Steve is right here! Sorry about my wording there! I can see how this can be misleading!

Anyhow, after placing the code on the first lines of getOutPutMediaFileUri(), the code should compile fine. Go ahead and paste the logcat in now and either me, Steve or another forum user will help you out.

Edit: Yes, apologies again! My fault for not reading your question in detail before. The code should be added after the declaration of mediaStorageDir which is within the getOutPutMediaFireUri() method towards the top.

Niyamat Almass
Niyamat Almass
8,176 Points

When I added that debug code in the getOutPutMediaFileUri()'s first line it shows error.

In the debug code's mediaStorageDir is red lined and say can not resolve.

Here is picture

https://drive.google.com/file/d/0Bze-_k4O9A53anY4UmIzR1JyQmc/view?usp=sharing&#13

Yes, it will.

Add it here:

        private Uri getOutPutMediaFileUri(int mediaType) {
                        if (isExternalStorageAvailable()) {
            //Get the external storage directory
                String appName = MainActivity.this.getString(R.string.app_name);
                File mediaStorageDir = new File(
                        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appName);
            // ADD HARRY's CODE HERE

because that is after the declaration and initialization of mediaStorageDir.

Make sense?

Steve.

Niyamat Almass
Niyamat Almass
8,176 Points

Steve Hunter

Yes its make sense.

Here is the logcat

logcat.java
11-11 08:05:34.819    2200-2200/com.example.niyamat.ribbit I/MainActivity? Niyamat
11-11 08:07:37.952    2200-2200/com.example.niyamat.ribbit D/MainActivity? >> Let's debug why this directory isn't being created:
11-11 08:07:37.953    2200-2200/com.example.niyamat.ribbit D/MainActivity? Is it working?: false
11-11 08:07:37.953    2200-2200/com.example.niyamat.ribbit D/MainActivity? Is it available?: true
11-11 08:07:37.953    2200-2200/com.example.niyamat.ribbit D/MainActivity? Does it exist?: false
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? What is the full URI?: file:/storage/0E16-1009/Pictures/Ribbit
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? --
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? Can we write to this file?: false
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? >> We can't write! Do we have WRITE_EXTERNAL_STORAGE permission?
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? >> We don't have permission to write - please add it.
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? Are we even allowed to read this file?: false
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? --
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? >> End of debugging.
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? failed to create directory

This thread is getting a little long now!

I can't see what the issue is, unfortunately. The tests seem to imply that you have no write access to the external storage. Your manifest tells a different story, though. Not sure why that is.

When you run your code, in Logcat, do you also see this line firing?:

Log.d(TAG, "failed to create directory");

Are there any other logs created by your code? I'd like to see precisely which null is being returned. I suspect the one immediately by the line above, but I'd like to check.

Also, to make your code clearer, can you reindent the lines? I think the curly braces are all OK but it's good to check.

Niyamat Almass
Niyamat Almass
8,176 Points

Hi Steve Hunter

I am updating my project to github so you have closer look of my code.

Just setting the github in android studio...............................

Perfect, I'll pull that down and see if I can replicate the error. And, more to the point, fix it!! :-)

Look me up on there - I'm OnlySteveH.

Niyamat Almass
Niyamat Almass
8,176 Points

Hi Steve Hunter

Here is the link of my github project

github.com/niyamatalmass/Ribbit

[Note: This is the first time I am using github so I knowledge about github is not good.So if do something on github please tell what should I do next,Please]

Yep - got it. Just loading an emulator. Will be right back.

3 Answers

Harry James
Harry James
14,780 Points

Hey Niyamat!

I've gone ahead and forked your project as well and the logcat produced good results for me.

In the logcat you provided, we got these results:

11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? Can we write to this file?: false
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? >> We can't write! Do we have WRITE_EXTERNAL_STORAGE permission?
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? >> We don't have permission to write - please add it.
11-11 08:07:37.954    2200-2200/com.example.niyamat.ribbit D/MainActivity? Are we even allowed to read this file?: false

The logcat specifies that we don't have permission to write and therefore, this code won't work. As I have checked your manifest and ran the code myself, I know that this does work perfectly fine.

I am assuming therefore that you are running your app on the latest API version, Android Marshmallow (API 23).


Things have changed in API 23 and now, permissions have multiple protection levels, primarily Normal and Dangerous permissions (See here for more info on this). if we take a look at the WRITE_EXTERNAL_STORAGE permission on the Android Developer site, we can see that this is classed as a Dangerous permission, this means that we must explicitly ask the user for permission if they are using API 23/Marshmallow and we should also check that they have the permission every time we want to use it (Yes, it's annoying and a lot of Android Developers are against it).

So, your app will work perfectly fine on API 22 and lower but on newer versions of Android, we must ask for the permission and check it each time. This is all explained very well on the Android Developer site however, it does take up a lot of code.


I've gone ahead and done the hard work for you and created a pull request on your GitHub repository. Go ahead and read through what I've done (See the Files Changed tab) and then accept the pull request to push it into your code.

I've made sure to comment a lot of my code so that you know what's going on but if there's anything you don't understand about it then please give me a shout here so that I can explain it to you :)

Also, if you are another student having this issue, you can find a gist of the changes to update your project, I've put them on GitHub Gist to keep this post short.

Niyamat Almass
Niyamat Almass
8,176 Points

Finally got it!

  1. Last night I installed genymotion on my computer (because Steve Hunter said that it works fine on his emulator) and there is old emulator that's api is 19, so I think why shouldn't I download the latest version? So I download the latest version of google nexus 5X api label 23 but again the same problem.

2.Then I made android studio emulator api label 21 ,and run it .Yes It works!!!!! 3.So I am really confused that what just happen in 23 api.But your answer remove my tension and make me understand what is happening in api 23.

4.Harry James Steve Hunter you guys are really great and helpful.

5.Thank you so much for helping me.

6.Now I am understanding what Harry James is done.Thank you Harry James for doing a hard work for me.

Niyamat Almass
Niyamat Almass
8,176 Points

Hi Harry James

I come again to disturb you.

Now I am making an app that take photo and store it in the external storage for practicing android runtime permission.

I share my project on github https://github.com/niyamatalmass/Marshallow

In the app I write same code for runtime permission that you have done in the Build a self destructing messaging app on github.

But when I run the app and choose to take photo ,the app crash.

Please help me to find out the problem!

Harry James
Harry James
14,780 Points

Hey Niyamat!

Sorry for my late response - I've been with work recently. Anyhow, if you're still having issues then I'll take a look at your project tomorrow for you :)

Harry James
Harry James
14,780 Points

Hey again Niyamat!

I had no issues with running the app on Marshmallow or Lollipop - could you please instead provide a Logcat so that I can see the issue you are having?

Niyamat Almass
Niyamat Almass
8,176 Points

Hi Harry James

Yes! After a lot of research over the internet I finally get rid of the problem of Marshallow project.

But I have another problem for understanding recycler view.

Here I post a question

https://teamtreehouse.com/community/need-help-for-understanding-recyclerview

Hi there,

I can't see anything immediately wrong with that, but something's clearly amiss.

Do you know which null you are receiving? There are three possible places that null can be returned. Perhaps place a Toast or a Log.e just above each one (the "failed to create directory" already has that) to see which part of getOutputMediaFileUri is giving you the null result.

Steve.

Let's move the conversation about my download of your project onto this answer, please. It keeps it all under some order, that way.

I've pulled your project from Github, got it into Android Studio and run it. I have created a new user on your Parse back-end. I then added you as a Friend from the two people created in the app, using Edit Friends in the overflow menu. I clicked the camera icon from the action bar. This produces the four optios, take photo, take video, choose photo or choose video. I selected Take Photo. The emulator emulates the takiing of a photo. This works fine, there is a log entry from your code saying that the jpg has been stored correctly.

What doesn't work on your app and what Log output are you seeing inLogcat. Let's pin down what behaviour you think you should see that you shouldn't or what exactly the problem is. This app appears to work fine - it certainly isn't returning null on selecting the 'take photo' option.

Steve.

Niyamat Almass
Niyamat Almass
8,176 Points

Why that happen?Can you just post a picture of configuration of your emulator.

What happen to my emulator?

I created new emulator but nothing change.

What happen to my computer?

I am just confused!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

I'm using Genymotion stock settings for a Nexus5 running 4.4.4 API 19.

What are you using?

Niyamat Almass
Niyamat Almass
8,176 Points

I am using nexux 5, android studio default emulator.

I have genymotion but not installed on my computer.But now I am installing it to check that work or not.

I'll checl the stock emulator too ... did you use default settings or did you change anything?