Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Unfortunately, camera has stopped" on camera intent

So im trying to allow the user to take a picture which I would then send to a server. I am a bit of a noob in android so i followed this tutorial on how to do so. I've seen a few questions similar to mine but not quite the same. The app does launch the camera app and allows me to take a picture, it's when I hit the "check" to accept the image so to say that I get the message "Unfortunately, camera has stopped" and the app returns to the activity where I don't get the thumbnail nor the image aparently. Logcat shows nothing from the moment the camera launches to when it stops working. Here is my code:

Activity

package com.example.ignacio.androidchat;


import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;


public class TaskCompletionForm extends AppCompatActivity{

    private static final String TAG = "TaskCompletionForm";
    private static int REQUEST_IMAGE_CAPTURE = 1;
    ImageView formImageView;
    private String currImagePath;

    private final boolean submitTime = false;
    private final boolean submitGPS = false;
    private final boolean requestImage = true;
    private final boolean requestQRRead = false;
    private final boolean requestRFID = false;

    private LinearLayout formContainer;


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

        formContainer = (LinearLayout) findViewById(R.id.ll_formContainer);

        final Button b_submit = (Button) findViewById(R.id.b_submit);
        b_submit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                submitForm();
            }
        });

        final Button b_takePicture = (Button) findViewById(R.id.b_takePicture);
        b_takePicture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dispatchPhotographIntent();
            }
        });

        formImageView = (ImageView) findViewById(R.id.formImageView);

    }

    private void submitForm() {
        final int childCount = formContainer.getChildCount() - 1;
        JSONObject jsonObject = new JSONObject();
        try {
            // add text fields
            int textFieldCount = 0;
            ArrayList<String> entries = new ArrayList<>();
            for (int i = 0; i < childCount; i++) {
                View v = formContainer.getChildAt(i);
                if (v instanceof EditText) {
                    entries.add(((EditText) v).getText().toString());
                    textFieldCount++;
                }
            }
            jsonObject.put("fieldCount", textFieldCount);
            jsonObject.put("fields", new JSONArray(entries));

            // add image
            if (requestImage && !currImagePath.equals(""))
            {
                Bitmap bitmap = BitmapFactory.decodeFile(currImagePath);
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
                byte[] byteArray = byteArrayOutputStream.toByteArray();
                String encodedImage = Base64.encodeToString(byteArray, Base64.DEFAULT);
                jsonObject.put("image", encodedImage);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

        WebSocket.getInstance().submitCompletionForm(jsonObject);
    }

    private void dispatchPhotographIntent()
    {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null)
        {
            File photoFile = null;
            try{
                photoFile = createImageFile();
            } catch (IOException e)
            {
                e.printStackTrace();
                // handle exeption
            }

            // continue only if the file was successfully created
            if (photoFile != null)
            {
                Uri photoURI = FileProvider.getUriForFile(this,
                        "com.example.ignacio.androidchat.fileprovider",
                        photoFile);
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
            }
        }
    }

    private File createImageFile() throws IOException
    {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File image = File.createTempFile(imageFileName, ".jpg", storageDir);
        if (!image.exists())
        {
            image.getParentFile().mkdirs();
            image.createNewFile();
        }
        currImagePath = image.getAbsolutePath();
        return image;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
            Bundle extras = data.getExtras();
            Bitmap imageBitmap = (Bitmap) extras.get("data");
            formImageView.setImageBitmap(imageBitmap);
        }
    }
}

Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.ignacio.androidchat">

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

    <!-- To auto-complete the email text field in the login form with the user's emails
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.READ_PROFILE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />-->

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.CAMERA"
        android:required = "true"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

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

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".HomeScreen"
            android:launchMode="singleTask"/>
        <activity android:name=".TaskDescription" />
        <activity android:name=".TaskCompletionForm"
            android:configChanges="keyboardHidden|orientation"/>

        <service android:name=".WebSocketIntentService" />
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version"/>

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

</manifest>

file_paths.xml


<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images"
        path="Android/data/com.example.ignacio.androidchat/files/Pictures/" />
</paths>
like image 754
ignacio murillo Avatar asked Feb 06 '17 00:02

ignacio murillo


2 Answers

I hypothesize that the camera application on older Samsung Android devices (api 17 in my case) requires a uri in the format "file:///.....". The problem is the file uri format causes Android Nougat (api 24 and greater) to throw the FileUriExposedException.

I got this to work:

Uri imageUri;
// N is for Nougat Api 24 Android 7
if (Build.VERSION_CODES.N <= android.os.Build.VERSION.SDK_INT) {
    // FileProvider required for Android 7.  Sending a file URI throws exception.
    imageUri = FileProvider.getUriForFile(getContext(),"com.<your name here>.fileprovider",imageFile);
} else {
    // For older devices:
    // Samsung Galaxy Tab 7" 2 (Samsung GT-P3113 Android 4.2.2, API 17)
    // Samsung S3
    imageUri = Uri.fromFile(imageFile);
}

The ideal is to only use FileProvider in Nougat or higher.
You may want to lower the api version of Build.VERSION_CODES. YMMV, depending on your tolerance for on the file uri exposure.

See: Android Documentation for note on FileUriExposedException in Android Nougat.

like image 173
fishjd Avatar answered Nov 13 '22 18:11

fishjd


So I found a solution, but still have no idea to the problem. What worked for me was to change the "dispatchPhotographIntent" to the following:

private void dispatchPhotographIntent()
{
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    imageFile = createImageFile();
    Uri uri = Uri.fromFile(imageFile);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
}

Here I am not using a file provider nor keeping track of the Uri of the file generated by the function "createImageFile". Instead I generate the file and get the Uri directly from it. I hope this is useful to someone or someone finds the problem itself since it still intrigues me.

Thanks to all who comented and tried to help!

like image 45
ignacio murillo Avatar answered Nov 13 '22 20:11

ignacio murillo