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.
Related
Is there a way to get notified when the user mutes the incoming call ring signal by pressing the volume button and/or turn to silence?
I have tried all methods I can find, using AudioManager.RINGER_MODE_CHANGE_ACTION, android.media.VOLUME_CHANGE_ACTION, android.media.MASTER_MUTE_CHANGED_ACTION and a few more...
None of them give a clear indication that the ring signal is muted.
My problem is not solved by the "possible duplicate", so I will expand with parts of my code for more help.
In my service:
#Override
public void onCreate() {
super.onCreate();
mVolumeReceiver = new VolumeReceiver();
IntentFilter intentFilter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
registerReceiver(mVolumeReceiver, intentFilter);
}
private class VolumeReceiver extends BroadcastReceiver {
private final String TAG = VolumeReceiver.class.getSimpleName();
#Override
public void onReceive(Context context, Intent intent) {
final String[] Modes = { "Unknown", "Silent", "Vibrate", "Normal" };
if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
int newMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
Log.i(TAG, "Ringer mode changed to: " + Modes[newMode + 1]);
}
}
}
I am certain that my service is started, since it's doing other things as it should.
I also tried to register a BroadcastReceiver class in my manifest, but that didn't work either.
I do get one Logcat entry telling me mode is changed to "Normal", probably when the receiver is registered. After that, nothing.
I have an application which uses the Agora library to facilitate a videochat between two parties.
What I want to do is to store the number that calls in the phone's call log, and then, when the user goes into the call log and presses on that number, that number to be passed into my app and used inside my app to call the respective user.
Here's how I currently log the number that is calling the user:
#Override
public void onInviteReceived(final String channelName, final String contactPhone, int uid, final String s2) { //call out other remote receiver
Log.i(TAG, "onInviteReceived channelName = " + channelName + " contactPhone = " + contactPhone);
runOnUiThread(new Runnable() {
#SuppressLint("MissingPermission")
#Override
public void run() {
Gson gson = new Gson();
CallExtra callExtra = gson.fromJson(s2, CallExtra.class);
ContentValues values = new ContentValues();
values.put(CallLog.Calls.CACHED_NUMBER_LABEL, "FairyApp");
values.put(CallLog.Calls.CACHED_NAME, "FairyApp");
values.put(CallLog.Calls.TYPE, CallLog.Calls.INCOMING_TYPE);
values.put(CallLog.Calls.DATE, System.currentTimeMillis());
values.put(CallLog.Calls.DURATION, 50);
values.put(CallLog.Calls.NUMBER, contactPhone);
getContentResolver().insert(CallLog.Calls.CONTENT_URI, values);
As you can see, I've just hardcoded the information just to see if it works, and indeed it does - the number is being recorded into the call log of the phone (obviously, I took care of the permissions to do this already).
Then, I created a broadcast receiver that intercepts an intent of type android.intent.action.NEW_OUTGOING_CALL , in order to intercept the user calling that number and redirecting him or her to my app and then initiate the call from there.
Here's how the Broadcast Receiver looks in the AndroidManifest:
<receiver android:name=".broadcastreceivers.OutgoingCallReceiver" >
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
Here's what the Receiver currently does (it's hardcoded, for now):
public class OutgoingCallReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
if (phoneNumber.equals("4444")){
context.startActivity(new Intent(context, HomeActivity.class));
}
}
}
So, it basically intercepts the intent to call, gets the phone number, and if the phone number is "4444", then it goes into my HomeActivity.
This works.
The problem that I'm having is trying to save a "label" for the stored number - I want to store an information that "this number is of type "MyApp"" and then, when the user presses to call that number from the phone's call log, I want to use that information in my Broadcast Receiver and check "if this is type "MyApp" then open my HomeActivity, else just ignore this broadcast".
So, my question is - how do I store a label into the call log, or some custom piece of information that I can then use in my Broadcast Receiver to identify calls that "belong" to my application?
Thank you.
Like many others, I am trying to create a simple app that logs an entry every time the phone is connected or disconnected from the charger. I plan to use this data to calculate average charge and discharge rate over several weeks/months to get an idea of how well the battery is performing over time.
I have got Intent filters declared in the manifest for ACTION_POWER_CONNECTED and ACTION_POWER_DISCONNECTED, and they seem to be firing fine.
Now when they are fired, I am registering to receive ACTION_BATTERY_CHANGED to get the battery EXTRA_LEVEL, EXTRA_STATUS and EXTRA_PLUGGED. (Reference - Obtaining usb cable plugged IN/OUT event using EXTRA_PLUGGED does not work)
public class PowerConnectionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intentPowerConn) {
Intent intentBatChange = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
int chargeStatus = intentBatChange.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
int chargeType = intentBatChange.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
int chargeLevel = intentBatChange.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
Toast.makeText(context, chargeStatus + " " + chargeType + " " + chargeLevel, Toast.LENGTH_SHORT).show();
}
}
However, the EXTRA_STATUS that is returned by this sticky broadcast still shows as Discharging when fired immediately after the phone is connected. Other extras like EXTRA_PLUGGED and EXTRA_LEVEL return correct values though.
A manual refresh to get the ACTION_BATTERY_CHANGED action after connecting returns the STATUS as Charging just fine.
It can't be that the intent received immediately after connecting is stale, since the EXTRA_PLUGGED returns the correct type (AC or USB).
I have a workaround where I use the ACTION_POWER_CONNECTED/ACTION_POWER_DISCONNECTED intent action to determine the charge state (as described here - How to detect power connected state?).
public class PowerConnectionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context , Intent intentPowerConn) {
Intent intentBatChange = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
int chargeStatus = intentBatChange.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
int chargeType = intentBatChange.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
int chargeLevel = intentBatChange.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
String action = intentPowerConn.getAction();
boolean chargeFlag = false;
if(action.equals(Intent.ACTION_POWER_CONNECTED)) {
chargeFlag = true;
}
else if(action.equals(Intent.ACTION_POWER_DISCONNECTED)) {
chargeFlag = false;
}
Toast.makeText(context, chargeStatus + " " + chargeType + " " + chargeLevel + " " + chargeFlag, Toast.LENGTH_SHORT).show();
}
}
I might need to make use of STATUS for other scenarios like BATTERY_STATUS_FULL, BATTERY_STATUS_NOT_CHARGING and BATTERY_STATUS_UNKNOWN.
So I don't want to rely on the Intent.ACTION_POWER_CONNECTED action.
Given this, what is a reliable way to detect the current charge state?
I was considering adding a 2 sec delay before I register the ACTION_BATTERY_CHANGED intent; though I haven't tested this out yet, I figure it might work.
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.
Is it possible to create an IntentFilter in android that matches ALL intents that are Broadcasted on the phone (perhaps by way of using a BroadcastReceiver)? I.E. the ones I see in ddms when I use the phone, under the ActivityManager tag? After digging through the documentation, and looking at the framework source, I am left to think it can't be done? That you must specify some sort of data, to paraphrase the docs, "some sort of data must be specified, or else you'll only get intents with no data". The app I am writing needs to know about every app that is started on the system. So far, the only way I have been able to do this is by polling ActivityManager. It seems the best way would be to have an event driven solution, using whatever underlying logic ActivityManager uses, but it's all greek to me inside of the ActivityManager.java framework source, and seems like a lot of the stuff underneath (if not ALL) is deliberately encapsulated from me.
Any ideas?
You said it yourself, the documentation quite clearly specifies how intent filters function and that this is not possible to receive all broadcasts.
Neither this nor retrieving task information is something that is supported by the APIs made public in the Android SDK.
You can register a costume receiver for each event type that will hold a reference to a parent broadcast receiver and call its onReceive method
class ChildBroadcastReceiver extends BroadcastReceiver {
private BroadcastReceiver parent;
public ChildBroadcastReceiver(BroadcastReceiver parent) {
this.parent = parent;
}
#Override
public void onReceive(Context context, Intent intent) {
parent.onReceive(context, intent);
}
}
Then you can register to all the possible events by using reflection:
final BroadcastReceiver parent = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
android.util.Log.d("GlobalBroadcastReceiver", "Recieved: " + intent.getAction() + " " + context.toString());
}
};
Intent intent = new Intent();
for(Field field : intent.getClass().getDeclaredFields()) {
int modifiers = field.getModifiers();
if( Modifier.isPublic(modifiers) &&
Modifier.isStatic(modifiers) &&
Modifier.isFinal(modifiers) &&
field.getType().equals(String.class)) {
String filter = (String)field.get(intent);
android.util.Log.d("GlobalBroadcastReceiver", "Registered: " + filter);
application.registerReceiver(new ChildBroadcastReceiver(parent), new IntentFilter(filter));
}
}