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>
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.
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With