Inside of a fragment in my android application, I take a video using an intent and then save it to my external storage:
private void dispatchTakeVideoIntent() {
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(getActivity().getPackageManager()) != null) {
startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE);
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent intent) {
if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == Activity.RESULT_OK) {
takenVideoUri = intent.getData();
String ext = ".mp4";
String filename = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss.SSS")
.format(System.currentTimeMillis()) + ext;
File root = getContext().getExternalFilesDir(Environment.DIRECTORY_MOVIES);
File file = new File(root, filename);
try(
InputStream is = getContext().getContentResolver().openInputStream(takenVideoUri);
FileOutputStream fos = new FileOutputStream(file)
) {
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] buf = new byte[1024];
while(is.read(buf)!=-1) {
bos.write(buf);
}
bos.close();
fos.close();
videoUri = Uri.fromFile(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Then, inside of another fragment, I have a VideoView to which I want to pass the Uri of the saved video for it to play it.
Uri videoUri = /* gotten from the other fragment */
videoView.setVideoURI(videoUri);
However, when I open that fragment, I get the errors:
2022-04-28 18:53:59.273 23764-23821/my.app E/MediaPlayerNative: error (1, -2147483648)
2022-04-28 18:53:59.305 23764-23764/my.app E/MediaPlayer: Error (1,-2147483648)
I understand those are generic errors, which makes it worse because I have no clue as to where the error is.
My manifest file does include permission for both reading and writing external storage. I have tried saving the video to internal storage too, but nothing changed.
What am I doing wrong?
It seems to me the file is incomplete/empty. You could check its size using file.length() before trying to play it. Copying the file might be unnecessary as you could simply call takeVideoIntent.putExtra(EXTRA_OUTPUT, uri) to the intent to specify the path(uri) the captured video's location.
What usually happens with camera capture outputs is that onActivityResult is called before the file contents were properly written to storage (bytes flushed and file closed) especially with slower storages (sd cards) so what you get is an incomplete file (often times even empty).
You can delay video playing after onActivityResult while checking if the video file's size and last modified time (file.lastModified()) have increased to see if writing/flushing has actually finished and you can play it. If it's a long video (more than a few seconds) and depending on the enconding used you may start playing it before its fully written to storage but if the video player reaches the end before the recording app has finished writing the playing will end abruptly and you'll probably need to reload the video in the player.
It's a big problem of linux with the slow writing on (ex/V)FAT partitions which are used on portable storage for interoperability with other OSs (Windows) especially over USB. It's always a long wait to copy a few GB of data on a USB stick.
Related
I'm new to android programming and need help for this problem which I'm encountering in capturing video file and uploading it on server.
My activity 1 starts with using native video capture -
private void recordVideo(){
Intent takeVideoIntent = new
Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE);
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
Uri videoUri = intent.getData();
mVideoView.setVideoURI(videoUri);
Log.d("Getting video path ", videoUri.getPath() );
}
}
and I upload to server by passing the intent to activity 2. However, while uploading the file to server, my app is getting crashed because my File object is not able to get the file path.
An example of file path, which I'm logging is -
/external/video/media/18518.
I'm not using any external storage and when I'm browsing my Lenovo K4 note on which I'm testing this using file browser, the file is not present in
/external/video/media directory. Instead the video is getting stored in
"/Internal Storage/DCIM/Camera/test.mp4"
As a workaround, I hardcoded this file path in my activity 2 code, which is reading the file and uploading it to server but still getting file not found exception -
int readedBytes;
byte[] buf = new byte[1024];
File file = new File("/Internal Storage/DCIM/Camera/test.mp4");
InputStream insputStream = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
long length = file.length();
byte[] bytes = new byte[(int) length];
insputStream.read(bytes);
while((readedBytes = insputStream.read(buf)) >0 ){
bos.write(buf, 0, readedBytes);
}
insputStream.close();
java.io.FileNotFoundException: /Internal Storage/DCIM/Camera/test.mp4: open failed: ENOENT (No such file or directory)
05-04 00:06:14.432 23628-24496/com.example.jayant.healthapp W/System.err: at libcore.io.IoBridge.open(IoBridge.java:496)
05-04 00:06:14.433 23628-24496/com.example.jayant.healthapp W/System.err: at java.io.FileInputStream.<init>(FileInputStream.java:76)
Any pointer in this problem will be of great help.
However, while uploading the file to server, my app is getting crashed because my File object is not able to get the file path.
You are not getting a file. You are getting a Uri. A Uri is not a file. In particular, the Uri that you are getting has a content scheme, which can point to whatever the camera app wants it to point to.
Use ContentResolver and openInputStream() to get an InputStream on the content identified by the Uri.
I hardcoded this file path in my activity 2 code
That is not a valid file path on any Android device.
Instead the video is getting stored in "/Internal Storage/DCIM/Camera/test.mp4"
No, it is not. You appear to be confusing what some file manager might be reporting with what is actually used on an Android device itself.
Use Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) to get the device's own filesystem path that should map to that DCIM/ directory that you are seeing.
I have implemented a method for users to save/export files created by my app to any location using the Storage Access Framework. The architecture is:
Use Intent.ACTION_OPEN_DOCUMENT_TREE to have the user select a directory
Use DocumentsContract to create a file in that directory
Write the data out to this file
The file is a custom file extension containing custom data, so not a standard file type or mime type.
I am using a Samsung Galaxy S7 with an SD card inserted. So it does not support Adoptable Storage. For testing/developing, I am using the My Files system app to open files and a Windows PC with MTP to pull files from the device.
This architecture/code works very well in the following circumstances:
Any internal storage location
Secondary external storage, unencrypted (removable SD card)
If the SD card is encrypted, I start running into strange issues.
Issue 1: "My Files" system app sees the file as having size 0 bytes and will not open it.
Issue 2: Windows PC also sees the file as having size 0 bytes and will not copy it to my PC over MTP.
Strangely, ES File Explorer can see the file and open it. DropBox can see the file and upload it. Issue 1 and 2 above are not present if the SD card is NOT encrypted.
So what is going on here? I have tried a lot of things to troubleshoot. My theory is that there's something going on with handling MIME types and Content URIs that behaves differently with encryption on or off - specifically concerning My Files and MTP. Why would ES File Explorer have no issues seeing this file and opening it?
Here are relevant code snippets:
private void selectStoragePathExtended() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_OPEN_DIRECTORY);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_OPEN_DIRECTORY) {
if (resultCode == Activity.RESULT_OK) {
mLocalStorageUri = DocumentsContract.buildDocumentUriUsingTree(data.getData(), DocumentsContract.getTreeDocumentId(data.getData()));
}
}
}
// This will get called with mLocalStorageUri and a File stored in the app's "ExternalFilesDir"
public static void copyFile(ContentResolver cr, File sourceFile, Uri destFolderUri) throws IOException {
if (!sourceFile.exists()) {
return;
}
FileChannel source = null;
FileChannel destination = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
try {
source = new FileInputStream(sourceFile).getChannel();
Uri destUri = DocumentsContract.createDocument(cr, destFolderUri, "*/*", sourceFile.getName());
ParcelFileDescriptor pfd = cr.openFileDescriptor(destUri, "w");
destination = new FileOutputStream(pfd.getFileDescriptor()).getChannel();
if (source.size() > FILE_COPY_MAX_BLOCK) {
// Transfer file in 128MB blocks
long position = 0;
while (destination.transferFrom(source, position, FILE_COPY_MAX_BLOCK) > 0) {
position += FILE_COPY_MAX_BLOCK;
}
} else {
long bytesCopied = destination.transferFrom(source, 0, source.size());
if (bytesCopied != source.size()) {
String errorMsg = String.format("Error: only %d out of %d bytes copied", bytesCopied, source.size());
throw new IOException(errorMsg);
}
}
destination.close();
pfd.close();
}
finally {
if(source != null) {
source.close();
}
if(destination != null) {
destination.close();
}
}
}
}
Yes I am using a non-standard file copy method with FileChannel, but I have tested with basic FileOutputStreams and other methods with same results. Also, remember, behavior is normal and working with SD card encryption off. And it actually does work with encryption on, but neither My Files nor Windows MTP can read the file. Only 3rd party tools like ES File Explorer and DropBox.
Any ideas? Gotta be a mime type/URI issue?
I've been searching for many topics about android file writing, yet most of them wanted to write files to android internal storage. Others who wanted to write files on external SD card didn't success at all. My case is quite similar but I think that writing files to external USB is a totally different case.
I am using Samsung galaxy Note II running stock TouchWiz 4.4.2 [not rooted]. My phone supports micro-USB-OTG and I can mount my USB as rwxrwx--x without rooting. The complete path of my USB is /storage/UsbDriveA.
I've tried to use Environment.getExternalStorageDirectory() to get the path or use the path (mentioned above) directly but neither of them succeed. The first one returns internal storage path and the second one returns an error with "permission denied". I have already put the
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
in Android Manifest so I wondered why my code didn't work.
Moreover, I can write anything to my USB using Root Browser (use it without root) and Simple Browser thus I believe that there's a way to do that.
Here's my code:
File file = new File(path.getAbsolutePath(), "test.txt");
// File file = new File("/storage/extSdCard","test.txt");
err = false;
try {
FileOutputStream f = new FileOutputStream(file);
PrintWriter pw = new PrintWriter(f);
pw.print(get);
pw.flush();
pw.close();
f.close();
}catch (Exception e) {
e.printStackTrace();
Toast.makeText(MainActivity.this, "writing error",Toast.LENGTH_LONG).show();
err = true;
}
Log.i("File Path:", file.getPath());
From android 4.4, you can use Storage Access Framework to access to removable media (see https://commonsware.com/blog/2014/04/09/storage-situation-removable-storage.html).
For example, I tried with success to copy a pdf file from local memory to removable memory connected by OTG adapter. The only limitation: the user has to choose a destination folder.
1) call Intent.ACTION_CREATE_DOCUMENT:
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, file.getName());
startActivityForResult(intent, REQUEST_CODE);
2) intercept the return intent
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
if(requestCode == REQUEST_CODE) {
if (resultCode != RESULT_OK) return;
copyFile(fileToCopy, data.getData());
}
}
3) use the ContentResolver to open the outputStream and use it to copy the file
private void copyFile(File src, Uri destUri) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(src));
bos = new BufferedOutputStream(getContentResolver().openOutputStream(destUri));
byte[] buf = new byte[1024];
bis.read(buf);
do {
bos.write(buf);
} while(bis.read(buf) != -1);
} catch (NullPointerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bis != null) bis.close();
if (bos != null) bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
From https://source.android.com/devices/storage/
Starting in Android 4.4, ...
The WRITE_EXTERNAL_STORAGE permission must only grant write access to
the primary external storage on a device. Apps must not be allowed to
write to secondary external storage devices, except in their
package-specific directories as allowed by synthesized permissions.
Restricting writes in this way ensures the system can clean up files
when applications are uninstalled.
So, starting from Android 4.4 in devices with multiple external storages you will be able to write only on the primary external storage. Take into account that External Storage does not mean only "real external" devices. It is defined as follows (from the External Storage reference)
External storage can be provided by physical media (such as an SD
card), or by exposing a portion of internal storage through an
emulation layer.
Anyway there is a workaround to write to secondary external storage using the media content provider. Take a look at http://forum.xda-developers.com/showthread.php?t=2634840
I have used it on a project of mine, but as the author says, it's far from the ideal solution, and it is not guaranteed to work on coming Android versions, so you must not let all your app to rely on this workaround.
I'm writing an app for school, of which one of the biggest parts is taking pictures. I came to conclusion that it would be perfect if I could just save them on Internal Storage, so they would be automatically removed during uninstall process.
I ran across few code samples, proving me there is no easy way to do that (and I realized saving images on external memory is a piece of cake too). However, I managed to find that question: Trouble writing internal memory android and came up with something similiar. After some time testing I ran across "Unable to resume activity" exception, but fortunately I've found a tweak: image from camera intent issue in android
Now my code looks like this:
private void dispatchTakePictureIntent(int actionCode) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), FITPAL_TEMP_PICTURE_NAME);
Uri outputFileUri = Uri.fromFile(file);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(takePictureIntent, actionCode);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case ACTION_TAKE_PHOTO_B: {
if (resultCode == RESULT_OK) {
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), FITPAL_TEMP_PICTURE_NAME);
Bitmap picture = BitmapFactory.decodeFile(file.getAbsolutePath());
if (picture == null) {
showShortToast(R.string.problem_with_taking_a_picture);
return;
}
File dir = getDir(Environment.DIRECTORY_PICTURES, Context.MODE_PRIVATE);
File internalFile = null;
internalFile = new File(dir, generateUniqueFilename());
internalFile.setReadable(true);
internalFile.setWritable(true);
internalFile.setExecutable(true);
try {
internalFile.createNewFile();
FileOutputStream fos = new FileOutputStream(internalFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
picture.compress(Bitmap.CompressFormat.JPEG, JPEG_FILE_QUALITY, bos);
bos.flush();
bos.close();
String filePath = internalFile.getAbsolutePath();
pictureFilePaths.add(filePath);
addPictureThumbnail(filePath);
Log.d("mounted?", "" + Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED));
Log.d("delete test 23123123", "" + file.getAbsolutePath() + " " + file.delete());
} catch (Exception e) {
Log.e("eks", e.getMessage());
showShortToast(R.string.problem_with_taking_a_picture);
}
}
else if (resultCode == RESULT_CANCELED) {
showShortToast(R.string.taking_picture_has_been_cancelled);
}
break;
}
default:
break;
}
}
Basically what I'm trying to do is write a picture to External Storage and move it to Internal Storage. It works in 80% of cases, it's not 100% because it sometimes just "resets" the activity - the new empty layout shows up, just as if it was just created. I have no idea what is happening. I realized that both onSaveInstanceState and onRestoreInstanceState fire in that case and I've no idea why (no exceptions are thrown along the way).
The activity that is invoking all that code is extending FragmentActivity. I've read there are some bugs that might cause that in older versions of Android Support Package, but I've just updated it to version 13 and the problem still persists.
The other thing is that the file saved on external storage won't delete - file.delete() always returns false. Is it a matter of me not having an sdcard? (I mean I have a slot in my phone, but I just have Internal Storage of 16GB). The file gets saved to /mnt/sdcard/Pictures/ folder as if it was emulated somehow.
In advance to the questions: I have both android.permission.READ_EXTERNAL_STORAGE and android.permission.WRITE_EXTERNAL_STORAGE permissions set:
I tried replacing Environment.getExternalStoragePublicDirectory with getCacheDir, getExternalCacheDir and none of them worked either..
I'm testing all that stuff on my Samsung Galaxy SII with Android 4.0.4 (stock)
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.