I have an sqlite database that contains all the sms messages of the user, however when you start the app I need to get all the sms's that I might not have if the user decides to use a different sms app inbetween.
I'm looking for the best way to sync my database with the base android content provider.
at the moment I do an Insert or Ignore for each sms like so :
insert or ignore into sms (smsID, smsCONID, smsMSG, smsNUM, smsREAD, smsTIMESTAMP, smsTYPE, smsSHORTMSG) values (?,?,?,?,?,?,?,?);
I load the inbox and sent messages in separate asynctasks in my splash screen activity
this takes about 10s or so.
my doInBackground :
#Override
protected Void doInBackground(Void... arg0) {
int thread_id = 0;
int _id = 0;
int read;
String number;
boolean first = true;
String msg;
long timestamp;
/**
* RECUPERATION DES MESSAGES RECUS
*/
// PREPARE CURSOR
cursorInbox.moveToFirst();
while (cursorInbox.moveToNext()) {
if(first){
cursorInbox.moveToFirst();
first = false;
}
// RECUPERE THREAD_ID + ID_SMS + NUMERO DU CONTACT
thread_id = cursorInbox.getInt(cursorInbox.getColumnIndexOrThrow("thread_id"));
_id = cursorInbox.getInt(cursorInbox.getColumnIndexOrThrow("_id"));
number = cursorInbox.getString(cursorInbox.getColumnIndexOrThrow("address"));
msg = cursorInbox.getString(cursorInbox.getColumnIndexOrThrow("body"));
read = cursorInbox.getInt(cursorInbox.getColumnIndexOrThrow("read"));
timestamp = cursorInbox.getLong(cursorInbox.getColumnIndexOrThrow("date"));
// CREER LE SMS
dataManip.insert(_id, thread_id, msg, number, read, timestamp, "received",ContactsFactory.getMessageShort(msg));
i++;
publishProgress(i);
}
Log.d(TAG,"LoadActivity - Inbox Loaded");
return null;
}
Ideas for impovement?
How about registering a ContentObserver in a separate Service to listen to changes in the Android SMS provider? I'm not sure if it notifies observers on changes. But if it does, your database will always be in sync. But don't depend solely on this, as the service may be terminated at any time due to various reasons.
For the actual sync, ensure that the "_id" column in your table is the PRIMARY KEY. Query the content provider sorted by "_id". Query your own table sorted by "_id" and read all ids. Now iterate through the two sorted lists, inserting the items missing in yours and deleting the ones missing the content provider. You do want to also remove messages that were deleted using the other SMS app, don't you? The "insert or ignore" statement won't delete the missing rows for you.
if your database is indexed, than remove/disable the indexes: the inserts will be faster. Rebuild the indexes later in a new thread/task.
solution 2: don't write to database in first step just in memory( in a collection) later you will do the writings.
How about comparing the timestamps of each of the SMSes in the inbox with the latest timestamp in your database, and then only insert SMSes with a newer timestamp.
If you also start backwards, with the last SMS in the inbox, then you can break out of your loop as soon as you get to the first SMS older than your newest SMS.
Related
I have spent some time researching possible answers on here for the problem as well as referring to the SQLite documentation and tutorials but I still cannot retrieve the specific rows from an SQLite database in an Android App I am writing.
I have a database of messages which I have retrieved from Twitter using the Twitter4j libraries and have stored then in the database, returning all the values and displaying them in a layout is working perfectly but when I try and get a specific chat thread it still returns all the values rather than just messages between the app's user and a specified contact.
I'm familiar with how logic operators work as I am part way through a Degree and have spent two years working with C++, however SQL is new to me.
It could be a case that I cant see the wood for the trees with this but below is the WHERE argument I am passing to the database query() method.
//Values to replace '?' in whereClause
String[] userArgs new String[] { senderName, recipientName };
//Argument to be passed to WHERE in query method
String whereClause = "(user_screen=? AND recipient_screen=?) OR (recipient_screen=? AND user_screen=?)"
The outcome I am trying to achieve is that any messages between the sender and recipient are shown without other messages from the database. The column headings in the database are below:
ID
MessageContent
Recipient
Sender
Time
Profile Image
I think that you want :-
String[] userArgs new String[] { senderName, recipientName, senderName, recipientName };
That is the WHERE clause has 4 parameters to be bound rather than the 2 parameters.
I've implemented a contacts app, and I would like my application's contact names to be displayed in the device's call log history (Phone app) in case I receive/make a call to these numbers. How could I achieve that?
The CallLog.Calls table contains fields for caching names, because these are cached names, they're not expected to always be true, and are refreshed from time to time.
Usually, in most Phone/Call-log apps, when you open the call-log it'll display the calls list along with their cached names stored in the Calls table, and then spin up a background service that refreshes cached names, adding names to numbers recently saved as contacts, or updating names that had recently changed.
So if your app stored some number from the call log as a contact, if you then launch the call log app you should see the updated name appearing within a second or two.
If you want to store that name programatically in your code, you can do that easily:
String someNumber = "+12125551234";
String aName = "Jane Addams";
int numberType = Phone.TYPE_MOBILE; // see https://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.Phone#column-aliases
final ContentValues values = new ContentValues(2);
values.put(Calls.CACHED_NAME, aName);
values.put(Calls.CACHED_NUMBER_TYPE, numberType);
// on Lollipop+ device, you can also set Calls.CACHED_LOOKUP_URI and Calls.CACHED_FORMATTED_NUMBER
getContentResolver().update(Calls.CONTENT_URI, values, Calls.NUMBER + "='" + someNumber + "'", null);
Thank you #PedroHawk. I found the answer in the link you provided. More specifically, I will create an Account of my app in the Device Accounts and then use a SyncAdapter to sync the contact data from my web service to the ContactsProvider of the device.
can anyone explain what is this items come from sms details in android?
_id = sms id
thread_id = sms thread id
address = number
person = ?
date = date
protocol =?
read = 0 not read ,1 read
status = ?
type = ?
reply_path_present =?
subject = ?
body = sms body
service_center=?
locked=?
error_code=?
seen=?
Those look like the columns of android.provider.Telephony.TextBasedSmsColumns, which are base columns for database tables that contain text-based sms messages. If you are asking where the actual data would come from to populate the rows, that data would be present in the SMS message PDU and would be stored in the Sms.Inbox or Sms.Outbox etc.
locked means that the message cannot be deleted automatically or in a batch operation.
type likely refers to one of the MESSAGE_TYPE_XXX constants.
for status there is STATUS_COMPLETE etc.
Hi am syncing my database with server for any incremental values on click of a button. Below is the code which checks all the values and inserts if the data is missing but from android perspective is there any optimized technique to do the sync
//new fields
public void updatenewfileds(ArrayList<String> s) {
ArrayList<String> da = new ArrayList<String>();
try {
String manu = "select MANUFACTURERID from ManufacturerDesc";
Cursor cc = mDb.rawQuery(manu,null);
Log.d("Cursor count", "Count =" + cc.getCount());
if (cc != null && cc.getCount() > 0) {
if (cc.moveToFirst());
do {
da.add(cc.getString(cc.getColumnIndex("MANUFACTURERID")));
System.out.println("here got all alreday avilable ids"+ cc.getString(cc.getColumnIndex("MANUFACTURERID")));
} while (cc.moveToNext());
cc.close();
} else {
cc.close();
}
// need to add only missing data
for(int i = 0; i<da.size(); i++){
boolean flag = false;
System.out.println(flag);
for(int j=0; j<i; j++){
if(da.get(i).equals(s.get(i*2))){
flag = true;
break;
}
}
if(flag == false){
String sql = "insert into ManufacturerDesc values('"+ s.get(i*2)+"','"+ s.get(i*2+1)+"');";
System.out.println("item inserted into db"+ s.get(i*2) +"******" + s.get(i*2+1) );
mDb.execSQL(sql);
}
}
} catch (SQLException mSQLException) {
Log.e(TAG, "getTestData >>" + mSQLException.toString());
throw mSQLException;
}
}
This would be my suggestion, the [] are just to emphasize, as I might get back to it:
Design your Android database tables like: { _id, [server_id], .. your data .. }
On all your tables on the server add a [time_changed] timestamp.
Whenever your device gets synced with the server, the server should additionally send a last sync timestamp e.g. System.currentTimeMilliseconds() (letting the server do this to avoid relying on synced clocks). This timestamp is stored on the android device and used whenever requesting a new sync.
When the server receives a sync request the stored last sync timestamp is yet again handed to the server from the device. Now a simple query can extract all the relevant added data since the timestamp (minus some constant to ensure you get everything). For example SELECT * FROM Mydata WHERE (time_changed > (last_sync-5000)); 5000 being 5 seconds.
Now as you receive data from the server, remember to add the [server_id], which is just the autoincremented _id from the server. This enables you to deduce whether some of thee received rows are known (which is likely with the minus 5 seconds above).
The deduction is a simple query on the device e.g: Do I already have a row with [server_id], if not we add it, if yes then skip it.
With this method you avoid to send more and more information over time, as you only send the rows that are changed after the last sync (plus a bit more).
If you edit rows on your server, simply update time_changed again to reflect the edit . Then it will automatically be included and overwritten on the device during the next sync.
If you plan on doing a lot of database operations on the android device, I would suggest trying MotoDev, it can be plugged in to eclipse and has some nice database features. Including a database perspective and automatic generation of ContentProviders (nice classes to make database operations simple).
Giving a full explanation or guide to do all this is way out of the scope of this answer. It should merely give you an idea as to how it can be done, and if you wish to improve your syncing process, you now have some guidance.
Regarding mechanism as autoincrement and timestamp on a database, there is plenty of examples to find on the interwebz.
Happy coding :)
I have created a small content provider (which I only use to query a db table and fetch some value). I have confirmed that the provider works fine.
The table values get updated on a regular basis through normal sql insertions(and not through anycontent provider)
Whenever an insert/update or delete occurs through a normal sqlite operation as mentioned above, I need to notify the content resolvers which was written to communicate with the content provider just for quering database and fetch some values.
Is this, possible? If yes what need to be done?
Any help is appreciated.
EDITED to include my DB insert method:
public void insert(ArrayList<Integer> profileValues, String tableName){
String duplicationCheck = " WHERE NOT EXISTS (SELECT 1 FROM profiles WHERE devID = "+"'"+profileValues.get(0)+"'"+");";
if(tableName.equals(PROFILES_TABLE)){
profilesDB.execSQL("INSERT INTO " +
PROFILES_TABLE +
"(devID,alert,findme,proximity,timep,conn_status) SELECT "+"'"+profileValues.get(0)+"'"+","+profileValues.get(1)+","+profileValues.get(2)+","+profileValues.get(3)+","+profileValues.get(4)+","+profileValues.get(5)+duplicationCheck);
}
getContentResolver().notifyChange(MY_CONTENT_URI, null);
}
Any time you manually update the underlying database and need to notify registered ContentObserver instances, call notifyChange() on the ContentResolver present for the Context.
So, for instance, let's say you had registered a ContentObserver with the constant value MY_CONTENT_URI:
//Insert, update, or delete data in the database
//If you are within a Context, like Activity or Service
getContentResolver().notifyChange(MY_CONTENT_URI, null);
//Otherwise pass in a Context you can call on
context.getContentResolver().notifyChange(MY_CONTENT_URI, null);
All registered observers for the particular Uri you pass will be notified.