I need to dismiss system alert window on back
pressed and home
button event.I have tried with onKeyEvent
but in vain. As we can't capture the back
pressed event in a service, how to achieve this?
Back pressed events with system alert window
4.6k views Asked by user3595323 AtThere are 10 answers
Use the code below
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
exitByBackKey();
//moveTaskToBack(false);
return true;
}
return super.onKeyDown(keyCode, event);
}
protected void exitByBackKey() {
AlertDialog alertbox = new AlertDialog.Builder(this)
.setMessage("Do you want to exit application?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
// do something when the button is clicked
public void onClick(DialogInterface arg0, int arg1) {
finish();
//close();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
// do something when the button is clicked
public void onClick(DialogInterface arg0, int arg1) {
}
})
.show();
}
It's simple. Follow these steps:
- Create a view like Relative Layout, Linear Layout or Frame Layout Dynamically. 2. Override the
dispatchKeyEvent
while creating the view. - Add your original view into this dynamically created view with
addView()
method. - Add the dynamically created view to your Window Manager or Alert Dialog whichever you want.
In addition to @Eliran Kuta's solution, this is more simple answer for Back button.
val view = getAlertView()
val windowParam = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT, // whatever
WindowManager.LayoutParams.WRAP_CONTENT, // whatever
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, // use WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY before Oreo
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // whatever
PixelFormat.TRANSLUCENT // whatever
)
view.isFocusableInTouchMode = true
view.setOnKeyListener { view, keyCode, event ->
when (keyCode) {
KeyEvent.KEYCODE_BACK -> {
// do your work here
true
}
else -> false
}
}
val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
windowManager.addView(view, windowParam)
Define a custom layout and override dispatchKeyEvent
, for example:
public class CustomSystemAlertWindow extends FrameLayout {
public static final String TAG = "CustomSystemAlertWindow";
private WeakReference<Context> mContext;
public CustomSystemAlertWindow(Context context) {
super(context);
mContext = new WeakReference<Context>(context);
// Set a background color to identify the view on the screen
this.setBackgroundColor(getResources().getColor(android.R.color.holo_red_light));
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
Log.d(TAG, "back button pressed");
if (mContext != null && mContext.get() != null) {
WindowManager wm = (WindowManager) mContext.get().getSystemService(Context.WINDOW_SERVICE);
wm.removeView(this);
}
return true;
}
return super.dispatchKeyEvent(event);
}
}
Then add the view with this code:
CustomSystemAlertWindow customSystemAlertWindow = new CustomSystemAlertWindow(context);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
wm.addView(customSystemAlertWindow, params);
When you press the back button the view will dismiss.
- Show the Alert window through the Activity so you can detect it.
Implement the code to detect easily Back Button or Home Button pressed.
public class alertPopup extends Activity {
Context context;
final AlertDialog alertDialog;
String TAG = "your Activity Name"
boolean homePressed = false; // to detect the Homebutton pressed
@Override
public void onCreate(Bundle savedInstanceState) {
AlertDialog.Builder builder = newAlertDialog.Builder(YourActivity.this, R.style.AppCompatAlertDialogStyle);
builder.setTitle("AlertDialog Title");
..........
....... // Build ur AlertDialog
alertDialog= builder.create();
alertDialog.show();
//to detect Alert Dialog cancel when user touches outside the Dialog prompt
alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
Log.v(TAG,"Alert Dialog cancelled when user touches outside the Dialog prompt")
}
});
}
@Override
public void onBackPressed()
{
Log.v(TAG,"Back Button Pressed");
super.onBackPressed();
alertDialog.dismiss(); //dismiss the alertDialog
alertPopup.this.finish(); // Destroy the current activity
homePressed = false;
}
@Override
public void onResume() {
super.onResume();
homePressed = true; // default: other wise onBackPressed will set it to false
}
@Override
public void onPause() {
super.onPause();
if(homePressed) {
alertDialog.dismiss(); //dismiss the alertDialog
alertPopup.this.finish(); // Destroy the current activity
Log.v(TAG, "Home Button Pressed"); }
}
public void onDestroy(){
super.onDestroy();
}
}
Note:
Add this Permission in Android Manifest to show the alert Window .
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Happy Coding :)
Since it's a service that hosting an overlay window, It's a bit tricky solution but it is possible.
You should handle these 2 cases separately (overriding home button press, and back button press).
1. Overriding home button press:
Create this HomeWatcher class which contains a BroadcastReceiver that will notify when home button was pressed. Register this receiver only when your window comes up.
Android: associate a method to home button of smartphone
Inside your service onCreate method use this:
HomeWatcher mHomeWatcher = new HomeWatcher(this);
mHomeWatcher.setOnHomePressedListener(new OnHomePressedListener() {
@Override
public void onHomePressed() {
yourWindow.hide() //means: windowManager.removeView(view);
}
@Override
public void onHomeLongPressed() {
}
});
mHomeWatcher.startWatch();
2. Overriding back button press:
The idea is creating an empty layout as a data member of your window class, and attach your view to it (even if its an inflated XML layout).
For example, this is gonna be your window class:
public class MyWindow
{
private WindowManager windowManager;
private WindowManager.LayoutParams params;
private View view;
// Add this empty layout:
private MyLayout myLayout;
public MyWindow()
{
windowManager = (WindowManager) context.getSystemService(context.WINDOW_SERVICE);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.your_original_window_layout, null);
// Add your original view to the new empty layout:
myLayout = new MyLayout(this);
myLayout.addView(view, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
// And show this layout instead of your original view:
public void show()
{
windowManager.addView(myLayout, params);
}
public void hide()
{
windowManager.removeView(myLayout);
}
}
And now create the MyLayout class to override the back button press:
public class MyLayout extends LinearLayout
{
private MyWindow myWindow;
public MyLayout(MyWindow myWindow)
{
super(myWindow.context);
this.myWindow = myWindow;
}
@Override public boolean dispatchKeyEvent(KeyEvent event)
{
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK)
{
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0)
{
getKeyDispatcherState().startTracking(event, this);
return true;
}
else if (event.getAction() == KeyEvent.ACTION_UP)
{
getKeyDispatcherState().handleUpEvent(event);
if (event.isTracking() && !event.isCanceled())
{
// dismiss your window:
myWindow.hide();
return true;
}
}
}
return super.dispatchKeyEvent(event);
}
}
I know it's a bit complicated as I said since it's a system alert window hosted by a service, BUT it's working. I have the same issue as well and it has been solved exactly like that. Good luck.
I understand that you are using the <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
permission for showing a floating view.
Using a floating view you can intercept the back
button press, but the home
button press cannot be intercepted (android won't let you primarily because of security reasons).
To intercept the back
button press you need to add a wrapper when you inflate your floating view.
Your wrapper should look like this:
// Wrapper for intercepting System/Hardware key events
ViewGroup wrapper = new FrameLayout(this) {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
hideAddNotesFloatingView();
return true;
}
return super.dispatchKeyEvent(event);
}
};
Then you add it to your floating view as a root:
mAddNoteFloatingView = mInflater.inflate(R.layout.floating_add_note, wrapper);
My complete code looks like this:
private void addFloatingView() {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_PHONE,
0,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER | Gravity.LEFT;
params.x = 0;
params.y = 0;
// Wrapper for intercepting System/Hardware key events
FrameLayout wrapper = new FrameLayout(this) {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
// Add your code for handling the back button press
return true; // Return true means that the event was handled
}
return super.dispatchKeyEvent(event);
}
};
mAddNoteFloatingView = mInflater.inflate(R.layout.floating_view, wrapper);
mWindowManager.addView(mAddNoteFloatingView, params);
}
Declare this on your activity. super.OnBackPressed automatically calls back method in android. it will surely cancel your dialog.
in addition, your dialog must look like this.
or you can set Negative button..
Hope this helps!