We are trying to use the native camera app to let the user take a new picture. It works just fine if we leave out the EXTRA_OUTPUT extra
and returns the small B
It is very simple to solve this problem with Activity Result Code Simple try this method
if (reqCode == RECORD_VIDEO) {
if(resCode == RESULT_OK) {
if (uri != null) {
compress();
}
} else if(resCode == RESULT_CANCELED && data!=null){
Toast.makeText(MainActivity.this,"No Video Recorded",Toast.LENGTH_SHORT).show();
}
}
this is a well documented bug in some versions of android. that is, on google experience builds of android, image capture doesn't work as documented. what i've generally used is something like this in a utilities class.
public boolean hasImageCaptureBug() {
// list of known devices that have the bug
ArrayList<String> devices = new ArrayList<String>();
devices.add("android-devphone1/dream_devphone/dream");
devices.add("generic/sdk/generic");
devices.add("vodafone/vfpioneer/sapphire");
devices.add("tmobile/kila/dream");
devices.add("verizon/voles/sholes");
devices.add("google_ion/google_ion/sapphire");
return devices.contains(android.os.Build.BRAND + "/" + android.os.Build.PRODUCT + "/"
+ android.os.Build.DEVICE);
}
then when i launch image capture, i create an intent that checks for the bug.
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
if (hasImageCaptureBug()) {
i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("/sdcard/tmp")));
} else {
i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(i, mRequestCode);
then in activity that i return to, i do different things based on the device.
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
switch (requestCode) {
case GlobalConstants.IMAGE_CAPTURE:
Uri u;
if (hasImageCaptureBug()) {
File fi = new File("/sdcard/tmp");
try {
u = Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(), fi.getAbsolutePath(), null, null));
if (!fi.delete()) {
Log.i("logMarker", "Failed to delete " + fi);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else {
u = intent.getData();
}
}
this saves you having to write a new camera app, but this code isn't great either. the big problems are
you never get full sized images from the devices with the bug. you get pictures that are 512px wide that are inserted into the image content provider. on devices without the bug, everything works as document, you get a big normal picture.
you have to maintain the list. as written, it is possible for devices to be flashed with a version of android (say cyanogenmod's builds) that has the bug fixed. if that happens, your code will crash. the fix is to use the entire device fingerprint.
I know this has been answered before but I know a lot of people get tripped up on this, so I'm going to add a comment.
I had this exact same problem happen on my Nexus One. This was from the file not existing on the disk before the camera app started. Therefore, I made sure that the file existing before started the camera app. Here's some sample code that I used:
String storageState = Environment.getExternalStorageState();
if(storageState.equals(Environment.MEDIA_MOUNTED)) {
String path = Environment.getExternalStorageDirectory().getName() + File.separatorChar + "Android/data/" + MainActivity.this.getPackageName() + "/files/" + md5(upc) + ".jpg";
_photoFile = new File(path);
try {
if(_photoFile.exists() == false) {
_photoFile.getParentFile().mkdirs();
_photoFile.createNewFile();
}
} catch (IOException e) {
Log.e(TAG, "Could not create file.", e);
}
Log.i(TAG, path);
_fileUri = Uri.fromFile(_photoFile);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE );
intent.putExtra( MediaStore.EXTRA_OUTPUT, _fileUri);
startActivityForResult(intent, TAKE_PICTURE);
} else {
new AlertDialog.Builder(MainActivity.this)
.setMessage("External Storeage (SD Card) is required.\n\nCurrent state: " + storageState)
.setCancelable(true).create().show();
}
I first create a unique (somewhat) file name using an MD5 hash and put it into the appropriate folder. I then check to see if it exists (shouldn't, but its good practice to check anyway). If it does not exist, I get the parent dir (a folder) and create the folder hierarchy up to it (therefore if the folders leading up to the location of the file don't exist, they will after this line. Then after that I create the file. Once the file is created I get the Uri and pass it to the intent and then the OK button works as expected and all is golden.
Now,when the Ok button is pressed on the camera app, the file will be present in the given location. In this example it would be /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg
There is no need to copy the file in the "onActivityResult" as posted above.
The file needs be writable by the camera, as Praveen pointed out.
In my usage I wanted to store the file in internal storage. I did this with:
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (i.resolveActivity(getPackageManager()!=null)){
try{
cacheFile = createTempFile("img",".jpg",getCacheDir());
cacheFile.setWritavle(true,false);
}catch(IOException e){}
if(cacheFile != null){
i.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(cacheFile));
startActivityForResult(i,REQUEST_IMAGE_CAPTURE);
}
}
Here cacheFile
is a global file used to refer to the file which is written.
Then in the result method the returned intent is null.
Then the method for processing the intent looks like:
protected void onActivityResult(int requestCode,int resultCode,Intent data){
if(requestCode != RESULT_OK){
return;
}
if(requestCode == REQUEST_IMAGE_CAPTURE){
try{
File output = getImageFile();
if(output != null && cacheFile != null){
copyFile(cacheFile,output);
//Process image file stored at output
cacheFile.delete();
cacheFile=null;
}
}catch(IOException e){}
}
}
Here getImageFile()
is a utility method to name and create the file in which the image should be stored, and copyFile()
is a method to copy a file.
The workflow you describe should work as you've described it. It might help if you could show us the code around the creation of the Intent. In general, the following pattern should let you do what you're trying.
private void saveFullImage() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
outputFileUri = Uri.fromFile(file);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(intent, TAKE_PICTURE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if ((requestCode == TAKE_PICTURE) && (resultCode == Activity.RESULT_OK)) {
// Check if the result includes a thumbnail Bitmap
if (data == null) {
// TODO Do something with the full image stored
// in outputFileUri. Perhaps copying it to the app folder
}
}
}
Note that it is the Camera Activity that will be creating and saving the file, and it's not actually part of your application, so it won't have write permission to your application folder. To save a file to your app folder, create a temporary file on the SD card and move it to your app folder in the onActivityResult
handler.
I had the same issue and i fixed it with the following:
The problem is that when you specify a file that only your app has access to (e.g. by calling getFileStreamPath("file");
)
That is why i just made sure that the given file really exists and that EVERYONE has write access to it.
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File outFile = getFileStreamPath(Config.IMAGE_FILENAME);
outFile.createNewFile();
outFile.setWritable(true, false);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT,Uri.fromFile(outFile));
startActivityForResult(intent, 2);
This way, the camera app has write access to the given Uri and the OK button works fine :)