I have a custom camera Android application that saves captured images, according to the Google tutorials, into the external memory, then Media Scanner triggers Gallery to detect them.
But I have a new client with LG G2 that has no SD card slot, not external memory -- only internal.
I explained to him that I can only make my app store the images in internal Cache of the app, and he would access them through Root Explorer of some kind.
But he asserts that other purchased camera apps store their images so that Gallery can detect them. HOW? Please help -- I need to make my app able to do that as well.
Thanks!
UPD: Following is my code that works for other devices and deals primarily with external memory:
public static File getBaseFolder() {
File f;
f = null;
try {
f = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
} catch (Exception e) {
Utils.toasterLong("Error accessing storage: " + e.getMessage());
}
return f;
}
public static File getImageFolder() {
File f;
f = null;
try {
f = new File(getBaseFolder(), "Magic");
} catch (Exception e) {
Utils.toasterLong("Error accessing storage: " + e.getMessage());
}
return f;
}
/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type) {
return Uri.fromFile(getOutputMediaFile(type));
}
/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type) {
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = getImageFolder();
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d(TAG, "failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
.format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator
+ "IMG_" + timeStamp + ".jpg");
} else if (type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator
+ "VID_" + timeStamp + ".mp4");
} else {
return null;
}
return mediaFile;
}
and the part of code that writes the image to external memory and triggers the scanner:
{
pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null) {
writeErrorDirty = true;
if (MyDebug.LOG)
Log.d(TAG,
"Error creating media file, check storage permissions");
} else if (MyDebug.LOG)
Log.d(TAG, "Valid path=" + pictureFile.getAbsolutePath());
FileOutputStream out = null;
try {
out = new FileOutputStream(pictureFile);
picTaken_mod.compress(Bitmap.CompressFormat.JPEG, 90, out);
} catch (Exception e) {
e.printStackTrace();
writeErrorDirty = true;
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
writeErrorDirty = true;
if (MyDebug.LOG)
Log.d(TAG,
"Error writing media file, check storage permissions");
}
} // end try
try {
MediaScannerConnection.scanFile(grandContext,
new String[] { pictureFile.getAbsolutePath() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
// now visible in gallery
}
});
} catch (Exception e) {
if (MyDebug.LOG)
Log.d(TAG,
"Error broadcasting the image: " + e.getMessage());
// writeErrorDirty = true;
}
}
But I have a new client with LG G2 that has no SD card slot, not external memory -- only internal.
You have both internal storage and external storage on that device. You may not have removable storage, but removable storage is not internal storage and it is not external storage.
Please understand that what the Android SDK refers to as internal storage and external storage is tied to the Android SDK as does not necessarily line up with what information is shown to the user (e.g., in Settings).
But he asserts that other purchased camera apps store their images so that Gallery can detect them. HOW?
They wrote the images to external storage, then used MediaScannerConnection to inform the Gallery and other apps that use the MediaStore ContentProvider that the files are there.
Related
I have an app that uses the Camera API, and it takes a photo with the call
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
mCamera.takePicture(shutterCallback, rawCallback, mPicture);
mCamera.startPreview();
mPicture is defined as
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null){
Log.d(TAG, "Error creating media file, check storage permissions: " /* +
e.getMessage()*/);
Toast.makeText(MainActivity.this, "Failed to write Photo to File", Toast.LENGTH_SHORT);
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
Toast.makeText(MainActivity.this, "Writing Photo to File", Toast.LENGTH_SHORT);
fos.write(data);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};
public static File getOutputMediaFile(int type){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File(sdCard.getAbsolutePath() + "/camtest");
dir.mkdirs();
File mediaFile;
if (type == MEDIA_TYPE_IMAGE){
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
mediaFile = new File(dir, "IMG_"+ timeStamp + ".jpg");
} else {
return null;
}
return mediaFile;
}
Why isn't any pictures saved? I went to my gallery & file manager app to check, nothing.
I also noticed that my app crashes when I exit the app, could that be why?
My onPause method is as follows
#Override
protected void onPause() {
mSensorManager.unregisterListener(mShakeDetector);
letGo();
super.onPause();
}
private void letGo(){
if(mCamera != null){
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
cameraPreview.getHolder().removeCallback(cameraPreview);
mCamera.release();
mCamera = null;
}
}
Why isn't any pictures saved?
It is entirely possible that they are being saved. However, bear in mind that you are doing disk I/O on the main application thread; depending on StrictMode settings, you may be crashing due to this I/O.
If the pictures are not being saved, you should be seeing your log messages in LogCat. I recommend switching them to error severity (Log.e()), though.
I went to my gallery & file manager app to check, nothing.
They will not show up in a device "gallery"-style app, or in your desktop OS's file manager, for quite some time. That is because those tools use the MediaStore to see what exists, and you have not arranged to have your file be indexed by the MediaStore. To do that, use MediaScannerConnection and its scanFile() method.
I also noticed that my app crashes when I exit the app
Use LogCat to examine the Java stack trace associated with your crash.
I've developed an app which
- download some data ( .png and .wav files )
- insert the path where each files is downloaded into a database (SQLite)
So far so good, everything works.
Some users asked me if there was a way to move the the downloaded data in the sd card in order to save some internal space.
By now i create the directory with this line of code
File directory = getApplicationContext().getDir("folderName", Context.MODE_PRIVATE);
Then the app will fill it with all the stuff I downloaded.
I tried using this piece of code:
try {
File newFolder = new File(Environment.getExternalStorageDirectory(), "TestFolder");
if (!newFolder.exists()) {
newFolder.mkdir();
}
try {
File file = new File(newFolder, "MyTest" + ".txt");
file.createNewFile();
System.out.println("Path: " + file.getPath());
} catch (Exception ex) {
System.out.println("ex: " + ex);
}
} catch (Exception e) {
System.out.println("e: " + e);
}
And this create a folder and a text file into: /storage/emulated/0/TestFolder/MyTest.txt
Which is not my sdcard directory, it should be:
/storage/sdcard1/TestFolder/MyTest.txt
So my question is:
- where and how I saved my app's private data (.png and .wav files) in the SD card?
The getExternalFilesDir, getExternalStorageDirectory or relatives, does not always return a folder on a SD card. On my Samsung for example, it returns an emulated, internal SD card.
You can get all external storage devices (also the removable) using ContextCompat.getExternalFilesDirs.
My next step, is to use the folder on the device with the largest free space. To get that, I enumerate the getExternalFilesDirs, and call getUsableSpace on every folder.
I use this code to store (cache) bitmaps in a folder named "bmp" on the device.
#SuppressWarnings("ResultOfMethodCallIgnored")
private static File[] allCacheFolders(Context context) {
File local = context.getCacheDir();
File[] extern = ContextCompat.getExternalCacheDirs(context);
List<File> result = new ArrayList<>(extern.length + 1);
File localFile = new File(local, "bmp");
localFile.mkdirs();
result.add(localFile);
for (File anExtern : extern) {
if (anExtern == null) {
continue;
}
try {
File externFile = new File(anExtern, "bmp");
externFile.mkdirs();
result.add(externFile);
} catch (Exception e) {
e.printStackTrace();
// Probably read-only device, not good for cache -> ignore
}
}
return result.toArray(new File[result.size()]);
}
private static File _cachedCacheFolderWithMaxFreeSpace;
private static File getCacheFolderWithMaxFreeSpace(Context context) {
if (_cachedCacheFolderWithMaxFreeSpace != null) {
return _cachedCacheFolderWithMaxFreeSpace;
}
File result = null;
long free = 0;
for (File folder : allCacheFolders(context)) {
if (!folder.canWrite()) {
continue;
}
long currentFree = folder.getUsableSpace();
if (currentFree < free) {
continue;
}
free = currentFree;
result = folder;
}
_cachedCacheFolderWithMaxFreeSpace = result;
return result;
}
try this
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File (sdCard.getAbsolutePath() + "/newfolder");
dir.mkdirs();
add permission in Manifest file
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I can press a button, open up the native camera app, and successfully take a picture. But then when I check the Gallery or Photos native apps on my phone, the picture isn't saved there. I'm very new to Android so it's likely I'm missing something important in my code.
Questions:
1) Where are these pictures being saved?
2) Can I modify the below code somehow to save instead to internal storage, so all pictures taken with my app are private and only accessible through my app?
3) If I wanted to save these pictures to an object, along with some text/other input, what would be the best way? Should I just save a Uri or some identifier to reference the image later, or save the actual BitMap image?
Any help is greatly appreciated, thanks!
Here is my code to take the picture:
mImageButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
imageUri = CameraUtils.getOutputMediaFileUri(CameraUtils.MEDIA_TYPE_IMAGE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, REQUEST_IMAGE);
}
}
CameraUtils class taken straight from Google developer guides:
public static Uri getOutputMediaFileUri(int type)
{
return Uri.fromFile(getOutputMediaFile(type));
}
public static File getOutputMediaFile(int type)
{
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "camera");
if (!mediaStorageDir.exists())
{
if (!mediaStorageDir.mkdirs())
{
return null;
}
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE)
{
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_" + timeStamp + ".jpg");
}
else if(type == MEDIA_TYPE_VIDEO)
{
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"VID_" + timeStamp + ".mp4");
}
else
{
return null;
}
return mediaFile;
}
1) By looking at the code, I'd expect the pictures to be saved in a directory called 'camera' which would be found in the Pictures folder on your device (external storage). These might not instantly appear in your gallery, however in later versions of Android (Kitkat and maybe jelly-bean though I can't verify that right now) you should be able to open the Photos app and find them somewhere in there. If that is not the case, then launch a file explorer app (example apps are ASTRO File Manager or X-Plore) and browse to the Pictures/camera directory where you should see your images. The next time your media gets re-indexed (phone reboot, or a re-index triggered from elsewhere), you should see these pictures in your gallery/photo apps. If you want to refresh your media programatically, here might help. Finally, make sure you have the READ_EXTERNAL_STORAGE permission in your Android manifest as specified this (Android doc).
2) If you want to save images to be only available to your application, you need to save them to your application's internal data directory. Take a look at this straight from the Android doc. Make sure to use the MODE_PRIVATE flag.
3) For this, you would want to store the file path somewhere accessible to your app. Either you could save your file paths to a text file with some other text data, or you could use a sqlite database. Finally, you could use an ORM like ORMLite for Android to save a java object which might hold data for your picture and have some fields you want to persist (title, description, path, etc). Here and here is an intro on how to get started with SQLite database in Android (straight from the official doc). If you want to use ORMLite, there is plenty of information on their site here. The developer has spent a lot of time answering StackOverflow questions..
All of your questions can be answered with a few simple Google searches. They are very standard and basic things to do in Android, so you should be able to find a plethora of information and tutorials online.
EDIT:
In response to your comment about the second question. This is what I would probably do (or something similar):
Note that I didn't test this. It's from the top of my head. If you have more issues comment here!
Activity code...
mImageButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
imageUri = CameraUtils.getOutputMediaFileUri(currentActivity, CameraUtils.MEDIA_TYPE_IMAGE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, REQUEST_IMAGE);
}
}
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == REQUEST_IMAGE)
{
if (resultCode == RESULT_OK)
{
String pathToInternallyStoredImage = CameraUtils.saveToInternalStorage(this, imageUri);
// Load the bitmap from the path and display it somewhere, or whatever
}
else if (resultCode == RESULT_CANCELED)
{
//Cancel code
}
}
}
CameraUtils class code...
public static Uri getOutputMediaFileUri(int type)
{
return Uri.fromFile(getOutputMediaFile(type));
}
public static File getOutputMediaFile(int type)
{
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "camera");
createMediaStorageDir(mediaStorageDir);
return createFile(type, mediaStorageDir);
}
private static File getOutputInternalMediaFile(Context context, int type)
{
File mediaStorageDir = new File(context.getFilesDir(), "myInternalPicturesDir");
createMediaStorageDir(mediaStorageDir);
return createFile(type, mediaStorageDir);
}
private static void createMediaStorageDir(File mediaStorageDir) // Used to be 'private void ...'
{
if (!mediaStorageDir.exists())
{
mediaStorageDir.mkdirs(); // Used to be 'mediaStorage.mkdirs();'
}
} // Was flipped the other way
private static File createFile(int type, File mediaStorageDir ) // Used to be 'private File ...'
{
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile = null;
if (type == MEDIA_TYPE_IMAGE)
{
mediaFile = new File(mediaStorageDir .getPath() + File.separator +
"IMG_" + timeStamp + ".jpg");
}
else if(type == MEDIA_TYPE_VIDEO)
{
mediaFile = new File(mediaStorageDir .getPath() + File.separator +
"VID_" + timeStamp + ".mp4");
}
return mediaFile;
}
public static String saveToInternalStorage(Context context, Uri tempUri)
{
InputStream in = null;
OutputStream out = null;
File sourceExternalImageFile = new File(tempUri.getPath());
File destinationInternalImageFile = new File(getOutputInternalMediaFile(context).getPath());
try
{
destinationInternalImageFile.createNewFile();
in = new FileInputStream(sourceExternalImageFile);
out = new FileOutputStream(destinationInternalImageFile);
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0)
{
out.write(buf, 0, len);
}
}
catch (IOException e)
{
e.printStackTrace();
//Handle error
}
finally
{
try {
if (in != null) {
in.close();
}
if (out != null) {
in.close();
}
} catch (IOException e) {
// Eh
}
}
return destinationInternalImageFile.getPath();
}
So now you have the path pointing to your internally stored image, which you can then manipulate/load from your onActivityResult.
I want my app to be able to capture photos without using another application. The code i used :
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File photo = null;
try
{
photo = this.createTemporaryFile("picture", ".jpg");
photo.delete();
}
catch(Exception e)
{
Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
}
mImageUri = Uri.fromFile(photo);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
But this code uses the phone's main camera app. Can anyone give me some code ?
Taking a picture directly using the Camera class is insanely complicated to get right.
I am working on a library to simplify this, where you just add a CameraFragment to your app for the basic preview UI, and call takePicture() on it to take a picture, with various ways to configure the behavior (e.g., where the pictures get saved). However, this library is still a work in progress.
Can anyone give me some code ?
"Some code" is going to be thousands of lines long (for a complete implementation, including dealing with various device-specific oddities).
You are welcome to read the Android developer documentation on the subject.
once you have the camera preview set, you need to do the following...
protected static final int MEDIA_TYPE_IMAGE = 0;
public void capture(View v)
{
PictureCallback pictureCB = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera cam) {
File picFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (picFile == null) {
Log.e(TAG, "Couldn't create media file; check storage permissions?");
return;
}
try {
FileOutputStream fos = new FileOutputStream(picFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.e(TAG, "File not found: " + e.getMessage());
e.getStackTrace();
} catch (IOException e) {
Log.e(TAG, "I/O error writing file: " + e.getMessage());
e.getStackTrace();
}
}
};
camera.takePicture(null, null, pictureCB);
}
And the getOutputMediaFile function:
private File getOutputMediaFile(int type)
{
File dir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), getPackageName());
if (!dir.exists())
{
if (!dir.mkdirs())
{
Log.e(TAG, "Failed to create storage directory.");
return null;
}
}
String timeStamp = new SimpleDateFormat("yyyMMdd_HHmmss", Locale.UK).format(new Date());
if (type == MEDIA_TYPE_IMAGE)
{
return new File(dir.getPath() + File.separator + "IMG_"+ timeStamp + ".jpg");
}
else
{
return null;
}
}
And you are done!!!
found it here
Camera was deprecated in API 21, the new way is the use android.hardware.camera2.
To enumerate, query, and open available camera devices, obtain a CameraManager instance.
To quickly summarize:
Obtain a camera manager instance by calling Context.getSystemService(String)
Get a string[] of device camera IDs by calling CameraManager.GetCameraIdList().
Call CameraManager.OpenCamera(...) with the desired camera ID from the previous step.
Once the camera is opened, the callback provided in OpenCamera(...) will be called.
I have been working on a camera app, I want it to be when I press the "Capture" button it takes a picture and saves it to the SDcard so it can be viewed in the gallery.
However at current it fails to save how I want it to. At current when I press capture it takes a picture, but the picture is only shown in the gallery after I fully restart my phone.
This problem has been messing with me for weeks, I have mostly followed the tutorial android gives.
Here is my code for the main class which handle pictures.
private PictureCallback mPicture = new PictureCallback()
{
#Override
public void onPictureTaken(byte[] data, Camera camera)
{
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null){
Log.d(TAG, "Error creating media file, check storage permissions: ");
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
// Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
// Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};
private static File getOutputMediaFile(int type){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "MyCameraApp");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE){
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
} else if(type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"VID_"+ timeStamp + ".mp4");
} else {
return null;
}
return mediaFile;
}
public void onClick(View v) {
mCamera.takePicture(null, null, mPicture);
Context context = getApplicationContext();
CharSequence text = "Click Detected";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
My log cat shows the following when I click capture
Level: E
Tag: Camera
Text: in handlemessage for CAMERA_MSG_RAW_IMAGE
My permissions are
uses-permission android:name="android.permission.CAMERA"
uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
Any help is much appreciated. Thanks
This question is a duplicate of Image, saved to sdcard, doesn't appear in Android's Gallery app (especially the answer by ShadowGod)
To fix your code, add the following to onPictureTaken after fos.close()
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "MyCameraApp");
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://"+ mediaStorageDir)));
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
Does not seem to work on KITKAT. It throws permission denial exception and crashes the app. So for this, I have done the following,
String path = mediaStorageDir.getPath() + File.separator
+ "IMG_Some_name.jpg";
CameraActivity.this.sendBroadcast(new Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri
.parse("file://" + path)));
Hope it helps.
MediaScannerConnection works for updating the gallery with videos or images in KitKat, examples and code listed in:
Image, saved to sdcard, doesn't appear in Android's Gallery app
Android MediaStore insertVideo
http://developer.android.com/reference/android/media/MediaScannerConnection.html