I'm showing a overlay window from service in my app. I want to hide the window when press back button. So to get the back button event I do the following code
class MyView extends MyLayout{
public MyView(Context context){
super(context);
LayoutInflater.from(context).inflater(R.layout.my_view,this);
}
}
Class MyLayout extends FrameLayout{
public MyLayout(Context context){
super(context);
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
Log.e("key event", "" + event.getKeyCode());
return super.dispatchKeyEvent(event);
}
}
//service code
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS |
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
PixelFormat.TRANSLUCENT);
final MyView myview = new MyView(this);
windowManager.addView(myview,param);
But dispatchKeyEvent method never called on back button pressed. I Googled and found out this is the way to capture the back button event. Somehow it's not working in my case. What am I missing ?
If you just want to capture the back button pressed event use:
#override
public void onBackPressed(){
// Do what you need done here
// ... or pass super.onBackPressed();
}
Real problem is the window params. I need to only WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH flags which prevent other app to get key events. So the correct code is following
//service code
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS |
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
PixelFormat.TRANSLUCENT);
Related
I am trying to block touch on a device using service and a transparent overlay.
The code seems to work fine on Android Oreo but doesn't work properly on devices running Android version below Oreo:
private void setWindowParams(){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
} else {
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
}
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
FrameLayout noTouchUi = new FrameLayout(this);
wm.addView(noTouchUi,params);
}
Here I setup a FrameLayout noTouchUi to intercept touch as an overlay using service using onTouchListener
noTouchUi.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG,"touch event");
return true;
}
});
This works properly on Android 8, but doesn't work on Android 7, the overlay shows up properly, but the items behind overlay gets clicked. What I want to do is block that click.
Change the type from:
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
to:
WindowManager.LayoutParams.TYPE_PHONE
and remove flags:
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
and:
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
I'm using a transparent screenoverlay to detect when a user long presses the power key of their phone (or rather, when the shutdown option dialog appears), this is working fine.
Unfortunately, when this screenoverlay is active, the soft keyboard stops appearing and that is a problem for me. How can I prevent that?
The code I'm using is heavily based on this: Detect power button long press
public void warnOnShutdown() {
if (Settings.canDrawOverlays(this)) {
LinearLayout linearLayout = new LinearLayout(getApplicationContext()) {
public void onCloseSystemDialogs(String reason) {
if ("globalactions".equals(reason)) {
AntitheftStateManager.setShuttingDown(AntitheftService.this, true);
}
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
return super.dispatchKeyEvent(event);
}
};
linearLayout.setFocusable(true);
View view = LayoutInflater.from(this).inflate(R.layout.system_overlay, linearLayout);
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
//params
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
100,
100,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
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;
windowManager.addView(view, params);
}
}
Layout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="1dp"
android:layout_height="1dp"
android:orientation="vertical">
</LinearLayout>
Edit:
I should probably mention that the LinearLayout is attached to the window manager from a service, that means that the keyboard is not just blocked for my app, it's blocked for the whole phone as long as the service is running.
That's what happens when someone elses code is taken at face value. Turns out that changing the type from TYPE_SYSTEM_ALERT to TYPE_SYSTEM_OVERLAY solved my problem.
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
100,
100,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
PixelFormat.TRANSLUCENT);
I have an activity that creates a view of type TYPE_SYSTEM_ALERT and I set a touch listener to the view. Is it possible to record the touch but still send the touch event to other views (example: to a widget from my home screen)?
Here is my code:
public class TestGestures extends Activity implements View.OnTouchListener {
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final WindowManager.LayoutParams param=new WindowManager.LayoutParams();
param.flags=WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
final View view=findViewById(R.id.view1);
final ViewGroup parent=(ViewGroup)view.getParent();
if(parent!=null)
parent.removeView(view);
param.format= PixelFormat.RGBA_8888;
param.type=WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
param.gravity= Gravity.TOP|Gravity.LEFT;
param.width=view.getLayoutParams().width;
param.height=view.getLayoutParams().height;
//view.setBackgroundColor(android.R.color.white);
final WindowManager wmgr=(WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
wmgr.addView(view,param);
view.setOnTouchListener(this);
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
return false;
}
}
When I'm in the home screen, the touch events are not sent to the widgets
You'll have to play around with the parameters that you're using to setup your WindowManager.LayoutParams. For example, the code below will make the touch events be delivered to the Activity (or View) which is below the view that you just created:
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
On the other hand, if you change TYPE_SYSTEM_OVERLAY to TYPE_SYSTEM_PHONE, the input events will be delivered to the View that is on top of the other ones:
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
So, in your case, since you want the input events to be delivered to the views below, try setting the TYPE_SYSTEM_OVERLAY flag.
I need to show a system overlay window on top of any running apps. It works fine, but only when my app's activity is in foreground. Here is the code I use:
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
WindowManager.LayoutParams.FLAG_DIM_BEHIND |
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER | Gravity.TOP;
params.dimAmount = 0.3f;
wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
mainAlertView = View.inflate(ctx, R.layout.system_alert_view, null);
wm.addView(mainAlertView, params);
The view added to WindowManager contains empty LinearLayout to which I add child views at runtime:
mainAlertLayout.addView(childView);
Manifest xml has SYSTEM_ALERT_WINDOW permission defined:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
It all works magnificently while my activity is running in foreground. As soon as I switch to any other app or home screen no overlay is shown. I debugged the code, and it indeed is running wm.addView(), and its not throwing any exceptions. I also played around with LayoutParams flags (removed KEEP_SCREEN_ON and related etc), but nothing made any difference.
Android 4.4.2 fwiw
You need to create a service to keep the system overlay( alert) to show even when your application closed.
Create a simple service , I will call as FloatingService , which will show the overlay(alert) when onStartCommand is called . Return START_STICKY to keep the service run in background.
public class FloatingService extends Service {
private WindowManager windowManager;
private View floatingView;
WindowManager.LayoutParams params;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flag, int startId){
// show the alert her
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
// use your custom view here
floatingView = View.inflate(getBaseContext(),R.layout.floating_layout,null); //floatingView.setOnTouchListener(mOnTouchListener);
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER | Gravity.CENTER;
// add the view to window manger
windowManager.addView(floatingView, params);
return START_STICKY;
}
}
Add an listener to the view so that you can close the overlay(alert) when required
floatingView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
windowManager.removeView(floatingView);
floatingView = null;
}
});
Finally , register the service in manifest
<service android:name="com.example.app.services.FloatingService" />
And System Alert permission
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
The problem was that I was running addView() call in main view's post() method:
mainView.post(new Runnable() {
public void run() {
wm.add(mainView);
}
);
Instead if I just run it in main thread it works fine. I use de.greenrobot.eventbus and its very simple to post Runnable to event bus and ensure it executes in main thread:
public void onEventMainThread(UIRunnableEvent event) {
event.r.run();
}
public static class UIRunnableEvent {
protected Runnable r;
protected UIRunnableEvent(Runnable r) {
this.r = r;
}
}
I have a window manager set up and it has the flag FLAG_NOT_FOCUSABLE which means it will not get the focus of key events. Is there anyway to reverse this upon a click for example? For example, if I click on a button, I want the window to be clickable. How could I do this?
Simply update the window manager with the new parameters.
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//remove FLAG_NOT_FOCUSABLE
params.flags = params.flags & ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
//update the view
windowManager.updateViewLayout(view, params);
}
});