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;
}
}
Related
my Android application has to detect the user interactions in order to use method A or method B in a loop.
I call this method in the onCreate() of the MainActivity...
#SuppressLint("ClickableViewAccessibility")
private void detectUserInteraction() {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
0, 0, 0, 0,
WindowManager.LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
final WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
if (wm != null) {
final View view = new View(this);
view.setOnTouchListener((v, event) -> {
UserInteractionManager.getInstance().setRecentUserInteraction(true);
return false;
});
wm.addView(view, params);
}
}
...and is not working for a few part of the users (latest Huawei owners). I have the log files of these users and the method only works as I expected a few minutes.
Anyone has any idea to fix the issue of the method?
Thank you a lot.
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);
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 implemented some features by integrating accessibility service for my application.
I currently have a 'system overlay' ImageView that can detect TouchEvent's. however i have seen it done that immersive mode, hiding the statusbar or navbar is possible without root.
i need to beable to access the view of my touch detection window in order to process any accessibility
My Accessibility service contains:
...
#Override
public void onServiceConnected() {
serviceInstance = this;
}
...
my touch detector:
...
WindowManager wm;
private MyAccessibilityService myAccessibilityService;
private ImageView mTouchDetector;
#Override
public void onCreate() {
super.onCreate();
myAccessibilityService = MyAccessibilityService.getSharedInstance();
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
100,
100,
0, 0,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, debugMode());
params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mTouchDetector, params);
}
...
I have partially solved this problem, however having some trouble with the back button, check here to see if it gets resolved.
create the service as normal but add these flags to the overlay:
to the window manager:
TYPE_PRIORITY_PHONE, FLAG_FULLSCREEN |
FLAG_NOT_TOUCH_MODAL
to the view:
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
FloatingView.setSystemUiVisibility(uiOptions);
Android floating window with hidden statusbar Accessibility problems
I am trying to add a view(ImageButton for example) to Window using WindowManager.addView(button),
What are the layout params so that the button can be seen on "lock screen".
Thanks in advance.
You can try adding type as WindowManager.LayoutParams.TYPE_SYSTEM_ERROR in the WindowManager.LayoutParams constructor which is illustrated below :
WindowManager.LayoutParams params=new WindowManager.LayoutParams();
params.type=WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
The aforementioned lines will make the view to appear on lock screen where you can capture touch events, click events and many others; However, you can use the below-mentioned flag for the non-touchable view.
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
Note: As per Android Developers The above-mentioned both flags are deprecated in API Level 26 and so you can use the flag TYPE_APPLICATION_OVERLAY as a substitute.
You have to make an activity with transparent background with view on the top which you want to show on the lockscreen. Now call that activity when your phone wakes up.
Note : You have to make an service which will start your activity. You have to register a broadcast receiver to that service.
public class CrackService extends Service
{
CrackView renderView;
LayoutParams params;
WindowManager wm;
#Override
public void onCreate()
{
params = new WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.RIGHT | Gravity.TOP;
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(renderView, params);
}
#Override
public IBinder onBind(Intent intent)
{
// TODO Auto-generated method stub
return null;
}
}