I have managed to extract contact details from the phone by using ContactContract example I found, but I noticed that most of the people on my phone has a unique id key associated to their emails and phone numbers separately. For example, Alan's contact detail is split up as following when I extract it out from database even though they are for the same person:
key name email phone
20121 Alan alan#gmail.com null
20133 Alan null 04xxxxxxxx
So how does the phone manage the association with all these different keys in the contact (I assume there must be a separate table for it)? Is there any way to obtain this association? Because I can not just try match the name as people can have exactly the same name, you have to keep them separated as how they are stored on your phone contact.
(Or the messed up situation is due to all apps are able to save contact related details into the same database on the phone?)
My code looks like following (I forgot where I get this code from, but getDetailedContactList function is returning a list of contact of the above problem):
public static String CONTACT_ID_URI = ContactsContract.Contacts._ID;
public static String DATA_CONTACT_ID_URI = ContactsContract.Data.CONTACT_ID;
public static String MIMETYPE_URI = ContactsContract.Data.MIMETYPE;
public static String EMAIL_URI = ContactsContract.CommonDataKinds.Email.DATA;
public static String PHONE_URI = ContactsContract.CommonDataKinds.Phone.DATA;
public static String NAME_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Data.DISPLAY_NAME_PRIMARY : ContactsContract.Data.DISPLAY_NAME;
public static String PICTURE_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Contacts.PHOTO_THUMBNAIL_URI : ContactsContract.Contacts.PHOTO_ID;
public static String MAIL_TYPE = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
public static String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
public Cursor getContactCursor(String stringQuery, String sortOrder) {
Log.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
Log.e(TAG, "ContactCursor search has started...");
Long t0 = System.currentTimeMillis();
Uri CONTENT_URI;
if (stringQuery == null)
CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;
else
CONTENT_URI = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, Uri.encode(stringQuery));
String[] PROJECTION = new String[]{
CONTACT_ID_URI,
NAME_URI,
PICTURE_URI
};
String SELECTION = NAME_URI + " NOT LIKE ?";
String[] SELECTION_ARGS = new String[]{"%" + "#" + "%"};
Cursor cursor = getContentResolver().query(CONTENT_URI, PROJECTION, SELECTION, SELECTION_ARGS, sortOrder);
Long t1 = System.currentTimeMillis();
Log.e(TAG, "ContactCursor finished in " + (t1 - t0) / 1000 + " secs");
Log.e(TAG, "ContactCursor found " + cursor.getCount() + " contacts");
Log.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
return cursor;
}
public Cursor getContactDetailsCursor() {
Log.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
Log.e(TAG, "ContactDetailsCursor search has started...");
Long t0 = System.currentTimeMillis();
String[] PROJECTION = new String[]{
DATA_CONTACT_ID_URI,
MIMETYPE_URI,
EMAIL_URI,
PHONE_URI
};
String SELECTION = NAME_URI + " NOT LIKE ?" + " AND " + "(" + MIMETYPE_URI + "=? " + " OR " + MIMETYPE_URI + "=? " + ")";
String[] SELECTION_ARGS = new String[]{"%" + "#" + "%", ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE};
Cursor cursor = getContentResolver().query(
ContactsContract.Data.CONTENT_URI,
PROJECTION,
SELECTION,
SELECTION_ARGS,
null);
Long t1 = System.currentTimeMillis();
Log.e(TAG, "ContactDetailsCursor finished in " + (t1 - t0) / 1000 + " secs");
Log.e(TAG, "ContactDetailsCursor found " + cursor.getCount() + " contacts");
Log.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
return cursor;
}
public List<ContactViewModel> getDetailedContactList(String queryString) {
/**
* First we fetch the contacts name and picture uri in alphabetical order for
* display purpose and store these data in HashMap.
*/
Cursor contactCursor = getContactCursor(queryString, NAME_URI);
if(contactCursor.getCount() == 0){
contactCursor.close();
return new ArrayList<>();
}
List<Integer> contactIds = new ArrayList<>();
if (contactCursor.moveToFirst()) {
do {
contactIds.add(contactCursor.getInt(contactCursor.getColumnIndex(CONTACT_ID_URI)));
} while (contactCursor.moveToNext());
}
HashMap<Integer, String> nameMap = new HashMap<>();
HashMap<Integer, String> pictureMap = new HashMap<>();
int idIdx = contactCursor.getColumnIndex(CONTACT_ID_URI);
int nameIdx = contactCursor.getColumnIndex(NAME_URI);
int pictureIdx = contactCursor.getColumnIndex(PICTURE_URI);
if (contactCursor.moveToFirst()) {
do {
nameMap.put(contactCursor.getInt(idIdx), contactCursor.getString(nameIdx));
pictureMap.put(contactCursor.getInt(idIdx), contactCursor.getString(pictureIdx));
} while (contactCursor.moveToNext());
}
/**
* Then we get the remaining contact information. Here email and phone
*/
Cursor detailsCursor = getContactDetailsCursor();
HashMap<Integer, String> emailMap = new HashMap<>();
HashMap<Integer, String> phoneMap = new HashMap<>();
idIdx = detailsCursor.getColumnIndex(DATA_CONTACT_ID_URI);
int mimeIdx = detailsCursor.getColumnIndex(MIMETYPE_URI);
int mailIdx = detailsCursor.getColumnIndex(EMAIL_URI);
int phoneIdx = detailsCursor.getColumnIndex(PHONE_URI);
String mailString;
String phoneString;
if (detailsCursor.moveToFirst()) {
do {
/**
* We forget all details which are not correlated with the contact list
*/
if (!contactIds.contains(detailsCursor.getInt(idIdx))) {
continue;
}
if(detailsCursor.getString(mimeIdx).equals(MAIL_TYPE)){
mailString = detailsCursor.getString(mailIdx);
/**
* We remove all double contact having the same email address
*/
if(!emailMap.containsValue(mailString.toLowerCase()))
emailMap.put(detailsCursor.getInt(idIdx), mailString.toLowerCase());
} else {
phoneString = detailsCursor.getString(phoneIdx);
phoneMap.put(detailsCursor.getInt(idIdx), phoneString);
}
} while (detailsCursor.moveToNext());
}
contactCursor.close();
detailsCursor.close();
/**
* Finally the contact list is build up
*/
List<ContactViewModel> contacts = new ArrayList<>();
Set<Integer> emailsKeySet = emailMap.keySet();
Set<Integer> phoneKeySet = phoneMap.keySet();
for (Integer key : contactIds) {
if( (!emailsKeySet.contains(key) && !phoneKeySet.contains(key))
|| (emailMap.get(key) == null && phoneMap.get(key) == null)
|| mContactDB.isContactExisted(key))
{
continue;
}
contacts.add(new ContactViewModel(key, nameMap.get(key), emailMap.get(key)));
}
return contacts;
}
Try below code to fetch contact number of specific person.
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(Phone.CONTENT_URI, null, Phone.DISPLAY_NAME + "=?", new String[]{contactName}, null);
if(cursor.getCount() > 0){
cursor.moveToFirst();
do {
String number = cursor.getString(mCursor.getColumnIndex(Phone.NUMBER));
}while (cursor.moveToNext() );
}
Android recommends using content resolvers and content providers to provide nicely packaged data between applications. You should probably not go messing around with the database itself, and it was clearly not designed with that in mind (as your experience demonstrates).
Instead, you should use the content resolver to query the Android ContactsContract to find what you need. There is a class called ContactsContract.Contacts that sounds like the entry point for what you need. Each record returned by a query to the class represents a single contact.
See the Content Providers Developer Guide for further details.
Related
Currently I have a query retrieves images from my bucket in MediaStore and it's in ascending order (the oldest posts appear first). How do I modify my query so that it displays the newest images first?
String[] PROJECTION_BUCKET = {
MediaStore.Images.ImageColumns.BUCKET_ID,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.DATA};
Uri images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
//Not sure how to modify query here
Cursor cur = getContentResolver().query(images, PROJECTION_BUCKET,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME + " like ? ",
new String[] {"%Download%"} , null);
Log.i("ListingImages"," query count=" + cur.getCount());
if (cur.moveToFirst()) {
String bucket;
String date;
String data;
int bucketColumn = cur.getColumnIndex(
MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
int dateColumn = cur.getColumnIndex(
MediaStore.Images.Media.DATE_TAKEN);
int dataColumn = cur.getColumnIndex(
MediaStore.Images.Media.DATA);
do {
bucket = cur.getString(bucketColumn);
date = cur.getString(dateColumn);
data = cur.getString(dataColumn);
Log.i("ListingImages", " bucket=" + bucket
+ " date_taken=" + date
+ " _data=" + data);
} while (cur.moveToNext());
}
You will have to use the last argument of the query() method which is the sortOrder.
So instead of null pass the column followed by DESC to sort descending:
Cursor cur = getContentResolver().query(
images,
PROJECTION_BUCKET,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME + " like ? ",
new String[] {"%Download%"},
MediaStore.Images.Media.DATE_TAKEN + " DESC"
);
I'm getting same contact three or two times in my app this happening with some contacts not with every contacts. In my app everything is working as expected but when clicking on show contact from my it's shows three time same contact but in mobile phone contact stored only one time. I tried everything from my side but not able to solve this can any body please help me. Or is there any alternative way for same.
Here is my code:-
#Override
protected Integer doInBackground(Void... params) {
try {
db = new WhooshhDB(myContext);
this.list = new ArrayList<>();
ContentResolver cr = myContext.getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, "UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ") ASC");
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));
ContactModel model = new ContactModel();
if (phoneNo.replaceAll("\\s", "").trim().length() > 7) {
model.name = name;
model.mobileNumber = phoneNo.replaceAll("\\s", "");
if (model.mobileNumber.contains("-")) {
model.mobileNumber = model.mobileNumber.replaceAll("-", "");
}
model.iconHexColor = AppConstant.getRandomSubscriptionHexColor(myContext);
if (!phoneNumber.equals(model.mobileNumber)) {
list.add(model);
}
}
Log.i("FetchContacts", "Name: " + name);
Log.i("FetchContacts", "Phone Number: " + phoneNo);
}
pCur.close();
}
}
}
if (cur != null) {
cur.close();
}
return AppConstant.SUCCESS;
} catch (Exception ex) {
return null;
}
}
You're printing those "FetchContacts" logs for per contact per phone, so if a contact has multiple phones stored for her you'll see it printed multiple times (even if it's the same phone number).
If you have an app like Whatsapp installed, then almost always you'll see all phone number duplicated for each contact causing those logs to be printed more then once per contact.
Also, that's a slow and painful way of getting contacts w/ phones.
Instead you can simply query directly over Phones.CONTENT_URI and get all phones in the DB, and map them out into contacts by Contact-ID:
Map<String, List<String>> contacts = new HashMap<String, List<String>>();
String[] projection = { Phone.CONTACT_ID, Phone.DISPLAY_NAME, Phone.NUMBER };
Cursor cur = cr.query(Phone.CONTENT_URI, projection, null, null, null);
while (cur != null && cur.moveToNext()) {
long id = cur.getLong(0); // contact ID
String name = cur.getString(1); // contact name
String data = cur.getString(2); // the actual info, e.g. +1-212-555-1234
Log.d(TAG, "got " + id + ", " + name + ", " + data);
// add info to existing list if this contact-id was already found, or create a new list in case it's new
String key = id + " - " + name;
List<String> infos;
if (contacts.containsKey(key)) {
infos = contacts.get(key);
} else {
infos = new ArrayList<String>();
contacts.put(key, infos);
}
infos.add(data);
}
// contacts will now contain a mapping from id+name to a list of phones.
// you can enforce uniqueness of phones while adding them to the list as well.
Get rid of
while (cur != null && cur.moveToNext()) {
Change it to
if(cur.moveToFirst()){
list.clear();
I want to create an update function which will change the entries of the columns (entire row) within the database based on the INTEGER PRIMARY KEY of the row - if at all possible.
I have a find function which will find and load the details of a database into an EditText field for editing/updating.
The find button (which calls the find handler), loads the contents into the EditText fields which can be altered/edited but when a button is pressed to update the edited entry, the entire database overwritten with the altered EditText entry.
Just to clarify, I want to change a single row containing the PORT, NAME and IP address within the database, update that single entry and put it back into the database without overwriting all my database entries- as is currently happening.
I have no way of seeing the database, in its raw unedited form, in the data/data/{package name}... folder as I am using an old Samsung phone (which doesn't contain the database.db for some unknown reason- stating the "run as: Could not set capabilities..." message) for debugging/running the app and the PC I am using does not have enough RAM to support a virtual device.
Any help, advise or examples will be greatly appreciated. I am very new to Android app development and java, so please forgive me if the problem is obvious or trivial.
This is how the Table is created:
public static final String TABLE_USER = "User";
public static final String COL_ID = "_id";
public static final String COLUMN_ID = "UserIP";
public static final String COLUMN_NAME = "UserName";
public static final String COLUMN_PORT = "UserPort";
public void onCreate(SQLiteDatabase db)
{
String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS " +
TABLE_USER + "(" + COL_ID + " INTEGER PRIMARY KEY, " +
COLUMN_PORT + " INTEGER, " + COLUMN_NAME
+ " TEXT, " + COLUMN_ID + " TEXT " + ")";
try
{
db.execSQL(CREATE_USER_TABLE);
}catch (SQLException e)
{
e.printStackTrace();
}
}
The find function is as follows:
public User findHandler(String username, String IP)
{
String query = "Select * FROM " + TABLE_USER + " WHERE " +
COLUMN_NAME + " = '" + username + "'" + " and " + COLUMN_ID + " =
'" + String.valueOf(IP) + "'";
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query, null);
User user = new User();
if (cursor.moveToFirst())
{
cursor.moveToFirst();
user.setUserPort(Integer.parseInt(cursor.getString(1)));
user.setUserName(cursor.getString(2));
user.setID(cursor.getString(3));
cursor.close();
//Log.d("Message1", msg);
}
else
{
user = null;
}
db.close();
return user;
}
In the MainActivity the find button press (onClick) is as follows:
btnfind.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
MyDBHandler dbHandler = new MyDBHandler(getApplicationContext());
User user = dbHandler.findHandler(username.getText().toString(),
userid.getText().toString());
if (user != null)
{
lst.setText(user.getID() + " " + user.getUserName()
+ " " + String.valueOf(user.getUserPort()));
//found_id =(user.getWhere());
// Load into the EditText for editing
userid.setText(user.getID());
username.setText(user.getUserName());
userport.setText(String.valueOf(user.getUserPort()));
}
else
{
lst.setText("No User Found");
}
}
});
The update function is as follows:
public boolean updateHandler(int Port, String username, String IP)
{
SQLiteDatabase db = this.getWritableDatabase();
ContentValues args = new ContentValues();
args.put(COLUMN_ID, IP);
args.put(COLUMN_NAME, username);
args.put(COLUMN_PORT, Port);
return db.update(TABLE_USER, args, COL_ID + " = _id " , null) >0;
}
In the MainActivity the update button press (onClick) is as follows:
btnupdate.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
MyDBHandler dbHandler = new
MyDBHandler(getApplicationContext());
if (userport.length() >= 1 && userid.length() >= 1 &&
username.length() >= 1)
{
userid.getText();
username.getText();
userport.getText();
boolean result =
dbHandler.updateHandler(Integer.parseInt(userport.getText().toString()),
username.getText().toString(), userid.getText().toString());
if (result)
{
dbHandler.updateHandler(Integer.parseInt(userport.getText().toString()),
username.getText().toString(), userid.getText().toString());
lst.setText("Record Updated.");
}else
{
lst.setText("No Match.");
}
}else
{
Toast.makeText(MainActivity.this, "Please enter a valid
user." , Toast.LENGTH_LONG).show();
}
}
});
Change COL_ID + " = _id " to COLUMN_ID + " = ?" and change the null argument to an array with the value of IP.
i am trying to add some items from database to my list but when i run the application i see that the list is empty.
this is what i did to add items to a list
car_size = dbHelper.getSize("cars"); // getSize is a method that count items in database
Random random = new Random();
int cars_random = random.nextInt(car_size);
List<String> myList = dbHelper.read_added_names("cars"); // and this line should add items from database to the list
if(myList!= null && myList.size() > 0) {
textView.setText(myList.get(truth_random));
}else {
Toast.makeText(activity.this, "" + myList.size(), Toast.LENGTH_SHORT).show();
} // here i can see that the size of my list is 0 and it's empty
I'm sure the methode that should read database (here i named it read_aded_names) works fine
public List<String> read_added_names (String subject){
String selectQuery;
List<String> list = new ArrayList<String>();
if (subject.equals("*")) {
selectQuery = "SELECT * FROM " + TABLE_NAME1;
}else {
selectQuery = "SELECT * FROM " + TABLE_NAME1 + " WHERE " + COLUMN_SUBJECT + " = '" + subject+" '";
}
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.moveToFirst()) {
do {
list.add(cursor.getString(1));
} while (cursor.moveToNext());
}
cursor.close();
db.close();
return list;
}
Query contains space after subject variable making it "cars " instead of "cars". This can be the issue.
I have the below function which I use to query the SQLite Db, put the retrieved records in to an array and return it back.
public String[][] getrecords(){
Log.i("SENDSERVER", "Get Records Called");
SQLiteDatabase sampleDB = this.getReadableDatabase();
Cursor c = sampleDB.rawQuery("SELECT id,welawa,lati,longi FROM " +
TABLE_LOCATIONS + " LIMIT 5", null);
String[][] aryDB = new String[5][4];
int i = 0;
if (c != null ) {
if (c.moveToFirst()) {
do {
String db_id = c.getString(c.getColumnIndex("id"));
String welawa = c.getString(c.getColumnIndex("welawa"));
String latitude = c.getString(c.getColumnIndex("lati"));
String longitude = c.getString(c.getColumnIndex("longi"));
aryDB[i][0] = db_id;
aryDB[i][1] = welawa;
aryDB[i][2] = latitude;
aryDB[i][3] = longitude;
Log.i("SENDSERVER", "Record Added"); //This doesn't get logged
i++;
}while (c.moveToNext());
}
}
Log.i("SENDSERVER", "Return Records");
return aryDB;
}
I try to retrieve the records and use them as below from my service class.
String aryDB[][] = dbh.getrecords();
Log.i("SENDSERVER", "GET DB RECORDS");
int i = 0;
int id = 0;
String welawa = "";
String lati = "";
String longi = "";
while(i < 5){
id = Integer.parseInt(aryDB[i][0]);
welawa = aryDB[i][1];
lati = aryDB[i][2];
longi = aryDB[i][3];
Log.i("SENDSERVERDB", id + " - " + welawa + " - " + lati + " - " + longi);
i++;
}
For some reason the array is not being returned.
My possible guesses are,
1. The defining of the function is wrong. My intention is to return the array.
2. I am getting the db connection / my retrieval code is wrong.
3. Some thing else that my noob brain can't comprehend.
Your help is greatly appreciated guys.
------EDIT
After adding some more logs the app crashes at
Cursor c = sampleDB.rawQuery("SELECT id,welawa,lati,longi FROM " +
TABLE_LOCATIONS + " LIMIT 5", null);
Any issue in my query?
It was a DB issue. Wrong ColumnName used.
The Cursor never returns null when there is no row which the requested conditions, just return a empty Cursor.
You code still has problem when the rawQuery returns 0 rows, cursor won't be null and do while loop will execute at least once.
So there will be problem on this line when the cursor is empty
c.getString(c.getColumnIndex("id"));
Change your code to this:
c.moveToFirst();
while (c.moveToNext())
{
String db_id = c.getString(c.getColumnIndex("id"));
String welawa = c.getString(c.getColumnIndex("welawa"));
String latitude = c.getString(c.getColumnIndex("lati"));
String longitude = c.getString(c.getColumnIndex("longi"));
aryDB[i][0] = db_id;
aryDB[i][1] = welawa;
aryDB[i][2] = latitude;
aryDB[i][3] = longitude;
Log.i("SENDSERVER", "Record Added"); //This doesn't get logged
i++;
}