I have an auto reply sms Android application I built and I don't want the auto reply (sent sms) to show in the default messaging app. I have searched and searched and couldn't find an answer. Is there a way to bypass writing the sent sms into the default messaging app?
Here my BroadcastReciever I am using to get the data and send out the message
public class SmsReceiver extends BroadcastReceiver {
ParseUser user = ParseUser.getCurrentUser();
// Auto reply message composed of the current reply and url from that business
String msg = user.getString("myCurrentReply") + " " + user.getString("couponUrlChosen");
List smsFromList = user.getList("smsFrom");
String userName = (String) user.get("username");
#Override
public void onReceive(final Context context, Intent intent) {
Bundle bundle = intent.getExtras();
Object messages[] = (Object[]) bundle.get("pdus");
SmsMessage smsMessage[] = new SmsMessage[messages.length];
for (int n = 0; n < messages.length; n++) {
smsMessage[n] = SmsMessage.createFromPdu((byte[]) messages[n]);
}
final String pno = smsMessage[0].getOriginatingAddress();
user.put("lastSmsFrom", pno);
user.saveInBackground();
// show first message
Toast toast = Toast.makeText(context, "Received SMS: " + smsMessage[0].getMessageBody(), Toast.LENGTH_LONG);
toast.show();
// Check Phone Number from SMS Received against Array in User Row
ParseQuery<ParseObject> query = ParseQuery.getQuery("_User");
Log.d("Username: ", userName);
query.whereEqualTo("username", userName);
query.whereContainedIn("lastSmsFrom", smsFromList);
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> smsList, ParseException e) {
if (e == null) {
Log.d("Errors", "none");
if (smsList.size() == 0) {
// Send SMS
sendSms(pno, msg);
// Add Phone number to smsFrom in currentUsers Row
user.addUnique("smsFrom", pno);
// Save Phone Number in Array
user.saveInBackground();
Log.d("List size: ", " " + smsList.size());
}
} else {
Log.d("Error Message: ",
e.getMessage());
}
Log.d("Already sent to this number today. ", " " + smsList.size());
}
});
}
private void sendSms(String phonenumber, String message) {
SmsManager manager = SmsManager.getDefault();
manager.sendTextMessage(phonenumber, null, message, null, null);
}
}
Prior to KitKat, SMS sent using SmsManager require the app sending the message to insert it into the Provider, so it would just be a matter of omitting that.
Starting with KitKat, any app that is not the default SMS app and uses SmsManager to send messages will have the messages automatically written to the Provider for it by the system. There's no way to prevent this, and, furthermore, the app won't be able to delete those messages, either, as it won't have write access to the Provider.*
The app that is the default SMS app is responsible for writing its outgoing messages, so it would be able to omit that step. The system does no automatic writes for the default SMS app.
* There is a security hole in 4.4 only, by which a non-default app can gain write access to the Provider. It is detailed in my answer here, but it will not work in versions after KitKat.
Related
I need the ability to both send and delete SMSes from my app. The code I'm using is shown. Sending an SMS does not require my app to be the default SMS app, however deleting does.
What is weird is that the code to send the SMS does work when the app is not default, but not when it is (the code to delete SMSes works the other way around, however this is expected).
I assume that this problem arises because even though another application may be sending the SMSes, only the default SMS app can write to the SMS provider, so I need to write to them manually. However, when having my app as default, other apps can still send SMSes using the same code provided, which is strange.
I have also tried writing to the SMS providers manually, specifically to content://sms/outbox and tocontent://sms/sent, none of which have worked. Although, the latter does display the message in the preinstalled SMS app.
public static void sendSMS(String phoneNo, String msg, Context context) {
try {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNo, null, msg, null, null);
Toast.makeText(context, "Message Sent",
Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(context,e.getMessage().toString(),
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
public static void deleteSMS(Context context, String message, String number) {
try {
Log.i(MainActivity.TAG, "delteSMS: try statement");
Uri uriSms = Uri.parse("content://sms/sent");
Cursor c = context.getContentResolver().query(uriSms,
new String[] { "_id", "thread_id", "address",
"person", "date", "body" }, null, null, null);
if (c != null && c.moveToFirst()) {
do {
long id = c.getLong(0);
long threadId = c.getLong(1);
String address = c.getString(2);
String body = c.getString(5);
// Log.i(MainActivity.TAG, "Found SMS: " + body);
if (message.equals(body) && address.equals(number)) {
Log.i(MainActivity.TAG, "Deleting sms with" + threadId);
context.getContentResolver().delete(
Uri.parse("content://sms/" + id), null, null);
}
} while (c.moveToNext());
}
} catch (Exception e) {
Log.i(MainActivity.TAG, "Could not delete SMS from inbox: " + e.getMessage());
}
}
TL;DR
I need to know how to send SMSes from an application which is set as default, since the usual procedure does not work.
Thanks
I'm trying to get my device registration ID with GCM. My code to do so is contained within an AsyncTask which is called from my main thread.
Main code
try
{
String deviceId = new Gcm().execute(this.activity).get(5, TimeUnit.SECONDS);
Log.i("Login", "User device id returned as " + deviceId);
return deviceId;
}
catch (Exception e)
{
Log.e("Login", "Exception", e);
}
GCM Class
public class Gcm extends AsyncTask<Activity,Void,String>
{
#Override
protected String doInBackground(Activity... params)
{
Log.i("GCM", "Attempting to get device id");
Activity activity = params[0];
try
{
Log.i("GCM", "Getting GCM instance");
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(activity.getApplicationContext());
Log.i("GCM", "Registering with GCM");
String regId = gcm.register(PROJECT_NUMBER);
Log.i("GCM", "Device registered, registration ID=" + regId);
return regId;
}
catch (IOException e)
{
throw new IllegalStateException(e);
}
}
}
And here is my log dump
07-28 13:07:39.093 I/GCM﹕ Attempting to get device id
07-28 13:07:39.093 I/GCM﹕ Getting GCM instance
07-28 13:07:39.093 I/GCM﹕ Registering with GCM
07-28 13:07:44.103 E/Login﹕ Exception
java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask.get(FutureTask.java:176)
at android.os.AsyncTask.get(AsyncTask.java:507)
I/GCM﹕ Device registered, registration ID=XXXXXX
So for some reason, calling gcm.register() is blocking until my timeout exception is hit. Does anyone have any idea why that might be happening?
The reason is cause you're executing gcm with
.get(5, TimeUnit.SECONDS);
This call blocks the thread for 5 seconds, however due to different reasons like unstable network connection the registration process can take more than 5 seconds. It is not the best approach to do what you want.
Take a look at this example, taken from official GCM demo:
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regid;
// You should send the registration ID to your server over HTTP, so it
// can use GCM/HTTP or CCS to send messages to your app.
sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device will send
// upstream messages to a server that echo back the message using the
// 'from' address in the message.
// Persist the regID - no need to register again.
storeRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the user to click a button again, or perform
// exponential back-off.
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
Incorporate this example and everything should work.
Using an AsyncTask like that doesn't make sense. AsyncTasks are used to avoid blocking the thread, but by using the blocking get() call you are blocking the thread anyway. You may as well call register() directly in that case.
You are getting a timeout because you're blocking for 5s, but GCM is taking longer than 5s to register. This could be due to bad network conditions as it can take a while for a network request to time out. Or maybe it is just taking more than 5s to get the registration ID.
I'm building a parse app and I want the user to be able to set the range in which they receive notifications from other users in. I know you can choose how far you want to send the notification with something like this
// Find users near a given location
ParseQuery userQuery = ParseUser.getQuery();
userQuery.whereWithinMiles("location", stadiumLocation, 1.0)
// Find devices associated with these users
ParseQuery pushQuery = ParseInstallation.getQuery();
pushQuery.whereMatchesQuery("user", userQuery);
// Send push notification to query
ParsePush push = new ParsePush();
push.setQuery(pushQuery); // Set our Installation query
push.setMessage("Free hotdogs at the Parse concession stand!");
push.sendInBackground();
But if the user receiving these notifications has their receiving range set as .5 miles or something like that I dont wan't them to receive it. Is there anyway to make this happen?
Ex: User A has a radius set to 1 mile and user B sends the message to everyone within 2 miles and User A is 1.5 miles away from User B so I don't want him to receive it.
Ex: User A has a radius set to 1 mile and user B sends message to everyone within 2 miles and User A is .5 miles away from User B he will receive that message and notification.
The second example is easy to implement its the first one that I'm not sure how to do.
Your code is working, but you need to make "SignUp" and "Login" on each device and put location to each loggined user
I do it like that but it is not properly, but working:
add it to your MyApplication.class extands Application :
Parse.enableLocalDatastore(this);
Parse.initialize(this, "TWRSFDFayfTlDsiiELyX37crtgezMs0DeQ5IXTdx", "ukuYgUJhhVybV4MtmlkKqtbkj2drEWjl5RJgpqYl");
ParseInstallation.getCurrentInstallation().saveInBackground();
// Also in this method, specify a default Activity to handle push notifications
PushService.setDefaultPushCallback(this, MainActivity.class);
String android_id = Secure.getString(getApplicationContext().getContentResolver(),Secure.ANDROID_ID);
Log.e("LOG","android id >>" + android_id);
ParseInstallation installation = ParseInstallation.getCurrentInstallation();
installation.put("UniqueId",android_id);
installation.saveInBackground();
String installationID = installation.getInstallationId();
ParseUser.logInInBackground(installationID, installationID, new LogInCallback() {
public void done(ParseUser user, ParseException e) {
if (user != null) {
Log.e(tag, "Logigned 1");
addCurrentLocation();
} else {
ParseInstallation installation = ParseInstallation.getCurrentInstallation();
String installationID = installation.getInstallationId();
user = new ParseUser();
user.setUsername(installationID);
user.setPassword(installationID);
user.setEmail(installationID+"#example.com");
// other fields can be set just like with ParseObject
// user.put("phone", "650-253-0000");
user.signUpInBackground(new SignUpCallback() {
public void done(ParseException e) {
if (e == null) {
Log.e(tag, "signUp 1");
ParseInstallation installation = ParseInstallation.getCurrentInstallation();
String installationID = installation.getInstallationId();
ParseUser.logInInBackground(installationID, installationID, new LogInCallback() {
public void done(ParseUser user, ParseException e) {
if (user != null) {
Log.e(tag, "Logigned 2");
addCurrentLocation();
} else {
Log.e(tag, "CANT LOGIN");
}
}
});
} else {
Log.e(tag, "signUp error 2");
}
}
});
}
}
});
after first run of app each device will be logigned with username= installationID and password = installationID you will see it on Core->Data->User Doasboard on website
after logining you need to set to each user his location simply method addCurrentLocation();
void addCurrentLocation(){
ParseGeoPoint point = new ParseGeoPoint(12, 12);
ParseInstallation parseInstallation = ParseInstallation.getCurrentInstallation();;
parseInstallation.put("user",
ParseUser.getCurrentUser());
ParseUser u= ParseUser.getCurrentUser() ;
u.put("location", point);
u.put("application", getString(R.string.app_name));
ParseGeoPoint userlocation = u.getParseGeoPoint("location");
Log.e(tag ,
"user current location: "
+userlocation.getLatitude()
+" : "+
userlocation.getLongitude());
u.saveInBackground();
}
and then post your push notification :
// Find users near a given location
ParseQuery userQuery = ParseUser.getQuery();
ParseGeoPoint stadiumLocation = new ParseGeoPoint(12.1,12.1);
userQuery.whereWithinMiles("location", stadiumLocation , 1.0);
// Find devices associated with these users
ParseQuery pushQuery = ParseInstallation.getQuery();
pushQuery.whereMatchesQuery("user", userQuery);
// Send push notification to query
ParsePush push = new ParsePush();
push.setQuery(pushQuery);
push.setMessage("Only users near 12,12 will recieve this push notification");
push.sendInBackground();
I'm writing my own SMS messenger and I need to completely suppress new SMS notification raised by default stock messenger. For sure I'm intercepting incoming SMS notification and aborting Broadcast through BroadcastReceiver.abortBroadcast(), like:
public void onReceive(Context context, Intent intent) {
Map<String, String> msgs = retrieveMessages(intent);
for (String address : msgs.keySet()) {
String msg = msgs.get(address);
Log.i(TAG, "New sms received=" + msg);
//saving message in phone
Message message = Message.createIncomingMessage(address, msg);
message.setSeen(1); ////mark message seen, so stock messenger couldn't arose notification
message.setRead(1); //mark message read, so stock messenger couldn't report it as unread
message.setUnread(true); //mark message unread for our messenger
message = Me.getMe().getMessageDAO().save(context, message); //saving message
SmsReceiver.updateNotification(context, message, true, true); //raise notification from "our" messenger
}
//aborting default broadcast, since everything already done and no need to do more
this.abortBroadcast();
}
Everything works as planned. But whenever I'm inserting new SMS into database through Me.getMe().getMessageDAO().save(context, message); within few seconds stock messenger somehow is being notified about new message in database and again raises notification.
Question is: how to suppress this notification?
first you need to set priority of your receiver see docs.
secondly, call abortBroadCast before you do heavy processing task(storing into DB).
or you can try this db storing thing in a thread, Start the thread and call abortBroadcast() immediately.
I know this question has been asked multiple times, but nobody has been able to come up with a working answer from what I have seen.
Im working on an app to intercept text messages and depending on the sending #, pop up with a custom alert. I have it working beautifully with a broadcast receiver, however if the user has goSms installed the onReceive() method is never called as goSms aborts it before it ever reaches my app.
To get around this, Im trying a content observer on content://sms/
Its working just fine, however the onChange() is called twice, with exactly the same parameters. Ive tried to check the time stamps, but they are the same, as is the type and every other parameter I have set.
From what I've seen, this is a common issue, but not one that I've seen answered anywhere.
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
querySMS();
}
protected void querySMS() {
Cursor cur = getContentResolver().query(u, null, null, null, null);
cur.moveToNext(); // this will make it point to the first record, which is the last SMS sent
String type = cur.getString(cur.getColumnIndex("type"));
String body = cur.getString(cur.getColumnIndex("body")); //content of sms
String add = cur.getString(cur.getColumnIndex("address")); //phone num
if (type.equals("1")) {
if (add.equals(Test.SENDER)) {
String[] bodys = body.split(" ", 7);
if (bodys[0].equals("test")) {
test = true;
}
cat = bodys[1];
level = bodys[2];
urgency = bodys[3];
certainty = bodys[4];
carrier = bodys[5];
message = bodys[6];
final Intent intent = new Intent(context, AlertActivity.class);
Bundle b = new Bundle();
b.putString("title", cat);
b.putString("certainty", certainty);
b.putString("urgency", urgency);
b.putString("level", level);
b.putString("message", message);
b.putBoolean("test", test);
intent.putExtras(b);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
carrierName = manager.getNetworkOperatorName();
if (carrierName.replaceAll(" ", "").equals(carrier)) {
context.startActivity(intent);
} else {
//testing
Toast.makeText(context, carrierName.replaceAll(" ", ""), Toast.LENGTH_LONG).show();
}
}
}
}
Because of the onChange() being fired twice, Im getting two alerts as well. I cannot for the life of me figure out a way around this.
If the two are identical:
store each message recv'd
compare it to previous messages recv'd
if not found, process
if found, discard the message
The life of the messages stored should be infinitesimal, a little circular buffer of 5 messages should be fine.
here is my code, it works fine for me
public class SmsObserver extends ContentObserver {
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;
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 {
if (initialPos != getLastMsgId()) {
// Here you get the last sms. Do what you want.
String receiver = cur.getString(cur.getColumnIndex("address"));
System.out.println(" Receiver Ph no :"+receiver);
// Then, set initialPos to the current position.
initialPos = getLastMsgId();
}
} catch (Exception e) {
// Treat exception here
}
}
cur.close();
}
}).start();
}
}//End of class SmsObserver
You can save last message's id and compare it to the id of the message that is returned by cur in onChange. you then can simply disregard the message if ids are the same.
// might contain mistakes, but you'll get the idea:
protected void querySMS() {
Cursor cur = getContentResolver().query(u, null, null, null, null);
cur.moveToNext();
if (lastId == cur.getLong(cur.getColumnIndex("_id")))
return;
lastId = cur.getLong(cur.getColumnIndex("_id"));
... //continue as it was
}
However - GO SMS only prevents other app's from recieving Broadcast if the user selected this option (Recieve Settings - Disable other message notification) - so if the user does not want other apps to disturb him - I think it's good idea not to do so.
I just use SharedPreference to remark last SMS info (like: id\type ...). if it is the same, I will return.