Android 10+ not retrieving DATE_TAKEN information from MediaStore - java

When downloading images from browser and listing them into the app, date taken column is always null only on android 10+
ArrayList<String> localImages = new ArrayList<>();
Uri uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon().build();
final String[] projection = {
MediaStore.MediaColumns.DATA,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
MediaStore.Images.Media.DATE_TAKEN,
MediaStore.MediaColumns.DATE_ADDED
};
final String orderBy = MediaStore.Images.Media.DATE_TAKEN;
String searchQuery = null;//dateQuery == null ? null : MediaStore.Images.Media.DATE_TAKEN + ">" + (dateQuery.getTime());
Cursor cursor = this.getContentResolver().query(uri, projection, searchQuery, null, orderBy + " DESC");
int columnIndexData = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
int columnDateTaken = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_TAKEN);
while (cursor.moveToNext()) {
String imagePath = cursor.getString(columnIndexData);
String dateTaken = cursor.getString(columnDateTaken); // THIS IS ALWAYS NULL
localImages.add(imagePath);
}

"Media Store insert DATE_TAKEN is always null"
DATE_TAKEN is documented to be "The time the media item was taken." and to deliver on that API description, we populate it based on the "DateTimeOriginal" Exif metadata field, or the . If the file being scanned doesn't have this metadata, we can't accurately determine when the file was captured, so DATE_TAKEN is set to NULL to avoid misleading data.
So, some devices don't have DATE_TAKEN field.If you use DATE_TAKEN, you will check value is null or not. And when not, you should fall back to use DATE_ADDED or DATE_MODIFIED, etc.
I referred to this Japanese article: https://zenn.dev/tsutou/articles/59fbb5e71b2988

Related

Passing phone number or CONTACT_ID from android contact details to activity (whatsapp like)

What I'm trying to achieve is to add shortcut to my app in android book contact details, similar to what whatsapp is doing.
I've been following this tutotial: http://blogs.quovantis.com/syncing-contacts-with-an-android-application-2/ and it works well but the author doesn't show how to pass data from contact details to ViewingActivity: https://github.com/ajkh35/ContactsDemo/blob/master/app/src/main/java/com/example/ajay/contacts_4/ViewingActivity.java
There was some comments below the article but no specific answer from the author, can't find anything useful in
Uri data = getIntent().getData(); //content://com.android.contacts/data/1169
List<String> params = data.getPathSegments();
String first = params.get(0);
String second = params.get(1);
there is some number passed in second param but it's not CONTACT_ID or RAW_CONTACT_ID. Any help?
Ok, so it seems the Uri you're getting from the Contacts app is a Data uri.
Data rows contain info about a specific data-item (like a phone number or an email) of a specific RawContact, so a single Data row "belongs" to a single RawContact which "belongs" to a single Contact.
Luckily, the ContactsContract API allows for implicit joins when querying the Data table, so you can do something like this:
Uri dataUri = getIntent().getData(); //content://com.android.contacts/data/1169
String[] projection = new String[]{
Data.CONTACT_ID,
Data.RAW_CONTACT_ID,
Data.DISPLAY_NAME,
Data.MIMETYPE,
Data.DATA1};
Cursor cur = getContentResolver().query(dataUri, projection, null, null, null);
cur.moveToFirst(); // there should always be exactly one result, since we have a specific data uri here
Log.i("Contact Info", "Got info: id=" + cur.getLong(0) + ", raw-id=" + cur.getLong(1) + ", " + cur.getString(2) + ", " + cur.getString(3) + ", " + cur.getString(4));
cur.close();
I know this is a very late response but checkout the following code.
class MessageActivity : AppCompatActivity() {
private val TAG: String = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_message)
if(intent != null && intent.data != null) {
Log.e(TAG, intent.data.toString())
var contactName = ""
val cursor = contentResolver.query(intent.data!!,
arrayOf(ContactsContract.Data.DATA1,ContactsContract.Data.DATA2,ContactsContract.Data.DATA3),
null,null,null)
if(cursor != null && cursor.moveToFirst()) {
do{
Log.e(TAG, cursor.getString(cursor
.getColumnIndexOrThrow(ContactsContract.Data.DATA1)))
contactName = cursor.getString(cursor
.getColumnIndexOrThrow(ContactsContract.Data.DATA2))
Log.e(TAG, contactName)
Log.e(TAG, cursor.getString(cursor
.getColumnIndexOrThrow(ContactsContract.Data.DATA3)))
}while (cursor.moveToNext())
cursor.close()
}
messaging_text.text = getString(R.string.messaging) + " $contactName"
}
}}
So when you register a contact you set some Data1, Data2 and Data3 values. Data3 is what gets displayed in the contacts. You can set Data1 and Data2 some value you like and then retrieve it like in the code I mentioned above.
You can also checkout my blog here. Look for the "Sync Service" section, towards the end you will find the MessageActivity.
Thanks & regards

How do I increase the fetching speed of an arraylist?

I am using an Arraylist to fetch all the available contacts in my application. This is not efficient because the Arraylist takes a long time to fetch and populate the Listview as there are almost 600+ contacts.
I'm seeking an alternative approach that would have better performance.
Although I searched for other relevant questions but I was't able to find the convenient one.
Here is my java code:
private List<String> getContactList() {
List<String> stringList=new ArrayList<>();
ContentResolver cr = context.getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if ((cur != null ? cur.getCount() : 0) > 0) {
while (cur != null && cur.moveToNext()) {
String id = cur.getString(
cur.getColumnIndex(ContactsContract.Contacts._ID));
String name = cur.getString(cur.getColumnIndex(
ContactsContract.Contacts.DISPLAY_NAME)
);
if (cur.getInt(cur.getColumnIndex(
ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0) {
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{id}, null
);
while (pCur.moveToNext()) {
String phoneNo = pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
Log.v("Data : ",""+id+" "+name+" "+phoneNo);
stringList.add(id);
stringList.add(name);
stringList.add(phoneNo);
}
pCur.close();
}
}
}
if(cur!=null){
cur.close();
}
return stringList;
}
Your query is inefficient, you're currently doing a query per contact which is very slow, you can get it all with one big query (which is pretty fast):
String[] projection = new String[] { Phone.CONTACT_ID, Phone.DISPLAY_NAME, Phone.NUMBER };
Cursor c = cr.query(Phone.CONTENT_URI, projection, null, null, null);
while (c.moveToNext()) {
long contactId = c.getLong(0);
String name = c.getString(1);
String phone = c.getString(2);
Log.i("Phones", "got contact phone: " + contactId + " - " + name + " - " + phone);
}
c.close();
You could consider using the Paging library: https://developer.android.com/topic/libraries/architecture/paging/
It was designed with the idea that a list only displays a certain number of items, so there's really no need to load way way way more than it can potentially show. For example, a ListView might only show 10 contacts, so there's no need to fetch 600 contacts.
Instead, the Paging library will fetch a smaller amount as the user scrolls, thus erasing the loading time of 600 contacts, the memory for 600 contacts, and etc... thus making it more efficient.
If you're worried of speed, I would try to use a Set, although with 600+ contacts in the ArrayList that shouldn't be a problem. It becomes a problem when the data set is in the millions and more. I would try to looking at any other inefficiencies in your code.
In terms of a Set, the two most common Java data structures are HashSet and TreeSet. TreeSet if you want to the set to be ordered. HashSet is a little bit faster, but you lose out on the ordering. Both of which has O(1) access time.

Can't remove contacts from SIM card

I've got a problem with removing contacts I previously created on SIM card.
First of all I'm checking what values are stored in DB like this:
private static final Uri URI_ICC_ADN = Uri.parse("content://icc/adn/");
private ContentResolver mContentResolver = this.getContentResolver();
Cursor c = mContentResolver.query(URI_ICC_ADN, null, null, null, null);
c.moveToFirst();
while(c.moveToNext()) {
Log.i(LOG_TAG, "name = " + c.getString(c.getColumnIndex("name")));
}
And this provides me with those logs:
name = 1
name = 2
name = 3
name = 1
name = 2
name = 5
// etc
It means that records with name = 1 exist in DB. Now I'm trying to delete those records with this code:
int rowsDeleted = mContentResolver.delete(URI_ICC_ADN, "name=?", new String[] { "1" });
But unfortunately those rows are not removed - rowsDeleted equals 0. I've also tried this:
int rowsDeleted = mContentResolver.delete(URI_ICC_ADN, "name=1", null);
But the result is the same. What am I doing wrong?
"name" is only for read query. For deletion you have to pass "tag" for the name column. Check this link for the actual implementation IccProvider

Retrieving a phone number with ContactsContract in Android - function doesn't work

I wrote the following function in order to retrieve one single phone number that belongs to the contact with id "contactID".
The function which is to retrieve the phone number:
private String getContactPhone(String contactID) {
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String[] projection = null;
String where = ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?";
String[] selectionArgs = new String[] { contactID };
String sortOrder = null;
Cursor result = managedQuery(uri, projection, where, selectionArgs, sortOrder);
if (result.moveToFirst()) {
String phone = result.getString(result.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
if (phone == null) {
result.close();
return null;
}
result.close();
return phone;
}
result.close();
return null;
}
How this function is called:
ArrayList<Contact> resultContacts = new ArrayList<Contact>();
Cursor result = null;
Uri uri = ContactsContract.Data.CONTENT_URI;
String[] projection = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Event.CONTACT_ID,
ContactsContract.CommonDataKinds.Event.START_DATE,
};
String where = ContactsContract.Data.MIMETYPE+" = ? AND "+ContactsContract.CommonDataKinds.Event.TYPE+" = "+ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY;
String[] selectionArgs = new String[] {ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE};
String sortOrder = null;
result = managedQuery(uri, projection, where, selectionArgs, sortOrder);
while (result.moveToNext()) {
Long id = result.getLong(result.getColumnIndex(ContactsContract.Contacts._ID));
String phone = getContactPhone(String.valueOf(id));
...
}
...
Unfortunately, it doesn't work. I get null if I call this function with the value that I got from "ContactsContract.Contacts._ID". Why is this so? What is wrong?
Edit: I used to map Contacts._ID to CommonDataKinds.Phone.CONTACT_ID - which didn't work. But now I map Contacts.DISPLAY_NAME to CommonDataKinds.Phone.DISPLAY_NAME and it works suddenly - strange, isn't it? But I would rather like to map the IDs instead of the display names. So the question is still topical. Could this be due to different IDs in those tables? Isn't this why there are lookup IDs?
To get the contact id in the first part, you should use:
ContactsContract.Data.CONTACT_ID
instead of:
ContactsContract.Contacts._ID
So the projection should be:
String[] projection = new String[] {
ContactsContract.Data.CONTACT_ID,
ContactsContract.CommonDataKinds.Event.CONTACT_ID,
ContactsContract.CommonDataKinds.Event.START_DATE,
};
And then of course get the correct row:
Long id = result.getLong(result.getColumnIndex(ContactsContract.Data.CONTACT_ID));
You are getting null because you have set your projection to null. The projection is basically the list of columns that you want returned e.g.
String[] projection = {ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.HAS_PHONE_NUMBER};
Usually, when you find the contact, they may have a list of phone numbers, so you have to use another cursor to iterate through the phone numbers, e.g.
Cursor phones = mContext.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId, null, null);
while (phones.moveToNext())
{
phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DATA));
}
Hope this helps.
Your code for getContactPhone() works fine on my end. I tested by launching a contact picker, selecting a contact, then using the ID that was returned and passed that into your method.
So I suspect you are indeed passing in an invalid ID. Can you post the full stack trace for the null pointer exception?
Yes, lookup keys are available because _IDs are not guaranteed to stay the same since syncs and contact aggregation changes them.

List all of one file type on Android device?

For example, I want to retrieve a list of all .mp3 files on the internal AND external storage. I would also like the path (String) of each .mp3 file for reference (store them in a List?) How could I do this? Is this a relatively expensive operation? If so, should it be put on a thread and while that is running, present the user with a "Loading" ProgressDialog?
Please find the function below . It will get all the mp3 files stored in external and Internal memory .
public void getAllSongs() {
Uri allsongsuri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
cursor = managedQuery(allsongsuri, STAR, selection, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
song_name = cursor
.getString(cursor
.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME));
int song_id = cursor.getInt(cursor
.getColumnIndex(MediaStore.Audio.Media._ID));
String fullpath = cursor.getString(cursor
.getColumnIndex(MediaStore.Audio.Media.DATA));
fullsongpath.add(fullpath);
album_name = cursor.getString(cursor
.getColumnIndex(MediaStore.Audio.Media.ALBUM));
int album_id = cursor.getInt(cursor
.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
artist_name = cursor.getString(cursor
.getColumnIndex(MediaStore.Audio.Media.ARTIST));
int artist_id = cursor.getInt(cursor
.getColumnIndex(MediaStore.Audio.Media.ARTIST_ID));
} while (cursor.moveToNext());
}
cursor.close();
db.closeDatabase();
}
}

Categories