I'm trying to have an Intent that allows user to pick a photo or a video from the phone, then post the image to a server.
Here is how I do it :
public void lookForMedia(View v) {
KeyboardHelper.hideKeyboard(this, postContentEtx);
Intent myIntent = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
myIntent.setType("image/*,video/*");
myIntent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(myIntent, MEDIA_GALLERY);
}
Then :
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case MEDIA_GALLERY:
if (resultCode == RESULT_OK) {
if (requestCode == PICTURE_SELECTED) {
mediaUri = data.getData();
ContentResolver cR = NewPostActivity.this.getContentResolver();
if (mediaUri == null) {
Toast.makeText(NewPostActivity.this, "Error while retrieving the media", Toast.LENGTH_LONG).show();
return;
}
String type = cR.getType(mediaUri);
if (type.startsWith("image")) {
mediaThumbnailImv.setImageURI(mediaUri);
videoUri = null;
thumbnailUri = null;
mediaRll.setVisibility(View.VISIBLE);
mediaThumbnailImv.setVisibility(View.VISIBLE);
mediaThumbnailNetworkImv.setVisibility(View.GONE);
} else if (type.startsWith("video")) {
videoUri = mediaUri;
mediaUri = null;
Bitmap videoThumbnail = ThumbnailUtils.createVideoThumbnail(FileUtil.getRealPathFromURI(NewPostActivity.this, videoUri), MediaStore.Images.Thumbnails.MINI_KIND);
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
FileOutputStream out = null;
try {
String filename = "thumb_" + timeStamp + "kfkb.png";
out = new FileOutputStream(NewPostActivity.this.getFilesDir() + filename);
videoThumbnail.compress(Bitmap.CompressFormat.PNG, 90, out);
thumbnailUri = Uri.fromFile(new File(NewPostActivity.this.getFilesDir() + filename));
mediaThumbnailImv.setImageURI(thumbnailUri);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (Throwable ignore) {
}
}
mediaRll.setVisibility(View.VISIBLE);
mediaThumbnailImv.setVisibility(View.VISIBLE);
mediaThumbnailNetworkImv.setVisibility(View.GONE);
} else {
Toast.makeText(NewPostActivity.this, "Invalid media", Toast.LENGTH_LONG).show();
return;
}
}
}
break;
}
}
It works fine on the devices I tried (Nexus 5, LG G3, Samsung Galaxy S3, Wiko Cink Five), but some users told me that they couldn't see their photos or videos when clicking on the button (it seems that it occurs on older version of Android, but I cannot have more precise information right now). Is there a different way to do this, and to be sure that it works on most devices ? Is there something that I am doing wrong ?
First of all i think you doesn't understand correct that is the cross-platform.
Cross-Platform - is the independent from OS code that will be executed equally on each OS. For each OS - will be compiled executable file. For Android - apk. For Windows - exe. etc...
To your problem. If the users complained about incorrect logic working i recommend to you several things :
Log your stack trace with device_info into dump file to sdcard. And ask the user before application end - send this log to your email. Or something like this.
Try to find alternative way for picking image. For the worst case - write your own activity and scan system for images.
Related
I created an app a few years ago which had it's own file explorer and does so many processes on selected files. recently I wanted to add ability to write to external storages like SD Cards and Hard drives connected through OTG.
The problem is I can't rewrite the whole project based on DocumentFile structure since it's a very big project and it would take forever for me to update. So I needed to somehow convert the old File methods in a few lines and be done with it. this is what I've added so far :
private void getPersistentPermission() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
startActivityForResult(intent, reqcode_storage);
}
and :
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (requestCode == reqcode_storage) {
Uri uri = data.getData();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final int perm_flag = Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
grantUriPermission(getPackageName(), uri, perm_flag);
getContentResolver().takePersistableUriPermission(uri, perm_flag);
}
}
} else {
Log.d(tag, "something`s wrong !");
}
}
then I try to do something like this :
try {
DocumentFile dfile = DocumentFile.fromFile(file);
OutputStream os = getContentResolver().openOutputStream(dfile.getUri());
os.write(some_string.getBytes());
} catch (Exception e) {
Log.e(tag, e.toString());
} finally {
try {
os.close();
} catch (Exception ignored) {}
}
I get the "file" using implemented file explorer inside my app. it works for internal storage.
I read so many threads about SAF but somehow I can't get it to work. it always shows this error :
java.io.FileNotFoundException: Permission denied
Can anyone tell me what am I missing here ?
thanks in advance.
What am I doing wrong here? I'm trying to call the intent to get a picture in full size:
takePictureIntent
private void takePictureIntent(int request) {
final Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) {
File file = null;
try {
file = createImageFile(request);
} catch (Exception e) {
showErrorDialog(getString(R.string.error), getString(R.string.error_saving_picture));
Log.e(TAG, "Error while creating image file.");
}
if (file != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
startActivityForResult(takePictureIntent, request);
} else {
Log.e(TAG, "Error while creating image file.");
showErrorDialog(getString(R.string.error), getString(R.string.error_saving_picture));
}
}
}
createImageFile
private File createImageFile(final int request) {
final File storageDir = new File(activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES), getString(R.string.app_name));
if (!storageDir.exists()) {
if (!storageDir.mkdirs()) {
Log.e(TAG, "Cannot create parent folders.");
return null;
}
}
File file = null;
try {
file = File.createTempFile("test_", ".jpg", storageDir);
} catch (Exception e) {
Log.e(TAG, "Error while creating temp file.");
}
fileProduct = file;
return file;
}
onActivityResult
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_IMAGE_PRODUCT) {
if (fileProduct == null ||!fileProduct.exists() ||fileProduct.length() == 0) {
showErrorDialog(getString(R.string.error), getString(R.string.error_taking_product_picture));
return;
}
}
Sometimes (yes, sometimes) the length of the resulting file is 0. I know for sure that the folders in private app context exist and the image files as well (with length > 0). Could you please provide some help? I'm on 6.0 on Nexus 5X.
I would start by getting rid of File.createTempFile(). You do not need it, it wastes time, and it might cause some camera apps to want to not store the photo in that file (since the file is already there). Just generate a unique filename yourself. This will incrementally help with your compatibility.
Also, you need to make sure that you are holding onto fileProduct in the saved instance state Bundle, as your app's process may be terminated while the camera app is in the foreground.
However, in general, ACTION_IMAGE_CAPTURE is not very reliable. You are delegating the work to one of hundreds of possible camera apps, and some of those apps have bugs. One such bug is ignoring EXTRA_OUTPUT. So, in onActivityResult(), if you determine that you have a valid fileProduct value, but there is no file there, call data.getData() and see if you have a Uri there. In that case, the camera app may have stored the photo at the location identified by that Uri, and you can use ContentResolver and DocumentFile to try to work with that Uri.
Using this:
final String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
file = new File(storageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
instead of
File.createTempFile()
magically seems to fix the problem. Thanks to CommonsWare for (somehow) pointing me in the right direction.
I am making an app that allows that user to record audio. I used Audio intent for this. What I am trying to do is to record audio, set its name, and save it in a folder. In my code, the audio was saved and named properly but when I try to play it, it says that "Sorry, it cannot be played." I don't know where I go a mistake. Help me please, I will really appreciate it. Thanks.
Here is my code:
.....
private void dispatchTakeAudioIntent(int actionCode)
{
Intent takeAudioIntent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
File a = null;
try {
a = setUpAudioFile();
mCurrentAudioPath = a.getAbsolutePath();
takeAudioIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(a));
} catch (IOException e)
{
e.printStackTrace();
a = null;
mCurrentVideoPath = null;
}
startActivityForResult(takeAudioIntent, ACTION_TAKE_AUDIO);
}
private File setUpAudioFile() throws IOException {
File v = createAudioFile();
mCurrentVideoPath = v.getAbsolutePath();
return v;
}
private File createAudioFile() throws IOException
{
// Create an audio file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String audioFileName = AUDIO_FILE_PREFIX + timeStamp + "_";
File albumF = getAlbumDir();
File audioF = File.createTempFile(audioFileName, AUDIO_FILE_SUFFIX, albumF);
return audioF;
}
private void galleryAddAudio()
{
Intent mediaScanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
File f = new File(mCurrentAudioPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case ACTION_TAKE_PHOTO:
{
if (resultCode == RESULT_OK)
{
handleBigCameraPhoto();
dispatchTakePictureIntent(ACTION_TAKE_PHOTO);
}
break;
}
case ACTION_TAKE_AUDIO:
{
if (resultCode == RESULT_OK) {
//audioFileUri = data.getData();
handleAudio(data);
//galleryAddVideo();
}
break;
}
} // switch
}
private void handleAudio(Intent data) {
audioFileUri = data.getData();
if (mCurrentAudioPath != null)
{
//audioFileUri = data.getData();
galleryAddAudio();
mCurrentAudioPath = null;
}
}
........
There are some limitations regarding RECORD_SOUND_ACTION intent that it is not supported to specify a file path to save the audio recording. The application shall save the audio in default location. You cannot use MediaStore.EXTRA_OUTPUT as extra because in document of MediaStore it is written under constant EXTRA_OUTPUT that it only use for image and video.
The name of the Intent-extra used to indicate a content resolver Uri to be used to store the requested image or video.
A solution to this cause is bit tricky. You can let the application save the audio to default but after you can cut, paste and rename your audio to your required location. I found two answers who claims that they found a way to cut paste.
Solution A
Solution B
Accept this answer or +1 if you find it useful.
I am using Camera from Android in my application ...... my requirement is , I am capturing live picture and saving it on a location decided by me not in a Camera folder from Gallery.
And then fetching photos with the help of Uri and displaying it in my application.
But what I have implemented is running properly on some device and on some devices my code couldn't able to create folder and captured image .... so I get nothing when my application wants to display photos.
I have tried my app on Galaxy S and Galaxy SII .... On galaxy S its not working properly, and strange thing is on some SII devices its working properly but some fails to create image and folder on specified location. But on Galaxy Tab everything is working fine.
Also I have tested it on Nexus S .... here problem is something different, on Nexus S when I start camera and capture an image, after that I can see image preview and there I have 2 option to save,cancel or retake .... but when I ask to save nothing happens and when I cancel it frees Camera, and for retake its working as it designed.
I dont understand why there is different findings on different devices .... even devices are from same manufacturer (like Galaxy SII)
I am posting my code ... if someone has hint please share it with me... Thank you.
public static String EXTERNAL_STORAGE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath().toString()+"/DCIM/";
private String imagePath = ImageDecoder.EXTERNAL_STORAGE_PATH;
imagePath = imagePath + projectdata.getProjectName().toString().trim() + "/"
+ System.currentTimeMillis() + ".jpg";
File imageFile = new File(imagePath);
Uri imageFileUri = Uri.fromFile(imageFile);
Intent captureImage = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
captureImage.putExtra(MediaStore.EXTRA_OUTPUT, imageFileUri);
startActivityForResult(captureImage, CAMERA_PIC_REQUEST);
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
String imgDescription = null;
switch (requestCode) {
case CAMERA_PIC_REQUEST:
if (resultCode == RESULT_OK) {
cam = new Controller_Camera(context, sessiondata, projectdata);
imgDescription = getString(R.string.DEFAULT_IMAGE_TITLE) + DateTimeUtility.GetSystemDate();
String response = cam.savePhotoWithIssue(imagePath, app, imgDescription,
getString(R.string.DEFAULT_ISSUE_NAME), getString(R.string.DEFAULT_IMAGE_TITLE));
if (response == "Successfull") {
Intent issueeditor = new Intent(context, IssueEditor.class);
issueeditor.putExtra("TODO", "editissue");
issueeditor.putExtra("TAB", "Photos");
issueeditor.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
finish();
startActivity(issueeditor);
} else {
Toast.makeText(context, getResources().getString(R.string.FAILURE_SAVING), Toast.LENGTH_SHORT)
.show();
}
}
break;
Just check whether SD card is mounted or not
private boolean isSDCARDMounted() {
String status = Environment.getExternalStorageState();
if (status.equals(Environment.MEDIA_MOUNTED))
return true;
return false;
}
if (isSDCARDMounted()) {
File f = new File(Environment.getExternalStorageDirectory(),
TEMP_PHOTO_FILE);
try {
f.createNewFile();
} catch (IOException e) {
}
return f;
}
Hope someone may give some pointers (or an out right answer)...
Simple app, take an image using the built-in camera app, save the image to a separate application. Be done.
Problem: The camera application saves the image in the default app location (/mnt/sdcard/external_sd/DCIM/Camera) as well as my custom path (in code below).
Both files are exactly the same except for the file name. The external_sd file (the one I want gone) is saved with dashes (-) vs my custom file path saved with underscores. File sizes are exactly the same.
How can I stop this double image issue?
Is there an extra intent option I'm missing?
Or am I doing this completely wrong, missing something?
I'm using a Galaxy S Vibrant.
Code snippet:
private static Uri _outputFileUri;
private static File _file;
private ImageView _image;
private SimpleDateFormat _simpleDateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
_takePicture = (Button) findViewById(R.id.takePicture);
_takePicture.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
_intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
_file = new File(Environment.getExternalStorageDirectory() +
"/Android/data/my own folder/files/",
_simpleDateFormat.format(new Date()).toString() +
".jpg");
_outputFileUri = Uri.fromFile(_file);
_intent.putExtra(MediaStore.EXTRA_OUTPUT, _outputFileUri);
startActivityForResult(_intent, CAMERA_ACTIVITY);
}
});
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "Activity cancelled", Toast.LENGTH_LONG).show();
return;
}
switch (requestCode) {
case CAMERA_ACTIVITY:
if (resultCode == RESULT_OK) {
try{
Bitmap b = MediaStore.Images.Media.getBitmap(getContentResolver(), _outputFileUri);
_image.setImageBitmap(b);
_image.invalidate();
}
catch(Exception e){
e.printStackTrace();
}
}
break;
}
}
This is device-dependent behavior. My observation is that HTC devices do not have this duplication problem, but Samsung devices do.
Please remove the following lines:
_file = new File(Environment.getExternalStorageDirectory() +
"/Android/data/my own folder/files/",
_simpleDateFormat.format(new Date()).toString() +
".jpg");
_outputFileUri = Uri.fromFile(_file);
_intent.putExtra(MediaStore.EXTRA_OUTPUT, _outputFileUri);
Also update the code to get the image from intent:
Bitmap b = (Bitmap) data.getExtras().get("data");
_image.setImageBitmap(b);
_image.invalidate();
This way picture wouldn't be saved on sd card or default location.
I had the same problem and gave up. Sometime later I found out that I was not getting it anymore and I'm not sure what change I made to my code, but I think that it was MediaStore's fault (check my unsolved question: Weird camera Intent behavior)
As you already have the image URI, why don't you use it to set the ImageViews' bitmap?
// void setImageURI(Uri uri)
_image.setImageBitmap(_outputFileUri);
I had this issue and here is how i solved it :
File createImageFile() throws IOException{
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String filename = "IMG_"+timestamp+"_";
File image = File.createTempFile(filename,".jpg",mGalleryFolder );
if (image.length() == 0 ){
boolean delete = image.delete();
}
mLocation = image.getAbsolutePath();
return image;
}
It's not exactly solving but works for me ;)