22

I've been reading some posts here on Stackoverflow, and I didn't find a good solution, I'm wondering if it's possible to detect when the user long press the power button when trying to power off the device, I'd like to know if you can detect that event, and let or not show that dialog where appears (Restart, Shut Down, etc...)

I've tried this:

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
        Toast.makeText(MainActivity.this, event.getKeyCode(), Toast.LENGTH_SHORT).show();
    return true;
}

but it doesn't show up, also it should work as a service, I mean that the app can or not be opened to show that toast.

EDIT

This is how I put the onCloseSystemDialog

//home or recent button
public void onCloseSystemDialogs(String reason) {
    if ("globalactions".equals(reason)) {
        Toast.makeText(PowerButtonService.this, "yaps", Toast.LENGTH_SHORT).show();
        Intent i= new Intent(getBaseContext(), Demo.class);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        getApplication().startActivity(i);
    //} else if ("homekey".equals(reason)) {
        //home key pressed
    //} else if ("recentapss".equals(reason)) {
        // recent apps button clicked
    }
}

It works fine, but only when the device is unlocked, when the device is locked isn't showing anything.

Also I'm trying to figure out how to remove the dialog when the user click powerbutton I tried this :

getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);

But if I want to show it again, how can I do it?

Morteza Jalambadani
  • 2,190
  • 6
  • 21
  • 35
Skizo-ozᴉʞS ツ
  • 19,464
  • 18
  • 81
  • 148

12 Answers12

23

Sharing my method to do what you would like to achieve.

Basically, what it does is

  1. Asking for system permission to draw overlay (This is not a normal or vulnerable permission). This is not a user permission, so You should really know, what you are doing, by asking for it.

    public class MainActivity extends AppCompatActivity {
    
        public final static int REQUEST_CODE = 10101;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (checkDrawOverlayPermission()) {
                startService(new Intent(this, PowerButtonService.class));
            }
        }
    
        public boolean checkDrawOverlayPermission() {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                return true;
            }
            if (!Settings.canDrawOverlays(this)) {
                /** if not construct intent to request permission */
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
            /** request permission via start activity for result */
                startActivityForResult(intent, REQUEST_CODE);
                return false;
            } else {
                return true;
            }
        }
    
        @Override
        @TargetApi(Build.VERSION_CODES.M)
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            if (requestCode == REQUEST_CODE) {
                if (Settings.canDrawOverlays(this)) {
                    startService(new Intent(this, PowerButtonService.class));
                }
            }
        }
    }
    
  2. Starting a service and adds a special view to WindowManager

  3. Waiting for an action inside View's onCloseSystemDialogs method.

    public class PowerButtonService extends Service {
    
        public PowerButtonService() {
    
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            LinearLayout mLinear = new LinearLayout(getApplicationContext()) {
    
                //home or recent button
                public void onCloseSystemDialogs(String reason) {
                    if ("globalactions".equals(reason)) {
                        Log.i("Key", "Long press on power button");
                    } else if ("homekey".equals(reason)) {
                        //home key pressed
                    } else if ("recentapps".equals(reason)) {
                        // recent apps button clicked
                    }
                }
    
                @Override
                public boolean dispatchKeyEvent(KeyEvent event) {
                    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
                        || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP
                        || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN
                        || event.getKeyCode() == KeyEvent.KEYCODE_CAMERA
                        || event.getKeyCode() == KeyEvent.KEYCODE_POWER) {
                        Log.i("Key", "keycode " + event.getKeyCode());
                    }
                    return super.dispatchKeyEvent(event);
                }
            };
    
            mLinear.setFocusable(true);
    
            View mView = LayoutInflater.from(this).inflate(R.layout.service_layout, mLinear);
            WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    
            //params
           final WindowManager.LayoutParams params;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    
        params = new WindowManager.LayoutParams(
                100,
                100,
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);
    } else {
        params = new WindowManager.LayoutParams(
                100,
                100,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_FULLSCREEN
                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                        | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
                PixelFormat.TRANSLUCENT);
    }
            params.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
            wm.addView(mView, params);
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="powerbuttonpress">

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        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>

        <service
            android:name=".PowerButtonService"
            android:enabled="true"
            android:exported="true">
        </service>

    </application>

</manifest>

service_layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

</LinearLayout>
R. Zagórski
  • 20,020
  • 5
  • 65
  • 90
  • I'll test your code in a few hours, I'll let you know, it looks good tho ;) – Skizo-ozᴉʞS ツ Aug 29 '16 at 12:38
  • One of the few that will work outside of an activity – Robin Dijkhof Aug 29 '16 at 18:54
  • It works @RobinDijkhof, the thing is is there any way to show a dialog when the device is locked? I mean I know how to show a toast when the device is unlocked, the thing is is there any way to show it when the device is locked? – Skizo-ozᴉʞS ツ Aug 30 '16 at 09:09
  • And by the way the view isn't showed, you know why? – Skizo-ozᴉʞS ツ Aug 30 '16 at 09:38
  • Detecting power long button press happens in `PowerButtonService` inside the function `onCloseSystemDialogs(String reason)` in the first condition. Currently only the logcat message is shown. If you would like to show `Toast`, then replace the `Log.i(...);` line with `Toast.makeText(getContext(), "Sample toast", Toast.LENGTH_SHORT).show();` – R. Zagórski Aug 30 '16 at 09:41
  • Yes, that's what I did, but I'd like to know if it's possible to open an Activity when the user press the button... I get it when the device is unlocked, but not when the device is locked, you know why? – Skizo-ozᴉʞS ツ Aug 30 '16 at 11:09
  • Of course it is possible to show `Activity`. Start it just like a normal `Activity` `Intent` with flag `Intent.FLAG_ACTIVITY_NEW_TASK`. If You would like to show `Activity` when the screen is locked, then use a `Keyguard` to unlock the screen first. – R. Zagórski Aug 30 '16 at 11:21
  • Tried it, but doesn't show up, since the device it's locked... I'll edit my question and you see my code ok? – Skizo-ozᴉʞS ツ Aug 30 '16 at 11:22
  • See my edit please if you don't understand something let me know – Skizo-ozᴉʞS ツ Aug 30 '16 at 11:24
  • Tried with invoking power button long press programatically but without luck. And can't open `Activity` while Lock screen is active. – R. Zagórski Aug 30 '16 at 13:12
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/122324/discussion-between-skizo-oziks-and-r-zagorski). – Skizo-ozᴉʞS ツ Aug 31 '16 at 16:05
  • 1
    While this works for me, it caused the soft keyboard to not open as long as the system overlay is active (which is always), so you actually couldn't type anything. Switching to TYPE_SYSTEM_OVERLAY instead of TYPE_SYSTEM_ALERT solved my problem. – Syzygy Apr 04 '17 at 12:09
  • 1
    It seems that this no longer works on Android P (Beta). onCloseSystemDialogs() is not called when power button is long pressed. Is there any workaround for this? – drk May 14 '18 at 10:19
  • 1
    If you use ProGuard make sure to add @Keep annotation to onCloseSystemDialogs method. Androd P is still a question though. – drk May 24 '18 at 17:31
  • @drk can you explain please how i can use @Keep in proguard. Complete syntax. As i don't know the class of `onCloseSystemDialogs()` method. – Mateen Chaudhry Aug 20 '18 at 04:38
  • Change `public void onCloseSystemDialogs(String reason)` to `@Keep public void onCloseSystemDialogs(String reason)`. (just add `@Keep` annotation to it) – drk Aug 20 '18 at 15:53
  • @drk did you find any solution for android P ? – Anurag Goel Sep 24 '18 at 03:30
  • Not yet, unfortunately. I guess it has something to do with Accessibility services but I haven't figured it out yet. – drk Sep 25 '18 at 04:42
  • 2
    I have found a workaround for Android P latest build on Pixel 2 see below. Not sure if it is super generic yet – D2TheC Oct 11 '18 at 09:38
  • This is working for RealMe Device but I want this for Samsung, Oppo and Vivo but not working for that. @R.Zagórski – Mr.Javed Multani Feb 26 '23 at 16:32
6

Go with this

<uses-permission android:name="android.permission.PREVENT_POWER_KEY" />

And when you want to capture that event do this

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_POWER) {
        // this is method which detect press even of button
        event.startTracking(); // Needed to track long presses
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_POWER) {
        // Here we can detect long press of power button
        return true;
    }
    return super.onKeyLongPress(keyCode, event);
}

It can be done with broadcast receiver. see this answer

Community
  • 1
  • 1
TapanHP
  • 5,969
  • 6
  • 37
  • 66
6

I see many answers trying to hookin to the powerbutton. However, wouldn't it be more easy to listen for the shutdown event? I'm not sure if this fits your need, but it seems like it.

Simply register a Broadcastreceiver to the following action(s):

<receiver android:name=".ShutdownReceiver">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_SHUTDOWN" />
        <action android:name="android.intent.action.QUICKBOOT_POWEROFF" />
    </intent-filter>
</receiver>

Don't forget to add the following permission:

<uses-permission android:name="android.permission.DEVICE_POWER" />

Robin Dijkhof
  • 18,665
  • 11
  • 65
  • 116
5

For Long Press.... Please Try this...

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class demo extends Activity {


    private final List<Integer> blockedKeys = new ArrayList<>(Arrays.asList(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_POWER));


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
        setContentView(R.layout.demo);


    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (!hasFocus) {
            // Close every kind of system dialog
            Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
            sendBroadcast(closeDialog);
            Toast.makeText(demo.this, "Your LongPress Power Button", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onBackPressed() {
        // nothing to do here
        // … really
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (blockedKeys.contains(event.getKeyCode())) {


            return true;
        } else {

            return super.dispatchKeyEvent(event);
        }
    }
}

Long Press Button Of power show toast for test...

enter image description here

Morteza Jalambadani
  • 2,190
  • 6
  • 21
  • 35
Arjun saini
  • 4,223
  • 3
  • 23
  • 51
  • I'd like to implement the `getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);` on a service, is it possible? – Skizo-ozᴉʞS ツ Aug 30 '16 at 09:00
  • http://stackoverflow.com/questions/7538923/keyguardmanager-flag-dismiss-keyguard-for-service See the Terrence Answer For run on service – Arjun saini Aug 30 '16 at 09:11
  • the thing is is there any way to show a dialog when the device is locked? I mean I know how to show a toast when the device is unlocked, the thing is is there any way to show it when the device is locked? – Skizo-ozᴉʞS ツ Aug 30 '16 at 09:11
  • If i don't want to show the options that shows when I press the power button I have to call this? FLAG_DISMISS_KEYGUARD – Skizo-ozᴉʞS ツ Aug 30 '16 at 10:29
5

This simple solution works:

    @Override
public boolean dispatchKeyEvent(KeyEvent event) {
    int keyPressed = event.getKeyCode();
    if(keyPressed==KeyEvent.KEYCODE_POWER){
        Log.d("###","Power button long click");
        Toast.makeText(MainActivity.this, "Clicked: "+keyPressed, Toast.LENGTH_SHORT).show();
        return true;}
    else
        return super.dispatchKeyEvent(event);
}

If you want to stop system pop-up, use the onWindowFocusChangedmethod specified in one of the answers below.

The output will show the toast with "Clicked:26" as attached below.enter image description here

DsD
  • 1,081
  • 8
  • 11
4

The method used above by R. Zagórski worked for me very well for a long time, but from some revision of Android 9 the onCloseSystemDialogs method stopped receiving "globalactions".

As a workaround, my app is already using Accessibility. From inside the AccessibilityService you can add this code in onAccessibilityEvent

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    Log.i(TAG,"Accessibilityevent");
    if (event == null || event.getPackageName()==null||event.getClassName()==null)
        return;


    if (event.getClassName().toString().contains("globalactions")){
        //TRIGGER YOUR CODE HERE

    }
        ....

Note that were I put the comment "TRIGGER YOUR CODE HERE" I actually just call the same code that was previously in onCloseSystemDialogs.

Note that so far this is only tested to be working on a Pixel 2 running latest Android. I do not have any other phones running a recent enough version so can't test if the solution is sufficiently generic across phones.

D2TheC
  • 2,203
  • 20
  • 23
  • 1
    Can you please post your accessibility service configuration? What flags and other parameters are you using to receive the "globalactions" callback? – drk Jan 20 '19 at 14:59
  • 1
    If anyone is running into this, by default if you don't specify the event types then you won't see `globalactions` event. The following is seemingly the most minimalist accessibility service configuration to have the `globalactions` callback: `` – drk Jan 21 '19 at 10:58
  • @drk not working – Vlad Nov 13 '21 at 14:28
3

Try this :

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_POWER) {
        Intent i = new Intent(this, ActivitySetupMenu.class);
        startActivity(i);
        return true;
    }

    return super.dispatchKeyEvent(event);
}
mrid
  • 5,782
  • 5
  • 28
  • 71
2

From Skizo-ozᴉʞS question, what I understand, you actually want to prevent the behavior of the power button and that can't be done. just like the home button pressed some events in android can't be prevented to avoid people having apps they can't quit.

1

You can catch the power button event by overriding onKeyDown method

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_POWER) {
            Log.e("key","long");
            /*//disable the system dialog
            Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
            sendBroadcast(closeDialog);*/
            Toast.makeText(getApplicationContext(),"Power",Toast.LENGTH_SHORT).show();
        }
        return super.onKeyDown(keyCode, event);
    }

As my knowledge we can't do this outside activity (such as background service)

Jinesh Francis
  • 3,377
  • 3
  • 22
  • 37
1

The accepted answer works flawlessly till Android Oreo but not in Android P. To make it work in android P you'll need to use Accessibility Service

Service Class

import android.accessibilityservice.AccessibilityService;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;

import com.bm.yucasa.Utils.LocationSetup;

import static com.bm.yucasa.controller.AppController.TAG;

public class PowerServiceTwo extends AccessibilityService {
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        Log.i(TAG,"Accessibilityevent");
        if (event == null || event.getPackageName()==null||event.getClassName()==null)
            return;


        if (event.getClassName().toString().contains("globalactions")){
            LocationSetup locationSetup = 
LocationSetup.getInstance(PowerServiceTwo.this);
            if (locationSetup.isConnected) {
                locationSetup.startOnDemandLocationUpdates(false, "0");
            } else {

                //Toast.makeText(getApplicationContext(), "please check gps", Toast.LENGTH_SHORT).show();
            }

        }
    }

    @Override
    public void onInterrupt() {

    }
}

As you cannot use startService() method to start an Accessibility Service,

So here it is how you can enable it.

Intent openSettings = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
openSettings.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openSettings);
Jaswant Singh
  • 9,900
  • 8
  • 29
  • 50
0

Try this :

 @Override
   public boolean onKeyLongPress( int keyCode, KeyEvent event ) {
     if( keyCode == KeyEvent.KEYCODE_POWER ) {
       //Handle what you want in long press.

       return true;
     }
     return super.onKeyLongPress( keyCode, event );
   }

Add this in Manifest :

 <uses-permission android:name="android.permission.DEVICE_POWER" />
 <uses-permission android:name="android.permission.PREVENT_POWER_KEY" />
Dipali Shah
  • 3,742
  • 32
  • 47
0

If you listen is screen sleep or wakes so this code for you

public class MyBroadCastReciever extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
        //Take count of the screen off position
    } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
        //Take count of the screen on position
    }
}
}

Add this in Manifest :

<receiver android:name=".ShutdownReceiver">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_SHUTDOWN" />
        <action android:name="android.intent.action.QUICKBOOT_POWEROFF" />
    </intent-filter>
</receiver>