I would like some help with saving pictures taken from my camera to a specific folder on the SD card. My camera opens up, takes the photos, and saves them; but it saves them to the standard folder. The code I have so for is:
public class Camera extends Activity {
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
String Path;
private Uri fileUri;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras=getIntent().getExtras();
Path= extras.getString("Path");
Log.d("camear","path: "+Path);
//File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp");
Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
this.startActivity(intent);
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
//mediaFile = new File(mediaStorageDir.getPath() + File.separator +"IMG_"+ timeStamp + ".jpg");
//fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
Uri outputFileUri= Uri.fromFile(new File(Path+"/camera/"+timeStamp+".jpg"));// create a file to save the image
intent.putExtra("output", outputFileUri); // set the image file name
// start the image capture Intent
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
The path is coming from another activity on the application, and it passes its values fine. In the previous activity, it creates the folders that I want to save the pictures into.
I have looked at the following answer, and tried to implement some of the suggestions: How to save images from Camera in Android to specific folder?. One thing I didnt try, was in the last suggestion on the OnActivityResult. Is that the key or is there somthing else I am missing? This page here mentions the ContentResolver The Camera Intent is simply not working, one thing is that both pages look like they want to do the same, but go about it in different ways.
I figured out what I was doing wrong. If you look at the code, I made two mistakes. The first mistake was with my path. I got the correct path from the previous activity, however I did not create a folder path here. This is the code that worked for me:
File Folder=new File(Path);
File picFile= new File(Folder.getPath()+"/"+timeStamp+".jpg");
The first line checks for the folder, the second line creates the file using the path to the folder. I didn't have either part. Without it finding the path, it was just saving were it wanted to.
Another issue that I had was with my Intent call, I used the: MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA. This without any #Overrides apparently acts like the actual camera saving in its folder. I changed this to:
MediaStore.ACTION_IMAGE_CAPTURE, which allows me a little more control.
Another issue that I Was having and figured out, was with the ACTION_IMAGE_CAPTURE, it would take and the close out. But if you implement onActivityResult using the result code returned from the camera application, you can either re-call the camera and make it run again, or you can close it out and go back to another activity. It was not part of the original question, but was an issue I was having.
Related
I'm building an app that allows the user to save the bitmap or share it without saving it. The 2nd functionality doesn't quite work. I understand that the app needs to save the file to the device before sharing it on a social media app so my idea was, immediately after the file was successfully shared, to automatically delete the file from the device. I've build a delete method trying 2 different approaches and neither have worked:
First approach:
public void deleteFile(String path){
File file = new File(path);
try {
file.getCanonicalFile().delete();
} catch (IOException e) {
e.printStackTrace();
}
}
Second approach:
public void deleteFile(String path){
File file = new File(path);
boolean deleted = file.delete();
}
And I'm calling deleteFile(String) from the sharing method:
public void shareMeme(Bitmap bitmap) {
String path = MediaStore.Images.Media.insertImage(Objects.requireNonNull(getContext()).getContentResolver(), bitmap, "Meme", null);
Uri uri = Uri.parse(path);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
share.putExtra(Intent.EXTRA_TEXT, "This is my Meme");
getContext().startActivity(Intent.createChooser(share, "Share Your Meme!"));
deleteFile(path);
}
With respect to your stated problem, insertImage() returns a string representation of a Uri. That Uri is not a file. Calling getPath() on it is pointless, and you cannot delete anything based on that path.
More broadly, if your intention is to delete the content right away:
Do not put it in the MediaStore
Do not share it, as you will be deleting it before the other app has a chance to do anything with it
If you want to share it, but then delete it:
Do not put it in the MediaStore
Delete it the next day, or in a few hours, or something, as you have no good way of knowing when the other app is done with the content
To share an image with another app without using the MediaStore:
Save the image to a file in getCacheDir() (call that on a Context, such as an Activity or Service)
Use FileProvider to make that file available to other apps
Beyond that:
Do not use wildcard MIME types in ACTION_SEND. You are the one who is supplying the content to send. You know the actual MIME type. Use it.
Note that there is no requirement for an ACTION_SEND activity to honor both EXTRA_TEXT and EXTRA_STREAM. Most seem to do so, but that behavior is outside of the ACTION_SEND specification.
Note that insertImage() is deprecated on Android Q.
First, you need to check if your file exists, (maybe you set the wrong path?). Then delete the file
File file = new File(path);
if (file.exists()){
if (file.delete()) {
Toast.makeText(this, "file Deleted :" + path, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "file not Deleted :" + path, Toast.LENGTH_SHORT).show();
}
}
Recently I am developing a file sharing application and I created a GridView, where the downloaded files are being shown. From this View, I would like to be able to open the default application through an intent, to open the whole file. Currently I am testing the app with only image files. All the files are downloaded to the external public directory this way:
File externalFolder = new File(Environment.getExternalStorageDirectory(), "My application");
if(!externalFolder.exists()){
externalFolder.mkdir();
}
...
File folder = new File(externalFolder, "Images");
if(!folder.exists()){
folder.mkdir();
}
...
String filename = folder.getAbsolutePath() + "/" + fileToDownload.getName() + "." + fileToDownload.getExtension();
FileOutputStream fos = new FileOutputStream(filename);
When the file is downloaded, I scan it with MediaScannerConnection.scanFile. The scan is successful, the picture is visible among other files in Photos app.
After the file is downloaded, I am able to extract a thumbnail in the adapter of the GridView, so I surely have a valid path to the file.
And where the fun begins: I tried to set an onClickListener to the GridView items in the adapter to be able to open the picture in Photos app this way:
listItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
if(new File(current.getPath()).exists()){
Log.e("path", "valid");
}else{
Log.e("path", "invalid");
}
Log.e("path", Uri.parse("content://"+current.getPath()).toString());
intent.setDataAndType(Uri.parse("content://"+current.getPath()), "image/*");
getContext().startActivity(intent);
}
});
The Intent is created successfully, I get the following in the log:
E/path: valid
E/path: content:///storage/emulated/0/My application/Images/best_picture_ever.jpeg
I have the option to choose among apps to open. When I select the app, it fails to open the image, like when it does not exist. All the 5 applications.
I tested this on my device with Oreo, and on two emulated devices with Nougat and Lollipop, all of them behaves the same way.
What am I doing wrong?
What am I doing wrong?
You are not creating a valid Uri. You cannot put content:// in front of arbitrary things and have a useful Uri, any more than you can put https:// in front of arbitrary things and have a usable URL.
Use FileProvider to serve up this file.
How would I go about saving an image to a folder within the app? I want to eventually allow users to take pictures and upload and allow others to "rate it." I'm new to android, so I'm sorry if this is very basic. This is what I have so far.
public void onClick(View v) {
// TODO Auto-generated method stub
Intent picture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//MainActivity.this.startActivity(picture);
startActivityForResult(picture, 1);
onActivityResult(1, 1, picture){
}
}
Have you tried saving it elsewhere and then attempting to move it?
You could write a file in which saves it to its original directory and then will move into the one that you wish to do automatically.
You obviously already have enough skills to relocate a file within that executable.
Sorry if im not much help im brand new to the whole stackoverflow community.
hope i helped you though!
read this tutorial: http://developer.android.com/training/camera/photobasics.html
hope this will help you..
Use this piece of code. Explanations are after the code
Intent getCameraImage = new Intent("android.media.action.IMAGE_CAPTURE");
File cameraFolder;
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
cameraFolder = new File(android.os.Environment.getExternalStorageDirectory(),"YOUR_FOLDER_NAME/");
else
cameraFolder= StatusUpdate.this.getCacheDir();
if(!cameraFolder.exists())
cameraFolder.mkdirs();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
String timeStamp = dateFormat.format(new Date());
String imageFileName = "picture_" + timeStamp + ".jpg";
File photo = new File(Environment.getExternalStorageDirectory(), "YOUR_FOLDER_NAME/" + imageFileName);
getCameraImage.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
initialURI = Uri.fromFile(photo);
startActivityForResult(getCameraImage, 1);
What this code essentially does is:
Creates a folder with the name provided where it reads
YOUR_FOLDER_NAME (Change this to your convenience)
The picture_" + timeStamp + ".jpg ensures that multiple images will
be stored in the folder of your choice, each with a new timestamp.
Naturally, the timestamp will be the current time.
The initialURI is globally defined to help you process the Image
taken later. For example, displaying it in an ImageView
I have an application in which I can use the device's camera to take a picture. What I would like to do is to start the ACTION_IMAGE_CAPTURE intent without assigning an EXTRA_OUTPUT, and then move the file that is created in the default location to my own custom location using file.renameTo. My code is something like this:
/* Start camera activity without EXTRA_OUTPUT */
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, _REQUESTCODE_ATTACH_CAMERA);
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch(requestCode) {
case _REQUESTCODE_ATTACH_CAMERA:
/* Get path to most recently added image */
final String[] imageColumns = { MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA };
final String imageOrderBy = MediaStore.Images.Media._ID + " DESC";
Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, null, null, imageOrderBy);
String fullPath = "";
if(imageCursor.moveToFirst()){
fullPath = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
imageCursor.close();
}
File f = Environment.getExternalStorageDirectory();
f = new File(f.getAbsolutePath() + File.separator + "DCIM" + File.separator + MY_APP_NAME;
if(!f.exists()) {
f.mkdirs();
}
/* Create new file based on name of most recently created image */
File oldFile = new File(fullPath);
String newPath = f.getAbsolutePath() + File.separator + oldFile.getName() ;
/* Move file with renameTo */
oldFile.renameTo(new File(newPath));
break;
...
}
}
}
All of this works quite well, however there is one strange thing that is occurring. In my app, I have another button that allows selecting an existing image from the phone's gallery. That code looks like this:
Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
galleryIntent.setType("image/*");
activity.startActivityForResult(galleryIntent, _REQUESTCODE_ATTACH_GALLERY);
This also works, but if I take a picture with the camera using the code posted above, and then try to select another image from the gallery, there will be blank "broken link" type items in the gallery that contain no content and are unselectable. These seem to correspond with photos taken and moved using renameTo; if I put in code in onActivityResult to post the filename to LogCat, the name that gets logged is the same as the name of the previously moved file that it corresponds to. Trying to create a File object or in any way access that filename, results in null objects and force closes.
The strange part is that there is no evidence of these "broken link" files in Eclipse DDMS, nor in the phone itself if I use Root Browser, and they disappear if I remount the SD Card.
The whole reason I am moving the images after capturing them with the camera is to avoid filling up the phone's gallery storage with unnecessary images. While these empty "broken link" type files don't appear to be taking up any storage space, they would still be very annoying to an end-user trying to browse through their gallery. Does anyone have any ideas on what is happening here or how to solve this problem?
EDIT:
Here is a photo showing what the gallery looks like with a "broken link" type image displayed. One of these will appear for every photo that is taken using my app, and they will all disappear if I remount the SD Card.
Thanks in part to this SO thread, I have discovered a solution. It actually makes sense that it would behave this way since there is a table kept for media content and so removing something without telling the table would definitely create a "broken link" type scenario.
The ultimate solution is to use contentResolver.delete to remove the reference to the file within the content resolver, but there are two different ways that I have found that will work.
/* Moving with renameTo */
//Use the same exact code as I had before (shortened for brevity) to move the file
oldFile.renameTo(newFile);
//Get URI from contentResolver using file Id from cursor
Uri oldUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media._ID)));
//Delete old file
getContentResolver().delete(oldUri, null, null);
Getting the URI in this way is necessary because it requires a reference to the image in the contentResolver rather than the path to its location in storage. This way might feel dirty to some since you are moving a file and then calling a delete function on that file in order to sort of trick the content resolver into removing the link to the file. If you would rather, you can do it without using renameTo so that the call to delete(...) actually does delete the image.
/* Moving with streams */
//Get streams
InputStream in = new FileInputStream(oldFile);
OutputStream out = new FileOutputStream(newFile);
byte[] buffer = new byte[1024];
int bytesRead = 0;
//Read old file into new file
while((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
//Get URI from contentResolver using file Id from cursor
Uri oldUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media._ID)));
//Delete old file
getContentResolver().delete(oldUri, null, null);
The call to contentResolver.delete is the same either way, I just wanted to point out that it will still work if the image has already been removed.
During this I discovered a solution to a problem that I didn't even realize that I had that I will post here as well in case anyone with this same problem comes across this in the future. In order to keep the image as selectable in the device gallery from the new location, you need to let the media scanner know that a change has been made. There are two ways that I found to do this:
/* This is the only way that I know of to handle multiple new files at once. I
really would use this sparingly, however, since it will rescan the entire
SD Card. Not only could this take a long time if the user has a lot of files
on their card, it will also show a notification so it is not exactly a
transparent operation. */
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
/* You *could* do multiple files with this by passing in the path for each one
in the array of Strings, however an instance of this will get called for each
one rather than it doing them all at once. Likewise, your onScanCompleted
(if you choose to include one) will get called once for each file in the list.
So really, while this is much better for a small number of files, if you plan
on scanning a very large amount then the full rescan above would probably be
a better option. */
MediaScannerConnection.scanFile(context, new String[]{ newFilePathAsString }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
//This executes when scanning is completed
}
}
);
I have a following question. I'm using camera intent that starts from a menu button like this
case R.id.camera:
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(getTempFile(this)));
startActivityForResult(intent, TAKE_PHOTO_CODE);
return true;
This is working just fine and it saves original image (original size which I want).
Than I also have the following code
private File getTempFile(LovneDobe lovneDobe) {
final File path = new File( Environment.getExternalStorageDirectory(), lovneDobe.getPackageName() );
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
if(!path.exists()){
path.mkdir();
}
return new File(path, "image.jpg");
}
and this code saves the picture on SDcard and it also puts it into my gallery. But the problem I'm facing is that it saves only one picture. When I take another one it overwrites the previous one.
And now my question is how can I modify this so that it would save every picture I would take?
Thank in advance to anyone who is willing to help.
I'm pretty sure It's because you are explicitly overriding the file. In your method "getTempFile", you always give the same path and name for every time you call the method. Try having a static counter, for example, to know how many pictures you have done, and in the return, put something like
return new File(path, "image"+counter+".jpg");