I'm trying to write code that launches the camera app, saves the image, and then displays a bitmap of the captured image.
This is the relevant code, which was adapted from https://guides.codepath.com/android/Accessing-the-Camera-and-Stored-Media:
public static final int PICTURE_REQUEST_CODE = 1034;
public static final String FILE_BEGIN = "note";
private int note_id = 4;
public static final String FILE_END = ".jpg";
private static final String APP_TAG = "School_App";
private void takePicture() {
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, getPhotoFileUri(FILE_BEGIN + note_id + FILE_END));
if (i.resolveActivity(getPackageManager()) != null){
startActivityForResult(i, PICTURE_REQUEST_CODE);
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PICTURE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
Uri takenPhotoUri = getPhotoFileUri(FILE_BEGIN + note_id + FILE_END);
Log.d("NotesDetail", takenPhotoUri.toString());
// by this point we have the camera photo on disk
Bitmap takenImage = BitmapFactory.decodeFile(takenPhotoUri.getPath());
// RESIZE BITMAP, see section below
// Load the taken image into a preview
ImageView ivPreview = (ImageView) findViewById(R.id.iv_notes_picture);
ivPreview.setImageBitmap(takenImage);
} else { // Result was a failure
Toast.makeText(this, "Picture wasn't taken!", Toast.LENGTH_SHORT).show();
}
}
}
private Uri getPhotoFileUri(String fileName) {
if(isSpaceAvailable()){
File mediaStorageDir = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), APP_TAG);
if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()){
Log.d(APP_TAG, "Failed to make directory");
}
File file = new File(mediaStorageDir.getPath() + File.separator + fileName);
Uri uri = FileProvider.getUriForFile(NotesDetail.this, "com.kfode.schoolapp.fileprovider", file);
Log.d("getPhotoFileUri", uri.toString());
return uri;
}
return null;
}
private boolean isSpaceAvailable() {
String state = Environment.getExternalStorageState();
return state.equals(Environment.MEDIA_MOUNTED);
}
I also have the following permissions set:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Within the application tag of the manifest...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.kfode.schoolapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"
/>
</provider>
And within the file_paths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="images"
path="Pictures"/>
When I run the app, I get the following log messages and exception:
D/getPhotoFileUri: content://com.kfode.schoolapp.fileprovider/images/School_App/note4.jpg
D/NotesDetail: content://com.kfode.schoolapp.fileprovider/images/School_App/note4.jpg
E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /images/School_App/note4.jpg: open failed: ENOENT (No such file or directory)
Not sure what is going on here. Help please?
Bitmap takenImage = BitmapFactory.decodeFile(takenPhotoUri.getPath());
This will not work. A Uri is not a File.
Refactor getPhotoFileUri() into two methods:
getPhotoFile() that generates your File
getPhotoFileUri() that obtains the FileProvider Uri for that File
Then, use an image-loading library (Glide, Picasso, etc.) to populate your ImageView from the file returned by getPhotoFile(). This not only will handle all the BitmapFactory stuff for you, but it will also do that work on a background thread, so you do not freeze your UI the way you are in your current code.
Related
I want to create an application that when the user clicks a button, it takes you to the phone's default camera app, captures a pictures and brings it back to the activity's imageview object to be displayed in the activity. Below is the code I have.
PROBLEM: The issue is that the image is not displaying in the imageview. It comes back to the activity after taking a pictures, but it does not display it.
AndroidManifest.xml has the below added:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera" android:required="true" />
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.application.hidden.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="my_images" path="/" />
</paths>
MainActivity.java
private File createImageFile() throws IOException {
String timeStamp =
new SimpleDateFormat("yyyyMMdd_HHmmss",
Locale.getDefault()).format(new Date());
String imageFileName = "IMG_" + timeStamp + "_";
File storageDir = this.getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName,
".jpg",
storageDir
);
imagePath = image.getAbsolutePath();
return image;
}
#OnClick({R.id.takePicture, R.id.pic})
void onTakePic() {
changeImageLayout.setVisibility(GONE);
System.out.println("Setting up for capture, clicked on camera");
Intent pictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if(pictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
//Create a file to store the image
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
}
if(photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this.getContext(),"com.application.hidden.provider", photoFile);
pictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(pictureIntent, RESULT_LOAD_CAMERA_IMG);
}
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
case RESULT_LOAD_CAMERA_IMG:
switch (resultCode) {
case Activity.RESULT_OK:
Glide.with(this).load(imagePath).into(listingImage);
break;
case Activity.RESULT_CANCELED:
isNewImageAdded = false;
break;
default:
break;
}
break;
default:
break;
}
}
Would appreciate any suggestions or help!
Thank you.
You need to convert your file path to file object, use new File(imagepath), instead of just imagepath inside glide.
I think you are trying to capture an image from the camera and store in local memory and then load from memory.
If you just want to display captured image in imageview then follow the below way.
For open camera:
intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 7);
Then in onActivityResult result:
if (requestCode == 7 && resultCode == RESULT_OK) {
Bitmap bitmap = (Bitmap) data.getExtras().get("data");
imageView.setImageBitmap(bitmap);
}
One of the features of my app is to allow user to assign a photo of an item stored in a DB. This could be done by either taking a new photo with the in-built camera or choosing an image from the library. Then app resizes the captured image, retrieve a full size image URI and store both in a DB. Full size image URI is stored for a later use in case user wants to load a full size image with the default image viewer. Everything works fine except viewer is unable to load image from the captured image URI right after the photo is taken, but it is possible to load the same image only when it is chosen from the library.
Ok here is the code:
manifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.packagename.inventoryapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/fileprovider" />
</provider>
fileprovider.xml
<external-files-path
name="images"
path="Pictures" />
Handle the camera:
#NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
public void onLaunchCamera(){
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
photoFileName = String.valueOf(System.currentTimeMillis()) + ".jpg";
photoFile = getPhotoFileUri(photoFileName);
Uri fileProvider = FileProvider.getUriForFile(this,
ProductContract.CONTENT_AUTHORITY + ".fileprovider", photoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileProvider);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
}
public File getPhotoFileUri(String fileName){
File mediaStorageDir = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), APP_TAG);
if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()){
Log.d(APP_TAG, "failed to create directory");
}
return new File(mediaStorageDir.getPath() + File.separator + fileName);
}
onActivtiyResult (takenImage - global Bitmap variable;
mPicUri - global Uri variable):
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
/* User chose to take a new photo */
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
File takenPhotoUri = getPhotoFileUri(photoFileName);
mPicUri = FileProvider.getUriForFile
(this, ProductContract.CONTENT_AUTHORITY + ".fileprovider", photoFile);
Bitmap fullSizeImage = BitmapFactory.decodeFile(takenPhotoUri.getAbsolutePath());
takenImage = BitmapScaler.scaleToFitWidth
(fullSizeImage, mProductImageView.getWidth() / 100 * 50);
} else { // Result was a failure
Toast.makeText(this, getString(R.string.no_picture_taken),
Toast.LENGTH_SHORT).show();
}
}
/* User chose to take an an existing photo from the gallery */
else if (requestCode == PICK_PHOTO_CODE){
if (resultCode == RESULT_OK) {
mPicUri = data.getData();
try {
Bitmap fullSizeImage = MediaStore.Images.Media.getBitmap
(getContentResolver(), mPicUri);
takenImage = BitmapScaler.scaleToFitWidth
(fullSizeImage, mProductImageView.getWidth() / 100 * 60);
} catch (IOException e) {
e.printStackTrace();
}
}
}
mProductImageView.setImageBitmap(takenImage);
/* Save image and it's URi to the database */
if (takenImage != null){
ContentValues values = new ContentValues();
values.put(ProductEntry.COLUMN_IMAGE, DbBitmapUtility.getBytesArray(takenImage));
values.put(ProductEntry.COLUMN_IMAGE_URL, mPicUri.toString());
int rows = getContentResolver().update(mProductUri, values, null, null);
}
}
Open default image image viewer to load a full size image from Uri:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(mPicUri);
startActivity(intent);
I realise that the problem is in Uri path of a captured photo. When I retrieve it in the above way I get something like:
content://com.packagename.inventoryapp.fileprovider/images/InventoryApp/1526632674426.jpg
and Image viewer is launching with the blank screen indicating it is searching for the image with no success.
I tried to get mPicUri with getAbsolutePath() method that leads to the app crashing on launching the intent with that message:
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=/storage/emulated/0/Android/data/com.packagename.inventoryapp/files/Pictures/InventoryApp/1526635391354.jpg }
On the contrary taking image from the existing library works fine and image Uri looks like:
content://com.google.android.apps.photos.contentprovider/0/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F2504/ORIGINAL/NONE/1872082740
So the question is it possible to somehow retrieve captured image Uri that is not app private and could be red by image viewer?
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=/storage/emulated/0/Android/data/com.packagename.inventoryapp/files/Pictures/InventoryApp/1526635391354.jpg }
That is not a valid Uri.
A Uri has a scheme. Yours does not. Yours resembles a bare filesystem path. In principle, you could convert that to a Uri using Uri.fromFile().
However, on Android 7.0+, using such a Uri will fail with a FileUriExposedException.
Instead, use the File with FileProvider.getUriForFile(), and provide that Uri to your ACTION_VIEW Intent. Be sure to also call addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) on that Intent, to allow third-party apps to read the content identified by that Uri.
I have an app in which I capture image and set it to the ImageView and then upload it to the server. But whenever I capture images the image is not getting displayed and when I try to upload the image I get FileNotFoundException as the path does not contain the image.
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.imageuploader.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="Image Uploader" path="Android/data/com.example.android.imageuploader/files/Pictures" />
</paths>
When i create image file I use this,
private File createImageFile() {
String timeStamp = new SimpleDateFormat(
getString(R.string.time_stamp_format), Locale.getDefault())
.format(new Date());
String fileName = getString(R.string.file_name_format, timeStamp);
File storageDirectory =
new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), getString(R.string.app_name));
if (!(storageDirectory.exists() || storageDirectory.mkdirs())) {
Helper.showToastMessage(mContext, getString(R.string.warn_storage_dir));
}
return new File(storageDirectory.getPath() + File.separator + fileName);
}
Actually I am saving the image in the internal storage directory 'Pictures' and in it I am creating folder after the app's name in which all the images are being saved. But while setting the image to the ImageView I am getting different file path say,
Image Uploader/Pictures/Image Uploader/20180406_101234.jpg
which is why the image is not being displayed as well as uploaded.
Where I am going wrong I am not able to figure that out. Please help.
This is how am doing, this works perfectly.
AndroidManifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.yourpackagename.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_path"/>
</provider>
provider_path.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="/storage/emulated/0" path="."/>
</paths>
createImageFile() make sure you have read and write external storage permission
private File createImageFile() throws IOException
{
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "MYAPPNAME-" + timeStamp + ".png";
File mediaStorageDir = new File(Environment.getExternalStorageDirectory(),
"YourAppFolder");
File storageDir = new File(mediaStorageDir + "/Profile_Images");
if (!storageDir.exists())
{
storageDir.mkdirs();
}
File image = new File(storageDir, imageFileName);
return image;
}
click listener on button to take camera image
===Global Variables===
Uri mUri;
private static final int CAMERA_IMAGE_RESULT = 202;
private static final String CAPTURE_IMAGE_FILE_PROVIDER = "com.yourpackagename.fileprovider";
===Global Variables===
takeImageBTN.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
File file = null;
try
{
file = createImageFile();
mUri = FileProvider.getUriForFile(this,
CAPTURE_IMAGE_FILE_PROVIDER, file);
Log.d("uri", mUri.toString());
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mUri);
startActivityForResult(cameraIntent, CAMERA_IMAGE_RESULT);
}
catch (IOException e)
{
e.printStackTrace();
}
}
});
Then lastly onActivityResult()
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode)
{
case CAMERA_IMAGE_RESULT:
{
if (resultCode == RESULT_OK)
{
if (mUri != null)
{
Log.d("uriPath", mUri.getPath().replace("//", "/"));
String profileImageFilepath = mUri.getPath().replace("//", "/");
Log.d("path", profileImageFilepath);
profileIV.setImageURI(mUri);
/*Your Asynctask to upload Image using profileImageFilepath*/
new PostDataAsyncTask().execute(profileImageFilepath);
}
}
break;
}
}
}
take run time permission for
<!-- == External Storage == -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
I am following this Android Developer's tutorial: http://developer.android.com/training/camera/photobasics.html
I used the exact codes in this tutorial. However, It returns null while creating the image file in createImageFile() function.
Could you please look at my code and tell me what I'm missing?
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_IMAGE_CAPTURE = 1;
ImageView mImageView;
String mCurrentPhotoPath;
Button takepicturebtn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView) findViewById(R.id.imageView);
takepicturebtn = (Button) findViewById(R.id.takepicturebtn);
takepicturebtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dispatchTakePictureIntent();
}
});
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath ="file:" + image.getAbsolutePath();
return image;
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
setPic();
}
}
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
}
// Continue only if the File was successfully created
if (photoFile != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
private void setPic() {
// Get the dimensions of the View
int targetW = mImageView.getWidth();
int targetH = mImageView.getHeight();
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
mImageView.setImageBitmap(bitmap);
}
}
And here is my manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.figengungor.takingcamerapicture" >
<uses-feature android:name="android.hardware.camera"></uses-feature>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<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>
</application>
</manifest>
I am new on this site, but I have already had same problem. What I did:
In Manifest I used such code:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera"></uses-feature>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
<activity android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="portrait">
Since I didn't use landscape orientation for my app, each time when I push button save the picture from camera new MainActivity is being created and method OnCreated is calling again. But created file left in previous one.
As for Inten data - it is still null, so I put in ImageView my file (change size before). Instead of using method setPic(), I use this code (do not forget to initialize File photoFile in class):
bitmap = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
imageView = (ImageView) findViewById(R.id.imageView);
// Resize the bitmap to 150x100 (width x height)
Bitmap bMapScaled = Bitmap.createScaledBitmap(bitmap, 150, 100, true);
// Loads the resized Bitmap into an ImageView
imageView.setImageBitmap(bMapScaled);
or
You can use method setPic(), but you need to change everywhere in this method mCurrentPhotoPath to photoFile.getAbsolutePath(). It works.
Hope it helps.
I used the exact codes in this tutorial
Actually, you combined things where you were supposed to choose one or the other.
The Save the Full-Size Photo section has you either use Environment.getExternalStoragePublicDirectory() or use getExternalFilesDir(). You chose the former, which is fine. However, you also added the android:maxSdkVersion to the <uses-permission> element, which is part of the instructions for using getExternalFilesDir(). You need WRITE_EXTERNAL_STORAGE on all relevant versions of Android to work with Environment.getExternalStoragePublicDirectory().
You have three choices:
Remove android:maxSdkVersion. Then, make sure that your targetSdkVersion (in app/build.gradle, assuming you have a traditional Android Studio project) is 22 or lower, or that you are testing on Android 5.1 or lower.
Remove android:maxSdkVersion, while leaving targetSdkVersion at 23 (or higher) and testing on Android 6.0 (or higher). In that case, you also have to deal with the fact that WRITE_EXTERNAL_STORAGE is a dangerous permission on Android 6.0+ and needs to be handled through the runtime permission system.
Switch to using getExternalFilesDir() instead of Environment.getExternalStoragePublicDirectory().
I'm following this tutorial on taking pictures, displaying thumbnails and storing the full pictures on local public storage available to my application only.
The problem: EACCESS (Permission denied) when trying to access local storage for my application
11-12 10:36:30.765 3746-3746/com.test.example.photo W/System.err﹕ java.io.IOException: open failed: EACCES (Permission denied)
11-12 10:36:30.765 3746-3746/com.test.example.photo W/System.err﹕ at java.io.File.createNewFile(File.java:948)
11-12 10:36:30.765 3746-3746/com.test.example.photo W/System.err﹕ at java.io.File.createTempFile(File.java:1013)
I've looked at this question but it appears to be outdated as none of the solutions work any more today. This question also provides no working solutions. Other results and solutions I've seen and tried seem only vaguely related.
My manifest permissions
</application>
<!-- PERMISSIONS -->
<permission
android:name="android.hardware.Camera.any"
android:required="true" />
<permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:required="true" />
<!-- android:maxSdkVersion="18" seemingly does nothing-->
</manifest>
The method that crashes
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
//THIS IS WHERE IT CRASHES
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
return image;
}
I am using an i9250 Galaxy Nexus 3 phone to run the examples, since my emulator doesn't have a camera and automatically GONEs the elements. My target SDK is 16 and I have updated my both my build tools and Android Studio to the latest versions.
I feel like I'm missing something obvious here, since taking pictures is so common in applications and I can't imagine it not working for everyone, but I'm stuck and I'd appreciate your guidance. I am quite new to android, the literature I'm primarily using is Beginning Android 4 Game Programming, Beginning Android 4 and Pro Android 4.
Thank you for your time!
Thanks for the help everyone, it works now!
Apparently I was using the SD card storage which required permissions as explained in permission vs uses-permisson instead of local sandboxed storage which requires no permissions starting from API level 19.
SD card access, requires write permission: Environment.getExternalStoragePublicDirectory
Sandboxed local storage for your app: getExternalFilesDir
I use this code for API level 16, it should require minimal effort to implement and change but if you encounter problems, leave a message and I'll try to help or clarify.
Most of the explanation is in the code as commentary
//OnClick hook, requires implements View.OnClickListener to work
public void takePicture(View v) {
dispatchTakePictureIntent();
}
private void dispatchTakePictureIntent() {
//Create intent to capture an image from the camera
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the directory File where the photo should go, do NOT try to create the image file itself
File photoFile = null;
try {
//mCurrentPhotoPath is a File outside of the methods, so all methods know the last directory for the last picture taken
mCurrentPhotoPath = createImageFile();
photoFile = mCurrentPhotoPath;
} catch (IOException ex) {
// Error occurred while creating the File
ex.printStackTrace();
}
// Continue only if the File was successfully created
if (photoFile != null) {
//photoFile MUST be a directory or the camera will hang on an internal
//error and will refuse to store the picture,
//resulting in not being able to to click accept
//MediaStore will automatically store a jpeg for you in the specific directory and add the filename to the path
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
//unique name, can be pretty much whatever you want
imageId = generateImageId();
//Get file.jpg as bitmap from MediaStore's returned File object
Bitmap imageBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath.getAbsolutePath());
//resize it to fit the screen
imageBitmap = Bitmap.createScaledBitmap(imageBitmap,300,300,false);
//Some ImageView in your layout.xml
ImageView imageView = (ImageView)findViewById(R.id.imageView);
imageView.setImageBitmap(imageBitmap);
Bitmap thumbnail = makeThumbnail(mCurrentPhotoPath);
ImageView thumbnail = (ImageView)findViewById(R.id.thumbnail);
thumbnail.setImageBitmap(imageBitmap);
}
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
//completely optional subdirectory structure
storageDir = new File(storageDir, "custom_directory");
return storageDir;
}
private Bitmap makeThumbnail(File currentPhotoPath) {
// Get the dimensions of the View, I strongly recommend creating a <dimens> resource for dip scaled pixels
int targetW = 45;
int targetH = 80;
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(currentPhotoPath.getAbsolutePath(), bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(currentPhotoPath.getAbsolutePath(), bmOptions);
return bitmap;
}
private long generateImageId() {
return Calendar.getInstance().getTimeInMillis();
}
Android 5.0, API 21, will use the Camera2 API where all of this will be hidden far away, from what I understand. You can read about it here
try this:
private File getDir() {
File sdDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
return new File(sdDir, "Your_photo_dir_here");
}
then:
File pictureFileDir = getDir();
if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {
Log.d("TAG", "Can't create directory to save image.");
return;
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
String date = dateFormat.format(new Date());
String photoFile = "myphoto_" + date + ".jpg";
String filename = pictureFileDir.getPath() + File.separator + photoFile;
File pictureFile = new File(filename);
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (Exception error) {
Log.d("TAG", "File" + filename + "not saved: "
+ error.getMessage());
}
Instead of permission tag use uses-permission
Add this in manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />