Android prevent saving photo on DCIM / public folder - android

I´m developing an Android application. The user could take a photo after a button click. This photo will be saved to internal package storage (data/data/package/...) with the following method:
private String saveToInternalSorage(Bitmap bitmapImage){
ContextWrapper cw = new ContextWrapper(getApplicationContext());
File directory = cw.getDir("TEST", Context.MODE_PRIVATE);
File pod = new File(directory, object.getTitle() + "" +
object.getName() + "" + object.getAge() + ".jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(pod);
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
return pod.getAbsolutePath();
}
Also it´s possible to delete the picture from this directory. This works like a charm. Tested on Emulator and rooted phone. But the photos were also saved to the public folder DCIM. I´m testing with HTC ONE mini (withtout SD CARD?). Below is the code which shows the methods to take and get the photos.
public void takePhoto() {
cameraintent = new Intent("android.media.action.IMAGE_CAPTURE");
startActivityForResult(cameraintent, CAMERA_CAPTURE_IMAGE_REQUEST_CODE);
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMERA_CAPTURE_IMAGE_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap bmp = (Bitmap) extras.get("data");
setPath(saveToInternalSorage(bmp));
I would like to prevent the storage of the photos in a public folder. My approach to delete the latest files in the DCIM folder failed because getExternalStorageDirectory() gives me a emulated path (like emulated/sdcard/...) on HTC One mini. And that´s definitly not the correct path. So how could i be sure that photos will be only stored to the internal package structure and not (without SD card/ with SD card) in a public folder. And when i have to delete photos in the public folder how to do i get the right path (for/on different devices)?
I found no solution to prevent the storage in a public folder "from the beginning".
Thanks in advance!
EDIT
The method below should be able to delete the photo from the DCIM/ public folder.
private void deleteLatestFromDCIM() {
File f = new File(Environment.getExternalStorageDirectory() + "");
File [] files = f.listFiles();
Arrays.sort( files, new Comparator<Object>()
{
public int compare(Object o1, Object o2) {
if (((File)o1).lastModified() > ((File)o2).lastModified()) {
return -1;
} else if (((File)o1).lastModified() < ((File)o2).lastModified()) {
return 1;
} else {
return 0;
}
...
if(files[0].exists())
files[0].delete();
The problem is that photos in DCIM/public folder get generic names. See image below:
So, how to delete images whose names i don´t "know"? Storing of photos in internal memory works fine! I don´t wont them in a public folder. And with the getExternalStorageDirectory() method i get an emulated path as described above. Is this really the path to the DCIM/public folder?

Sorry for answering my own question, hope this will be helpfully for other developers:
My strategy: Capture the photo as described in the question, save it into the internal memory (data/data/com.package...)
After that delete it from the public folder (DCIM/MEDIA/100MEDIA) with the following method (delete last taken picture from that folder...):
private void deleteLastPhotoTaken() {
String[] projection = new String[] {
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE };
final Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection,
null,null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if (cursor != null) {
cursor.moveToFirst();
int column_index_data =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
String image_path = cursor.getString(column_index_data);
File file = new File(image_path);
if (file.exists()) {
file.delete();
}
}
}
As mentioned in other solutions: Don´t close the cursor! Android will do that for you.

Do I understand correctly that you want to take a photo with the camera and store it in the internal memory? Then I have two proposals:
Did you try the way described in
http://developer.android.com/training/camera/photobasics.html
filling MediaStore.EXTRA_OUTPUT with an Uri to the internal storage?
If this does not help to save the image directly in the internal storage, why don't you just delete it from the emulated path returned by getExternalStorageDirectory()? As far as I know, the file access via this path works perfectly, even though it's probably only a link.

Related

Camera Intent - how to?

Sorry to bother you guys, but I am not able to get a solution where In we take picture using intents. I know the default behavior of native camera is to save the picture at default directory/place of O.S. The thing is I have some requirements where I do not want to save the picture when clicked using camera app. There has to be a solution of this issue, be it like once we take a picture we could delete it right away, or there should be an alternate by which we won't allow O.S to save Image, please help.
Here is a piece of code I tried, tried several ways by creating a directory and then deleting file, nothing works.
public void takeImageFromCamera() {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check for the integer request code originally supplied to startResolutionForResult().
if (requestCode == CAMERA_REQUEST && resultCode == RESULT_OK) {
if (isCameraPermissionGranted()) {
bitmap= (Bitmap) data.getExtras().get("data");
// bitmap = processReuiredImage(picUri);
getProfileDetailViaFace(encodeImageBitmapToString(bitmap));
Log.d("path",String.valueOf(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)));
// getApplicationContext().getContentResolver().delete(, "/storage/emulated/0/Pictures", null);
// mediaStorageDir.getPath().delete();
} else {
requestCameraPermission();
}
}
public void takeImageFromCamera() {
File file = getOutputMediaFile(CAMERA_FILE_TYPE);
if (Build.VERSION.SDK_INT >= 24) {
try {
Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
m.invoke(null);
} catch (Exception e) {
e.printStackTrace();
}
}
picUri = Uri.fromFile(file);
Intent takePictureIntent = new
Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, picUri);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, CAMERA_REQUEST);
}
}
private File getOutputMediaFile(int type) {
mediaStorageDir = new
File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "peppercard");
/**Create the storage directory if it does not exist*/
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
return null;
}
}
/**Create a media file name*/
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
if (type == CAMERA_FILE_TYPE) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_" + timeStamp + ".jpeg");
} else {
return null;
}
}
return mediaFile;
}
The thing is I have some requirements where I do not want to save the picture when clicked using camera app
The decision of whether or not to save an image is up to the camera app, not you. There are hundreds of camera apps that might respond to ACTION_IMAGE_CAPTURE, and the developers of those apps can do whatever they want.
There has to be a solution of this issue, be it like once we take a picture we could delete it right away, or there should be an alternate by which we won't allow O.S to save Image,
Take the photo yourself, using the camera APIs or libraries that wrap around them (e.g., CameraKit-Android, Fotoapparat).
There has to be a solution of this issue, be it like once we take
a picture we could delete it right away
Indeed there is. You could specify a path (even using a file provider) where the camera app has to put the image in a file.
Then when the camera app is done you can get the image from that file and then delete the file.
Have a look at Intent.EXTRA_OUTPUT.
Pretty standard your question. You can find a lot of example code on this site.
Final I have found the answer after waiting from past 2 days, yay..It will not save the file as I am just deleting the file after returning from the activity.
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, null)
int column_index_data = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToLast();
String imagePath = cursor.getString(column_index_data);
Bitmap bitmapImage = BitmapFactory.decodeFile(imagePath );
Log.d("bitmapImage", bitmapImage.toString()); /*delete file after taking picture*/
Log.d("imagePath", imagePath.toString());
File f = new File(imagePath);
if (f.exists()){
f.delete();
}
sendBroadcast(newIntent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.fromFile(f)));

how to get photo absolute path from removable SD card

background information
I have been writing a backup photos service, which needs to get all photo absolute paths from Android external storage (like photos stored in 'DCIM' directory and its subdirectores) and upload them to remote server. The problem is how to get all validate photo absolute paths from Android device. Since there is a vast majority of Android devices, it`s tough to ensure the get-photo-absolute-path algorithm to successfully reach all validate photos Gallery directory and traverse all photos paths inside of it.
Now my app only supports uploading photos from primary external storage (not the secondary external storage, like removable SD card). That`s to say.
if the device only has one emulated external storage (on-board flash), camera upload service can scan photo paths on it correctly.
if the device only has a removable storage (like SD card), camera upload service can scan photo paths correctly as well.
the algorithm above scans photo paths from primary external storage which works correctly. But when it comes to
if the device both has a emulated external storage and a removable storage, camera upload service only scans photo paths on the emulated external storage (primary storage), but a majority of users save their photos to the 16G or bigger size removable SD card (secondary storage) which will be ignored by my app, that`s the problem. see the complete issue here.
Code implementation
To get absolute photo path from internal storage, I hard coded an "external directory list",
String[] paths = {
"/DCIM",
"/DCIM/Camera",
"/DCIM/100MEDIA",
// Many Samsung phones mount the external sd card to /sdcard/external_sd
"/external_sd/DCIM",
"/external_sd/DCIM/Camera",
"/external_sd/DCIM/100MEDIA"
};
and combined the absolute path like
String fullPath = Environment.getExternalStorageDirectory().getAbsolutePath() + path;
I know that`s not the best practice, that`s why ask for help. BTW, see the complete external directory list
Question
To get absolute photo paths from Android storage
check if external storage mounted, then scan photos from internal storage by default. This can fit a majority of getting photo path requirements, see the complete implementation here
let user choose a specific directory to upload photos from SD card (if mounted one)
So I wonder if the proposal above is right or not?
Any comments or reference will be greatly appreciated.
EDIT
Different manufacturers set their SD card mounted point differently, there is no regular rules for that, it almost impossible (or say, bad practice) to scan and upload photos by the app in the background automatically. To get photos path from SD card, the practical way I think is to only scan root directories, then shows such directories in a file browser window to let user choose a specific gallery directory and persist the path locally instead of scanning by the app itself. Because it`s error prone to scan photos directives automatically on SD card.
You can try this way
for popup
private void selectImage()
{
final CharSequence[] items = { "Camera", "Gallery","Cancel" };
AlertDialog.Builder builder = new AlertDialog.Builder(Detail_mul.this);
builder.setTitle("Add Photo!");
builder.setItems(items, new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int item)
{
if (items[item].equals("Camera"))
{
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File f = new File(android.os.Environment.getExternalStorageDirectory(), "temp.jpg");
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
startActivityForResult(intent, REQUEST_CAMERA);
} else if (items[item].equals("Gallery")) {
Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, "Select File"),SELECT_FILE);
} else if (items[item].equals("Cancel")) {
dialog.dismiss();
}
}
});
builder.show();
}
for getting the result
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Bitmap bm = null;
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_CAMERA) {
File f = new File(Environment.getExternalStorageDirectory().toString());
for (File temp : f.listFiles()) {
if (temp.getName().equals("temp.jpg")) {
f = temp;
break;
}
}
try {
BitmapFactory.Options btmapOptions = new BitmapFactory.Options();
bm = BitmapFactory.decodeFile(f.getAbsolutePath(),btmapOptions);
bm = Bitmap.createScaledBitmap(bm, 300, 200, true);
String path = android.os.Environment.getExternalStorageDirectory()+ File.separator+ "Phoenix" + File.separator + "default";
PreferenceManager.getDefaultSharedPreferences(getBaseContext()).edit().putString("endum_image_"+count, f.toString()).commit();
OutputStream fOut = null;
File file = new File(path, String.valueOf(System.currentTimeMillis()) + ".jpg");
try {
fOut = new FileOutputStream(file);
bm.compress(Bitmap.CompressFormat.JPEG, 85, fOut);
fOut.flush();
fOut.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
} else if (requestCode == SELECT_FILE)
{
Uri selectedImageUri = data.getData();
//getRealPathFromURI(selectedImageUri);
String tempPath = getPath(selectedImageUri, Detail_mul.this);
PreferenceManager.getDefaultSharedPreferences(getBaseContext()).edit().putString("endum_image_"+count, tempPath).commit();
BitmapFactory.Options btmapOptions = new BitmapFactory.Options();
bm = BitmapFactory.decodeFile(tempPath,btmapOptions);
bm = Bitmap.createScaledBitmap(bm, 300, 200, true);
bm = BitmapFactory.decodeFile(tempPath, btmapOptions);
}
} }
I think you are uploading images for your photo service.You can access the gallery to select a particular picture because every picture in your phone is there in your Gallery whether on SDcard or Primary memory.
Code for accessing gallery
you can see
this code.I think this would help

Android - Why my saved image is not appearing in the default gallery of my phone?

I am trying to save an image from my application to the default gallery of my phone. The code below works perfectly if I have a SD card on the phone. The image saved appears in the phone's gallery and everything, as expected:
private Uri saveMediaEntry(File f, String title, String description, int orientation, Location loc) {
ContentValues v = new ContentValues();
v.put(Images.Media.TITLE, title);
v.put(Images.Media.DISPLAY_NAME, title);
v.put(Images.Media.DESCRIPTION, description);
v.put(Images.Media.ORIENTATION, orientation);
String nameFile = f.getName();
File parent = f.getParentFile() ;
String path = parent.toString().toLowerCase() ;
String nameParent = parent.getName().toLowerCase() ;
v.put(Images.ImageColumns.BUCKET_ID, path.hashCode());
v.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, nameParent);
v.put(Images.Media.SIZE,f.length()) ;
if( nameFile.toLowerCase().contains(".png") ){
v.put(Images.Media.MIME_TYPE, "image/png");
}else if( nameFile.toLowerCase().contains(".jpg") ||
nameFile.toLowerCase().contains(".jpeg") ){
v.put(Images.Media.MIME_TYPE, "image/jpeg");
}else{
v.put(Images.Media.MIME_TYPE, "image/jpeg");
}
String imagePath = f.getAbsolutePath();
v.put("_data", imagePath) ;
ContentResolver c = getContentResolver() ;
Uri uriOfSucessfulySavedImage = null;
uriOfSucessfulySavedImage = c.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, v);
return uriOfSucessfulySavedImage;
}
However, if I try to save the same image into the internal storage (for when the phone does not have a SD card), the image does not appear in the phone's gallery! To try to do that, I only change one line from the above code:
uriOfSucessfulySavedImage = c.insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, v);
The interesting thing about this, however, is that the variable uriOfSucessfulySavedImage is not null (it returns content://media/internal/images/media/x, where 'x' is a number). So, the image is being saved somewhere in the internal storage of the phone, but it is not getting displayed in the phone gallery's as when I use MediaStore.Images.Media.EXTERNAL_CONTENT_URI.
Does anybody have any clue what is going on? How can I save an image into the internal storage of the phone and have that image in the phone's gallery?
Update
I forgot one important information. The File "f" in the parameters of the method "saveMediaEntry" is coming from this other method for when the SD card is mounted (that is, for the first code):
public static File getCacheDirectory(String desiredNameOfTheDirectory){
File fileCacheDir = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ){
fileCacheDir = new File( Environment.getExternalStorageDirectory(), desiredNameOfTheDirectory );
}
if(!fileCacheDir.exists()){
fileCacheDir.mkdirs();
}
return fileCacheDir;
}
and from the following code for when the SD card is not founded:
public static File getCacheDirectory(String desiredNameOfTheDirectory, Context context){
File fileCacheDir = null;
fileCacheDir = context.getCacheDir();
if(!fileCacheDir.exists()){
fileCacheDir.mkdirs();
}
return fileCacheDir;
}
Another easy way to do it. Add this after saving your file.
File imageFile = ...
MediaScannerConnection.scanFile(this, new String[] { imageFile.getPath() }, new String[] { "image/jpeg" }, null);
I haven't tried this, but I believe you need to run the Media Scanner to scan the internal storage directory so that the gallery can see your newly saved image. Check this post here.
Copy Past this Function in your Activity
private void scanner(String path) {
MediaScannerConnection.scanFile(FrameActivity.this,
new String[] { path }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("TAG", "Finished scanning " + path);
}
});
}
And then add this Line where you save your image
scanner(imageFile.getAbsolutePath());
Try this.
Write down this line once image stored in gallery.
File file = ..... // Save file
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
} else {
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
}

How Can I Get Thumbnails For Picasa Images Selected From The Gallery?

I'm working on an application where the user is able to select files, either a new image from the camera, an image from the gallery, or a plain old file. It then shows an icon and the name for the selected item. I have this working with one exception. The gallery application integrates picasaweb pictures. If the user selects a picture from a picasa album, I'm not able to get a thumbnail for it.
I'm using the MediaStore.Images.Thumbnails.getThumbnail() method, and it works for other images in the gallery just fine, but for the picasaweb files, I get, regardless of what "kind" of thumbnail I attempt to get (although MICRO is what I'm after):
ERROR/MiniThumbFile(2051): Got exception when reading magic, id =
5634890756050069570, disk full or mount read-only? class
java.lang.IllegalArgumentException
I noticed the URI's given for the selected files are different. The local image files look like:
content://media/external/images/media/6912
and the picasaweb urls look like:
content://com.android.gallery3d.provider/picasa/item/5634890756050069570
I attempted to use a query to get at the raw THUMB_DATA, using Thumbnails.queryMiniThumbnails(), with Thumbnails.THUMB_DATA in the projection array, but I got a "no such column" error.
Is there another method for getting thumbnails that would work better? And will I have the same problem when I try and access the full image data?
What I have found is that on my Galaxy Nexus, the images for Picassa are stored in one of subdirectories under the /sdcard/Android/data/com.google.android.apps.plus/cache directory. When the content provider is com.google.android.gallery3d.provider then the number after "item" in the URL contains the name of the image (in your example above "5634890756050069570"). This data correspondes to a file in one of the subdirectories under /sdcard/Android/data/com.google.android.apps.plus/cache with the extension ".screen". If you were to copy this image from your phone (in your case 5634890756050069570.screen) using DDMS and rename it with the extension ".jpeg" you could open it and view it on your computer.
The following onActivityResult method will check for this content provider being returned, and then will recursively search for the file in the /sdcard/Android/data/com.google.android.apps.plus/cache directory. The private member variable fileSearchPathResults is filled in by the recursive search method walkDirectoryRecursivelySearchingForFile().
private String fileSearchPathResult = null;
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Uri selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
String filePath = null;
// This code is required to get the image path on content providers
// on API > 10 where the image is from a picassa web album, since Google changed
// the content provider in versions with API > 10
if (selectedImage.toString().contains("com.google.android.gallery3d.provider")) {
StringBuilder contentProviderPath = new StringBuilder(selectedImage.toString());
int beginningIndex = contentProviderPath.lastIndexOf("/");
String fileNameWithoutExt = contentProviderPath.subSequence(beginningIndex + 1,
contentProviderPath.length()).toString();
Log.i(TAG, fileNameWithoutExt);
try {
File path = new File("/sdcard/Android/data/com.google.android.apps.plus/cache");
if (path.exists() && path.isDirectory()) {
fileSearchPathResult = null;
walkDirectoryRecursivelySearchingForFile(fileNameWithoutExt, path);
if (fileSearchPathResult != null) {
filePath = fileSearchPathResult;
}
}
} catch (Exception e) {
Log.i(TAG, "Picassa gallery content provider directory not found.");
}
}
}
public void walkDirectoryRecursivelySearchingForFile(String fileName, File dir) {
String pattern = fileName;
File listFile[] = dir.listFiles();
if (listFile != null) {
for (int i = 0; i < listFile.length; i++) {
if (listFile[i].isDirectory()) {
walkDirectoryRecursivelySearchingForFile(fileName, listFile[i]);
} else {
if (listFile[i].getName().contains(pattern)) {
fileSearchPathResult = listFile[i].getPath();
}
}
}
}
}
With the filePath, you can create a Bitmap of the image with the following code:
Bitmap sourceImageBitmap = BitmapFactory.decodeFile(filePath);
ACTIVITYRESULT_CHOOSEPICTURE is the int you use when calling startActivity(intent, requestCode);
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == ACTIVITYRESULT_CHOOSEPICTURE) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
final InputStream is = context.getContentResolver().openInputStream(intent.getData());
final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
is.close();
}
}
That code will load the whole image. You can adjust the sample size to something reasonable to get a thumbnail sized image.

Trouble writing internal memory android

void launchImageCapture(Activity context) {
Uri imageFileUri = context.getContentResolver()
.insert(Media.INTERNAL_CONTENT_URI, new ContentValues());
m_queue.add(imageFileUri);
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri);
context.startActivityForResult(i, ImportActivity.CAMERA_REQUEST);
}
The above code, which has always worked, is now generating this exception for me at insert().
java.lang.UnsupportedOperationException: Writing to internal storage is not supported.
at com.android.providers.media.MediaProvider.generateFileName(MediaProvider.java:2336)
at com.android.providers.media.MediaProvider.ensureFile(MediaProvider.java:1851)
at com.android.providers.media.MediaProvider.insertInternal(MediaProvider.java:2006)
at com.android.providers.media.MediaProvider.insert(MediaProvider.java:1974)
at android.content.ContentProvider$Transport.insert(ContentProvider.java:150)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:140)
at android.os.Binder.execTransact(Binder.java:287)
at dalvik.system.NativeStart.run(Native Method)
It is not a space issue, and the only thing I changed was the package of an unrelated class all together. Also, I restarted my phone.
Facing same problem here, I was happy to find this thread. Even though two things were bugging me in this workaround, this post had me looking in the right direction. I'd like to share my own workaround/solution.
Let me begin by stating what I did not see myself living with.
First, I did not want to leave the application private file as MODE_WORLD_WRITEABLE. This looks like non-sense to me, although I cannot figure exactly how another application could access this file unless knowing where to look for it with complete name and path. I'm not saying it is necessarily bad for your scenario, but it is still bugging me somehow. I would prefer to cover all my bases by having picture files really private to my app. In my business case, pictures are of no use outside of the application and by no means should they be deleteable via, say, the Android Gallery. My app will trigger cleanup at an appropriate time so as to not vampirize Droid device storage space.
Second, openFileOutput() do not leave any option but to save the resulting file in the root of getFilesDir(). What if I need some directory structure to keep things in order? In addition, my application must handle more than one picture, so I would like to have the filename generated so I can refer to it later on.
See, it is easy to capture a photo with the camera and save it to public image area (via MediaStore) on the Droid device. It is also easy to manipulate (query, update, delete) media from MediaStore. Interestingly, inserting camera picture to MediaStore genreates a filename which appears to be unique. It is also easy to create private File for an application with a directory structure. The crux of the "Capturea camera picture and save it to internal memory" problem is that you can't do so directly because Android prevents ContentResolver to use Media.INTERNAL_CONTENT_URI, and because private app files are by definition not accessible via the (outside) Camera activity.
Finally I adopted the following strategy:
Start the Camera activity for result from my app with the Intent to capture image.
When returning to my app, insert capture to the MediaStore.
Query the MediaStore to obtain generated image file name.
Create a truly internal file onto whatever path relative to private application data folder using Context.getDir().
Use an OutputStream to write Bitmap data to this private file.
Delete capture from MediaStore.
(Optional) show an ImageView of the capture in my app.
Here is the code starting the cam:
public void onClick (View v)
{
ContentValues values = new ContentValues ();
values.put (Media.IS_PRIVATE, 1);
values.put (Media.TITLE, "Xenios Mobile Private Image");
values.put (Media.DESCRIPTION, "Classification Picture taken via Xenios Mobile.");
Uri picUri = getActivity ().getContentResolver ().insert (Media.EXTERNAL_CONTENT_URI, values);
//Keep a reference in app for now, we might need it later.
((XeniosMob) getActivity ().getApplication ()).setCamPicUri (picUri);
Intent takePicture = new Intent (MediaStore.ACTION_IMAGE_CAPTURE);
//May or may not be populated depending on devices.
takePicture.putExtra (MediaStore.EXTRA_OUTPUT, picUri);
getActivity ().startActivityForResult (takePicture, R.id.action_camera_start);
}
And here is my activity getting cam result:
#Override
protected void onActivityResult (int requestCode, int resultCode, Intent data)
{
super.onActivityResult (requestCode, resultCode, data);
if (requestCode == R.id.action_camera_start)
{
if (resultCode == RESULT_OK)
{
Bitmap pic = null;
Uri picUri = null;
//Some Droid devices (as mine: Acer 500 tablet) leave data Intent null.
if (data == null) {
picUri = ((XeniosMob) getApplication ()).getCamPicUri ();
} else
{
Bundle extras = data.getExtras ();
picUri = (Uri) extras.get (MediaStore.EXTRA_OUTPUT);
}
try
{
pic = Media.getBitmap (getContentResolver (), picUri);
} catch (FileNotFoundException ex)
{
Logger.getLogger (getClass ().getName ()).log (Level.SEVERE, null, ex);
} catch (IOException ex)
{
Logger.getLogger (getClass ().getName ()).log (Level.SEVERE, null, ex);
}
//Getting (creating it if necessary) a private directory named app_Pictures
//Using MODE_PRIVATE seems to prefix the directory name provided with "app_".
File dir = getDir (Environment.DIRECTORY_PICTURES, Context.MODE_PRIVATE);
//Query the MediaStore to retrieve generated filename for the capture.
Cursor query = getContentResolver ().query (
picUri,
new String [] {
Media.DISPLAY_NAME,
Media.TITLE
},
null, null, null
);
boolean gotOne = query.moveToFirst ();
File internalFile = null;
if (gotOne)
{
String dn = query.getString (query.getColumnIndexOrThrow (Media.DISPLAY_NAME));
String title = query.getString (query.getColumnIndexOrThrow (Media.TITLE));
query.close ();
//Generated name is a ".jpg" on my device (tablet Acer 500).
//I prefer to work with ".png".
internalFile = new File (dir, dn.subSequence (0, dn.lastIndexOf (".")).toString () + ".png");
internalFile.setReadable (true);
internalFile.setWritable (true);
internalFile.setExecutable (true);
try
{
internalFile.createNewFile ();
//Use an output stream to write picture data to internal file.
FileOutputStream fos = new FileOutputStream (internalFile);
BufferedOutputStream bos = new BufferedOutputStream (fos);
//Use lossless compression.
pic.compress (Bitmap.CompressFormat.PNG, 100, bos);
bos.flush ();
bos.close ();
} catch (FileNotFoundException ex)
{
Logger.getLogger (EvaluationActivity.class.getName()).log (Level.SEVERE, null, ex);
} catch (IOException ex)
{
Logger.getLogger (EvaluationActivity.class.getName()).log (Level.SEVERE, null, ex);
}
}
//Update picture Uri to that of internal file.
((XeniosMob) getApplication ()).setCamPicUri (Uri.fromFile (internalFile));
//Don't keep capture in public storage space (no Android Gallery use)
int delete = getContentResolver ().delete (picUri, null, null);
//rather just keep Uri references here
//visit.add (pic);
//Show the picture in app!
ViewGroup photoLayout = (ViewGroup) findViewById (R.id.layout_photo_area);
ImageView iv = new ImageView (photoLayout.getContext ());
iv.setImageBitmap (pic);
photoLayout.addView (iv, 120, 120);
}
else if (resultCode == RESULT_CANCELED)
{
Toast toast = Toast.makeText (this, "Picture capture has been cancelled.", Toast.LENGTH_LONG);
toast.show ();
}
}
}
Voila! Now we have a truly application private picture file, which name has been generated by the Droid device. And nothing is kept in the public storage area, thus preventing accidental picture manipulation.
here is my working code to save a captured image from the camera to app internal storage:
first, create the file with the desired filename. in this case it is "MyFile.jpg", then start the activity with the intent below. you're callback method(onActivityResult), will be called once complete. After onActivityResult has been called your image should be saved to internal storage. key note: the mode used in openFileOutput needs to be global.. Context.MODE_WORLD_WRITEABLE works fine, i have not tested other modes.
try {
FileOutputStream fos = openFileOutput("MyFile.jpg", Context.MODE_WORLD_WRITEABLE);
fos.close();
File f = new File(getFilesDir() + File.separator + "MyFile.jpg");
startActivityForResult(
new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f))
, IMAGE_CAPTURE_REQUEST_CODE);
}
catch(IOException e) {
}
and in the activity result method:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == IMAGE_CAPTURE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Log.i(TAG, "Image is saved.");
}
}
to retrieve your image:
try {
InputStream is = openFileInput("MyFile.jpg");
BitmapFactory.Options options = new BitmapFactory.Options();
//options.inSampleSize = 4;
Bitmap retrievedBitmap = BitmapFactory.decodeStream(is, null, options);
}
catch(IOException e) {
}
The camera apparently doesn't support writing to internal storage.
Unfortunately this is not mentioned in the documentation.
MediaProvider.java has the following code:
private String generateFileName(boolean internal,
String preferredExtension, String directoryName)
{
// create a random file
String name = String.valueOf(System.currentTimeMillis());
if (internal) {
throw new UnsupportedOperationException(
"Writing to internal storage is not supported.");
// return Environment.getDataDirectory()
// + "/" + directoryName + "/" + name + preferredExtension;
} else {
return Environment.getExternalStorageDirectory()
+ "/" + directoryName + "/" + name + preferredExtension;
}
}
So writing to internal storage has been intentionally disabled for the time being.
Edit - I think you can use binnyb's method as a work-around, but I wouldn't recommend it; I'm not sure if this will continue to work on future versions. I think the intention is to disallow writing to internal storage for media files.
I filed a bug in the Android issue tracker.
Edit - I now understand why binnyb's method works. The camera app is considered to be just another application. It can't write to internal storage if it doesn't have permissions. Setting your file to be world-writable gives other applications permission to write to that file.
I still don't think that this is a very good idea, however, for a few reasons:
You don't generally want other apps writing to your private storage.
Internal storage is quite limited on some phones, and raw camera images are quite large.
If you were planning on resizing the image anyway, then you can read it from external storage and write it yourself to your internal storage.

Categories

Resources