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.
Related
I'm developing a mission-critical application for an Android Tablet.
I'd like to foolproof the application, specifically to prevent the user from shutting down the app or turning the screen off during some important processes, which take some time.
For preventing the user from exiting or hiding the app, there's Lock task mode.
After searching on SO, I found that this is probably not possible is not really possible - however that answer is from 2012 - Is this still the case?
Meanwhile, I did implement the workaround of turning requesting to turn the screen back on if the ACTION_SCREEN_OFF intent is detected, described here, but it's fairly ugly, and also the keyguard is sometimes disabled and sometimes not, I'm not sure why.
Here's my code:
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action == Intent.ACTION_SCREEN_OFF) {
Log.i(LOG_TAG, "Screen off was detected, requesting to turning the screen back on...")
// Disable key lock, so keygoard will not be shown once the screen light back up
val keyguardManager = getSystemService(KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this#MainActivity, null)
// Ask to turn the screen back on - lifted from here
// https://stackoverflow.com/a/10143686/4574731
// Ask device to keep screen awake
val powerManager = getSystemService(POWER_SERVICE) as PowerManager
val wakeLock = powerManager.newWakeLock(
PowerManager.SCREEN_DIM_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.ON_AFTER_RELEASE,
"rpicapp:turnScreenOnReciever"
)
wakeLock.acquire(10*1000L /* 10 seconds */)
try {
// Broadcast the ACTION_SCREEN_ON intent after 10 milliseconds
val alarmMgr = getSystemService(ALARM_SERVICE) as AlarmManager
val screenOnIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_SCREEN_ON), 0)
alarmMgr[AlarmManager.ELAPSED_REALTIME_WAKEUP, 10] = screenOnIntent
} finally {
wakeLock.release()
}
}
}
Is there a better workaround for this in 2021?
Thanks
Simple answer unfortunately: it's not possible unless the phone is rooted.
You could do what you want by getting only root permissions, however, users with rooted phones are very few nowadays .. but it always depends on your context. If you provide the tablet to run the application on, you could root it and use this to realize your functionality.
I'm trying to catch the volume Up/Down button press with the screen off, but despite acquiring a wakelock the app doesn't stay in the foreground when pressing the lock button. I have a breakpoint in the OnPause method and it gets hit when the screen turns off, and I can confirm that the wakelock is on through ADB terminal with the command:
adb shell dumpsys power
I omitted the volume buttons events since they're not relevant, but they do work when the screen is on.
I don't know what I'm missing, maybe I'm missunderstanding how the wakelock is supposed to behave?
I'm testing it in an emulator with Android 6.0 and in a physical phone with Android 7.1.2.
Thanks in advance for any help..
Button getLockBtn;
PowerManager _powerManager;
PowerManager.WakeLock _wakeLock;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
RequestWindowFeature(WindowFeatures.NoTitle);
SetContentView(Resource.Layout.Main);
getLockBtn = FindViewById<Button>(Resource.Id.GetLockBtn);
_powerManager = (PowerManager)GetSystemService(PowerService);
_wakeLock = _powerManager.NewWakeLock(WakeLockFlags.Partial, "MyTag");
getLockBtn.Click += (s, e) =>
{
if (_wakeLock.IsHeld)
{
_wakeLock.Release();
getLockBtn.Text = "Get lock";
//Remove the notification
}
else
{
//Here I show a notification
_wakeLock.Acquire();
getLockBtn.Text = "Release lock";
}
};
}
So how do apps like Google Maps achieve that behavior
When the screen is locked, the volume control comes from the system, and it is not the time to apply the control itself.Google Maps do nothing with the volume.
Your problem may not be the use of the volume button under the lock screen, but the program can still work when you lock the screen.
If you want a task to continue running, even when your Activity is not active, you need to use a Service. https://developer.android.com/guide/components/services.html.
Also can and a Notification to show something on the lock screen. https://developer.android.com/guide/topics/ui/notifiers/notifications.html
I'm creating an app which detects Power Key press (both in foreground and background). I'm using the following BroadcastReceiver for this
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
String action = arg1.getAction();
if (action.equals(Intent.ACTION_SCREEN_OFF)
|| action.equals(Intent.ACTION_SCREEN_ON)) {
Toast.makeText(context, "DETECTED", 5000).show();
}
}
I'm starting the broadcast from my MainActivity
Intent i = new Intent(MainActivity.this, Receive.class);
sendBroadcast(i);
This works fine in both foreground and background, but when I swipe away my app from Task manager, it won't detect it any more. Also, this works in other phones.
I thought it might be a case with my phone only, but I've an app in my mobile which detects the power key press even on removing it from Task manager (So, it's also possible in my phone)
You have to define all the action in manifest file. Why it is needed because Android system maintains list of intent-filters at the time of installation of application. You should use staic intent-filter when you want to hanldle broadcast even if application is not in running state.
You have use dynamin intent-filter registering. This should be used only when you want to handle broadcast when application is in running state.
I am working on Android Wear app that is somehow a timer that displays a notification when the time is elapsed and then start again. The notification displays a message on the watch and make the device vibrating.
The app is working fine except when the watch is in sleep mode. In this case, the user needs to touch the screen in order that the watch vibrates and the timer starts again.
The notification is created and displayed from a WearableListenerService. I tried many solutions such as Wake locks. The code is written in C# with Xamarin.Android but i think any java dev will also be able to read:
static PowerManager.WakeLock sWakeLock;
static object LOCK = new object();
void DisplayNotification()
{
//create the wake lock
lock (LOCK)
{
if (sWakeLock == null)
{
// This is called from BroadcastReceiver, there is no init.
var pm = (PowerManager)GetSystemService(Context.PowerService));
sWakeLock = pm.NewWakeLock(
WakeLockFlags.ScreenBright | WakeLockFlags.Full | WakeLockFlags.AcquireCausesWakeup, "My WakeLock Tag");
}
}
sWakeLock.Acquire();
//display the notification
//....
//release the wake lock
lock (LOCK)
{
//Sanity check for null as this is a public method
if (sWakeLock != null)
sWakeLock.Release();
}
}
My issue is that the watch will wake up at some point (after few minutes) but not immediately when requested. I tried to put a delay on the release of the wake lock or to make an sWakeLock.Acquire(2000) in order that the wake lock is automatically released after 2 secs. Then, I also tried other solutions with the WakefulBroadcastReceiver and the AlarmManager but I still have the same behaviour.
I've done something like that on mobile just by adding one line in onCreate of the activity i was calling by service.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//add this below getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD|WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
I would avoid Xamarin for Wear as it might cause some conflicts and not work well as it wasn't designed to.
Also if you created Service that is triggered on Android Wear - I would say Message API is correct way to do it, you should have no problem with a creating notification (on a watch itself) with vibrations (without need to touch screen) and update notification from your Service - to update timer value.
No wake locks needed.
Might help: How can I create notification that is different on device and wear?
I currently listen for changes in the Wi-Fi in my Android application and for the most part it works OK, however when the user has there device is idle/sleep mode, as in when the screen is blank. If they walk into a Wi-Fi area and automatically connect I don't get the intent until the user has turned on the screen. This is not good and leads to complaints about my application. Can anyone help with why I don't get the intent until the screen wakes up?
Here is my code:
BroadcastReciever:
public class WifiReciever extends BroadcastReceiver{
#Override
public void onReceive(Context arg0, Intent arg1) {
String action = arg1.getAction();
if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)){
return;
}
if (!noConnectivity)
{
if (aNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI)
{
//Handle connected case
Log.e("E", "GOT CONNECTION");
}
}
else
{
if (aNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI)
{
//Handle disconnected case
Log.e("E", "LOST CONNECTION");
}
}
}
If a running Service I have that as a variable:
WifiReciever mConnectionReceiver;
And I start and stop the receiver in the service with the following calls:
private synchronized void startMonitoringConnection()
{
IntentFilter aFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mConnectionReceiver, aFilter);
}
private synchronized void stopMonitoringConnection()
{
unregisterReceiver(mConnectionReceiver);
}
I call a WakeLock to make sure that I am getting CPU time like this:
pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "CicCPULock");
wl.acquire();
However even with all that I never receive the Connectivity changed intent if the screen is blank, it works 100% of the time when the screen is on bit if I a user walks into a Wi-Fi are and connects when the screen is blank I fail to get the intent, is anyone aware of something I am missing?
Actually there are many problems with WakeLocks, WIFI and WIFILocks in Android. First of all check your WIFI policy to see what will happen when screen goes dark. It must be "Always on". Then try to obtain a WIFI Lock to prevent the wifi from sleeping because keeping the CPU on does not cause the WIFI to go off. For a sample code refer to:
http://goltermann.cc/2011/11/android-accessing-wifi-even-in-standby-using-wakelock-wifilock-alarmmanager-and-services/
Also note that you must have the permission to use WakkeLocks:
<uses-permission android:name="android.permission.WAKE_LOCK" />
There are many other issues in this area check this question, and refer to Google issue tracker about WIFI problems, it helps you alot and saves you a lot of time:
Android strange cross device behaviour