Android: How to cover Soft Nav buttons with View using LayoutParams - android

I have a custom view that adds a variable filter (light effects, etc) over the entire screen. This runs as a service when the main activity is closed, so the user can change apps etc with the filters running.
The code is effectively:
EffectsView view = new EffectsView();
WindowManager windowManager = (WindowManager) service.getSystemService(Context.WINDOW_SERVICE);
windowManager.addView(view, new Params());
private class Params extends WindowManager.LayoutParams
{
public Params()
{
super();
this.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
this.width = WindowManager.LayoutParams.MATCH_PARENT;
this.height = WindowManager.LayoutParams.MATCH_PARENT;
this.flags = 280;
this.format = PixelFormat.TRANSPARENT;
}
}
I'm currently using the flags = 280 parameter as it covers the notification bar, but it doesn't cover the soft nav buttons. This is most noticeable in the App Switcher.
I have tried the using the flag WindowManager.LayoutParams.FLAG_FULLSCREEN, but that doesn't cover the nav buttons or the notification bar.
Is there a way to get the view to cover the nav bar area?
Note: my supported SDK versions are 17+

Related

How to overlap system bottom navigation bar.?

I am trying to overlap system bottom navigation bar using window manager but i can`t do it. I am Using bellow code.I have set gravity to bottom, therefore it show view layer in bottom of my activity view not not overlapping bottom navigation bar.
public void onCreate(Bundle savedInstancestate)
{
super.onCreate(savedInstancestate);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);7
manager = ((WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
localLayoutParams.gravity = Gravity.BOTTOM;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
// this is to enable the notification to recieve touch events
//WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN |
//WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
// Draws over navigation bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
//localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
localLayoutParams.height = (int) (50 * getResources().getDisplayMetrics().scaledDensity);
localLayoutParams.format = PixelFormat.TRANSPARENT;
view = new customView(this);
manager.addView(view, localLayoutParams);
setContentView(R.layout.Imges);
}
public class customView extends ViewGroup {
public customView(Context context) {
super(context);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.v("customView", "------Intercepted-------");
return true;
}
}
Using this code i can't overlap navigation,Its shows new custom view in bottom of my activity view but can not overlap navigation bar with custom view.
any one can help me on this, to overlap navigation bar with custom view.?
There are actually some solutions.You cannot overlap in the means of making it totally disappear since there are devices in market without the hardware buttons. However you can elegantly arrange your layout accordingly.
For example,
Add this to your styles.xml (v21 )in a values dir:
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
or if it does not work,
boolean hasMenuKey = ViewConfiguration.get(getContext()).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if(!hasMenuKey && !hasBackKey) {
// Do whatever you need to do, this device has a navigation bar
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
);
params.setMargins(0, 0, 0, 75);
entrancelayout.setLayoutParams(params);
entrancelayout.requestLayout();
}
I had a FrameLayout, you can use it for whatever layout you have. SetMargins adds margin to buttom in example. It assumes System Bar is there if there are no hardware back and menu buttons.

Blocking status bar in top down swiping

I've a full screen application covering the entire screen, top status bar included.
Since a top / down swipe is enabled to show some options to the user, it happens that swiping from top to down, the status bar is showing (as when you want to see notifications and swipe top down).
Is there a way to avoid this ?
Use type TYPE_SYSTEM_ERROR for WindowManager.LayoutParams, to create hide impossible fullscreen view.
Swipes for show status bar and navigation will be blocked.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMainLayout = (RelativeLayout) LayoutInflater.from(this).inflate(R.layout.activity_fullscreen, null);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
WindowManager.LayoutParams handleParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
handleParams.gravity = Gravity.TOP;
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
windowManager.addView(mMainLayout, handleParams);
}
And you should be show it after display power on (unlock screen) if needs. Add code:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
There are a couple of options. Before the line
super.onCreate(savedInstanceState)
add the following:
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
This should completely get rid of the status bar. Basicaly the feature no title removes the top toolbar, and to ensure fullscreen size we are setting fullscreen flags for height and width.
As far as preventing from swiping at all, there is a trick that can be used. Basically place an invisible view at the top once you are in true fullscreen that prevents any swiping. For example,
View disableStatusBarView = new View(context);
WindowManager.LayoutParams handleParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FILL_PARENT,
<height of the status bar>,
// This allows the view to be displayed over the status bar
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
// this is to keep button presses going to the background window
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
// this is to enable the notification to recieve touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
handleParams.gravity = Gravity.TOP;
context.getWindow().addView(disableStatusBarView, handleParams);
This creates an invisible view over the status bar that will receive the touch events and block the events from reaching the status bar, therefore preventing it to be expanded.
Alternatively, and this works the best, is to override the windowFocusChanged method. Essentially you are NOT preventing the status bar from expanding, but YOU ARE preventing use. Because once expanded, it is closed. I say that this works best because with the invisible view methodology, you may want to consider the dimensions of the screen to really make it effective for all device. So there is more work.
First, declare permission in manifest
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR"/>
Then Override onWindowFocusChanged method,
public void onWindowFocusChanged(boolean hasFocus)
{
try
{
if(!hasFocus)
{
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method collapse = statusbarManager.getMethod("collapse");
collapse .setAccessible(true);
collapse .invoke(service);
}
}
catch(Exception ex)
{
}
}

Prevent status bar for appearing android (modified)

I am implementing a kiosk mode application and i have successfully made the application full-screen without status bar appearance post 4.3 but unable to hide status bar in 4.3 and 4.4 as status-bar appears when we swipe down at the top of the screen.
I have tried to make it full screen by
speciflying the full screen theme in manifest
setting window Flags ie setFlags
setSystemUiVisibility
Possible duplicate but no concrete solution found
Permanently hide Android Status Bar
Finally the thing i want is, how to hide status bar permanently in an activity?? in android 4.3,4.4,5,6versions
We could not prevent the status appearing in full screen mode in kitkat devices, so made a hack which still suits the requirement ie block the status bar from expanding.
For that to work, the app was not made full screen. We put a overlay over status bar and consumed all input events. It prevented the status from expanding.
note:
customViewGroup is custom class which extends any
layout(frame,relative layout etc) and consumes touch event.
to consume touch event override the onInterceptTouchEvent method of
the view group and return true
Updated
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
customViewGroup implementation
Code :
WindowManager manager = ((WindowManager) getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE));
WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
// this is to enable the notification to recieve touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
localLayoutParams.height = (int) (50 * getResources()
.getDisplayMetrics().scaledDensity);
localLayoutParams.format = PixelFormat.TRANSPARENT;
customViewGroup view = new customViewGroup(this);
manager.addView(view, localLayoutParams);
In Android M you have to get an extra permission for making overlays. android.permission.SYSTEM_ALERT_WINDOW is not enough! So I used the code from the answer of Abhimaan within disableStatusBar() and had to make an intent to open the right settings dialog. I also added removing view in onDestroy() in order to enable status bar when the app exits. I also reduced the overlay height to 40 as it seems to be enough. Code works with 5.1 and 6.0 here.
public static final int OVERLAY_PERMISSION_REQ_CODE = 4545;
protected CustomViewGroup blockingView = null;
#Override
protected void onDestroy() {
super.onDestroy();
if (blockingView!=null) {
WindowManager manager = ((WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
manager.removeView(blockingView);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "Please give my app this permission!", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
} else {
disableStatusBar();
}
}
else {
disableStatusBar();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "User can access system settings without this permission!", Toast.LENGTH_SHORT).show();
}
else
{ disableStatusBar();
}
}
}
protected void disableStatusBar() {
WindowManager manager = ((WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
// this is to enable the notification to receive touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
localLayoutParams.height = (int) (40 * getResources().getDisplayMetrics().scaledDensity);
localLayoutParams.format = PixelFormat.TRANSPARENT;
blockingView = new CustomViewGroup(this);
manager.addView(blockingView, localLayoutParams);
}
For a project I worked on I had found a solution for this but it took a long time. Various threads on Stackoverflow and elsewhere helped me to come up with it. It was a work around on Android M but it worked perfectly. As someone asked for it so I thought I should post it here if it can benefit anyone.
Now that its been a while, I don't remember all the details, but the CustomViewGroup is the class which overrides the main ViewGroup, and detects that a user has swiped from top to show the status bar. But we didn't want to show it, so the user's intercept was detected and any further action was ignored, i.e. Android OS won't get a signal to open the hidden status bar.
And then the methods to show and hide the status bar are also included which you can copy/paste as is in your code where you want to show/hide the status bar.
/**
* This class creates the overlay on the status bar which stops it from expanding.
*/
public static class CustomViewGroup extends ViewGroup {
public CustomViewGroup(Context context) {
super(context);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.v("customViewGroup", "********** Status bar swipe intercepted");
return true;
}
}
public static void allowStatusBarExpansion(Context context) {
CustomViewGroup view = new CustomViewGroup(context);
WindowManager manager = ((WindowManager) context.getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE));
manager.removeView(view);
}
// Stop expansion of the status bar on swipe down.
public static void preventStatusBarExpansion(Context context) {
WindowManager manager = ((WindowManager) context.getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE));
Activity activity = (Activity) context;
WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
// this is to enable the notification to receive touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
//http://stackoverflow.com/questions/1016896/get-screen-dimensions-in-pixels
int resId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
int result = 0;
if (resId > 0) {
result = activity.getResources().getDimensionPixelSize(resId);
}
localLayoutParams.height = result;
localLayoutParams.format = PixelFormat.TRANSPARENT;
CustomViewGroup view = new CustomViewGroup(context);
manager.addView(view, localLayoutParams);
}

Showing refreshing message in Action Bar

I'm using an Action Bar (a regular one, not sherlock) in my android app, and when the app opens I want to show a refreshing message in the action bar. This means I want to hide the menu items and title (similar to how the GMail app appears when it's refreshing).
What is the best approach for this? Is it using a contextual action bar?
Is it possible to show the refreshing animation just below the action bar, like in the GMail app (ie, the blue lines sliding over).
I know I can use a 3rd party pull-to-refresh, but I'd prefer not to use this (as I don't need the pull-to-refresh capability).
I'm targeting Jelly Bean and newer devices.
Thanks!
I want to hide the menu items and title (similar to how the GMail app
appears when it's refreshing).
This can be done by using WindowManager.addView(View, LayoutParams). Here's an example of displaying a message on top of the ActionBar that should give you a pretty solid idea about how to proceed.
The layout
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#android:color/white"
android:textSize="18sp" />
Implementation
/** The attribute depicting the size of the {#link ActionBar} */
private static final int[] ACTION_BAR_SIZE = new int[] {
android.R.attr.actionBarSize
};
/** The notification layout */
private TextView mMessage;
private void showLoadingMessage() {
// Remove any previous notifications
removeLoadingMessage();
// Initialize the layout
if (mMessage == null) {
final LayoutInflater inflater = getLayoutInflater();
mMessage = (TextView) inflater.inflate(R.layout.your_layout, null);
mMessage.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_dark));
mMessage.setText("Loading...");
}
// Add the View to the Window
getWindowManager().addView(mMessage, getActionBarLayoutParams());
}
private void removeLoadingMessage() {
if (mMessage != null && mMessage.getWindowToken() != null) {
getWindowManager().removeViewImmediate(mMessage);
mMessage = null;
}
}
/**
* To use, #see {#link WindowManager#addView(View, LayoutParams)}
*
* #return The {#link WindowManager.LayoutParams} to assign to a
* {#link View} that can be placed on top of the {#link ActionBar}
*/
private WindowManager.LayoutParams getActionBarLayoutParams() {
// Retrieve the height of the status bar
final Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
final int statusBarHeight = rect.top;
// Retrieve the height of the ActionBar
final TypedArray actionBarSize = obtainStyledAttributes(ACTION_BAR_SIZE);
final int actionBarHeight = actionBarSize.getDimensionPixelSize(0, 0);
actionBarSize.recycle();
// Create the LayoutParams for the View
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, actionBarHeight,
WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP;
params.x = 0;
params.y = statusBarHeight;
return params;
}
Results
Conclusion
This implementation is very similar to Gmail and other apps, minus the pull-to-refresh pattern.
When you call showLoadingMessage, post a Runnable or use a View.OnClickListener. You don't want to call WindowManager.addView too early or you'll throw a WindowManager.BadTokenException. Also, it's important to call removeLoadingMessage in Activity.onDestroy, otherwise you run the risk of leaking the View you add to the Window.

How do I make an app/service for virtual/soft buttons for (Home, menu, Back, Search) always on top?

I want make an app/service that looks like (Nexus One touch buttons)
for the navigation keys (Home, menu, Back, Search)
The buttons should always stay on top and send the command to the actually app that's running.
Someone have ideas and sample codes how to do that?
Update:
I also see and test an app which shows a "cracked display" always on top
so that technique maybe should be useful to always show the buttons on top.
Those function, show the button and catch the "touch event" and send the event
to the active program should be in a service module which runs in background.
You cannot do this kind of application. First, you cannot keep an app always on top, then you cannot dispatch key events to other apps.
You could do this:
WindowManager mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
System.out.println("Accesibilty cargado correctametne");
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mTopView = (ViewGroup) inflater.inflate(R.layout.resourceshower, null);
LayoutParams mWmlp = new WindowManager.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
mWmlp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
mWmlp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
mWmlp.width = 100; //size of window
mWmlp.height = 50;//size of window
mWmlp.format = PixelFormat.TRANSPARENT;
mWmlp.x =50; //position of window
mWmlp.y = 50; //position of window
mWindowManager.addView(mTopView, mWmlp);
Then if you want to get button clicks inside it, in the layout you are inflating (R.layout.resourceshower) on the buttons add this: android:onClick="launch"
and create a public method with the same name like: public void launch(View v){..}
you must create this method in the service/activity you create the floating window.

Categories

Resources