In Honeycomb I was able to create a system overlay with TYPE_SYSTEM_OVERLAY and receive touches with FLAG_WATCH_OUTSIDE_TOUCH.
Now ICS has changed something. I can still create the system overlay, but i can't get touches.
Now, i am able to create the same with TYPE_SYSTEM_ALERT and get touches, but it just catches the touchevents, and not passing them on like in Honeycomb.
Any idea's?
Kind regards
To create an overlay view, when setting up the LayoutParams
DON'T set the type to TYPE_SYSTEM_OVERLAY.
Instead set it to TYPE_PHONE.
Use the following flags:
FLAG_NOT_TOUCH_MODAL
FLAG_WATCH_OUTSIDE_TOUCH
FLAG_NOT_TOUCH_MODAL << I found this one to be quite important. Without it, focus is given to the overlay and soft-key (home, menu, etc.) presses are not passed to the activity below.
Everything you describe is true. It is presumably to tighten up security, as the former behavior was the source of tapjacking attacks. I wrote a blog post recently about this change.
Any idea's?
Don't use either of them.
Related
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.
I am playing around with controlling the UI Navigation buttons in ICS.
The current mechanism for suppressing the Nav Buttons is to call setSystemUiVisibility from a View using the SYSTEM_UI_FLAG_HIDE_NAVIGATION or SYSTEM_UI_FLAG_LOW_PROFILE flags. This seems like a strange place for setting these flags, as most other related settings (such as hiding the status bar) have been done through window LayoutParams properties.
My question is if any of you have ideas for a good way to do it from an Activity context. Currently my app is designed to start with a base activity class which contains any functionality I want throughout my entire application. Specific Activities are then derived from that base class. I would like to be able to set the UI nav flags from that base Activity so I don't have to do it in multiple spots throughout my source code... but my base Activity does not contain any View objects.
As a secondary statement, what I would really like to be able to do is completely remove the NAV buttons (such as using SYSTEM_UI_FLAG_HIDE_NAVIGATION) and not have them come back on user input (giving my app complete control over the UI). I know this is not something that any app from the market should be able to do... but I am not developing something that will available via the market. My current plan involves a custom build of the OS that will allow me to accomplish this, but it would be nice if there was some method of eliminating those soft buttons in the meantime.
Thanks!
This is what I put in onCreate of my activities:
View main_layout = dialog.findViewById(android.R.id.content).getRootView();
main_layout.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
It's almost like calling it from an activity context. At least it's not dependent on having a view defined at compile time. I know STATUS_BAR_HIDDEN is deprecated, but I can't get SYSTEM_UI_FLAG_LOW_PROFILE to compile at the moment...
But +1 on the "this seems like a strange place for these settings". Should be something you can define in the manifest once for the entire app.
You can't completely remove the ICS navigation buttons.
You can hide them completely, but they'll reappear as soon as you touch the screen:
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
Unfortunately, some ICS UI layers like Samsung's TouchWiz won't recognize SYSTEM_UI_FLAG_HIDE_NAVIGATION.
Alternatively, you can minimize them and they'll only appear when the bar it tapped:
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
You'll probably have to build your own ROM to eliminate them completely.
I am trying to get multiple touch events on a system overlay view, but am only receiving the MotionEvent.ACTION_OUTSIDE event.
Is there any possible way of getting multiple touch events on a system overlay?
Any examples or links would be very helpful.
To create an overlay view, when setting up the LayoutParams you need to set the type to TYPE_SYSTEM_OVERLAY and use the flag FLAG_WATCH_OUTSIDE_TOUCH. This presents a problem because as the Android documentation states:
you will not receive the full down/move/up gesture, only the location of the first down as an ACTION_OUTSIDE.
In order to receive the full array of touch events you need to use the TYPE_SYSTEM_ALERT type, but this causes the overlay to take over the screen and stop interaction with other elements. The solution is to use both TYPE_SYSTEM_OVERLAY and TYPE_SYSTEM_ALERT and switch between them by changing the type of the LayoutParams as needed.
This is accomplished by:
Watch for the ACTION_OUTSIDE motion event.
When it occurs, test if it occured within the overlay.
If it did, switch the LayoutParams type to TYPE_SYSTEM_ALERT
Once the interaction with the overlay is complete, switch back to TYPE_SYSTEM_OVERLAY
Repeat
The one thing to keep in mind is that the ACTION_OUTSIDE motion event is always passed on to the rest of the elements on the screen. So, for example, if the overlay is on top of a button, that button will also receive the motion event and there is no way to stop it.
Also make sure you add the SYSTEM_ALERT_WINDOW permission to the mainifest file.
I've posted the complete solution here:
http://www.jawsware.mobi/code_OverlayView/ (UPDATED)
It includes all the source code and a link to download the entire example project.
Update for Android 4.0 - 1/3/2013
To create an overlay view, when setting up the LayoutParams DON'T set the type to TYPE_SYSTEM_OVERLAY.
Instead set it to TYPE_PHONE.
Use the following flags:
FLAG_NOT_TOUCH_MODAL
FLAG_WATCH_OUTSIDE_TOUCH
FLAG_NOT_TOUCH_MODAL << I found this one to be quite important. Without it, focus is given to the overlay and soft-key (home, menu, etc.) presses are not passed to the activity below.
Also, the previous link (above) has been updated to reflect this update.
Starting from Android 4.x, Android team Android team fixed a potential
security problem by adding a new function adjustWindowParamsLw() in which it
will add FLAG_NOT_FOCUSABLE, FLAG_NOT_TOUCHABLE and remove FLAG_WATCH_OUTSIDE_TOUCH flags for TYPE_SYSTEM_OVERLAY window.
That's TYPE_SYSTEM_OVERLAY window won't receive any touch event on ICS platform.
Updated: 2016/5/4
TYPE_TOAST cannot receive touch events on Android 4.0~4.3. It's the same as TYPE_SYSTEM_OVERLAY. On Android 4.4+, TYPE_TOAST removed from the excluded list, so you can use TYPE_TOAST to receive touch events on Android 4.4+ devices.
My question refers directly to this question. The answer to that question shows how one can create a ViewGroup, embed it inside a WindowManager, and allow the WindowManager to catch MotionEvents through onTouchEvent(MotionEvent event). WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH is the flag that allows this this ViewGroup to receive MotionEvents. However, according to documentation, this flag
...will not receive the full down/move/up gesture
I want to know if there's a work-around or a way so that I can get all touch events including down, move, and up. A proof of concept is in the app Wave Launcher which uses the same concept but is able to receive more than just a single ACTION_OUTSIDE event.
No you can not, and that is very much by design.
Wave Launcher doesn't do this, it has a UI element where you start your touch gesture and then as with standard even dispatching all touch events are delivered to the window of the first down point until the final up.
I realize this is an old question, but I've stumbled across it while trying to accomplish something similar. I've also found this, hopefully it is helpful to someone: http://www.section465.com/code_OverlayView/
To create an overlay view, when setting up the LayoutParams you need
to set the type to TYPE_SYSTEM_OVERLAY and use the flag
FLAG_WATCH_OUTSIDE_TOUCH. This presents a problem because as the
Android documentation states: "you will not receive the full
down/move/up gesture, only the location of the first down as an
ACTION_OUTSIDE." In order to receive the full array of touch events
you need to use the TYPE_SYSTEM_ALERT type, but this causes the
overlay to take over the screen and stop interaction with other
elements. The solution is to use both TYPE_SYSTEM_OVERLAY and
TYPE_SYSTEM_ALERT and switch between them by changing the type of the
LayoutParams as needed.
I have an app that runs fullscreen by using:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
Because of this the layout, android:windowSoftInputMode="adjustResize" is not working properly, i.e. it dose not resize.
Is there any way to get over the problem?
FYI: This is an existing AOSP (Android Open Source Project) bug: http://code.google.com/p/android/issues/detail?id=5497
Ideally this bug would be fixed, but until then here are a couple thoughts of how it could be worked around. Since I have no idea what application scenario this pertains to, these may not be very applicable.
In agreement with my best interpretation of the previous answer, design your layout so that adjustPan works ok with it. The first thing I can think of here is not having any headers or footers that are intended to remain on screen when the keyboard is up.
Don't use FLAG_FULLSCREEN with a layout that can accept text input. Possibly it wouldn't be a big deal to show the status bar when accepting input. However, for something that views content with embedded input fields (like a web browser) that has a fullscreen mode, this doesn't make much sense at all.
Implement adjustResize-like behavior of your own. I'm not sure how well this would work, but possibly you could write a subclass of whichever class is causing the keyboard to be shown (ex: EditText) where you either track when the keyboard is shown or take over the calls to show and hide the keyboard (overriding at least onKeyUp and onTouchEvent). When shown, resize your content - possibly with a best guess of the softinput height, since users can install different soft input methods. I believe this would be technically difficult and not reasonable to attempt without extreme need.
Instead of android:windowSoftInputMode="adjustResize" you can try with android:windowSoftInputMode="adjustPan"