Ok so I fixed the code so the tutorial will work on SDK23+. Basically you need to ask for a permission to write to external directory.
- Add the following line to build.grade (Module: app) and sync.
compile 'com.android.support:support-v4:25.0.0'
- Add import statements to TakePictureActivity
import android.support.v4.content.ContextCompat;
import android.support.v4.app.ActivityCompat;
- Modify the following functions in TakePictureActivity to ask for permission. If no permission is granted, tapping takePictureImageView does nothing.
private void takePictureWithCamera() {
if (appHasExternalStorageWritePermission()) {
// create intent to capture image from camera
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File photoFile = createImageFile();
selectedPhotoPath = Uri.parse(photoFile.getAbsolutePath());
captureIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
startActivityForResult(captureIntent, TAKE_PHOTO_REQUEST_CODE);
} else {
acquireExternalStorageWritePermissionIfNeeded();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == TAKE_PHOTO_REQUEST_CODE && resultCode == RESULT_OK) {
setImageViewWithImage();
}
}
private boolean appHasExternalStorageWritePermission() {
return ContextCompat.checkSelfPermission(TakePictureActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED;
}
private void acquireExternalStorageWritePermissionIfNeeded() {
// Here, thisActivity is the current activity
if (!appHasExternalStorageWritePermission()) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(TakePictureActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(TakePictureActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_WRITE_EXTERNAL_STORAGE);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_WRITE_EXTERNAL_STORAGE: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
takePictureWithCamera();
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
Optionally you can write to internal storage if external storage is not available (not tested)
a. in TakePictureActivity
private File createImageFile() {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
String state = Environment.getExternalStorageState();
File imageFile = null;
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES + APP_PICTURE_DIRECTORY);
if (Environment.MEDIA_MOUNTED.equals(state)) {
storageDir.mkdirs();
try {
imageFile = File.createTempFile(
imageFileName, /* prefix */
FILE_SUFFIX_JPG, /* suffix */
storageDir /* directory */
);
} catch (IOException e) {
//ask for permission here if the permission was denied
// Log data here
//ContextWrapper cw = new ContextWrapper(getApplicationContext());
//imageFile = new File(cw.getFilesDir(), imageFileName);
}
} else {
ContextWrapper cw = new ContextWrapper(getApplicationContext());
imageFile = new File(cw.getFilesDir(), imageFileName);
}
return imageFile;
}
b. in EnterTextActivity
private void saveImageToGallery(Bitmap memeBitmap) {
if (!originalImage) {
// save bitmap to file
String state = Environment.getExternalStorageState();
File imageFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES + APP_PICTURE_DIRECTORY), memeBitmap + FILE_SUFFIX_JPG);
if (!Environment.MEDIA_MOUNTED.equals(state)) {
ContextWrapper cw = new ContextWrapper(getApplicationContext());
imageFile = new File(cw.getFilesDir(), memeBitmap + FILE_SUFFIX_JPG);
}
try {
// create outputstream, compress image and write to file, flush and close outputstream
FileOutputStream fos = new FileOutputStream(imageFile);
memeBitmap.compress(Bitmap.CompressFormat.JPEG, 85, fos);
fos.flush();
fos.close();
} catch (IOException e) {
Toast.makeText(this, getResources().getText(R.string.save_image_failed).toString(), Toast.LENGTH_SHORT).show();
}
// Create intent to request newly created file to be scanned, pass picture uri and broadcast intent
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(Uri.fromFile(imageFile));
sendBroadcast(mediaScanIntent);
Toast.makeText(this, getResources().getText(R.string.save_image_succeeded).toString(), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, getResources().getText(R.string.add_meme_message).toString(), Toast.LENGTH_SHORT).show();
}
}
Not coded: it is a good idea to delete the temporary file after saving the file, or when users tap back from EnterTextActivity to not increase the app directory size.