I want to develop a single activity android text messaging application that receives messages from a particular phone number. All other messages are to be sent to the inbox. For this purpose, I registered a BroadcastReceiver in the AndroidManifest.xml which checks for the incoming message's phone number and then aborts the broadcast if the number matches. The message is then stored in a sqlite database which is used to display messages in a ListActivity.
However, the issue I am facing is the broadcast is not received consistently by my app, sometimes it receives the intended message and sometimes it does not(goes to inbox). The message is received in th app as long as the app is opened, and a bit after it is closed. It is simply not consistent. I gave my app a higher priority (999) in the manifest.
My Manifest is below:
`
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:icon="#drawable/app_icon"
android:label="#string/app_name"
android:theme="#style/AppTheme"
android:allowBackup="true">
<activity
android:launchMode="singleInstance"
android:name="org.trial.hiv.HIVActivity"
android:label="#string/title_activity_hiv"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="org.trial.hiv.SMSReceiver"
android:exported="true">
<intent-filter android:priority="999">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<service android:name="org.trial.hiv.SMSService" ></service>
<receiver android:name="org.trial.hiv.BootReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service android:name="org.trial.hiv.BootUpService" ></service>
</application>
`
And my SMSReceiver.java, which starts a service to perform necessary housekeeping tasks on the message received...
public class SMSReceiver extends BroadcastReceiver
{
public void onReceive(final Context context, Intent intent)
{
Intent smsServiceIntent = new Intent(context, SMSService.class);
smsServiceIntent.setClass(context, SMSService.class);
Bundle bundle = intent.getExtras();
Object pdus[] = (Object[]) bundle.get("pdus");
SmsMessage smsMessage[] = new SmsMessage[pdus.length];
for (int n = 0; n<pdus.length; n++)`
{
smsMessage[n] = SmsMessage.createFromPdu((byte[]) pdus[n]);
}
String sender = smsMessage[0].getOriginatingAddress();
String body = smsMessage[0].getMessageBody();
if (sender.equalsIgnoreCase(activity.number))//incoming number is checked
{
this.abortBroadcast();
smsServiceIntent.putExtra("sender", sender);
smsServiceIntent.putExtra("body", body);
context.startService(smsServiceIntent);
}
}
}
I am using an smsService because I read that the BroadcastReceiver must not to any task for more than 10 seconds though I simply write the message to a SQLiteDatabase, I thought I still use a service.
Could someone point out where I am going wrong, or if I am missing out something critical. My requirement is to always receive all messages from a given number in my app, at any time.
After much debugging and understanding how BroadcastReceivers work in Android, I understand that the broadcast intent needs to be polled for, which I never did and hence the inconsistent behaviour... the following chage in code resolved my issue
do
{
bundle = intent.getExtras();
}
while (bundle == null);
in the onReceive() method of the BroadcastReceiver, and it resolved my issue. I could not find this documented anywhere, even in several SMS App tutorials.
Related
I've implemented a BroadcastReceiver according to https://developer.android.com/guide/components/broadcasts but it does not seem to work.
The receiver part of my Manifest.xml looks like this (I use android.permission.ACCESS_NETWORK_STATE and android.permission.ACCESS_WIFI_STATE):
...
<receiver android:name=".BackgroundTask">
<intent-filter>
<action android:name="android.net.wifi.WIFI_STATE_CHANGED"/>
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
...
and it's declared within my application.
My BroadcastReceiver class looks like this:
public class BackgroundTask extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("Connection changed!");
Toast.makeText(context, "Network state changed", Toast.LENGTH_LONG).show();
}
}
Neither the toast is displayed nor I receive the println-ed output on my logcat.
I also read the note which recommends me to use scheduled jobs instead of receivers, but I could not find an example how to use this for listening to network changes.
The reason I want to use this is because I want my (background)-app only to run while my phone is connected to my WiFi - if you have any different suggestions on how to implement this, I'm also very thankful.
Btw. I'm using Android 8.0.0.
Cheers,
Nikolar
Update:
Somehow it still does not work. Am I forgetting anything?
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mypackagename">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
<receiver android:name=".BackgroundPoller">
</receiver>
</application>
</manifest>
Just for clearence I renamed my BackgroundTask-Class to BootReceiver:
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("INFO", "BootReceiver started");
Intent newIntent = new Intent(context, BackgroundPoller.class);
context.sendBroadcast(newIntent);
Log.d("INFO", "BackgroundPoller activated");
}
}
When I successfully send my broadcast
adb.exe shell am broadcast -a android.intent.action.ACTION_BOOT_COMPLETED
I still don't get a log output.
To get around Oreo's broadcast receiver restrictions, you have to register your receivers using Context.registerReceiver(). You could do this by subclassing Application and registering receivers in onCreate(). The only downside to doing this is your app would have to be launched to have Application.onCreate() get called. To get around this, you could register an ACTION_BOOT_COMPLETED receiver in your manifest as this action is exempt from the restriction. Your ACTION_BOOT_COMPLETED receiver doesn't have to do anything, but it will force your app to start running on boot and therefore Application.onCreate() will get called. Then the only challenge is keeping your app running because the system will kill it if the user doesn't go into it. There's a couple options to solve this. You could write a foreground service whose only purpose is to keep the app alive. Or perhaps use AlarmManager to schedule some intent that will wake your app back up, which will register your receivers again.
I am working on an app which needs receive BOOT_COMPLETED broadcast when the device boots.
According to the documents in Android Developers, I already know that since android 3.1, apps that never run after installing or force killed by user will not receive the boot broadcast when device boots, so with the rules, how can I receive boot broadcast? By the way, I have already tried open my app when boot is finished, but it is still not working.
Here's the code:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.guoyonghui.todo"
android:installLocation="auto">
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:name=".BaseApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".tasks.TasksActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".taskdetail.TaskDetailActivity"/>
<activity android:name=".addedittask.AddEditTaskActivity"/>
<activity android:name=".statistics.StatisticsActivity"/>
<receiver android:name=".alarm.AlarmReceiver">
<intent-filter>
<action android:name="com.guoyonghui.todo.alarm.ACTION_TASK_ALARM"/>
</intent-filter>
</receiver>
<receiver
android:name=".alarm.BootReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
</application>
</manifest>
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Toast.makeText(context, action, Toast.LENGTH_SHORT).show();
Log.d("AlarmReceiver", action);
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
TasksRepository tasksRepository = TasksRepository.getInstance(TasksLocalDataSource.getInstance(context));
List<Task> tasks = tasksRepository.loadTasks();
for (Task task : tasks) {
if (task.isActive()) {
AlarmReceiver.setAlarm(context, task);
}
}
}
}
}
You don't want to use the Default category in this case. According to the documentation of this category, it is used for the following purpose:
Set if the activity should be an option for the default action (center press) to perform on a piece of data. Setting this will hide from the user any activities without it set when performing an action on some data. Note that this is normally -not- set in the Intent when initiating an action -- it is for use in intent filters specified in packages. (Emphasis mine)
See the list of categories and what they're for here:
https://developer.android.com/reference/android/content/Intent.html
How can I receive SMS messages when app is closed?
Following code works fine, but when app is closed, e.g. after reboot, It doesn't work. (Actually, it works just first few minutes after app is closed, Strange...)
My AndroidManifest.xml
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application android:hardwareAccelerated="true" android:icon="#drawable/icon" android:label="#string/app_name" android:supportsRtl="true">
<activity ....>
....
</activity>
<receiver android:name=".Receiver" android:exported="true" android:enabled="true">
<intent-filter android:priority="1">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
My Receiver.java
public class Receiver extends BroadcastReceiver
{
#Override public void onReceive(Context context, Intent intent)
{
if(cond)
{
abortBroadcast();
Toast.makeText(context, "Registration Completed", Toast.LENGTH_LONG).show();
}
}
}
If you want to continue receiving SMS after your app is closed, you should implement all the stuff relied to SMS receiving in a Service that won't be closing with your application. Refer this sample to do this:
Having a Service receive SMS messages
I am currently trying to create a phone number verification on registering on my Android app. Thank you for your help to read my long post!
Here is my Manifest file with other activities omitted
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.opensem"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<application
android:name=".AppHelper"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme"
>
<receiver android:name=".SMSReceiver">
<intent-filter android:priority="2147483647">
<action android:name="android.provider.Telephony.SMS_RECEIVED"></action>
</intent-filter>
</receiver>
<activity
android:name=".OpenSem"
android:label="#string/title_activity_opensem" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Here is my class which I want to show a log on receive SMS
public class SMSReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
Log.d("TAG", "Received");
}
}
Here is my class sending the SMS
public class RegisterPageSubmitListener implements OnClickListener{
public void onClick(View v) {
SmsManager sm = SmsManager.getDefault();
number = ParamList.get("phone");
code = randInt(100000, 999999) + "";
sm.sendTextMessage(number, null, code, null, null);
}
}
My problem is that I successfully send the SMS and received it on my phone, but the "Received" Log was never shown. I came across the priority problem on StackOverflow and thus I added the android:priority="2147483647" in my manifest. I then added these lines (Copied from another SO post) in other activities to see if my receiver is properly registered.
Intent intent = new Intent("android.provider.Telephony.SMS_RECEIVED");
List<ResolveInfo> infos = getPackageManager().queryBroadcastReceivers(intent, 0);
for (ResolveInfo info : infos) {
Log.d("OpenSEM", "Receiver name:" + info.activityInfo.name + "; priority=" + info.priority);
}
and the resulting log is shown below:
08-01 15:27:10.417: D/OpenSEM(12880): Receiver name:com.android.mms.transaction.HighPrivilegedSmsReceiver; priority=1000
08-01 15:27:10.417: D/OpenSEM(12880): Receiver name:com.example.opensem.SMSReceiver; priority=999
08-01 15:27:10.417: D/OpenSEM(12880): Receiver name:com.android.mms.transaction.PrivilegedSmsReceiver; priority=0
It seems that my receiver's priority was set to 999 instead of 2147483647. Also, I didn't install any SMS applications, so the other priority=1000 receiver seems to belong to the system messaging application.
Did I do anything wrong? I tested my code on a Red MI (MIUI) and Samsung note II. My SMSReceiver seems to be registered and not receiving the SMS which was successfully sent back to both phones. I would really appreciate any help!!
Potato. For the long post.
It may be the restrict of your ROM, that is MIUI, I have encountered the problem that my BroadcastReceiver can't receive broadcast, but my app is OK on SamSung phone.
I need to write data in to a file ,when system reboots not on boot complete.
i am using broadcast receiver "android.intent.action.REBOOT" Below is my code and manifest files
public class broadcastreceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
Log.i("LOG_TAG","rebooted");
}
manifest file:
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".broadcast"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="com.example.broadcastreceiver.broadcastreceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.REBOOT">
<intent-filter>
<action android:name="android.intent.action.REBOOT" />
</intent-filter>
</receiver>
</application>
but i am not able to write even a log when reboots.
note:i donot want to use Bootcompleted action in broadcast receiver
I can't see why you don't want to use BootCompleted, could you provide your reasons?
There is no other action that will alert your broadcast receiver of the boot. You will have to use BootCompleted.
As a note, I hope you are registering you BroadcastReceiver with the context (since you didn't include that code). If you're not using BootComplete, I don't know what action you've registered to expect your above code to execute.
Add this, to me work, but on new devices, there are no testing.
For example in the recent nexus not working.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>