I am listening for the email received broadcast from k9 and I cannot seem to get it to work.
I have registered a receiver for the following:
com.fsck.k9.intent.action.EMAIL_RECEIVED
I'm wondering if I need to add a permission - I can't seem to receive this broadcast.
I know my receiver is working as I can receive broadcasts for SMS and the phone. Has anyone else used this that can maybe shed some light on the subject?
Here's my filter:
IntentFilter fltr = new IntentFilter();
fltr.addAction("android.provider.Telephony.SMS_RECEIVED");
fltr.addAction("com.fsck.k9.intent.action.EMAIL_RECEIVED");
fltr.addAction("android.intent.action.PHONE_STATE");
registerReceiver(mRcv, fltr);
In my BroadcastReceiver:
private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
private static final String EMAIL_RECEIVED = "com.fsck.k9.intent.action.EMAIL_RECEIVED";
private static final String CALL_RECEIVED = "android.intent.action.PHONE_STATE";
public void onReceive( Context context, Intent intent ) {
Log.d(TAG, "Inside Broadcast Reciever");
Log.d(TAG, "Action: " + intent.getAction());
if(intent.getAction().equals(SMS_RECEIVED)) {
StartAct("SMS Received", context);
}else if(intent.getAction().equals(EMAIL_RECEIVED)){
StartAct("Email received", context);
}else if(intent.getAction().equals(CALL_RECEIVED)){
if(intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING))
StartAct("Incoming Phone Call", context);
}
}
I have it working - finally....
You have to add a data scheme to the intent filter. This will allow you to receive the broadcast. BUT it will break the other actions that I had in the filter, so I had to break them into separate receivers - one for SMS and phone and another for the k9 email broadcast.
Here's the one for k9 email:
IntentFilter fltr = new IntentFilter();
fltr.addAction("com.fsck.k9.intent.action.EMAIL_RECEIVED");
fltr.addDataScheme("email"); //This is needed to even receive the broadcast
registerReceiver(mRcv, fltr);
Try looking at the actual K9 source code. The AndroidManifest shows several Permissions that they have. Try compiling your program with it hooked up to eclipse and check LogCat, it'll usually show in it's error section if there is a missing permission. I only see 4 declared permissions that they have created, so READ_MESSAGE would be my guess, but check the logcat and see what it says.
Unfortunately it looks like their documentation is lacking quite a bit, which is a damn shame.
Related
I have a service running in foreground mode and I'd like to detect switching between user sessions on tablets running Android 4.2 or above.
Is there any broadcast receiver I can register to get notified?
I have noticed that Google Music stops the music playback as soon as another user session is chosen on the lock screen. How does it detect the switch?
ANSWER EXPLAINED
Thanks #CommonsWare for the correct answer. I will explain a bit more how to detect a user switch.
First be aware that the documentation explicitly says that receivers must be registered through Context.registerReceiver. Therefore do something like:
UserSwitchReceiver receiver = new UserSwitchReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction( Intent.ACTION_USER_BACKGROUND );
filter.addAction( Intent.ACTION_USER_FOREGROUND );
registerReceiver( receiver, filter );
Then in the receiver you can also retrieve the user id. Here is a small snippet:
public class UserSwitchReceiver extends BroadcastReceiver {
private static final String TAG = "UserSwitchReceiver";
#Override
public void onReceive(Context context, Intent intent)
{
boolean userSentBackground = intent.getAction().equals( Intent.ACTION_USER_BACKGROUND );
boolean userSentForeground = intent.getAction().equals( Intent.ACTION_USER_FOREGROUND );
Log.d( TAG, "Switch received. User sent background = " + userSentBackground + "; User sent foreground = " + userSentForeground + ";" );
int user = intent.getExtras().getInt( "android.intent.extra.user_handle" );
Log.d( TAG, "user = " + user );
}
}
Try ACTION_USER_FOREGROUND and ACTION_USER_BACKGROUND. I have not used them, but they were added in API Level 17, and their description seems like it may help.
From the examples this looked straightforward. Maybe you can show me what I did wrong. I can't get an activity to receive a broadcast sent from a local service.
I have Activity1 that start Service1:
startService(new Intent(Activity1.this, Service1.class));
Activity1 then starts Activity2:
startActivity(new Intent(Activity1.this, Activity2.class));
Service1, a local service, listens for downloads:
protected final BroadcastReceiver service2DownloadBroadcastReceiver = new BroadcastReceiver()
{
public void onReceive(final Context context, final Intent intent)
{
...
broadcastDownloadFinished(Uri.fromFile(downloadedFile));
The broadcast receiver of Service1 then broadcasts its own message:
protected Intent broadcastDownloadFinished(final Uri uri)
{
final Intent intent = new Intent(ACTION_DOWNLOAD_FINISHED).setData(checkNotNull(uri));
sendBroadcast(intent);
Activity2, which is in the foreground at the time, listens for the ACTION_DOWNLOAD_FINISHED intent using its own broadcast receiver:
private final BroadcastReceiver activity2DownloadBroadcastReceiver = new BroadcastReceiver()
{
public void onReceive(final Context context, final Intent intent)
{
Log.i(Activity2.class.getSimpleName(), "Received download event: " + intent.getAction() + " " + intent.getData());
Activity2 of course registers the receiver:
protected void onResume()
{
super.onResume();
final IntentFilter downloadIntentFilter = new IntentFilter();
downloadIntentFilter.addAction(ACTION_DOWNLOAD_FINISHED);
registerReceiver(activity2DownloadBroadcastReceiver, downloadIntentFilter);
In case it matters, ACTION_DOWNLOAD_FINISHED is something like "com.example.intent.action.DOWNLOAD_FINISHED".
Service1 receives the download manager event in its receiver and apparently broadcasts its own custom event, but Activity2 never seems to receive it. What did I do wrong? Is it a problem to broadcast an intent in the middle of processing another one? (I wouldn't think so---this is asynchronous, right?)
Update: Just to make sure there is no problem sending a broadcast in the middle of receiving a broadcast, I changed my broadcast code to actually perform the broadcast three seconds later on the main thread:
Log.i(getClass().getSimpleName(), "...ready to broadcast");
final Intent intent = new Intent(ACTION_DOWNLOAD_FINISHED).setData(checkNotNull(uri));
mainThreadHandler.postDelayed(new Runnable()
{
public void run()
{
Log.i(getClass().getSimpleName(), "...broadcasting");
sendBroadcast(intent);
Log.i(getClass().getSimpleName(), "...broadcasted");
}
}, 3000);
Log.i(getClass().getSimpleName(), "...scheduled to broadcast");
As expected, the log says:
...ready to broadcast
...scheduled to broadcast
...broadcasting
...broadcasted
Yet nothing is received in the activity. Please help.
Eureka! I found it! The problem is that I supplied a data URI in my broadcast intent. The Android intent matching rules get a little complicated. If you supply a data URI, then your intent filter must specify a matching MIME type.
Unfortunately, although the Android documentation says that the data type can be inferred from the data URI, apparently Android doesn't know that a file://.../example.jpg is an image. So this doesn't work:
intentFilter.addDataType("image/*");
However, instead of specifying a type, I can specify a scheme that I accept:
intentFilter.addDataScheme("file");
That works! It's a little rough---and a little artificial to restrict my broadcasts to file: URIs, but as that's all I'm using for the moment, it works.
Note that apparently I could manually specify the MIME type in the intent when I broadcast it, but that's too much trouble for now, as I'm downloading images from Picasa so I already know that they are images (and don't care the specific MIME type). And if it gets too much trouble, I could ditch the whole setData() thing altogether and set an extra---but of course I want to do things the Right Way.
have you included your receiver in your activity's manifest?
<receiver
android:name=".YourReceiver">
<intent-filter>
<action
android:name="intent_name"></action>
</intent-filter>
</receiver>
EDIT: I found the solution, see below
My first post on StackOverFlow. However I have been reading about this problem for a while without a solution that works.
What I would like to do is register the following Intent: android.nfc.action.TAG_DISCOVERED
I am doing the following in my Code:
IntentFilter filter = new IntentFilter();
filter.addAction("android.nfc.action.TAG_DISCOVERED");
filter.addCategory("android.intent.category.DEFAULT");
Log.d(TAG, "Created the new filter");
reciever = new NFCBroadcastReciever(this);
Log.d(TAG, "Created the new Broadcast Reciever");
this.registerReceiver(reciever, filter);
Log.d(TAG, "Registered new reciever");
The BroadCastReciever is defined as follows:
public class NFCBroadcastReciever extends BroadcastReceiver {
private Screen screen;
public static String TAG = "NFCBroadcastReciever";
NFCBroadcastReciever(Screen _screen){
screen = _screen;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "Action recieved: "+action);
if(action != null && NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)){
paymentScreen.onNewIntent(intent);
}
}
}
However I get an exception that the intent being fired from a tag read has no corresponding Activity. I would like to be able to only start listening for NFC events at a certain point in my application.
Thanks in advance for your help.
I found the solution to the problem actually, the key to getting NFC events to occur only on a specific activity while it is active and not when other activities are running. The sample in the Android SDK explains it: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/nfc/ForegroundDispatch.html
I found the solution to the problem actually, the key to getting NFC events to occur only on a specific activity while it is active and not when other activities are running. The sample in the Android SDK explains it: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/nfc/ForegroundDispatch.html
Is your intention to start an activity when the broadcast is received? It doesn't seem to me that paymentScreen.onNewIntent(intent); is going to accomplish that. Instead, you will likely need to build an intent that you can use with startActivity() and you'll likely want to include the relevant data from your broadcast receiver's intent into your activity intent in the form of extras.
Is it possible for my application to receive an SMS from a specific phone number, without letting it trig a system notification, but let all other messages pass to the default SMS application to be treated normally?
If so, how can the system know which process is first on queue to pick which messages to receive?
I didn't try this, but in theory it should work:
In Android SMS broadcast is sent as ordered broadcast, which means that receivers are handled in order and can cancel the broadcast. See SMSDispatcher.java, line 420.
In order to be called first, a receiver must have a higher priority then others.
<intent-filter android:priority="1000" >
. . .
</intent-filter>
To cancel a broadcast call broadcastReceiver.setResultCode(RESULT_CANCELED). That way a SMS broadcast will be cancelled and will not be shown by system SMS app (and SMS notifier).
Update:
Also try using broadcastReceiver.setResultCode(Intents.RESULT_SMS_HANDLED).
Update 2:
user672601 noted in another answer that this indeed works, but he used abortBroadcast() inside broadcast receiver.
I second farhan its not possible for number of reason. Anybody can do anything with such allowance. Check this out for details http://groups.google.com/group/android-developers/browse_thread/thread/78fecbc156f4a1ea
Peter Knego's answer is correct. I was trying to do this exact thing, tried his solution, and it indeed works except for I used:
this.abortBroadcast();
inside the broadcastReceiver.
public class NotifyServiceReceiver extends BroadcastReceiver{
static final String ACTION ="android.provider.Telephony.SMS_RECEIVED";
#Override
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
if(arg1.getAction().equalsIgnoreCase(ACTION))
{
Bundle extras = arg1.getExtras();
String strMessage = "private message";
if ( extras != null )
{
Object[] smsextras = (Object[]) extras.get( "pdus" );
for ( int i = 0; i < smsextras.length; i++ )
{
SmsMessage smsmsg = SmsMessage.createFromPdu((byte[])smsextras[i]);
String strMsgBody = smsmsg.getMessageBody().toString();
String strMsgSrc = smsmsg.getOriginatingAddress();
//Toast.makeText(GasService.this,strMessage, Toast.LENGTH_SHORT).show();
if(strMsgSrc.equals("+919XXXXXXXXX"))
{
strMessage += "SMS from " + strMsgSrc + " : " + strMsgBody;
Toast.makeText(PrivatesmsService.this,strMessage, Toast.LENGTH_SHORT).show();
abortBroadcast();
}
}
}
}
}
}
you can tell to the system by setting the priority of the activity to 100 or greater then that in manifest file so that when ever you receive sms then your application will access it and by calling abortBroadcast() it will prevent sms reaching to inbox or any other application which has set BroadcastReceiver to receive sms
I dont think its possible.... because android gives us broadcast Listener which only listen the event. so you have to read every message and check the number if its yours, do an operation else just ignore it.... the default messaging application will automatically handle it....
I'd like my app to catch incoming SMS messages. There are a few examples of this around. Looks like we just need to do this:
// AndroidManifest.xml
<receiver android:name=".SMSReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
// SMSReceiver.java
public class SMSReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "SMS received.");
....
}
}
is this correct? I'm sending my phone some sms messages, but the log statement never gets printed. I do have some other SMS applications installed on the phone, which display a popup when the sms is received - are they somehow blocking the intent from getting passed down to my app, they are just consuming it completely?
Thanks
You would also need to specify a uses-permission in your manifest file:
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
The following tutorials should help:
React on incoming SMS
SMS messaging in Android
There are a few gotchas on the way. You can find all the needed info on stackoverflow. I have gathered all the info in this answer, for convenience.
Things to be noticed
I assume android kitkat and above.
The intent for incomming sms is "android.provider.Telephony.SMS_RECEIVED"
You can change the priority of the intent filter, but it's not necessary.
You need this permission "android.permission.RECEIVE_SMS" in manifest xml, in order to receive sms messages. In android 6 and above, you additionally need to ask for the permission in runtime.
You do not need to set the MIME type of data in the intent filter. Intent filter should pass only on empty data if no MIME type is set, but fortunately it will still work without MIME.
adb shell am broadcast will not work. Use telnet connection to simulator to test sms receiving.
Long sms messages are divided into small sms chunks. We need to concatenate them.
How to send a sms message to the emulator
The most important thing is to have the possibility to send fake sms messages to the device, so we can test the code.
For this we will use a virtual device and a telnet connection to it.
Create a virtual device in android studio and run the simulator
Look at the title bar in the simulator window. There is the device name and a port number. We need to know this port number in the next steps.
Now connect to the port number shown in the simulator title bar with telnet
$ telnet localhost 5554
If you see this: Android Console: Authentication required, then you need to authenticate the connection with this command:
auth xxxxxx
Replace the xxxxxx above with the token read from ~/.emulator_console_auth_token file.
Now you should be able to run all the commands. To send a sms message, type this command:
sms send 555 "This is a message"
Where you can replace 555 with the sender telephone number and a message of your own.
How to listen to SMS_RECEIVED broadcasts
To get the broadcasts, you need to register a BroadcastReceiver object. You can do this in the manifest.xml OR just call registerReceiver function. I will show you the latter, as it is easier to reason about and yet more flexible.
Connecting the broadcast receiver with the main activity
The data flow is one way. From broadcast receiver to the main activity. So the simplest way to get them to talk is to use a function interface. The activity will implement such a function and the broadcast receiver will have the activity instance passed as a parameter in the constructor.
File SmsHandler.java:
package ...
interface SmsHandler {
void handleSms(String sender, String message);
}
Implementing the broadcast receiver
The broadcast receiver will get the intent in a callback. We will use the function Telephony.Sms.Intents.getMessagesFromIntent(intent) to get the sms messages. Notice the SmsHandler parameter in the constructor. It will be the activity to which we will send the received sms.
File SmsInterceptor.java:
package ...
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.provider.Telephony;
import android.telephony.SmsMessage;
public class SmsInterceptor extends BroadcastReceiver {
private SmsHandler handler;
/* Constructor. Handler is the activity *
* which will show the messages to user. */
public SmsInterceptor(SmsHandler handler) {
this.handler = handler;
}
#Override
public void onReceive(Context context, Intent intent) {
/* Retrieve the sms message chunks from the intent */
SmsMessage[] rawSmsChunks;
try {
rawSmsChunks = Telephony.Sms.Intents.getMessagesFromIntent(intent);
} catch (NullPointerException ignored) { return; }
/* Gather all sms chunks for each sender separately */
Map<String, StringBuilder> sendersMap = new HashMap<>();
for (SmsMessage rawSmsChunk : rawSmsChunks) {
if (rawSmsChunk != null) {
String sender = rawSmsChunk.getDisplayOriginatingAddress();
String smsChunk = rawSmsChunk.getDisplayMessageBody();
StringBuilder smsBuilder;
if ( ! sendersMap.containsKey(sender) ) {
/* For each new sender create a separate StringBuilder */
smsBuilder = new StringBuilder();
sendersMap.put(sender, smsBuilder);
} else {
/* Sender already in map. Retrieve the StringBuilder */
smsBuilder = sendersMap.get(sender);
}
/* Add the sms chunk to the string builder */
smsBuilder.append(smsChunk);
}
}
/* Loop over every sms thread and concatenate the sms chunks to one piece */
for ( Map.Entry<String, StringBuilder> smsThread : sendersMap.entrySet() ) {
String sender = smsThread.getKey();
StringBuilder smsBuilder = smsThread.getValue();
String message = smsBuilder.toString();
handler.handleSms(sender, message);
}
}
}
The main activity
Finally we need to implement SmsHandler interface into the main activity and add registering the broadcast receiver and permission check to the onCreate function.
File MainActivity.java:
package ...
import ...
public class MainActivity extends AppCompatActivity implements SmsHandler {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* Register the broadcast receiver */
registerSmsListener();
/* Make sure, we have the permissions */
requestSmsPermission();
}
/* This function will be called by the broadcast receiver */
#Override
public void handleSms(String sender, String message) {
/* Here you can display the message to the user */
}
private void registerSmsListener() {
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
/* filter.setPriority(999); This is optional. */
SmsInterceptor receiver = new SmsInterceptor(this);
registerReceiver(receiver, filter);
}
private void requestSmsPermission() {
String permission = Manifest.permission.RECEIVE_SMS;
int grant = ContextCompat.checkSelfPermission(this, permission);
if ( grant != PackageManager.PERMISSION_GRANTED) {
String[] permission_list = new String[1];
permission_list[0] = permission;
ActivityCompat.requestPermissions(this, permission_list, 1);
}
}
}
Finally remember to add RECEIVE_SMS permission to your manifest xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<application>
...
</application>
</manifest>
One more thing that these answers haven't mentioned - you should require the permission android.permission.BROADCAST_SMS. If you don't do this, any application can spoof messages in your app.
<receiver android:name=".SMSReceiver"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
Also note that the Hangouts application will currently block my BroadcastReceiver from receiving SMS messages. I had to disable SMS functionality in the Hangouts application (Settings->SMS->Turn on SMS), before my SMS BroadcastReceived started getting fired.
Edit: It appears as though some applications will abortBroadcast() on the intent which will prevent other applications from receiving the intent. The solution is to increase the android:priority attribute in the intent-filter tag:
<receiver android:name="com.company.application.SMSBroadcastReceiver" >
<intent-filter android:priority="500">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
See more details here: Enabling SMS support in Hangouts 2.0 breaks the BroadcastReceiver of SMS_RECEIVED in my app
Did you try with the emulator ?
After deploying your application in the emulator, you can send events like SMS via the DDMS or via the command line by connecting with telnet :
telnet localhost <port_emulator>
send sms <incoming_tel_number> <sms_content>
port_emulator is usually 5554
You should read this acticle about send and receive sms programmatically.
http://mobiforge.com/developing/story/sms-messaging-android
Android Messenger (the SMS client) has a "Chat" feature which transmits messages over WiFi instead of SMS.
If the person you are testing with uses Messenger as well, you'll need to disable this feature on one or both of your devices otherwise there is no SMS message actually being received:
To turn chat features off:
Open Messages Messages Logo Round.
Tap More More and then Settings.
Tap Advanced and then Chat features.
Turn Enable chat features on or off.
https://support.google.com/messages/answer/7189714?hl=en