My app has functionality for allowing the user to either select a photo or video from their current library, or to take a new one, and then attach to an email. It seems to be working on the majority of devices and Android versions. However, I'm seeing some crashes intermittently for some device setups when getting the uri to the image or video to attach to the email. An example crash is: Fatal Exception: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=67425, result=-1, data=Intent { dat=content://com.google.android.apps.docs.storage/document/acc=1;doc=3 flg=0x1 }} to activity: java.lang.NullPointerException: Attempt to invoke virtual method 'char[] java.lang.String.toCharArray()' on a null object reference.
This happens on the "File file = new File(imagePath);
" line in the email() method below. Are there changes I can make to my code to allow for a more universal solution to this? My minSdkVersion is 16, and my targetSdkVersion is 23.
Below is my code for the implementation:
private enum Type {
PHOTO,
VIDEO
}
private enum Source {
LIBRARY,
CAMERA
}
private void select(Type type) {
if(selectedSource == Source.LIBRARY) {
// choose from library
Intent getIntent = new Intent(Intent.ACTION_GET_CONTENT);
if (type == Type.PHOTO) {
getIntent.setType("image/*");
} else {
getIntent.setType("video/*");
}
Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
if (type == Type.PHOTO) {
pickIntent.setType("image/*");
} else {
pickIntent.setType("video/*");
}
Intent chooserIntent = Intent.createChooser(getIntent, "Select Photo/Video");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{pickIntent});
startActivityForResult(chooserIntent, CHOOSE_IMAGE_VIDEO_ACTIVITY_REQUEST_CODE);
} else if(selectedSource == Source.CAMERA) {
// take photo/video from camera
// check for camera permission
if ( ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ) {
requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA);
}
if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//Toast.makeText(getActivity(), "You need to allow camera access", Toast.LENGTH_LONG).show();
return;
} else {
if (getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
if ( ContextCompat.checkSelfPermission(getActivity(), READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ) {
requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, REQUEST_EXTERNAL_STORAGE);
}
if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(getActivity(), READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
//Toast.makeText(getActivity(), "You need to allow external storage access", Toast.LENGTH_LONG).show();
return;
} else {
if (selectedType == Type.PHOTO) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
File photo = null;
try {
// place where to store camera taken picture
photo = this.createTemporaryFile("photo", ".jpg");
photo.delete();
} catch (Exception e) {
//Log.v(TAG, "Can't create file to take picture!");
Toast.makeText(getActivity(), "Please check SD card! Image shot is impossible!", Toast.LENGTH_LONG).show();
}
mImageUri = Uri.fromFile(photo);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
startActivityForResult(intent, TAKE_IMAGE_ACTIVITY_REQUEST_CODE);
} else {
Toast.makeText(getActivity(), "Unable to access the camera", Toast.LENGTH_LONG).show();
}
} else if (selectedType == Type.VIDEO) {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
File video = null;
try {
// place where to store camera taken video
video = this.createTemporaryFile("video", ".mp4");
video.delete();
} catch (Exception e) {
//Log.v(TAG, "Can't create file to take picture!");
Toast.makeText(getActivity(), "Please check SD card! Video is impossible!", Toast.LENGTH_LONG).show();
}
mImageUri = Uri.fromFile(video);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
startActivityForResult(intent, TAKE_VIDEO_ACTIVITY_REQUEST_CODE);
} else {
Toast.makeText(getActivity(), "Unable to access the camera", Toast.LENGTH_LONG).show();
}
}
}
} else {
Toast.makeText(getActivity(), "No camera available", Toast.LENGTH_LONG).show();
}
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CAMERA: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("REQUEST CAMERA RESULT");
select(selectedType);
} else {
//Permission denied
Toast.makeText(getActivity(), "You need to allow camera access", Toast.LENGTH_LONG).show();
}
return;
}
case REQUEST_EXTERNAL_STORAGE: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("REQUEST EXTERNAL STORAGE RESULT");
select(selectedType);
} else {
//Permission denied
Toast.makeText(getActivity(), "You need to allow external storage access", Toast.LENGTH_LONG).show();
}
return;
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CHOOSE_IMAGE_VIDEO_ACTIVITY_REQUEST_CODE) {
// from image picker
if (resultCode == Activity.RESULT_OK) {
if(data != null) {
//InputStream inputStream = getActivity().getContentResolver().openInputStream(data.getData());
mImageUri = data.getData();
imagePath = getPath(getActivity(), mImageUri);
email();
}
}
} else if(requestCode == TAKE_IMAGE_ACTIVITY_REQUEST_CODE || requestCode == TAKE_VIDEO_ACTIVITY_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
grabImageOrVideoTaken();
}
}
}
private void grabImageOrVideoTaken() {
getActivity().getContentResolver().notifyChange(mImageUri, null);
imagePath = getPath(getActivity(), mImageUri);
email();
}
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
private void email() {
String mediaType = "photo";
if(selectedType == Type.VIDEO) {
mediaType = "video";
}
String email = "test@test.com";
Intent intent = new Intent(Intent.ACTION_SEND, Uri.fromParts("mailto", email, null));
intent.putExtra(Intent.EXTRA_SUBJECT, getResources().getString(R.string.app_name) + ": Photo/Video Submission");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{email});
File file = new File(imagePath);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (selectedType == Type.PHOTO) {
intent.setType("image/jpeg");
} else {
intent.setType("video/3gp");
}
intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + file.getAbsolutePath()));
startActivity(Intent.createChooser(intent, "Submit Photo/Video"));
}