Android detect lock Unlock event on Chromebook - android

I want to detect the Chromebook lock, Unlock event. I am using Foreground service and register Broadcast to detect Intent.ACTION_SCREEN_ON, Intent.ACTION_SCREEN_OFF, Intent.ACTION_USER_PRESENT. Only getting Intent.ACTION_SCREEN_ON, Intent.ACTION_SCREEN_OFF broadcast but not getting Intent.ACTION_USER_PRESENT broadcast on Chromebook.
override fun onCreate() {
super.onCreate()
startForeground()
intentFilter = IntentFilter(Intent.ACTION_SCREEN_ON)
intentFilter = IntentFilter(Intent.ACTION_USER_PRESENT)
intentFilter.addAction(Intent.ACTION_SCREEN_OFF)
//other code
}
class XYZBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
val action = intent.action
if (Intent.ACTION_USER_PRESENT == action) {
}
else if (Intent.ACTION_SCREEN_OFF == action) {
}
else if(Intent.ACTION_SCREEN_ON == action){
}
}
I am trying lots of methods from Android detect phone lock event, A way to get unlock event in android?, Android - detect phone unlock event, not screen on but nothing works for Chromebook.
Is there any other way to detect lock, Unlock events on Android Chromebook?

The information you're referring to may be outdated ...
Since API level 26 you won't receive this implicit broadcast on regular Android either:
https://developer.android.com/guide/components/broadcast-exceptions

Related

Starting a foreground service when an app is background restricted on Android Pie

With Android 9.0 (Pie), it's possible for the user to restrict your app from doing background work through settings. Our app has seen the following exception when we try to start a foreground service on Pie devices if the app is background restricted even when an app activity is fully in the foreground.
RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
We do call startForeground() in the started Service, but the system log when attempting to start the service shows:
system_process W/ActivityManager: Service.startForeground() not allowed due to bg restriction
Seems odd to receive an exception when you're following the documented steps for foreground services, and the system is rejecting the app from starting the foreground service when your app is also in the foreground still. I have yet to find much documentation relating to this, but is this documented behavior? And is there at least a way for your app to know if it's been background restricted and thus not attempt to start the service in the foreground?
My Service code essentially looks like below. Our target api is 27.
class MyService : Service() {
override fun onCreate() {
super.onCreate()
// create notification
startForeground(NOTIFICATION_ID, notification)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null || intent.action == null || intent.action == ACTION_STOP) {
quit()
}
doWork()
return START_NOT_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
quit()
}
private fun quit() {
stopForeground(true)
stopSelf()
}
companion object {
fun start(context: Context) {
val intent = Intent(context, MyService::class.java)
intent.action = ACTION_START
ContextCompat.startForegroundService(context, intent)
}
fun stop(context: Context) {
val intent = Intent(context, MyService::class.java)
intent.action = ACTION_STOP
ContextCompat.startForegroundService(context, intent)
}
}
}
Figured out that the crash will actually happen from the second call to ContextCompat.startForegroundService() in MyService.stop(), which I was using to send an Intent with a "stop" Action to stop the service rather than calling context.stopService(). At first I though I needed to manually call stopForeground() in the Service, but calling context.stopService() seems to stop the foreground service and remove the notification anyways and won't lead to the crash, so I've decided to refactor how I handle stopping the service.
UPDATE: I think an additional part of the problem was also trying to use Intents to start and stop the service, especially because in some cases my service was started and then stopped too quickly. A very helpful thread with Ian Lake gives these recommendations about services:
I'd strongly suggest against using startService as a way to pass messages to your service. Using an EventBus or LocalBroadcastReceiver is a lot better way of passing messages without conflating that with lifecycle actions. I'd also avoid having external components call stopService() directly - let the service itself manage its own lifecycle, reacting to events you send its way.

Accepting a Call via Bluetooth Headset

i am working on a VoIP-Android-App. I would like to accept and decline Calls via a connnected Bluetooth Headset in an Activity.
What I have tried so far:
Using a Media Session to receive Media Button clicks.
Problem: If we start BluetoothSCO we do not receive any Media Button clicks. If we do not start BluetoothSCO we do receive Media Button clicks but we cannot differentiate long and short button clicks because downtime is always 0, the keycode is always KEYCODE_MEDIA_PLAY and the ACTION_DOWN is immediately followed by ACTION_UP. Those problems only occur if we are connected via Bluetooth. If we are connnected over a cable Headset we do get the appropriate keycodes (KEYCODE_HEADSETHOOK) and the downtime is not 0.
Using a BroadcastReceiver to listen for Bluetooth SCO connection changes.
private val scoReceiver = object : BroadcastReceiver() {
fun onReceive(context: Context, intent: Intent) {
val state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1)
val previousState = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, -1)
if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED && previousState == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
Log.e(TAG, "SCO Disconnected")
hangupCall()
}
}
}
protected fun onStart() {
super.onStart()
val intentFilter = IntentFilter()
intentFilter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
registerReceiver(scoReceiver, intentFilter)
}
With this approach i can detect when the user wants to hang up the call, for example a long press on the bluetooth headset because this triggers the SCO to disconnect.
Problem: We can not detect if the user wants to accept an incoming call.
Using dispatchKeyEvent, onKeyDown and onKeyUp.
Problem: They never get called at all.
Does anyone has any advice or a best practice how to correctly handle bluetooth headsets? Any help is very appreciated. Thanks in advance!
During normal and virtual voice call (including ringing) all events of Bluetooth headset unit buttons are processed by Bluetooth Headset Service internaly and not broadcasted as button events. Bluetooth Headset Service redirects these events into Telecom framework (answer/hangupCall).
These events are handled internally in HeadsetStateMachine (under packages/apps/Bluetooth).
These events are forwarded to IBluetoothHeadsetPhone interface. The single application to which all the events are forwarded is defined at run-time by following binding code in HeadsetStateMachine.java. This is to allow phone manufacturers to forward them to custom phone application instead of default one in cases where default one is not used.
Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName());
intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0));
if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) {
Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
}
To make the events get forwarded to your application instead of default phone application you would have to modify aosp code.
You would need to intercept the events at one of HeadsetStateMachine , BluetoothHeadsetPhone proxy or the phone application.
Unfortunately what you are looking for is currently not possible without modifying aosp code. Some headsets like Plantronics have custom BT events which are forwarded to all applications - some of the existing VoIP applications support these custom intents to support at-least answering calls for some of the headsets.
You should use android Telecom API and implement android.telecom.ConnectionService and android.telecom.Connection where you should override onAnswer() callback which will be called when you try to answer a call via bluetooth headset.
For more details read docs - https://developer.android.com/guide/topics/connectivity/telecom/selfManaged

How to register for ACTION_PACKAGE_ADDED and ACTION_PACKAGE_REMOVED on Android Oreo?

Looking at the latest Android Oreo release notes, it seems like only a handful of implicit broadcasts can be registered by the apps. ACTION_PACKAGE_ADDED and ACTION_PACKAGE_REMOVED is not among them. Is there a workaround for receiving these broadcasts?
From the docs:
Apps that target Android 8.0 or higher can no longer register broadcast receivers for implicit broadcasts in their manifest. An implicit broadcast is a broadcast that does not target that app specifically. For example, ACTION_PACKAGE_REPLACED is an implicit broadcast, since it is sent to all registered listeners, letting them know that some package on the device was replaced.
This says that you cannot register these intents in your manifest. You can still register them programmatically to receive them when your app is running.
You might also try ACTION_PACKAGE_FULLY_REMOVED, which is one of the exceptions that you can still listen to by registering it in the manifest. There is no such 'alternative' for when a package is added.
As CW noted, you could also periodically check for changes in the roster of installed apps.
You can also use polling, setting up a JobScheduler job to check every so often, asking PackageManager for what has changed in the roster of installed apps via getChangedPackages().
As posted in https://stackoverflow.com/a/55819091/1848826
I could make it work with the following code
class KotlinBroadcastReceiver(action: (context: Context, intent: Intent) -> Unit) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) = action(context, intent)
}
class MainActivity : AppCompatActivity() {
private val broadcastReceiver = KotlinBroadcastReceiver { context, _ ->
Toast.makeText(context, "It works", Toast.LENGTH_LONG).show()
}
override fun onCreate(savedInstanceState: Bundle?) {
registerReceiver(broadcastReceiver, IntentFilter().apply {
addAction(Intent.ACTION_PACKAGE_ADDED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
addDataScheme("package") // I could not find a constant for that :(
})
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(broadcastReceiver)
}
}

Check whether Android screen is locked or not

I need to do something whenever an Android device (running Android 4.0 onwards) is locked or unlocked. The requisites are as follow:
If screen lock is set to "None", I do not consider the device to be locked when the power button is pressed and the screen goes off.
If screen lock is set to anything other than "None", I consider the device to be unlocked when the keyguard screen is not present.
I have implemented this piece of code which seems to be working for Android 5.0, and which takes into account the not-so-great behaviour of older Android versions when "None" is used. I have also checked other questions such as this one before posting this question.
private class KeyguardWatcher extends BroadcastReceiver {
public KeyguardWatcher() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
MyService.this.registerReceiver(this, intentFilter);
}
public void destroy() {
MyService.this.unregisterReceiver(this);
}
#Override
public void onReceive(Context context, Intent intent) {
// Old Android versions will not send the ACTION_USER_PRESENT intent if
// 'None' is set as the screen lock setting under Security. Android 5.0
// will not do this either if the screen is turned on using the power
// button as soon as it is turned off. Therefore, some checking is needed
// with the KeyguardManager.
final String action = intent.getAction();
if (action == null) {
return;
}
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
// If 'None' is set as the screen lock (ie. if keyguard is not
// visible once the screen goes off), we do not consider the
// device locked
if (mKeyguardManager.inKeyguardRestrictedInputMode()) {
doStuffForDeviceLocked();
}
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
if (!mKeyguardManager.inKeyguardRestrictedInputMode()) {
// The screen has just been turned on and there is no
// keyguard.
doStuffForDeviceUnlocked();
}
// If keyguard is on, we are to expect ACTION_USER_PRESENT when
// the device is unlocked.
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
doStuffForDeviceUnlocked();
}
}
}
This seems to be working for me in Android 5.0. I was wondering however if it is possible for this to be prone to race conditions when processing ACTION_SCREEN_OFF. Is it possible that something other than "None" is being used (for example "Swipe") and that by the time I am processing ACTION_SCREEN_OFF keyguard is not in restricted input mode but it will be soon afterwards? If that were the case, I would never consider the device locked but it might be.

Android - detect phone unlock event, not screen on

Is there a way to detect when a user unlocks the phone? I know about ACTION_SCREEN_ON and ACTION_SCREEN_OFF, but these seem to be fired when the screen switches on/off when pressing the power button, but not actually when the phone gets unlocked when pressing the Menu button...
I am trying to detect the unlock/lock while an activity is running, and I want to resume the activity once unlocked.
Here's what to do:
Say you want to detect the unlock event and do something in your activity when the phone is unlocked. Have a Broadcast Receiver for ACTION_SCREEN_ON, ACTION_SCREEN_OFF and ACTION_USER_PRESENT.
onResume of the activity will be called when ACTION_SCREEN_ON is fired. Create a handler and wait for ACTION_USER_PRESENT. When it is fired, implement what you want for your activity.
Credit goes to CommonsWare's answer here: Android -- What happens when device is unlocked?
After struggling with this for a while, I've found that the best way to do this is to register a BroadcastReceiver on the "android.intent.action.USER_PRESENT" action.
"Broadcast Action: Sent when the user is present after device wakes up (e.g when the key-guard is gone)."
To distinguish between cases where the user has turned on phone screen when it wasn't locked, and when they actually unlocked it use the KeyguardManager to check the security settings.
Code example:
Add this to your activity:
registerReceiver(new PhoneUnlockedReceiver(), new IntentFilter("android.intent.action.USER_PRESENT"));
Then use this class:
public class PhoneUnlockedReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
KeyguardManager keyguardManager = (KeyguardManager)context.getSystemService(Context.KEYGUARD_SERVICE);
if (keyguardManager.isKeyguardSecure()) {
//phone was unlocked, do stuff here
}
}
}
public class PhoneUnlockedReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)){
Log.d(TAG, "Phone unlocked");
}else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)){
Log.d(TAG, "Phone locked");
}
}
}
register receiver by this statement
receiver = new PhoneUnlockedReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_PRESENT);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(receiver, filter);
Didn't test it but try the following:
Wait for ACTION_SCREEN_ON.
(After screen is on,) Wait for ACTION_MAIN with category CATEGORY_HOME (Which launches the home screen) - This is probably what is sent after the phone gets unlocked.
The 1st step is needed to filter out regular HOME key presses.

Categories

Resources