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 Sending Messages Sending the Message

Choose Picture and Video intent calls not attaching data to message, returns null and I can't figure out why exactly.

Take Photo and Video works like a charm but Choose Photo or Video returns null to main activity with no errors caught or error toasts.

Logcat states:

/com.blabla.ribbit E/ViewRootImpl﹕ sendUserActionEvent() mView == null

which leads me to think data is not being attached to intent upon choosing of picture or video

My MainActivity code follows:

MainActivity.java
public class MyActivity extends FragmentActivity implements ActionBar.TabListener {

    public static String TAG = MyActivity.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;

    public static final int FILE_SIZE_LIMIT = 1024*1024*10; //10MB

    protected Uri mMediaUri;

    protected DialogInterface.OnClickListener mDialogListener =
            new DialogInterface.OnClickListener() {


                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    switch (i) {
                        case 0: //Picture
                            Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                            mMediaUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
                            if (mMediaUri == null){
                                //display error
                                Toast.makeText(MyActivity.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: //Video
                            Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                            mMediaUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);
                            mMediaUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
                            if (mMediaUri == null) {
                                //display error
                                Toast.makeText(MyActivity.this, R.string.error_external_storage, Toast.LENGTH_LONG).show();
                            }
                        else{

                                takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mMediaUri);
                                takeVideoIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10);
                                takeVideoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
                                startActivityForResult(takeVideoIntent,TAKE_VIDEO_REQUEST);
                            }

                            break;
                        case 2: //Choose Pic

                            Intent choosePhotoIntent = new Intent(Intent.ACTION_GET_CONTENT);
                            choosePhotoIntent.setType("image/*");
                            startActivityForResult(choosePhotoIntent, PICK_PHOTO_REQUEST);
                            break;
                        case 3: //Choose Video
                            Intent chooseVideoIntent = new Intent(Intent.ACTION_GET_CONTENT);
                            chooseVideoIntent.setType("video/*");
                            Toast.makeText(MyActivity.this,getString(R.string.video_message), Toast.LENGTH_LONG).show();
                            startActivityForResult(chooseVideoIntent, PICK_VIDEO_REQUEST);
                            break;
                        }
                    }

                private Uri getOutputMediaFileUri(int mediaType) {
                    // To be safe, you should check that the SDCard is mounted
                    // using Environment.getExternalStorageState() before doing this.
                    if (isExternalStorageAvailable()) {
                        //Get Uri
                        //1. Get ex storage directory
                        String appName = MyActivity.this.getString(R.string.app_name);
                        File mediaStorageDir = new File(
                                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appName);
                        //2. Create own subdirectory
                        if (!mediaStorageDir.exists()) {
                            if (!mediaStorageDir.mkdirs()) {
                                Log.e(TAG, "Failed to create directory");
                                return null;
                            }
                        }
                        //3.Create file name
                        //4.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 + ".jpeg");
                        } else if (mediaType == MEDIA_TYPE_VIDEO) {
                            mediaFile = new File(path + "VID_" + timestamp + ".mp4");
                        } else {
                            return null;
                        }
                        Log.d(TAG, "File" + Uri.fromFile(mediaFile));
                        //5. return 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;
                    }
                }
            };

    /**
     * The {@link android.support.v4.view.PagerAdapter} that will provide
     * fragments for each of the sections. We use a
     * {@link FragmentPagerAdapter} derivative, which will keep every
     * loaded fragment in memory. If this becomes too memory intensive, it
     * may be best to switch to a
     * {@link android.support.v13.app.FragmentStatePagerAdapter}.
     */
    SectionsPagerAdapter mSectionsPagerAdapter;

    /**
     * The {@link ViewPager} that will host the section contents.
     */
    ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.activity_my);

        ParseAnalytics.trackAppOpened(getIntent());

        //ParseUser.logOut();
        ParseUser currentUser = ParseUser.getCurrentUser();

        if(currentUser==null){
            GoToLogin();
        }
        else{
            Log.i(TAG, currentUser.getUsername());
        }

        // Set up the action bar.
        final ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        // Create the adapter that will return a fragment for each of the three
        // primary sections of the activity.
        mSectionsPagerAdapter = new SectionsPagerAdapter(this,getSupportFragmentManager());

        // Set up the ViewPager with the sections adapter.
        mViewPager = (ViewPager) findViewById(R.id.pager);
        mViewPager.setAdapter(mSectionsPagerAdapter);

        // When swiping between different sections, select the corresponding
        // tab. We can also use ActionBar.Tab#select() to do this if we have
        // a reference to the Tab.
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                actionBar.setSelectedNavigationItem(position);
            }
        });

        // For each of the sections in the app, add a tab to the action bar.
        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
            // Create a tab with text corresponding to the page title defined by
            // the adapter. Also specify this Activity object, which implements
            // the TabListener interface, as the callback (listener) for when
            // this tab is selected.
            actionBar.addTab(
                    actionBar.newTab()
                            .setText(mSectionsPagerAdapter.getPageTitle(i))
                            .setTabListener(this));
        }
    }

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

        if (resultCode == RESULT_OK) {
            if (requestCode == PICK_PHOTO_REQUEST || requestCode == PICK_VIDEO_REQUEST) {
                if (data == null) {
                    Toast.makeText(this, getString(R.string.general_error), Toast.LENGTH_LONG).show();
                } else {
                    mMediaUri = data.getData();
                }
                if (requestCode == PICK_VIDEO_REQUEST) {
                    //make sure file less than 10MB
                    int fileSize = 0;
                    InputStream inputStream = null;
                    try {
                        inputStream = getContentResolver().openInputStream(mMediaUri);
                        fileSize = inputStream.available();
                    } catch (FileNotFoundException e) {
                        Toast.makeText(this, getString(R.string.filesize_error), Toast.LENGTH_LONG).show();
                        return;
                    } catch (IOException e) {
                        Toast.makeText(this, getString(R.string.filesize_error), Toast.LENGTH_LONG).show();
                        return;
                    } finally {
                        try {
                            inputStream.close();
                        } catch (IOException e) {

                        }
                    }
                    if (fileSize >= FILE_SIZE_LIMIT) {
                        Toast.makeText(this, getString(R.string.file_size_warning), Toast.LENGTH_LONG).show();
                        return;
                    }
                }
            } else {
                //add to gallery
                Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                mediaScanIntent.setData(mMediaUri);

                String fileType;
                if (requestCode == PICK_VIDEO_REQUEST || requestCode == TAKE_VIDEO_REQUEST) {
                    fileType = ParseConstants.TYPE_VIDEO;
                } else {
                    fileType = ParseConstants.TYPE_IMAGE;
                }
                sendBroadcast(mediaScanIntent);

                Intent recipientIntent = new Intent(this, RecipientsActivity.class);
                recipientIntent.setData(mMediaUri);
                recipientIntent.putExtra(ParseConstants.KEY_FILE_TYPE, fileType);
                startActivity(recipientIntent);
            }
        }
        else if(resultCode != RESULT_CANCELED){
           Toast.makeText(this,R.string.general_error, Toast.LENGTH_LONG).show();
        }
    }

    private void GoToLogin() {
        Intent intent = new Intent(this,LoginActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.my, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        switch(item.getItemId()){

            case R.id.action_logout_item:
            ParseUser.logOut();
            GoToLogin();
                break;
            case R.id.action_edit_friends:
            Intent intent = new Intent(this, EditFriendsActivity.class);
            startActivity(intent);
                break;
            case R.id.action_camera:
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setItems(R.array.camera_choices, mDialogListener);
                AlertDialog dialog = builder.create();
                dialog.show();

        return super.onOptionsItemSelected(item);
            default: return super.onOptionsItemSelected(item);
    }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
        // When the given tab is selected, switch to the corresponding page in
        // the ViewPager.
        mViewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }

    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }
}

Could it be one of the statements being excluded in onActivityforResult?

14 Answers

This works.

I don't have a network error so I don't call the AlertDialog.Builder() block, so I'm of no use, unfortunately.

But, despite it being a little clunky in places (which will be fixed as you finish the app) it works fine. Although, I'm not receiving messages in the Inbox. You may have as I've been sending my messages to everyone on the Friends list! ;-)

I have installed this on a device, a Sony Xperia Z, and it is working OK. To try to replicate the error takes some doing. I tried to send a message in Flight Mode; that won't work as the Friends List is populated on the fly. So, I chose the image to send, selected recipients THEN put it in flight mode and hit send. Yes, I get the error you get but my code errors at the end of the callback block - not at the .show() line.

This might be a Parse issue, rather than a code one?

I've sent a few messages; they're not appearing so maybe you've not got that far yet. But this seems to work fine. There's the odd error creeping in but I think that's more a package name issue which is confusing Gradle and my Mac generally!

But this looks good - I don't know what else to say!

Hi,

To make the code a little easier to read, could you add three ` marks (I'm not sure what it is called - it is the reverse apostrophe mark) before the start of the code and then a further three after. If at the three beginning ones you add the word java, the post will be formatted a little more clearly.

It then looks like:

@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}

Also, where is the code for startActivityForResult() - I can't see it but I can see that it is called in a few places. I'll have a look through onActivityForResult to see if your suspicions are correct!

Hi again,

In onActivityResult() you start by checking the resultCode is OK then move on to check requestCode. In that If statement, you're checking to see if the request is a PICK, rather than a TAKE. That's fine. It is checking whether the requestCode is either a PICK_PHOTO or PICK_VIDEO.

However, in the else of this if statement, you then go on to check whether the requestCode is a PICK or TAKE video. That seems at odds to the starting conditions? I'd expect to see the two tAKE options down there so as to be mutually exclusive of the initial conditional?

Hope that helps - I may be wrong, obviously!!

Hi Steve,

Am actually only seeing this reply now and had come to the same conclusion myself. I now changed onActivitResult to the following but still getting an uncaught exception error just before it toasts 'message sent' on either a PICK_VIDEO or TAKE_VIDEO request:

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

        if (resultCode == RESULT_OK) {
            if (requestCode == PICK_PHOTO_REQUEST || requestCode == PICK_VIDEO_REQUEST) {
                if (data == null) {
                    Toast.makeText(this, getString(R.string.general_error), Toast.LENGTH_LONG).show();
                } else {
                    mMediaUri = data.getData();
                }
                if (requestCode == PICK_VIDEO_REQUEST) {
                    //make sure file less than 10MB
                    int fileSize = 0;
                    InputStream inputStream = null;
                    try {
                        inputStream = getContentResolver().openInputStream(mMediaUri);
                        fileSize = inputStream.available();
                    } catch (FileNotFoundException e) {
                        Toast.makeText(this, getString(R.string.filesize_error), Toast.LENGTH_LONG).show();
                        return;
                    } catch (IOException e) {
                        Toast.makeText(this, getString(R.string.filesize_error), Toast.LENGTH_LONG).show();
                        return;
                    } finally {
                        try {
                            inputStream.close();
                        } catch (IOException e) {

                        }
                    }
                    if (fileSize >= FILE_SIZE_LIMIT) {
                        Toast.makeText(this, getString(R.string.file_size_warning), Toast.LENGTH_LONG).show();
                        return;
                    }
                }
            } else {
                //add to gallery
                Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                mediaScanIntent.setData(mMediaUri);
                sendBroadcast(mediaScanIntent);
            }

            Intent recipientIntent = new Intent(this, RecipientsActivity.class);
            recipientIntent.setData(mMediaUri);

                String fileType;
                if (requestCode == PICK_PHOTO_REQUEST || requestCode == TAKE_PHOTO_REQUEST) {
                    fileType = ParseConstants.TYPE_IMAGE;
                } else {
                    fileType = ParseConstants.TYPE_VIDEO;
                }

                recipientIntent.putExtra(ParseConstants.KEY_FILE_TYPE, fileType);
                startActivity(recipientIntent);

        }
        else if(resultCode != RESULT_CANCELED){
           Toast.makeText(this,R.string.general_error, Toast.LENGTH_LONG).show();
        }
    }

Looks like the error is at line 198 of RecipientsActivity. Which line is that?

Line 198 is right on: dialog.show()

Found that I was declaring an extra mMediaUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE); in the TAKE_VIDEO_REQUEST case, fixed it but am still getting:

02-16 17:35:23.561 1612-1612/com.thetechguys.ribbit E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.thetechguys.ribbit, PID: 1612 android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@41fc6c30 is not valid; is your activity running? at android.view.ViewRootImpl.setView(ViewRootImpl.java:771) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:278) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69) at android.app.Dialog.show(Dialog.java:289) at com.thetechguys.ribbit.RecipientsActivity$2.done(RecipientsActivity.java:198) at com.parse.SaveCallback.internalDone(SaveCallback.java:39) at com.parse.SaveCallback.internalDone(SaveCallback.java:27) at com.parse.Parse$6$1.run(Parse.java:917) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:146) at android.app.ActivityThread.main(ActivityThread.java:5598) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099) at dalvik.system.NativeStart.main(Native Method) from Logcat

This one?:

case R.id.action_camera:
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setItems(R.array.camera_choices, mDialogListener);
            AlertDialog dialog = builder.create();
            dialog.show();

I meant the case(I found error in) is for choosing actions in MainActivity but the error Logcat gives is actually on 'send' method in RecipientsActivity:

``` protected void send(ParseObject message){ message.saveInBackground(new SaveCallback() { @Override public void done(ParseException e) { if(e==null){ //success Toast.makeText(RecipientsActivity.this,getString(R.string.sucess_message),Toast.LENGTH_LONG).show(); }

            else{
                //error
                AlertDialog.Builder builder = new AlertDialog.Builder(RecipientsActivity.this);
                builder.setMessage(getString(R.string.error_sending_message))
                        .setTitle(getString(R.string.error_selecting_message_title))
                        .setPositiveButton(android.R.string.ok, null);
                AlertDialog dialog = builder.create();
                dialog.show();
            }
        }
    });
} ```

OK - you've got the else part running because e != null

That's good ... add:

Log.d(TAG, "Weird error: " + e);

That might add a little more detail regarding what Parse is doing as that's mentioned in the errors.

It is ironic that the dialog you want to show the connection error is the thing causing the problem!

Is R.string.sucess_message correct? Or is it R.string.success_message Just checking the spelling is the same in both files.

It's a: com.parse.ParseException: i/o failure: java.net.SocketTimeoutException:! Checking Parse documentation to see how that's avoided.

That's only one problem, unfortunately.

The else code is being run because your connection is timing out - that's one issue. The problems is that your error handling code is then causing an exception in the code, due to the dialog.show() line, for some reason.

Fixing the Parse issue won't fix the dialog issue but it will avoid it as the else code will never run.

You're right, am still getting the uncaught exception just before dialog.show(). Do you have an idea on where I could start debugging?

I'm wondering if the context is correct given that you are inside a callback method. Maybe try using

AlertDialog.Builder builder = new AlertDialog.Builder(RecipientsActivity.getContext());

One option is to just replace the AlertDialog with a Toast although that is cheating a little!!!

Can you send me your project in a zip file? I can see what I get at this end.

When I substitute it for a Toast, I still get timeoutexception but app doesn't crash, I'm guessing it is probably AlertDialog context that causes it.

Where do I send zip to?

Downloaded (over my appallingly slow broadband) and am currently installing the relevant SDKs and modifying locations etc.

I'll then have a go at fixing the problem. Leave it with me!

Changing the Dialog for a Toast will fix that issue, yes. The timeout exception is a separate one - good luck with that!

Thanks Steve, I'm just about to start the retrieving messages section so that's why they're not yet appearing in inbox but if it's as you say I should be fine to continue. I've checked on Parse back end and I have actually managed to send one video message even though it still gave me the timeout exception so I gather it most probably is a Parse issue.

Let me know if I can help with anything else as you progress through the app. :-)

Jorge De Pablos
Jorge De Pablos
6,260 Points

Is it possible that the Exception is caused because you finish the Recipients Activity after sending the message??

I think Dialogs only can live within the lifecycle of the Activity so if you finish the Activity you get an error.

I'm getting the same error and when I comment the line

finish();

the dialog is shown correctly.

Vincent Rickey
Vincent Rickey
5,581 Points

Hi. I also experienced the sendUserActionEvent() mView == null error message. I think there are a few additional pieces to the puzzle in your issue, but I was able to resolve the error by correcting the if statement conditions at the bottom of the onActivityResult else block. The problem was that I accidentally used "CHOOSE_PHOTO_REQUEST" and "CHOOSE_VIDEO_REQUEST" instead of "CHOOSE_PHOTO_REQUEST" and "TAKE_PHOTO_REQUEST". This set a video file type equal to an image, thereby causing the app to crash when I tried to choose a video to add to the message.

Incorrect code - Error - Incorrect combo of CHOOSE_PHOTO_REQUEST and CHOOSE_VIDEO_REQUEST

String fileType;
            if (requestCode == CHOOSE_PHOTO_REQUEST || requestCode == CHOOSE_VIDEO_REQUEST) {
                fileType = ParseConstants.TYPE_IMAGE;
            }
            else {
                fileType = ParseConstants.TYPE_VIDEO;
            }

Resolved - The if statement checks for photos, the else statement checks for videos

String fileType;
            if (requestCode == CHOOSE_PHOTO_REQUEST || requestCode == TAKE_PHOTO_REQUEST) {
                fileType = ParseConstants.TYPE_IMAGE;
            }
            else {
                fileType = ParseConstants.TYPE_VIDEO;
            }