I'm trying to write an Android service app which intercepts failed outgoing sms messages (due to service unavailable) to store them and try to resend them as soon as the phone service is back. I almost got it to work, but i have this problem, which is not so simple to explain but i'll try, hoping you understand:
I have set a ContentObserver on the URI content://sms even if what i'm interested on is content://sms/failure because if i set it on this last URI the onChange method doesn't get triggered, and i don't know why. Provided successfully sent and received messages don't bother me since, in the onChange method in the observer, I read just the content in content://sms/failed, here is the real problem: onChange gets triggered also on DELETE events, of course, which occur, for example, in the moment a previously failed message is succesfully sent. This is bad for my application because if I can't distinguish between a delete event and an add event i don't know if i have to add or not the first sms in the failed message queue into the "resend" list. So, my question is: is there a way to distinguish between delete an add events with a content observer?
PS: It would be nice to understand why a ContentObserver on content://sms/failed doesn't work.
PPS: I have another minor issue: i'm resending the messages using the SmsManager, which works fine, the only problem is i can only set the destination number and the body of the message but not the ID of the message, so when i resent an sms the system think it's just a new sms and not an old one being resent (and so the old failed messages remains in the queue and doesn't get removed by the system).
...is there a way to distinguish between delete an add events with a content observer?
Query the URI passed into the onChange() method. If the Cursor is empty - i.e., getCount() returns 0 - then it was a deletion. For example:
public void onChange(boolean selfChange, Uri uri)
{
boolean deletion = false;
Cursor cursor = getContentResolver().query(uri, null, null,
null, null);
if(cursor != null)
deletion = cursor.getCount() == 0;
else
return;
...
}
It would be nice to understand why a ContentObserver on content://sms/failed doesn't work.
That's just how the SMS ContentProvider is implemented. You can't get any more specific than content://sms.
I have another minor issue...
The ID for a message is assigned by the ContentProvider, and the soonest your app can know its value is in the onChange() method. You have no control over the assignment of IDs, so your app will have to track failed message IDs itself, and delete the appropriate message upon a successful send.
Related
In my application, I am offering video and voice calls between users. I'm using Firebase Realtime database and FCM as well to do so.
Here is the data that my notification is delivering:
data: {
channel_id: channel_id,
user_id: user_id
}
In my FirebaseMessagingService class, I retrieve the data and open the Video/Call activity like so:
if(remoteMessage.getData().size > 0) {
Intent intent = new Intent(this, VideoCallActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("channel_id", remoteMessage.getData().get("channel_id"));
intent.putExtra("user_id", remoteMessage.getData().get("user_id"));
getApplicationContext().startActivity(intent);
}
Everything is working perfectly even when the application is in background. However, I'm facing this issue:
If an user didn't have internet connection when he was called, when he connects to internet his phone will automatically start the VideoCallActivity even if the call has been aborted (by the caller) and the notification removed from Firebase real time database.
So, please is there a way to cancel sent notifications, or know the delivery status of those notifications, or another way around to do so? That will allow me to add the list of missed calls for users.
Thanks!
In a scenario like this, I know of two options:
Send another FCM message when the call is cancelled
Only use FCM to send a tickle
Send another FCM message when the call is cancelled
One of the properties you can give an FCM message is an collapse_key. When you send a message with a collapse_key, that message replaces any previous message with the same collapse_key value.
You can use this to send a "nope, forget about it" message, when the user cancels the call.
Only use FCM to send a tickle
Alternatively you can have the FCM message just be a so-called tickle: an almost empty message, that just tells the app to wake up and go check its queue of incoming messages in the database.
That way: if the call is still in the database, the app can pick it up. But if there is no call in the database, it can just do nothing.
You'll want to do this type of database interaction in a service, so that it can run when the app is backgrounded.
Giving an expiration time is feasible as suggested in the documentation
Check Here
Setting the lifespan of a message
On Android and Web/JavaScript, you can specify the maximum lifespan of a message. The value must be a duration from 0 to 2,419,200 seconds (28 days), and it corresponds to the maximum period of time for which FCM stores and attempts to deliver the message. Requests that don't contain this field default to the maximum period of four weeks.
Here are some possible uses for this feature:
Video chat incoming calls
Expiring invitation events
Calendar events
I am most concerned about Performance issue and don't want users to wait for progress.
I have a chatActivity, where i show a ListView.
Here i send a chatMessage
Chats chat = new Chats(chatBox.getText().toString(),Chats.TYPE_MINE, dt.format(now));
chat.personId = chatee.getMyId();
chat.isDelievered = Chats.DELIEVERED_NONE;
chats.add(chat);
mAdapter.notifyDataSetChanged();
Notice that Chat delievery is set to NONE Right now. So basically the message is being added to the Chat List even its not delivered yet.
Now on back thread here is what's happening
It takes few seconds to send message where i do this
boolean bool = sendMessage(m);
if (bool)
chatee.isDelievered = Chats.DELIEVERED_DONE; (MESSAGE SENT)
if (chatee.isDelievered == Chats.DELIEVERED_DONE)
{
app.mDbHelper.saveMessage(chatee); // SAVING TO DATABASE
Intent i = new Intent(Constants.REFRESH_NOTIF).putExtra("refresh",Constants.REFRESH_NOTIF);
context.sendBroadcast(i);
}
It will send a broadcast to the activity.
Now here is the problem.
Broadcast call this function
public void callUIMethodForRefresh(Intent intent)
{
String ref = intent.getStringExtra("refresh");
if (ref == null)
{
}
else if (ref.equals(Constants.REFRESH_NOTIF))
{
}
}
Here i am confused of how can I reset that previous Chat object added to my List.
Points to be noted , i can be sending messages at a very fast speed and the refresh could be called for an old message whereas a new message is already typed.
ONE way is i make a For loop and check for all the "ChatList" array for the message sent and then replace its delivery notice, but again this is very low performance incase i have 1000+ objects in the list.
Is there any way, i can attach the sqlite database with my listView adapter that automatically detects the changes and reset the listView etc and etc?
What could be the best strategies here to avoid performance issues.
I would suggest looking into ContentProviders and Loaders (specifically a CusorLoader). Combining these with a CursorAdapter, you can use the ContentProvider which inserts/deletes/updates your sqlite database and notifies your loader to reload it's dataset and update the CursorAdapter/ListView.
I have built an appWidget which update it's unread sms count for specific contact when a new sms comes in, for that I've registered a contentObserver to monitor incoming sms.
The mySMSObserver class calls the method below for getting the number of unread messages for specific contact, by his ID.
So far so good, the problem is that the query below works fine on the emulator, but when I try that on my android device it allways return 0 unread messages (as shown by Toast).
private static String updateUnreadSMS(String contactID, Context context) {
ContentResolver cr=context.getContentResolver();
//final Uri SMS_INBOX=(Uri.parse("content://mms-sms/conversations"));
//final Uri SMS_INBOX=(Uri.parse("content://mms/inbox"));
final Uri SMS_INBOX=Uri.parse("content://sms/inbox");
Cursor c = cr.query(SMS_INBOX, null, "read = 0 AND person=?", new String[]{contactID}, null);
// get number of unread sms messages
int unreadMessagesCount = c.getCount();
Log.d("DEBUG", "UnreadSMS: "+unreadMessagesCount);
Toast.makeText(context, "updateUnreadSMS "+unreadMessagesCount, Toast.LENGTH_LONG).show();
c.deactivate();
return String.valueOf(unreadMessagesCount);
}
Is there different query's needed for different devices?
how do I write the same query for "content://mms/inbox"?, Because "person=?" is an illegal field for that query.
Will be glad for your help and advice for solving this problem :)
Accessing the databases directly might not be the best idea. As locations and data structure might change with different Android versions (It did so with the Calendar storage from 7 => 8). Nevertheless check if your device location and database structure is the same.
For MMS you might take a peek at PduParser used to parse MMS messages and other classes in this folder.
I know that the SMS content provider is not part of the public API (at least not documented), but if I understand correctly it's still possible to use many of the SMS features as long as you know how to use the API(?).
E.g it's pretty straightforward to insert an SMS into your inbox:
ContentValues values = new ContentValues();
values.put("address", "+457014921911");
contentResolver.insert(Uri.parse("content://sms"), values);
Unfortunately this does not trigger the standard "new-SMS-in-your-inbox" notification. Is it possible to trigger this manually?
Edit: AFAIK the "standard mail application (Messaging)" in Android is listening for incoming SMSes using the android.permission.RECEIVE_SMS permission. And then, when a new SMS has arrived, a status bar notification is inserted with a "special" notification id.
So one solution to my problem (stated above) could be to find, and send the correct broadcast intent; something like "NEW SMS HAS ARRIVED"-intent.
Edit: Downloaded a third party messaging application (chompsms) from Android market. This application satisfies my needs better. When i execute the code above the chompsms notice the new sms and shows the "standard status bar notification". So I would say that the standard Android Messaging application is not detecting sms properly? Or am I wrong?
Unfortunately the code responsible for these notifications is hidden in the messaging application. The class MessagingNotification has a static method updateAllNotifications that you could call using a PathClassLoader and reflection:
PathClassLoader c = new PathClassLoader("/system/app/Mms.apk", getClassLoader());
Class.forName("com.android.mms.util.ContactInfoCache", true, c)
.getMethod("init", Context.class).invoke(null, context);
Class.forName("com.android.mms.transaction.MessagingNotification", true, c)
.getMethod("updateAllNotifications", Context.class).invoke(null, context);
This is obviously a very bad idea for several reasons but I can't think of another way to do what you described.
Could you trigger a PUSH notification after the SMS?
Thread: Does Android support near real time push notification?
Maybe you should replace
content://sms
with
content://sms/inbox
How can I register for SMS database changes?
I tried:
mCursor = mActivity.getContentResolver().query(Sms.CONTENT_URI, new String[] {
Sms.ADDRESS
}, null, null, null);
mCursor.registerDataSetObserver(mydataSetObserver);
where mydataSetObserver is implemented like this:
private class MyDataSetObserver extends DataSetObserver {
public void onChanged() {
System.out.println ("1");
}
public void onInvalidated() {
System.out.println ("2");
}
}
But when I tried sending a SMS message in the emulator,
MyDataSetObserver never get called.
Can you please tell me why?
Thank you.
It sounds like all you are trying to do is have the ability to make changes to the SMS database on the device.
The way I have done it in the past is by using tags in the AndroidManifest.xml. The application I made needed to use the READ_SMS permission as well as the READ_CONTACTS permission, however gaining permission for writing to the database would be done in the same way.
I defined these desired permissions in the AndroidManifest.xml file with the following tag:
Included in the list of permissions you can use is WRITE_SMS, which should give you the desired capability.
Please note: because I am a new user, StackOverflow would only let me post one hyperlink for this post, I tried including alot more information however was unable to do so. Please go to the android developer website and search for the AndroidManifest.xml file and see more info if need be.
DataSetObservers only observe DataSetObservables they are registered with. Your MyDataSetObserver is registered with your mCursor and will be notified whenever mCursor changes (e.g. after requery) but not when the content is written by another process (like the Messaging application).
Unfortunately there is currently no good way to listen for the event of sent text messages, the best alternative seems to be polling content://sms/sent, potentially using a ContentObserver.
This question is related.