Is there a way on android to keep the status bar while disabling all interaction you can do with it, like pulling it down?
I want to keep the information this bar gives, but I don't want users to interact with it.
This is the method that I like to use. You can unwrap it from the method and place it inside a base Activity instead. iirc, I got this from StackOverflow as well, but I didn't make a note of it so I'm not sure where the original post is.
What it basically does is place a transparent overlay over the top bar that intercepts all touch events. It's worked fine for me thus far, see how it works for you.
You MAY need to put this line in the AndroidManifest:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
I have it in my project, but I can't remember if it's because of this or something else. If you get a permission error, add that in.
WindowManager manager;
CustomViewGroup lockView;
public void lock(Activity activity) {
//lock top notification bar
manager = ((WindowManager) activity.getApplicationContext()
.getSystemService(Context.WINDOW_SERVICE));
WindowManager.LayoutParams topBlockParams = new WindowManager.LayoutParams();
topBlockParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
topBlockParams.gravity = Gravity.TOP;
topBlockParams.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;
topBlockParams.width = WindowManager.LayoutParams.MATCH_PARENT;
topBlockParams.height = (int) (50 * activity.getResources()
.getDisplayMetrics().scaledDensity);
topBlockParams.format = PixelFormat.TRANSPARENT;
lockView = new CustomViewGroup(activity);
manager.addView(lockView, topBlockParams);
}
and CustomViewGroup is
private class CustomViewGroup extends ViewGroup {
Context context;
public CustomViewGroup(Context context) {
super(context);
this.context = context;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i("StatusBarBlocker", "intercepted by "+ this.toString());
return true;
}
}
Also! You also have to remove this view when your activity ends, because I think it will continue to block the screen even after you kill the application. Always, always ALWAYS call this onPause and onDestroy.
if (lockView!=null) {
if (lockView.isShown()) {
//unlock top
manager.removeView(lockView);
}
}
Related
I want to detect pressing of a back button in service. I've just tried this code but it didn't show me any log. Can somebody explain me why? And what should I do to make it work?
The whole idea of doing this was was taken from this tutorial http://www.kpbird.com/2013/03/android-detect-global-touch-event.html
public class MyService extends Service implements View.OnKeyListener{
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
LinearLayout touchLayout = new LinearLayout(this);
// set layout width 30 px and height is equal to full screen
LayoutParams lp = new LayoutParams(30, LayoutParams.MATCH_PARENT);
touchLayout.setLayoutParams(lp);
touchLayout.setBackgroundColor(Color.RED);
touchLayout.setOnKeyListener(this);
WindowManager mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
// set layout parameter of window manager
WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
30, // width of layout 30 px
WindowManager.LayoutParams.MATCH_PARENT, // height is equal to full screen
WindowManager.LayoutParams.TYPE_PHONE, // Type Phone, These are non-application windows providing user interaction with the phone (in particular incoming calls).
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // this window won't ever get key input focus
PixelFormat.TRANSLUCENT);
mParams.gravity = Gravity.LEFT | Gravity.TOP;
mWindowManager.addView(touchLayout, mParams);
}
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK){
Log.v("Point","KeyCode_Back");
return false;
}
return false;
}
}
Your Service is not a View, implementing a View.OnKeyListener does not deliver your desired functionality.
A Service is intended to be an "Activity without UI" which runs in the background of your application. You can use Binders/Broadcasts to communicate with your service but UI interaction is best left to Activity/Fragments.
Annex:
I guess you are trying to build a overlay like in the link you posted in the comment. This Tutorial is from 2013 so things have changed.
In general the Android system discourages App beheaviour like the below described method. Coding like this, goes into the category Lockscreen/Kiosk-App behaviour which is considered as malware.
If you want to accomplish a little side menu inside your app you can do this perfectly fine without using such a service. Outside your App you still have the options of using widgets, which are more user friendly than hardcoding something on the screen.
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.
I am working on a project on android studio. I need to detect whether the screen is touched or not in a background service (and pop up a message for that). But, I have problem detecting whether the screen is touched or not in a background service without affecting user using the smartphone.
When I say "detect in background", I mean the effect of the app will not interfere with what the user is doing on the screen. My progress below should explain what i mean here:
I made an app that will detect whether the user touch the screen in background by implementing onTouchListener in android and use a layout for that onTouchListener. I did this by following the instruction in the website
"http://kpbird.blogspot.com.au/2013/03/android-detect-global-touch-event.html"
and I made some changes to the layout to cover the whole screen so it can detect the whole screen.
But, after I ran my app which created the service that keeps detecting screen touch, every time when the user touch the screen, that touch is absorbed by my service (the layout), so the user can't properly use their phone anymore (like pressing an icon to start other apps). This is because the layout covers the whole screen and blocking any touch info to the icon underneath it (the layout detect screen touch), so the icon don't know there is a touch and hence will not react to user. But I want my app and service to allow the user to use their phone normally.
I heard that the service in android is designed to not interact with user (i.e. screen touches) and work in background, but I want to know if there is a way around this.
Below is the code of my service:
public class GlobalTouchService extends Service implements OnTouchListener{
private String TAG = this.getClass().getSimpleName();
// window manager
private WindowManager mWindowManager;
// linear layout will use to detect touch event
private LinearLayout touchLayout;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
// create linear layout
touchLayout = new LinearLayout(this);
// set layout width 30 px and height is equal to full screen
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
touchLayout.setLayoutParams(lp);
// set color if you want layout visible on screen
//touchLayout.setBackgroundColor(Color.CYAN);
// set on touch listener
touchLayout.setOnTouchListener(this);
// fetch window manager object
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
// set layout parameter of window manager
WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT, // width is equal to full screen
WindowManager.LayoutParams.MATCH_PARENT, // height is equal to full screen
WindowManager.LayoutParams.TYPE_PHONE, // Type Phone, These are non-application windows providing user interaction with the phone (in particular incoming calls).
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // this window won't ever get key input focus
PixelFormat.TRANSLUCENT);
mParams.gravity = Gravity.LEFT | Gravity.TOP;
Log.i(TAG, "add View");
mWindowManager.addView(touchLayout, mParams);
}
#Override
public void onDestroy() {
if(mWindowManager != null) {
if(touchLayout != null) mWindowManager.removeView(touchLayout);
}
super.onDestroy();
}
public void showAlert(View view) {
AlertDialog.Builder myAlertBuilder = new AlertDialog.Builder(this);
myAlertBuilder.setMessage(getString(R.string.alertMessage))
.setTitle(getString(R.string.alertTitle))
.setPositiveButton(getString(R.string.alertPositiveChoice), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setNegativeButton(getString(R.string.alertNegativeChoice), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
onDestroy();
}
});
AlertDialog myAlert = myAlertBuilder.create();
myAlert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
myAlert.show();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
//if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_UP)
// Log.i(TAG, "Action :" + event.getAction() + "\t X :" + event.getRawX() + "\t Y :" + event.getRawY());
////////////////////
View myView = null;
//showAlert(myView);
///////////////////////
return false;
}
}
You have onTouch method there. Just return false and touch won't be absorbed by your app.
Please also check this:
https://stackoverflow.com/a/6384443/1723095
Edit:
It's probably not what you want but if you have rooted device you can do something like that:
adb shell getevent dev/input/event1
In my case event1 is responsible for touch events but it can differ.
I am attempting to familiarize myself with the android Service, for the purpose of overlaying the screen and drawing to it/interacting with the user outside of the app.
I know services aren't meant to interact with the user, but I can't find another way of accomplishing this.
I have found some resources that were quite a bit of help here on Stack Overflow. Here is another project on GitHub that is an example of something similar, but done in a different way.
As of right now, just because I am learning and want to accomplish this specific useless thing, I want there to be a Toast every time the screen is touched.
From reading and looking, my understanding is I need to start an Intent of a service. I must use the service to add a ViewGroup to the WindowManager. The handling of the touches and what is drawn to the screen is handled on the ViewGroup.
Yes, I have the service and overlay permission properly placed in the AndroidManifest.xml
Here are my 3 classes
public class MainActivity extends Activity
{
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startService(new Intent(this, MyService.class));
finish();
}
}
public class MyService extends Service
{
#Override
public IBinder onBind(Intent p1)
{
// TODO: Implement this method
return null;
}
private MyView view;
#Override
public void onCreate()
{
// TODO: Implement this method
super.onCreate();
view = new MyView(this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.RIGHT | Gravity.TOP;
params.setTitle("Load Average");
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(view, params);
}
}
public class MyView extends ViewGroup
{
public MyView(Context context){
super(context);
setLayoutParams(new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
#Override
protected void onLayout(boolean p1, int p2, int p3, int p4, int p5)
{
// TODO: Implement this method
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
// TODO: Implement this method
Toast.makeText(getContext(), "Touched", Toast.LENGTH_SHORT).show();
return true;
}
}
This code compiles and runs with no errors, and while messing with it, I CAN draw things to the screen in the draw method, but the toast in onTouchEvent never appears on the screen. Why won't it show up?
Edit: Using setOnTouchListener(...): and creating the toast in there didn't work either.
Your service creation and drawing is OK, as is your toast code. Throw a breakpoint on the Toast.makeText() and see if it is even getting called. My suspicion is that it is not.
Try creating a OnTouchListener and binding it to the ViewGroup, rather than using OnTouchEvent. This has worked for me in the past.
Edit: Corrected to OnTouchListner.
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);
}