What I want to do is to have a Service with more than one ContentObserver registered and check which ContentObserver triggers onChange() to do specific reactions. I dont know if I just have to include a if/else inside the onchange(), or Overwrite it to each ContentObserver, in either cases I wouldnt know exactly how to do it. Thanks in advance for any help.
public class SmsObserverService extends Service {
private String BABAS = "babas";
#Override
public void onCreate() {
Handler handler = new Handler();
this.getContentResolver().registerContentObserver(Uri.parse("content://sms/"), true, new SmsObserver(handler));
//Second observer
this.getContentResolver().registerContentObserver(Uri.parse("CallLog.Calls.CONTENT_URI"), true, new SmsObserver(handler));
}
#Override
public IBinder onBind(Intent intent) {
// TODO Put your code here
return null;
}
public class SmsObserver extends ContentObserver{
public SmsObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
//Where I should check somehow which ContentObserver triggers the onChange
//This code to the sms log stuff, the call log part will be included
//when I find out how to check whic observer trigerred the onChange
Uri uri = Uri.parse("content://sms/");
Cursor cursor = getApplicationContext().getContentResolver().query( uri, null, null, null, null);
cursor.moveToNext();
String body = cursor.getString(cursor.getColumnIndex("body"));
String add = cursor.getString(cursor.getColumnIndex("address"));
String time = cursor.getString(cursor.getColumnIndex("date"));
String protocol = cursor.getString(cursor.getColumnIndex("protocol"));
if(protocol == null){
Toast.makeText(getApplicationContext(), "Enviada para: " +add + ", Hora: "+time +" - "+body, Toast.LENGTH_SHORT).show();
Log.i(BABAS, "Enviada para: "+add +" " +"Time: "+time +" - "+body);
}else{
Toast.makeText(getApplicationContext(), "Recebida de: "+add + ", Hora: "+time +" - "+body, Toast.LENGTH_SHORT).show();
Log.i(BABAS, "Recebida de: "+add +" " +"Time: "+time +" - "+body);
}
}
}
}
I'd simply extend ContentObserver and add whatever I am missing there.
mObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
#Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
}
}
};
You can check uri with above method
Related
I am developing an app that notify the user when any SMS marked as read even if the app isn't running
I simply created a contentobserver and I registered it in a service
the problem is that the contentobserver runs if the new SMS inserted or deleted but when the SMS marked as read ( Update operation) it doesn't work
here is my service code
public class Smssendservice extends Service {
private static Timer timer = new Timer();
private Context ctx;
public IBinder onBind(Intent arg0)
{
return null;
}
public void onCreate()
{
super.onCreate();
ctx = this;
startService();
}
private void startService()
{
//timer.scheduleAtFixedRate(new mainTask(), 0, 5000);
Toast.makeText(getApplicationContext(), "Before Register", Toast.LENGTH_SHORT).show();
final Uri SMS_STATUS_URI = Uri.parse("content://sms");
SMSLogger sl= new SMSLogger();
SMSObserver smsSentObserver = new SMSObserver(sl, ctx);
getContentResolver().registerContentObserver(SMS_STATUS_URI, true, smsSentObserver);
Toast.makeText(getApplicationContext(), "After Register", Toast.LENGTH_SHORT).show();
}
}
I am registering my content observer in the service
here is the content observer code
public class SMSObserver extends ContentObserver
{
SMSLogger smsLogger;
Context context;
public SMSObserver(SMSLogger smsLogger, Context context) {
super(new Handler());
this.context=context;
this.smsLogger = smsLogger;
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
smsLogger.querySMS(context);
}
}
eventually here is the SMS logger that I show the TOAST if the SMS data changed
public class SMSLogger {
protected void querySMS(Context context) {
Uri uriSMS = Uri.parse("content://sms/");
Cursor cur = context.getContentResolver().query(uriSMS, null, null, null, null);
/* cur.moveToNext(); // this will make it point to the first record, which is the last SMS sent
String body = cur.getString(cur.getColumnIndex("body")); //content of sms
String add = cur.getString(cur.getColumnIndex("address")); //phone num
String time = cur.getString(cur.getColumnIndex("date")); //date
String protocol = cur.getString(cur.getColumnIndex("protocol")); //protocol*/
Toast.makeText(context, "Data Changed CHECK SMS" , Toast.LENGTH_SHORT).show();
/*logging action HERE...*/
}
}
it showed this message "Data Changed CHECK SMS" if new SMS inserted or SMS deleted but in case of update the toast doesnt appear. any clue ?
In your update method, check if the number of entries updated is more than 0.
If it is, do getContext().getContentResolver().notifyChange(uri, null); before you return the number of entries updated.
I want to listen for Contacts changes. How do I do that?
I've done it.
protected class MyContentObserver extends ContentObserver {
public MyContentObserver() {
super(null);
text.append("created at + " + new GregorianCalendar().getTime() + "\n");
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
text.append("Changed\n");
}
}
and also we need to register ContentObserver:
this.getApplicationContext()
.getContentResolver()
.registerContentObserver(Contacts.CONTENT_URI, true,
new MyContentObserver());
How to receive broadcast when a user sends SMS from his Android phone? I am creating an application which is taking track of sent SMS and calls. I am done with the calls part, please help me with the SMS. Note that sms are sent by the phone not any application.
----------//solution-----------
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(VIEW_RESOURCE_ID);
SendSmsObserver smsObeserver = (new SendSmsObserver(new Handler()));
ContentResolver contentResolver = this.getContentResolver();
contentResolver.registerContentObserver(Uri.parse("content://sms"),true, smsObeserver);
}
public class SendSmsObserver extends ContentObserver {
public SendSmsObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// save the message to the SD card here
Log.d("sent sms", "one text send");
}
}
I found the answer
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(VIEW_RESOURCE_ID);
SendSmsObserver smsObeserver = (new SendSmsObserver(new Handler()));
ContentResolver contentResolver = this.getContentResolver();
contentResolver.registerContentObserver(Uri.parse("content://sms"),true, smsObeserver);
}
public class SendSmsObserver extends ContentObserver {
public SendSmsObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// save the message to the SD card here
Log.d("sent sms", "one text send");
}
}
You could build on CallLog. The CallLog provider contains information about placed and received calls.
The Following code can work
Cursor c = null; try {
c = getContentResolver().query(CallLog.Calls.CONTENT_URI, null, null, null, null);
if (c != null && c.moveToFirst()) {
do {
int duration = c.getInt(c.getColumnIndex(CallLog.Calls.DURATION));
// do something with duration
} while (c.moveToNext());
} } finally {
if (c != null) {
c.close();
} }
--------------------------ADDED NEW SOLUTION------------------------
Have a look at:
http://groups.google.com/group/android-developers/browse_thread/thread/9bc7d7ba0229a1d2
and :
http://code.google.com/p/android/issues/detail?id=914
Basically, you can do it by registering a content observer on the SMS
message store.Try
this:
ContentResolver contentResolver = this.getContentResolver();
contentResolver.registerContentObserver(Uri.parse("content://sms"),true, smsObeserver);
I have defined the following service with an observer of messages sent. The problem is that when sending a message, I sense that is called 3 times onChange method of contentobserver. ¿Someone know tell me why?
Thanks
public class DSMSService extends Service {
private static final String CONTENT_SMS = "content://sms";
private class MyContentObserver extends ContentObserver {
ContentValues values = new ContentValues();
int threadId;
public MyContentObserver() {
super(null);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.v(TAG, "****************************** SMS change detected *************************************");
Log.v(TAG, "Notification on SMS observer");
// save the message to the SD card here
Uri uriSMSURI = Uri.parse("content://sms");
Cursor cur = getBaseContext().getContentResolver().query(uriSMSURI, null, null, null, null);
// this will make it point to the first record, which is the last SMS sent
cur.moveToNext();
String content = cur.getString(cur.getColumnIndex("body"));
Log.v(TAG, "content: " + content);
}
#Override
public boolean deliverSelfNotifications() {
return false;
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Log.v(TAG, "starting........");
MyContentObserver contentObserver = new MyContentObserver();
ContentResolver contentResolver = getBaseContext().getContentResolver();
contentResolver.registerContentObserver(Uri.parse("content://sms"),true, contentObserver);
DAO = new DAOaBlackList(getBaseContext());
}
#Override
public void onDestroy() {
Log.d(TAG, "stopping........");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "Received start id " + startId + ": " + intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
#Override
public void onStart(Intent intent, int startid) {
Log.v(TAG, "onStart........");
}
}
What you want to do is check for the _id of the last item in the content://sms/sent uri inside onChange. You need to store the previous _id (maybe in a static global variable) and compare it to the _id of the last item (cursor.moveToLast())of the cursor after you query for content://sms/sent. If the _id is the same, you can choose to ignore the call to onChange. This multiple calls to onChange I believe is due to the sms being moved from folder to folder during sending - outbox, sent items, some other "invisible folder" (which we can't know exactly what, as this particular feature REALLY REALLY needs proper documentation). As you cannot listen to a more specific Uri than content://sms/sent you'll have to implement this checking for _id everytime you want to detect an sms being sent.
If the previous _id is different from the one in your static global variable, then you have an sms being sent.
You have kept the Observer for the SMS database through URI. so whenever message is being send the database is updated and 3 of the column of that table is getting updated. so it will notify the observer for each of them. so it is being called for as many times as table data is updated.
I have following ContentObserver implementation for receiving and writing SMS, but it is called multiple times.
Code:
public class SMSObserverActivity extends Activity {
protected MyContentObserver observer = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String url = "content://mms-sms/";
Uri uri = Uri.parse(url);
observer = new MyContentObserver(new Handler());
getContentResolver().registerContentObserver(uri, true, observer);
}
#Override
protected void onDestroy(){
super.onDestroy();
getContentResolver().unregisterContentObserver(observer);
}
class MyContentObserver extends ContentObserver {
ContentValues values = new ContentValues();
Handler handler;
public MyContentObserver(Handler handler){
super(handler);
this.handler = handler;
}
#Override
public boolean deliverSelfNotifications(){
return false;
}
#Override
public void onChange(boolean arg0){
super.onChange(arg0);
Log.v("SMS", "Notification on SMS observer");
values.put("status", 5);
Message msg = new Message();
msg.obj = "xxxxxxxxxx";
int threadId = 0;
handler.sendMessage(msg);
Uri uriSMSURI = Uri.parse("content://sms/");
Cursor cur =
getContentResolver().query(uriSMSURI, null, null, null,
null);
cur.moveToNext();
Log.e("sms", cur.getString(4)+" "+cur.getString(11));
}
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.READ_SMS"></uses-permission>
<uses-permission android:name="android.permission.WRITE_SMS"></uses-permission>
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".SMSObserverActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Why is it called multiple times?
EDIT:
There was the idea that the problem is caused by the missing unregisterContentObserver, but it makes no difference.
This is occurring because you are registering your content observer for the entire SMS database. So your content observer gets notified each time a table entry in the database gets updated.
In this case when a message is sent for example around 7 tables entries get updated so your content observer gets notified 7 times.
Since I'm only interested if a message is sent I've changed to only observe the queued messages and this means my observer always gets notified exactly three times so I have implemented code to protect against that.
There are likely to be some other issues such as multi recipient or multi part messages but the basics work so far.
To avoid sending multiple sms by content observer try this
public class SmsObserver extends ContentObserver {
SharedPreferences trackMeData;
private Context context;
private static int initialPos;
private static final String TAG = "SMSContentObserver";
private static final Uri uriSMS = Uri.parse("content://sms/sent");
public SmsObserver(Handler handler, Context ctx) {
super(handler);
context = ctx;
trackMeData = context.getSharedPreferences("LockedSIM", 0);
initialPos = getLastMsgId();
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
queryLastSentSMS();
}
public int getLastMsgId() {
Cursor cur = context.getContentResolver().query(uriSMS, null, null, null, null);
cur.moveToFirst();
int lastMsgId = cur.getInt(cur.getColumnIndex("_id"));
Log.i(TAG, "Last sent message id: " + String.valueOf(lastMsgId));
return lastMsgId;
}
protected void queryLastSentSMS() {
new Thread(new Runnable() {
#Override
public void run() {
Cursor cur =
context.getContentResolver().query(uriSMS, null, null, null, null);
if (cur.moveToNext()) {
try {
String body = cur.getString(cur.getColumnIndex("body"));
if (initialPos != getLastMsgId()) {
String receiver = cur.getString(cur.getColumnIndex("address"));
Log.i("account", myDeviceId);
Log.i("date", day + "-" + month + "-" + year + " "
+ hour + ":" + minute + ":" + seconde);
Log.i("sender", myTelephoneNumber);
Log.i("receiver", receiver );
// Then, set initialPos to the current position.
initialPos = getLastMsgId();
sendsmstoph(receiver, body);
}
} catch (Exception e) {
// Treat exception here
}
}
cur.close();
}
}).start();
}
If you want to have your observer enabled only when the activity is in active state, I advise you to move registerContentObserver() and unregisterContentObserver() to methods onResume() and onPause() respectively. onDestroy() may not be called if your application exits, but onPause() is guaranteed to be.