How to control WindowManager overlay priority in Oreo? - android

I have an overlay for my app, OV1, which is basically a button the user can move around on their screen. When this overlay is pressed, it spawns a second overlay, OV2, which contains multiple sub-buttons positioned around OV1's current location. OV2 also dims the rest of the screen while present, by use of the flag WindowManager.LayoutParams.FLAG_DIM_BEHIND.
One issue I had when initially implementing the above, is that the dimming effect was cast over everything that isn't OV2, including OV1, darkening and making it unclickable, which prevents OV2 from being closed as OV1 is the toggle button. The solution I found was setting layout params differently for each overlay:
OV1 gets:
params.type = WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
Whereas OV2 gets:
params.type = WindowManager.LayoutParams.TYPE_PHONE
Since OV1 had higher priority, it always stayed on top of OV2, so it wasn't affected by FLAG_DIM_BEHIND, and the user could interact with OV1 just fine to close the OV2 sub-button menu.
This has always worked fine, until I started updating the app to target Android's API 27 (Oreo). As part of the required changes, I've had to change both overlays' param types to WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, so I lost the ability to give OV1 a higher priority to make sure it stays on top of OV2. Is there an alternate way to replicate this behavior, ensuring OV1 remains the topmost overlay, or forcing it to the foreground whenever OV2 is spawned?

While I never found a solution for this, one workaround is to reattach the overlays to the window manager in the intended order (when several views of type TYPE_APPLICATION_OVERLAY are added to the window manager, the most recent ones go on top), by calling removeViewImmediate() followed by addView() for the view you want to be on top.
It's not pretty, as the user can see the overlay blink out of existence for a split second, but until there's a better way, it'll have to do.

Related

Keep android overlay always on top

I'm looking for a way to keep an application (System application) that creates an overlay of type WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY always on top of the screen, even on above any other overlay that can be created.
I'm adding the view directly with the WindowManager so I have tried few things:
Add an observer to the window and on each change call bringViewToFront for the overlay and it does not work.
Adding and removing the view each second..it works and it is always over other overlays but performance is very bad (Logically..)
Is what I'm looking for even possible? Thanks

Receiving hidden status bar/entering a full screen activity event on a Service

I have a service that adds some views to the window manager (something like facebook's chat heads). This views stay on top of everything all the time, even when I'm on the camera app.
Is it possible to know when some activity (like the camera or a video player) is using full screen so I can hide my views?
Edit:
There's something called View.OnSystemUiVisibilityChangeListener but it requires API 11. My target is 9, but it looks like this is exactly what I need. Any alternative for API 9?
Ok, if anyone is interested, I've solved my problem this way:
Added a transparent view to the window manager, with 1px width and match_parent height. Then added a global layout listener to this view, and everytime there's a change on the layout, I check the position of this view on screen. If it's Y is 0, then the status bar is not visible, so a full screen app must be running. This works even better than the native View.OnSystemUiVisibilityChangeListener because it seems there are some apps that somehow don't trigger that method (like the new yahoo weather).

Showing an interactive floating layout during calls

Background
There are some nice apps out there that show some layout on top , while the user is making a call or answering one (like "current caller id").
I need to create an app with the ability to show something on top , during a call, and allow it to be interactive.
The problem
Using broadcastReceiver ,foreground service and SYSTEM_ALERT permission, I've succeeded showing something on the screen during calls.
As long as the content being shown is static, I have no problems.
However, I've noticed that when I try to make the content being shown to be interactive , I face some problems:
Everything is jumpy and this includes not only animations, but also setting visibility to visible/gone. I hate to think how it would work like when I need to make things draggable.
Not sure if this is the reason, but using the SlidingDrawer make the entire width belong to the SlidingDrawer and you cannot click through it. This means that if its location is at the bottom, you can't touch the "answer" button when someone calls you.
The question
What is the reason for those problems?
How can I fix them and be able to show things right?
How do other apps handle it right ?
EDIT: about the SlidingDrawer , it seems that it has terrible bugs about its location and size, and the content area, even when it's not shown to the user and the user can see through, it cannot be touched through. Still, I don't know why, and how to fix it, and I also don't know why things are so jumpy compared to normal apps (probably because of over-drawing, but it's really really slow).
Maybe this question should be more general: how to make a floating window like on AirCalc, that can be moved easily yet still be quite fast.
For the dragging functionality, I've tried to get the layoutParams of the root view (which is of type WindowManager.LayoutParams ) that I show, update it and set it again, but for some reason it didn't do anything. Wonder what I'm doing wrong.
EDIT: it seems that i should be using windowManager.updateViewLayout for updating the layoutParams. Using this post , I've made it perfectly draggable.
Ok, I've come up to some conclusions about this, first to answer my original questions:
it's probably because of overdraw and the views i've used. I wanted to try out libraries that could replace the slidingDrawer , but each had a different problem. using simple views proved that the idea in general works.
in the case of visibility changes, it was jumpy because the size of the view wasn't able to fit using the current WindowManager.LayoutParams size.
slidingDrawaer does have issues since it uses the whole size it get when closed or opened.
now to the rest of the issues :
unable to drag ? instead of the regular setLayoutParams , use windowManager.updateViewLayout .
unable to touch outside of the view ? set its minimal size according to your needs. you can also set the window flags so that touch event would go through .
want to listen to calls events ? you can use the broadcastReceiver for triggering the showing of the app, but I suspect that hanging the call might cause the intent be received later sometimes. I think you can use telephonyManager and listen to events there, using the service you run in the foreground (that i've created just to make sure the app won't close in the middle).
if anyone else has questions, i can help.

Detect Status Bar Visibility / TYPE_SYSTEM_OVERLAY not resizing automatically

I'm using the well known TYPE_SYSTEM_OVERLAY method to create a full screen overlay that's always on top of other views.
On a Moto Atrix2 API15 and for each AVD from API 10 through 17 the overlay view resizes automatically (expands vertically) when the status bar is hidden by another app. The overlay appears to "stay on top" of all other views, as I think it should. When the status bar is in view again, the overlay reduces it's size automatically again (less vertical pixels, basically subtracting the height of the status bar).
This is the desired behavior and am inclined to think this is not the exception or flawed behavior due to it working on many different emulators as well as at least one device manufacturer (motorola), perhaps more, but I don't have access to addtl devices.
The problem: On a Samsung GS2 (API15) & also tested on Kindle Fire (API10?), the overlay does not automatically resize (fill the space where the status bar once was) when the status bar is hidden by another app (status bar behind full screen app). On these devices, after the view is created the first time I don't get any more events from an onSizeChanged override for the view.
Here's the pertinent code:
params = new WindowManager.LayoutParams(
1, WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSPARENT);
wm.addView(sizeLayout, params);
sizeLayout has the onSizeChanged override.
I've played around with just about every windowmanager layoutparam I can find, including SYSTEM_ALERT, FLAG_LAYOUT_IN_SCREEN, etc. I won't sleep until I can figure this out!
Edit 11/30/12: I found this from #deviant: https://stackoverflow.com/a/9195733/1851478 which is basically what I'm doing, however the problem remains with certain devices not resizing the system_overlay automatically. Perhaps I should raise a bug.
More INFO: IF I force a redraw (or even just load the overlay app) while there is a full screen app at the top of the view tree, my overlay still does not draw over the space the status bar normally takes up (even though the status bar is not in view at the time of draw), so it seems as if the overlay is not "overlayed" on top of the frontmost window which is full screen (and I've both created and tested many full screen apps using a variety of methods to make them go full screen). On these devices, the only way to get the overlay to draw over the status bar (even if the status bar is not in view) is to use the flag FLAG_LAYOUT_IN_SCREEN, but then the size of the layout never changes, is always full screen, and thus back to the original problem why this works on some devices but not others.
Edit 12/12/12: So I haven't had any further luck on this, and I did try using getLocalVisibleRect on views, but unfortunately since my app doesn't own the SystemUI views it can't detect what's below the overlays. I suppose with a custom ROM I could do this, but I need a solution without root.
I do see other apps on the marketplace are having the same problem detecting the status bar on the non-working phones I listed above. Example: cool tool, omega statusbar & status bar +. Bounty raised.
Edit 12/19/12 Pictures added. Note that on a working device the gray area also "covers" the black area (any app that is currently full screen), the black area actually covers entire screen, I'm just showing the gray area as my overlay even though it is also full screen (minus or plus the height of the status bar depending on working/non-working device and whether it is in view or not).
Hopefully i understand what you are after here but, in my app (TEAM BatteryBar) i had a similar problem. ( no root minimal permissions )
I couldn't detect a full screen on anything below API11 (and intermittent on some (Samsung mostly) devices above API11). So i made a custom listener for it by adding a second layout to the WindowManager with a different layout param then overrode the onLayout to check if the statusBar is still there or not by checking the window size. from there you can do what ever you need to to your main view.
I was going to tidy it up but it worked so i didn't :P code is over a year old and is from when i first started coding for android so yeah. Food for thought tho. hope it helps.
here is the code in a repo at my github with a copy paste straight out of my app on how i am using it. small snippet here
wm.addView(sizeLayout, params);// add your view
// then add the next one
Screendetect mDetector;
mDetector = new Screendetect(this);
mDetector.setOnFullScreenListener(new OnFullScreenListener() {
#Override
public void fsChanged(boolean FS_Bool) {
// TODO rethink this to be better.... bit hacky...
if (FS_Bool) {
Log.d("battbardetect", "Statusbar hidden");
FS_vissible(); // do what ever you need to
} else if (!FS_Bool) {
Log.d("battbardetect", "Statusbar Vissible");
FS_hidden(); // do what ever you need to
}
}
});
wm.addView(mDetector, params);
I think this may be the same issue with hiding the titlebar on dialog windows where the system sets the visibility to hidden instead of gone. This causes the dialog window to get incorrectly centered.
For that, here's how I fix it. This might work for your situation but you may need to adjust it for the name of the status bar identifier.
Window window = dialog.getWindow();
View view = window.getDecorView();
int topPanelId = getResources().getIdentifier("topPanel", "id", "android");
LinearLayout topPanel = (LinearLayout) view.findViewById(topPanelId);
topPanel.setVisibility(View.GONE);

Difference between screen lock and home button in Android

I do not understand the differences between when a user locks the screen (using the top screen lock button) and immediately returns to the application vs. when the user presses the home button and then immediately returns to the application.
It seems that all the same calls are being made. From my observations:
Called when home button or screen lock are pressed: onPause -> onStop
Called when application is pressed after home button or screen lock is re-pressed: onRestart -> onStart -> onResume
My individual problem:
This is particularly causing me greif because I am recreating a SurfaceView and a GLSurfaceView to a FrameLayout upon onResume, however, depending on the button pressed, the ordering of the elements is getting changed. I have the following code in my onResume:
cameraPreviewArea = (FrameLayout) findViewById(id.camera_preview);
cameraPreviewArea.addView(glView, glLayout);
cameraPreviewArea.addView(camprevSurfaceView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
This has the effect of displaying my glSurfaceView on top in the following situations: the first time the app has launched, and when the app is being resumed from being screen-locked and then screen-unlocked. However, upon pressing the home button, and then reopening the application, the SurfaceView is being placed ON TOP of the glSurfaceView!
If I switch the addView calls as follows, the opposite situations will occur. I could fix this with some boolean flag, but it it unclear where I would set the boolean because of my uncertainty as to the difference between a screen lock/unlock and the home button. Also, I do not want to solve the problem in this manner anyway because it seems hacky and lacks any real understanding of the problem.
Thank you in advance!
In general, there is no reason you should need to constantly add and remove views from the hierarchy of your Activity, and removing this code will make your application more consistent.
Since both of the views you are interested in are SurfaceView components, if there is some action that you need to take when the window becomes visible or hidden, you can take advantage of the SurfaceHolder.Callback to monitor the onSurfaceCreated() and onSurfaceDestroyed() methods.
This specific situation - using two surfaceViews and specifying their Z order within a window - does not seem to currently be supported by Android. This thread over at the android developer group shares the following information:
Multiple active overlapping surface views, of any sort, are not
currently supported by the framework. You may get them to work, but
it is mostly due to luck -- the view hierarchy does not define the
Z-ordering of those surfaces, nor try to ensure they are Z-ordered in
any particular way, so this may change for whatever reason.
Well... there you go!
...But for anyone attempting this I found a workaround: make your camera preview size a tiny 1x1 square. This will allow you to display both simultaneously (because a camera preview must be visible in order for the preview to continue) and ignore the pesky SurfaceView issues that the cameraPreview presents you with. I believe there is a better solution using strictly one GLSurfaceView, but it is not compatible below 3.0.

Categories

Resources