I've written a simple app that opens the camera and supplies a path for saving any captured images.
The code basically looks like this:
File file = new File( Environment.getExternalStorage() + "myimages/",
"my_image.jpg" );
Uri outputUri = Uri.fromFile( file );
Intent intent = new
Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE );
Intent.putExtra( MediaStore.EXTRA_OUTPUT, outputUri );
This works perfectly on my Droid. Images are consistently saved at 2592x1936. However, when testing on the Milestone, the images are saved at much smaller sizes such as 320x240 and 1280 x 1900. Using adb logcat, I can see that the image size is set as soon as the photo is taken.
It seems like there is a default setting on the Milestone causing this behavior.
Any help would be greatly appreciated.
Thanks,
~Jeremy
Every phone has its own manufacturer written camera app. Every camera app decides how much to resize and/or compress any images taken. If you test on more phones, you will probably find that you're getting a variety of different image sizes and qualities. As long as you are using intents, it is up to whatever Activity that the user chooses to handle that intent (which could always be different than the default camera app anyway) to decide what to do with it.
If you want to enforce a particular image size or quality, you need to write your own camera activity. Keep in mind that different phones are going to have different hardware that takes images at a huge variety of different resolutions anyway. You should be able to get better than 320x240, but you will not be able to get a consistent size across all phones because the hardware is different.
Related
On my Android device I can take videos one of 2 ways:
1) I can write a custom class / set of methods that will allow me to utilize the camera and have direct control over the features
or
2) I can use an intent to open the video and take a recording.
This question is with regards to the latter (using an intent). This is the intent code I am using to take a video:
private void takeVideo(Activity activity, Context context){
//Actual intent used to capture the video
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
//30 Second time limit (can make it anything, just using 30 here)
takeVideoIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 30);
//Utility method I have for generating a uri to use for the video
android.net.Uri myUri = generateVideoUri(context);
//Uri to use
takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, myUri);
//Cap it at 20mb
takeVideoIntent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, 20000000L);
//HERE IS WHERE MY QUESTION LIES //0 is low, 1 is high
takeVideoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
activity.startActivityForResult(takeVideoIntent, 100);
}
The above code works just fine. My question is surrounding the quality.
If I send in 0 as the quality, it is absolutely horrible quality (IE, 10 seconds of video is about 0.3mb and appears to be the lowest quality the phone can handle). If I send in 1 as the quality, it is ungodly large and unwieldy (IE, 10 seconds of video is about 50mb).
Bearing in mind that some phone makers will ignore the intent extras sent, How do I go about getting medium quality video to use for uploads, messaging, etc?
Do I need to run some sort of compression on the large files?
Can this only be done via a custom video/ camera class?
Is there some other way I am not thinking of?
What would you recommend I do to get a "medium quality" video as opposed to the 2 extremes of way too large or way too small?
Thanks all!
It seems to be the simplest thing in the world: taking a picture within your Android app using the default camera activity. However, there are many pitfalls which are covered in several posts across StackOverflow and the web as, for instance, Null Intents being passed back, the orientation of the picture not being correct or OutOfMemoryErrors.
I'm looking for a solution that allows me to
start the camera activity via the camera intent,
retrieve the Uri of the photo, and
retrieve the correct orientation of the photo.
Moreover, I would like to avoid a device configuration (manufacturer, model, os version) specific implementation as far as possible. So I'm wondering: what is the best way to achieve this?
UPDATE: January 2nd, 2014:
I tried really hard to avoid implementing different strategies based on the device manufacturer. Unfortunately, I did not get around it. Going through hundreds of posts and talking to several developers, nobody found a solution that works on all devices without implementing device manufacturer specific code.
After I posted my solution here on StackOverflow, some developers asked me to publish my code on github. So here it is now: AndroidCameraUtil on github
The code was successfully tested on a wide variety of devices with Android API-Level >= 8. For a complete list, please see the Readme file on github.
The CameraIntentHelperActivity provides the main functionality, which is also described in more detail in the following.
Calling the default camera activity:
for Samsung and Sony devices: I call the camera activity with the method call to startActivityForResult. I only set the constant CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE. I do NOT set any other intent extras.
for all other devices: I call the camera activity with the method call to startActivityForResult as previously. This time, however, I additionally set the intent extra MediaStore.EXTRA_OUTPUT and provide an URI, where I want the image to be stored.
In both cases I remember the time the camera activity was started.
On camera activity result:
Mediastore: First, I try to read the photo being captured from the MediaStore. Using a mangedQuery on the MediaStore content, I retrieve the latest image being taken, as well as its orientation property and its timestamp. If I find an image and it was not taken before the camera intent was called, it is the image I was looking for. Otherwise, I dismiss the result and try one of the following approaches.
Intent extra: Second, I try to get an image Uri from intent.getData() of the returning intent. If this is not successful either, I continue with step 3.
Default photo Uri: If all of the above mentioned steps did not work, I use the image Uri I passed to the camera activity.
At this point, I retrieved the photo Uri and its orientation which I pass to my UploadPhotoActivity.
Image processing
Please take a close look at my BitmapHelper class. It is based on the code described in detail in that tutorial.
Moreover, the shrinkBitmap method also rotates the image if required based on the orientation information extracted earlier.
I hope this is helpful to some of you.
I have tested this code with a Sony Xperia Go, Samsung Galaxy SII, Samsung Galaxy SIII mini and a Samsung Galaxy Y it worked on all devices!
But on the LG E400 (2.3.6) it didn’t work and you get double pictures in the gallery. So i have added the manufacturer.contains("lge") in the void startCameraIntent() and it fixed the problem.
if(!(manufacturer.contains("samsung")) && !(manufacturer.contains("sony")) && !(manufacturer.contains("lge"))) {
String filename = System.currentTimeMillis() + ".jpg";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, filename);
cameraPicUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraPicUri);
}
On a Galaxy S3 with CM 10.1 I get a nullpointer exception in BitmapHelper:
bm = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options);
subsequently my UploadPhotoActivity fails at:
try {
photo = BitmapHelper.readBitmap(this, cameraPicUri);
if (photo != null) {
photo = BitmapHelper.shrinkBitmap(photo, 600, rotateXDegrees);
thumbnail = BitmapHelper.shrinkBitmap(photo, 100);
ImageView imageView = (ImageView) findViewById(R.id.sustainable_action_photo);
imageView.setImageBitmap(photo);
} else {
Log.e(TAG,"IMAGE ERROR 1");
}
} catch (Exception e) {
Log.e(TAG,"IMAGE ERROR 2");
e.printStackTrace();
}
at the second log (IMAGE ERROR 2).
After a couple of tries my camera broke and I got a "Could not connect to camera"-error.
Tested it on a nexus 7 and it works perfectly.
Edit: Narrowed it down to this:
fileDescriptor = context.getContentResolver().openAssetFileDescriptor(selectedImage, "r");
Although selectedImage contains this:
file:///storage/emulated/0/DCIM/Camera/IMG_20131023_183343.jpg
The fileDescriptor returns a FileNotFoundException. I checked the file system and the image is not saved at this location. The cameraPicUri in TakePhotoActivity points to a non existant image. I am currently checking where it all goes wrong.
Edit2: I figured out the error: Since the device is a Samsung, and tells the App that it is a Samsung device, your Samsung specific fixes are applied. Cyanogenmod does not need those fixes though, and in the end the code breaks. Once you remove
(manufacturer.contains("samsung")) &&
It works. Since this is a custom ROM you could not plan for that of course. I am trying to figure out a way to detect if the device is running cyanogenmod and then include this in your code.
Thanks for a nice camera fix!
Edit3: I fixed it to run on Cyanogenmod on the Galaxy S3 by changing your code to this:
Well, now it sometimes works, sometimes it does not. Strange.
if (getPackageManager().hasSystemFeature("com.cyanogenmod.android") || (!(manufacturer.contains("samsung")) && !(manufacturer.contains("sony")) && !(manufacturer.contains("lge"))))
I experience some problems when using this with Sony Xperia Z5.
I added this and it got a lot better.
if (buildType.contains("sony")&& buildDevice.contains("e5823")) {
setPreDefinedCameraUri = true;}
But 4 times out of 22 it restarted the camera and once it restarted two times. I restarted the App due to every test.
Is there some way to get around this or do I accept this result?
The thing is that if the camera restarts I can press the back button twice and boom, the image is there in my Imageview and saved
So this is kind of weird because out of the three devices I'm testing on, I only get this issue on the Google Nexus S with 4.0.3. I'm starting the native camera app to take a picture, and I don't care where the image is saved to, so I don't specify, hoping that it will get saved to the default location, but no location is saved at all!
Does work fine on the Galaxy S 2 and Samsung Skyrocket (both with 2.3.something).
Code I'm using to start the app
Intent camIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(camIntent, TAKE_PHOTO);
return true;
I'm keeping it really simple, that's why I'm confused! Does anyone know of any issues specific to the Nexus S's camera?
EDIT: It would seem that maybe the Nexus S's camera app saves the file in some onActivityResult, and since I start up the native camera app and don't save the image upon return, it doesn't save it. Does anyone know this this to be true? Seen this behavior?
EDIT: No one has seen this? I find it hard to believe I'm the first person to run into this...
EDIT: Alright, well after working on it some more, I tried adding a URI into the EXTRA_OUTPUT of the intent like so:
camIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.parse(folderPath + String.format("%d.jpg", System.currentTimeMillis())));
And now I see the behavior described here: Android ACTION_IMAGE_CAPTURE Intent where the camera app doesn't do anything when I hit ok, and creating the file beforehand doesn't work either, as I tried like this:
File f = new File(folderPath, filename);
f.createNewFile();
camIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse(folderPath + filename));
Alright, figured it out. Oddly, and I have NO idea why, but changing this line:
camIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse(folderPath + filename));
to
camIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
fixed it. Don't know if I should understand why... but whatever, it works. I did try a billion other things...
not specifying an EXTRA_OUTPUT at all -> camera would act like everything was good... except it wouldn't save the image ANYWHERE
getting the bitmap taken by intent.getExtras().get("data") -> did return a bitmap, but it was not the full size image as documented extensively in many bug reports
using the above Uri.parse method -> causes the native camera app checkmark button to not do anything when clicked
Hope this helps someone..
I'm trying to shoot picture and store it into internal storage by using the following code:
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
takenPhoto = new File(uploadsFolder, getNewPicFileName());
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(takenPhoto));
startActivityForResult(intent, SHOOT_MEDIA_REQUEST_CODE);
The problem is that on Nexus S and Galaxy S devices default and the single orientation for ACTION_IMAGE_CAPTURE intent is landscape. If i shoot picture in portrait mode, that picture is stored into "takenPhoto" file rotated.
That problem seems appearing only on Samsung Galaxy S devices (Galaxy S and Nexus S), another devices i tried make auto-rotate depending on orientation during image shooting.
I will very appreciate any help on that issue.
From what I can tell, this is happening because the MediaStore.Images.ImageColumns.ORIENTATION value isn't being set by this Intent. It does get set when things come through the normal camera app.
On my Nexus S, however, the file still gets the correct EXIF data. So you could get the orientation like this:
ExifInterface exif = new ExifInterface("filepath");
exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
Then you could use use ContentResolver.update add the correct ORIENTATION data. You just need to translate the ExifInterface orientation options to degrees rotated.
Your other option is to create your own Activity to operate the camera hardware and record the file. You'd then keep track of rotations & write the value into the metadata when saving a captured image. Since the Camera app is part of Android, you could probably copy & modify it without too much pain.
I have expereienced this issue also on the Samsung phones, including the Galaxy Ace, (I called the camera action an entirely different method from yours)
My guess is that this is the OS/hardware level issue. Have you tried taking a picture using the native camera application, and managed it to get the correct orientation in that app?
I guess this question has been asked before, but I can't seem to find a proper answer/solution.
Have a note-taking app, which allows to take pictures. For that I start an intent, that starts up the built-in camera-app. So far so good.
But when I show that image in my app, it's in a much smaller format :(
The funny/weird thing is, that the camera-app did take a full-resolution picture! But for some reason I can't get the full version to show in my app???
So, when I use the standard Android Gallery app, and go to that picture, it is very obvious that it's full size (I can zoom in and see details I really can't see when I zoom in, in my own app). Also, the dimensions are really those of the original picture, taken with the 5MP camera.
In my app, they are very small. My phone has Android 2.2, but the same happens on my emulator (Android 2.1).
How should I retrieve the pictures in my app??? Tried a couple of ways, but none works :( Don't need a complete example (allthough that's very welcome), just a few clues are enough to search for myself.
Tx in advance!!
Greetingz,
Koen<
Very weird, I found the solution/answer by looking at the _ID-values that were being inserted in my own database. First I noticed that when I selected an existing image (via the build-in Android Gallery), I did get the full size image.
When I first took a picture, I got a scaled image. So where was the difference. Apparantly at the location where the _ID of the picture got stored in my database. In my case, and probably most cases, this happens in the onActivityResult procedure.
First take a look at what I initially had:
if(requestCode == REQUEST_CAMERA && resultCode == RESULT_OK){
String timestamp = Long.toString(System.currentTimeMillis());
// get the picture
mPicture = (Bitmap)result.getExtras().get("data");
//save image to gallery
String pictureUrl = MediaStore.Images.Media.insertImage(getContentResolver(), mPicture, getResources().getString(R.string.app_name_short), timestamp);
insertPictureAttachment(mRowId.intValue(), Integer.parseInt(Uri.parse(pictureUrl).getLastPathSegment()));
The "insertPictureAttachment"-method does the actual inserting into the database.
Looking backwards, this was a bit weird anyway ... make a picture, so I could make an URI of it, and then get the last path segment (which is the _ID), so I could insert that into my database.
Eventually, it turns out that I can replace the above code with just one line:
insertPictureAttachment(mRowId.intValue(), Integer.parseInt(result.getData().getLastPathSegment()));
Much shorter, and actually makes more sense ... rather than getting the info from result.getExtras().get("data"), I get my info from result.getData(), which gives the _ID of the original, full-size image.
I will do some further research on this though, cause it's not clear to me yet why I actually don't have to call MediaStore.Images.Media.insertImage(...) ... maybe I will have to if I want specific features (like a custom file location or something like that).
Greetingz,
Koen<