In my app I'm trying to add an EXIF attribute to a contact photo when I add it to either a new or existing contact. This is so I can later check to see if it was My_App that changed the photo. I add the EXIF data like this:
private void addPhotoToExistingContact(long rawContactId, byte[] photoByteArray) {
if (photoByteArray != null) {
try {
photoByteArray = addExifDataToContactPhoto(photoByteArray);
} catch (IOException e) {
e.printStackTrace();
}
ContentValues values = new ContentValues();
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, photoByteArray);
context.getContentResolver().insert(
ContactsContract.Data.CONTENT_URI,
values);
}
}
private byte[] addExifDataToContactPhoto(byte[] photoByteArray) throws IOException {
// Convert to temp file
File file = new File(context.getCacheDir(), "contact_photo_exif_temp_file.jpg");
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(file.getAbsoluteFile());
fos.write(photoByteArray);
fos.close();
// Add EXIF data
ExifInterface exif = new ExifInterface(file.getAbsolutePath());
exif.setAttribute(MY_EXIF_TAG, MY_EXIF_VALUE);
exif.saveAttributes();
// Convert back to byte[]
byte[] photoByteArrayWithExifData = FileUtils.readFileToByteArray(file);
// Delete temp file
file.delete();
return photoByteArrayWithExifData;
}
My check for EXIF data (done at a later time) is as follows:
private boolean shouldReplaceContactPhoto(long contactId) {
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(
ContactsContract.Data.CONTENT_URI,
null,
ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?",
new String[] { ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE, String.valueOf(contactId) },
null);
if (cursor != null) {
if (cursor.moveToFirst()) {
long photoId = cursor.getLong(cursor.getColumnIndex(ContactsContract.Data.PHOTO_ID));
if (photoId == 0) {
cursor.close();
return true;
}
else {
// Read EXIF data to check if photo is a My_App photo
File contactPhotoTempFile = getExistingContactImageFile(contactId);
if (contactPhotoTempFile != null) {
try {
ExifInterface exif = new ExifInterface(contactPhotoTempFile.getAbsolutePath());
String swopTag = exif.getAttribute(MY_EXIF_TAG);
// Temporary image, so delete it when we're done reading EXIF data
contactPhotoTempFile.delete();
cursor.close();
// If tag is null, the photo came from a different source - return 'true'
// so it is not replaced.
return myTag != null;
} catch (IOException e) {
e.printStackTrace();
// Temporary image, so delete it when we're done reading EXIF data
contactPhotoTempFile.delete();
}
}
cursor.close();
return true;
}
}
cursor.close();
}
return true;
}
private File getExistingContactImageFile(long contactId) {
Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);
Uri displayPhotoUri = Uri.withAppendedPath(contactUri, ContactsContract.Contacts.Photo.DISPLAY_PHOTO);
byte[] imageBytes;
try {
AssetFileDescriptor fd = context.getContentResolver().openAssetFileDescriptor(displayPhotoUri, "r");
InputStream inputStream = fd.createInputStream();
imageBytes = IOUtils.toByteArray(inputStream);
} catch (IOException e) {
return null;
}
if (imageBytes == null) {
return null;
}
File file = new File(context.getCacheDir(), "contact_photo_temp_file.jpg");
if (file.exists()) {
file.delete();
}
try {
FileOutputStream fos = new FileOutputStream(file.getPath());
fos.write(imageBytes);
fos.close();
} catch (java.io.IOException e) {
e.printStackTrace();
}
return file;
}
I've added break points and log statements and I'm almost 100% positive that the attribute is being written, but when I read the data, the property is missing. I also noticed that the orientation attribute is changed from 1 to 0 as well, which leads me to believe that Android is overwriting the EXIF data.
Is this the case, or am I doing something incorrectly? Any help at all is extremely appreciated!
Related
I am trying to write an image on storage then reading it. Write operation is successful but read fails. I have tried using intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); when calling the responsible activity but was of no help.
This is how I write image:
public void savepic(Bitmap pic)
{
SQLiteDatabase db = dBcontract.getWritableDatabase();
ContentValues values = new ContentValues();
FileOutputStream out = null;
try
{
File image = createImageFile();
Uri photoURI = FileProvider.getUriForFile(context, "lcukerd.com.android.fileprovider", image);
out = new FileOutputStream(image);
pic.compress(Bitmap.CompressFormat.PNG, 100, out);
values.put(eventDBcontract.ListofItem.columnpic, photoURI.toString());
db.insert(eventDBcontract.ListofItem.tableName2, null, values);
Log.i(TAG, "Pic saved " + photoURI.toString());
} catch (Exception e)
{
e.printStackTrace();
} finally
{
try
{
if (out != null)
{
out.close();
}
} catch (IOException e)
{
e.printStackTrace();
}
}
}
private File createImageFile() throws IOException
{
String EName = "Image";
File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(EName, ".jpg", storageDir);
return image;
}
This is how I read image:
public Bitmap getDownloadedpics(int index)
{
SQLiteDatabase db = dBcontract.getReadableDatabase();
Cursor cursor = db.query(eventDBcontract.ListofItem.tableName2, projection2, null, null, null, null, null);
cursor.moveToPosition(index);
Bitmap photo = null;
try
{
Log.d(TAG,cursor.getString(cursor.getColumnIndex(eventDBcontract.ListofItem.columnpic)));
photo = MediaStore.Images.Media.getBitmap(context.getContentResolver(),
Uri.parse(cursor.getString(cursor.getColumnIndex(eventDBcontract.ListofItem.columnpic))));
} catch (IOException e)
{
Log.e(TAG, "Can't read image");
}
Log.d(TAG, "Returned " + String.valueOf(cursor.getCount()) + " pics");
return (photo);
}
I get Exception at `photo =
MediaStore.Images.Media.getBitmap(context.getContentResolver(),
Uri.parse(cursor.getString(cursor.getColumnIndex(eventDBcontract.ListofItem.columnpic))));
Exception is :
java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{6118b35 21772:lcukerd.com.instaswipe/u0a157} (pid=21772, uid=10157) that is not exported from uid 10101
I have checked other similar question but seems like they are all for some other type of problem. Help me solve it.
Instead of photoUri.toString() put image.getAbsolutePath() in your database.
If you want to read the file get the path from the database. Construct a File object en use FileInputStream. Then let BitmapFactory read the data from the stream.
My app takes photo from camera and saves it in a file whose Uri is stored in SQL database. Initializing bitmap using Uri from database works flawlessly. However, when i try to initialize a file using Uri from database and then delete using imagefile.delete()it does not work. I have tried few methods given in other posts but none worked.
This i how i save file.
Intent for Camera:
Intent startCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (startCamera.resolveActivity(getPackageManager())!=null)
{
photoFile= null;
try{
photoFile = createImageFile();
}
catch (IOException ex)
{
Log.e("File Creation","error");
}
if (photoFile!=null)
{
photoURI = FileProvider.getUriForFile(context,"lcukerd.com.android.fileprovider",photoFile);
startCamera.putExtra(MediaStore.EXTRA_OUTPUT,photoURI);
camerastarted=true;
startActivityForResult(startCamera,CAMERA_REQUEST);
}
}
Declared method:
private File createImageFile() throws IOException
{
String EName = "Stuff";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(EName,".jpg",storageDir);
return image;
}
Compressing image:
photoFile.delete();
FileOutputStream out = null;
try {
File image = createImageFile();
photoURI = FileProvider.getUriForFile(context, "lcukerd.com.android.fileprovider", image);
out = new FileOutputStream(image);
photo.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
saving Uri to database is simple conversion of Uri to string and then saving in TEXT column of table.
Help me delete image file .
Edit:
I tried following.
private void deleteimage(String imageloc)
{
File imagefile = new File(getRealPathFromURI(Uri.parse(imageloc)));
Log.d("file deletion",String.valueOf(imagefile.delete())+" "+imageloc);
}
public String getRealPathFromURI( Uri contentUri) {
String result;
Cursor cursor = getContentResolver().query(contentUri, null, null, null, null);
if (cursor == null) { // Source is Dropbox or other similar local file path
result = contentUri.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
and..
private void deleteimage(String imageloc)
{
File imagefile = new File(Uri.parse(imageloc).getpath());
Log.d("file deletion",String.valueOf(imagefile.delete())+" "+imageloc);
}
None worked.
Since this is a Uri that you are getting from FileProvider, call delete() on a ContentResolver, passing in the Uri (plus null for the remaining parameters). That will delete the underlying file, at least according to the FileProvider documentation.
Or, store the file path, rather than the Uri, in your database, since this is your file. You can recreate the Uri later as needed using FileProvider.getUriForFile(), and you can use delete() on File to delete the file, given its path.
What I am trying to achieve. I have a button, when the button is clicked the app opens a file picker and the user selects a file. The app then uses a FileInputStream to read the file and generates a byte[]. I have a TextView below the button which will then simply display the byte[].length. Here is the code in the button.onClick() event:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
requestFilePickerCode = parent.registerActivityResultListener(this);
try
{
parent.startActivityForResult(intent, requestFilePickerCode);
}
catch (ActivityNotFoundException e)
{
Toast.makeText(task.getParent(), "Please install a file manager", Toast.LENGTH_SHORT).show();
}
Now this code works and I have confirmed that it fires onActivityResult when the file is chosen. I simply print a Log to display data.toString() which produces the following output:
11-02 15:14:36.196 2535-2535/? V/class za.co.gpsts.gpsjobcard.utility.handlers.PebbleTypeHandlerBinary: -----> content:/com.android.providers.downloads.documents/document/1
So it seems to be getting the selected file. When I run the app and I select a file it throws my custom error:
11-02 15:14:36.196 2535-2535/? E/class za.co.gpsts.gpsjobcard.utility.handlers.PebbleTypeHandlerBinary: -----> File does not exist
This obviously indicates that I am not getting the file. Here is my code:
#Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data)
{
byte[] fileContent;
// check that data is not null and assign to file if not null
if (data != null)
{
Uri uri = data.getData();
String uriString = uri.toString();
file = new File(uriString);
Log.v(PebbleTypeHandlerBinary.class.toString(), "-----> " + file.toString());
// declare file input stream and read bytes
// write to string variable to test and test output
FileInputStream fin = null;
try
{
fin = new FileInputStream(file);
fileContent = new byte[(int) file.length()];
fin.read(fileContent);
String test = new String(fileContent);
Log.v(PebbleTypeHandlerBinary.class.toString(), "=====> " + test);
}
catch (FileNotFoundException e)
{
Toast.makeText(task.getParent(), "File not found", Toast.LENGTH_SHORT).show();
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> File does not exist");
}
catch (IOException e)
{
Toast.makeText(task.getParent(), "Error reading file", Toast.LENGTH_SHORT).show();
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> Error while reading the file");
}
finally
{
// close the file input stream to stop mem leaks
try
{
if (fin != null)
{
fin.close();
}
} catch (IOException e)
{
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> Error closing the stream");
}
}
Log.v(PebbleTypeHandlerBinary.class.toString(), data.toString());
}
return false;
}
Please can you guys review my code and help me to get this working. Any help would be appreciated.
/* you can get just name and size with this method.
use Cursor .
Uri uri = data.getData();
get data from onActivityResult()
*/;
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
cursor.moveToFirst();
String name = cursor.getString(nameIndex);
String size = Long.toString(cursor.getLong(sizeIndex));
Toast.makeText(this, "name : "+name+"\nsize : "+size, Toast.LENGTH_SHORT).show();
I managed to fix it as follows:
I used inputStream = task.getParent().getContentResolver().openInputStream(uri); to get an InputStream. Then used a ByteArrayOutputStream to write to a byte[]. See code below.
#Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data)
{
Uri uri = data.getData();
byte[] fileContent;
InputStream inputStream = null;
try
{
inputStream = task.getParent().getContentResolver().openInputStream(uri);
if (inputStream != null)
{
fileContent = new byte[(int)file.length()];
inputStream.read(fileContent);
fileContent = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int read;
while((read=inputStream.read(fileContent))>-1) baos.write(fileContent,0,read);
fileContent = baos.toByteArray();
baos.close();
Log.v(PebbleTypeHandlerBinary.class.toString(), "-----> Input Stream: " + inputStream);
Log.v(PebbleTypeHandlerBinary.class.toString(), "-----> Byte Array: " + fileContent.length);
}
else
{
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> Input Stream is null");
}
}
catch (FileNotFoundException e)
{
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> File not found", e);
}
catch (IOException e)
{
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> Error reading file", e);
}
finally
{
if (inputStream != null)
{
try
{
inputStream.close();
}
catch (IOException e)
{
Log.e(PebbleTypeHandlerBinary.class.toString(), "-----> Error reading file", e);
}
}
}
return false;
}
Thanks for all your help.
U can search converting uri to filepath.
GetData() retruns a uri.
But new File() need a filepath param;
Like this:
public static String getRealFilePath( final Context context, final Uri uri ) {
if ( null == uri ) return null;
final String scheme = uri.getScheme();
String data = null;
if ( scheme == null )
data = uri.getPath();
else if ( ContentResolver.SCHEME_FILE.equals( scheme ) ) {
data = uri.getPath();
} else if
( ContentResolver.SCHEME_CONTENT.equals( scheme ) ) {
Cursor cursor = context.getContentResolver().query( uri, new String[] { ImageColumns.DATA }, null, null, null );
if ( null != cursor ) {
if ( cursor.moveToFirst() ) {
int index = cursor.getColumnIndex( ImageColumns.DATA );
if ( index > -1 ) {
data = cursor.getString( index );
}
}
cursor.close();
}
}
return data;
}
1. File Name:
You can get the file name with the following method:
public static String getFileName(Context context, Uri uri) {
String result = null;
if (uri.getScheme().equals("content")) {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} finally {
cursor.close();
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}
You will call it inside onActivityResult() and pass to it the context and uri. and you can find the uri through the intent you get from the onActivityResult which is mostly called "data", you will get the Uri from it like this:
data.data
which ".data" is the Uri.
Ex:
Utils.getFileName(this, data!!.data)
And finally it will return the file name as a String.
2. File Path:
Simply to get the file path you can get it from the data intent uri like this:
data!!.data!!.path.toString()
It will get you the path of the file as a String.
In my android application, i have used google drive to pick images and files to my application, it works perfectly in all API version except 4.4.2, whenever i tried to pick image or file i can get the file name but not able to get file path, it always returns empty path
My code :
// Get real path from Google Drive
public String getPathfromGoogleDrive(Intent data, Uri contentURI) {
if (contentURI == null) {
return null;
}
String[] filePathColumn = { MediaStore.Images.Media.DATA };
String mCurrentPhotoPath = new String();
Cursor cursor = null;
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
LogUtil.d("currentapiVersion" + currentapiVersion);
if (currentapiVersion == 19) {
String wholeID = DocumentsContract.getDocumentId(contentURI);
// Split at colon, use second item in the array
String id = wholeID.split(";")[0];
// where id is equal to
String sel = MediaStore.Images.Media._ID + "=?";
cursor = getActivity().getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
filePathColumn, sel, new String[] { id }, null);
LogUtil.d("Cursor Count" + cursor.getCount());
if (cursor.getCount() > 0 && cursor.moveToFirst()) {
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
mCurrentPhotoPath = cursor.getString(columnIndex);
cursor.close();
}
}
My Intent :
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
if (currentapiVersion == 19) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
String strType = "*/*";
intent.setDataAndType(null, strType);
startActivityForResult(intent, Gallery);
} else {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setPackage("com.google.android.apps.docs");
String strType = "*/*";
intent.setDataAndType(null, strType);
startActivityForResult(intent, Gallery);
}
Please correct me if i have did any mistake
Thanks in advance
Instead of getting file real path, we can use input stream as like below
Bitmap bitmap = null;
InputStream input = null;
try {
input = getActivity().getContentResolver().openInputStream(selectedImageURI);
bitmap = BitmapFactory.decodeStream(input);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
To Get File from drive and write that into locale(sd card)
sourceuri - your cnontent uri
destination - path where you want to save in sd card
public boolean savefile(String name, Uri sourceuri, String destination)
throws IOException {
// String sourceFilename = sourceuri.getPath();
int originalsize = 0;
InputStream input = null;
try {
input = getContentResolver().openInputStream(sourceuri);
Log.Logger().finest("input in profileview Activity" + input);
} catch (FileNotFoundException e) {
e.printStackTrace();
filenotfoundexecption = true;
}
try {
originalsize = input.available();
Log.Logger().finest(
"Profile view activity originalsize" + originalsize);
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(input);
bos = new BufferedOutputStream(new FileOutputStream(
destination, false));
byte[] buf = new byte[originalsize];
bis.read(buf);
do {
bos.write(buf);
} while (bis.read(buf) != -1);
} catch (IOException e) {
Mint.logException(e);
filenotfoundexecption = true;
return false;
}
} catch (NullPointerException e1) {
Mint.logException(e1);
filenotfoundexecption = true;
}
/*
* String[] cmd = new String[] { "logcat", "-f", GridViewDemo_LOGPATH,
* "-v", "time", "ActivityManager:W", "myapp:D" };
*
* Runtime.getRuntime().exec(cmd);
*/
return true;
}
I want to update/insert a new image for an album in MediaStore but i can't get it work..
This is my code:
public void updateAlbumImage(String path, int albumID) {
ContentValues values = new ContentValues();
values.put(MediaStore.Audio.Albums.ALBUM_ART, path);
int n = contentResolver.update(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, values, MediaStore.Audio.Albums.ALBUM_ID + "=" + albumID, null);
Log.e(TAG, "updateAlbumImage(" + path + ", " + albumID + "): " + n);
}
The error is:
03-24 03:09:46.323: ERROR/AndroidRuntime(5319): java.lang.UnsupportedOperationException: Unknown or unsupported URL: content://media/external/audio/albums
03-24 03:09:46.323: ERROR/AndroidRuntime(5319): at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:131)
03-24 03:09:46.323: ERROR/AndroidRuntime(5319): at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:111)
03-24 03:09:46.323: ERROR/AndroidRuntime(5319): at android.content.ContentProviderProxy.update(ContentProviderNative.java:405)
03-24 03:09:46.323: ERROR/AndroidRuntime(5319): at android.content.ContentResolver.update(ContentResolver.java:554)
03-24 03:09:46.323: ERROR/AndroidRuntime(5319): at com.liviu.app.smpp.managers.AudioManager.updateAlbumImage(AudioManager.java:563)
03-24 03:09:46.323: ERROR/AndroidRuntime(5319): at com.liviu.app.smpp.ShowAlbumsActivity.saveImageFile(ShowAlbumsActivity.java:375)
03-24 03:09:46.323: ERROR/AndroidRuntime(5319): at com.liviu.app.smpp.ShowAlbumsActivity.onClick(ShowAlbumsActivity.java:350)
Thank you!
See this post:
Android set Album Thumbnail
The bit you need is here:
ContentResolver res = context.getContentResolver();
Uri uri = ContentUris.withAppendedId(sArtworkUri, album_id);
if (uri != null) {
InputStream in = null;
try {
in = res.openInputStream(uri);
return BitmapFactory.decodeStream(in, null, sBitmapOptions);
} catch (FileNotFoundException ex) {
// The album art thumbnail does not actually exist. Maybe the user deleted it, or
// maybe it never existed to begin with.
Bitmap bm = getArtworkFromFile(context, null, album_id);
if (bm != null) {
// Put the newly found artwork in the database.
// Note that this shouldn't be done for the "unknown" album,
// but if this method is called correctly, that won't happen.
// first write it somewhere
String file = Environment.getExternalStorageDirectory()
+ "/albumthumbs/" + String.valueOf(System.currentTimeMillis());
if (ensureFileExists(file)) {
try {
OutputStream outstream = new FileOutputStream(file);
if (bm.getConfig() == null) {
bm = bm.copy(Bitmap.Config.RGB_565, false);
if (bm == null) {
return getDefaultArtwork(context);
}
}
boolean success = bm.compress(Bitmap.CompressFormat.JPEG, 75, outstream);
outstream.close();
if (success) {
ContentValues values = new ContentValues();
values.put("album_id", album_id);
values.put("_data", file);
Uri newuri = res.insert(sArtworkUri, values);
if (newuri == null) {
// Failed to insert in to the database. The most likely
// cause of this is that the item already existed in the
// database, and the most likely cause of that is that
// the album was scanned before, but the user deleted the
// album art from the sd card.
// We can ignore that case here, since the media provider
// will regenerate the album art for those entries when
// it detects this.
success = false;
}
}
if (!success) {
File f = new File(file);
f.delete();
}
} catch (FileNotFoundException e) {
Log.e(TAG, "error creating file", e);
} catch (IOException e) {
Log.e(TAG, "error creating file", e);
}
}
} else {
bm = getDefaultArtwork(context);
}
return bm;
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException ex) {
}
}
}