Android 5.0, prevent status bar expansion - android

This is a very old problem, but after making so much effort, I still cannot figure out a possible solution.
The Aim
Actually quite simple. I would like to develop some full screen apps (e.g. games), where the status bar does not expand, however the user touches the screen.
The app should be able to work on Android 5.0, sdk 21 (Lollipop).
What I have done so far
I find this post, which describes how to cover the status bar with a CustomView that absorbs touch events, but it doesn't work for me ...
I even changed the size of the CustomView so that it covers the whole screen, but it still doesn't work, and it cannot even cover my button.
The codes I use are attached below. When run on the device, both the button and the status bar work - nothing is blocked.
Am I missing something?
Codes
In AndroidManifest.xml:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="#android:style/Theme.Holo.NoActionBar.Fullscreen">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
In activity_main.xml:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="#+id/text_show" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click"
android:id="#+id/button_click"
android:onClick="changeText"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
In MainActivity.java:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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);
setContentView(R.layout.activity_main);
}
boolean key = false;
public void changeText(View view) {
TextView tv = (TextView) findViewById(R.id.text_show);
if (key) {
tv.setText("hahaha");
} else {
tv.setText("eroero");
}
key = ! key;
}
In CustomViewGroup (copied from the above link):
public 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", "**********Intercepted");
return true;
}
}

Finally the best I can do is to use the Task Lock feature introduced in Android 5.0.
Reference: startLockTask.
The issues of this method:
Unless you have a helper app that grant lock task permission to your full screen app, the user will be asked if (s)he allows the lock.
Once locked, the user can still use physical buttons (home, back, volumn, etc.).
Since my intention is not to have a kiosk mode, I don't really care about point 2.
Point 1 is also acceptable, but a bit annoying since it will (and should) be asked every time onResume is called.
This can be solved by setting a service app as device owner, which grants permission to apps that demand it. But the user has to do this him(her)self (c.f. this answer), which is reasonable, otherwise an app would be able to totally kidnap your device.

Related

How to play YouTube in Background with Android Webview?

I want to "minimize" the application, leaving it in background doing exactly the same that when the ghostbutton mode is pressed when the user clicks a button (but don't finish it) How can I do that?
So far I'm able to create a Activity. I've initialize my members and load the WebView with "https://www.youtube.com". I'm also able to create a Service that lets me minimize the Activity, but what I want is to minimize the Activity that I've my WebView loaded. Problem When the Activity paused, WebView is also paused.
What I want now.
A ghost mode -> basically minimizing this current activity with
system overlay and showing notification for media controls that can
play/resume and exit from the ghost mode.
A MainActivity that is visible to me and only becomes the sevice
and show me
Notification control after pressing ghostmode btn
What I've handled so far
Screen orientation, meaning Activity doesn't recreate the WebView. If I'm watching some video,it just pause the video on screen orientation.
What answers I want
Can I make my current Activity to become a service?
If no, can I just create a SplashActivity that opens this make MainActivity and make this Service on button click.
AndroidManifest.xml
<service android:enabled="true" android:name=".Services.GhostModeService" />
android:hardwareAccelerated="true"
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="#string/app_name"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
MainActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG,"classmain-> onCreate");
initializeM();
settingWebview();
initializeNavigationTab();
myoutube.loadUrl(URL);
ghostModeServiceIntent=new Intent(MainActivity.this,GhostModeService.class);
}
These are my settings.
private void settingWebview() {
myoutube.setWebViewClient(new Myyoutube());
myoutube.getSettings().setLoadsImagesAutomatically(true);
myoutube.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
myoutube.getSettings().setBuiltInZoomControls(false);
myoutube.getSettings().setLoadsImagesAutomatically(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
myoutube.getSettings().setMediaPlaybackRequiresUserGesture(true);
myoutube.getSettings().setJavaScriptEnabled(true);
myoutube.getSettings().setPluginState(WebSettings.PluginState.ON);
}
GhostmodeService
private WindowManager mWindowManager;
private View mGhostmode;
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"classghostservice-> onCreate()");
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
mGhostmode = inflater.inflate(R.layout.layout_ghostmode,null,false);
//setting the layout parameters
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
500,
500,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.x=0;
params.y=0;
params.gravity=Gravity.END | Gravity.BOTTOM;
//getting windows services and adding the floating view to it
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mWindowManager.addView(mGhostmode, params);
}
Method that create my notification. This notification resumes the MainActivity.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"classghostservice-> onstartCommand()");
foregroundNotification(1);
return START_NOT_STICKY;
}
[SOLVED] After a week I'm able to answer this long awaited question of How to Run Youtube In background using Webview. Yes, webview requires UI to run we can't run it in background using service, even if you do find a you way, you will still get in issues.
The clever tricks are as following:
Yes, you need to add android.permission.SYSTEM_ALERT_WINDOW because you want to make a floating window that stays on top of other apps.
Let's say you have asked for permission and ready to start MainActivity, at this point forget about setContentView(R.layout.activity_main); instead you need to add your layout in WindowManager using windowManager.addView(yourLayout, yourWindowParams);
activity_main.xml
In my layout my parent layout was DrawerLayout
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent".....
So, I had to define my member variable as follows
private static DrawerLayout windowMain;
MainActivity
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
windowMain = (DrawerLayout) inflater.inflate(R.layout.activity_main, null);
Now the main part is by using this windowMain, you have to call
findViewById()
eg
windowMain.findViewById(R.id.btn);
windowMain.findViewById(R.id.webview);
You also need to define its WindowManager.LayoutParams. Initializing params.
WindowManager.LayoutParams=expandParams
expandParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON|
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
expandParams.gravity = Gravity.START | Gravity.TOP;
expandParams.x = 0;
expandParams.y = 0;
Finally, you need to add windowMain and its params into windowManager by
windowManager.addView(windowMain, expandParams);
Everything is done to make a window that floats around and take whole of your screen, then do what you need to do like loading URL in webview using wv.loadURL(url). This ensures that we have loaded our webview inside a window and we just need to update the params of our windowMain so that it minimize it or even completely disappear by setting w:0 and h:0 instead of MATCH_PARENT
You can have a btn_ghost that updatesthe window like:
windowManager.updateViewLayout(windowMain, ghostParams);
Lastly, when we've created this created we can call moveTaskToBack(true); this will stop out activity move it to task, becuase we don't need it. When finishing this activity that was created but stopped we must remove our windowMain .
windowManager.removeView(windowMain);
finish()

Detect press of back button

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.

How to disable Android status bar interaction

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);
}
}

Floating widgets/pop up window in android

Im developing an app where a floating component should appear when the call is received and the component will have several buttons to perform necessary actions.
I have tried the follow.
I implemented a popup window by making the main activity translucent.when this component pops up, Im able to move it on the screen, but since the activity is translucent, im not able to perform any other activity.
here u can see the popup window, i can move it, but i cannot scroll the menudrawer in the background. How can i implement in such a way that i can perform both operations, i.e on popupwindow and the background screen.
My codes
`public class MainActivity extends Activity {
int mCurrentX;
int mCurrentY;
private float mDx;
private float mDy;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
final View cv = new View(this);
TextView tv = new TextView(this);
tv.setBackgroundColor(0xffeeeeee);
tv.setTextColor(0xff000000);
tv.setTextSize(24);
tv.setText("click me\nthen drag me");
tv.setPadding(8, 8, 8, 8);
final PopupWindow mPopup = new PopupWindow(tv, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
OnTouchListener otl = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
mDx = mCurrentX - event.getRawX();
mDy = mCurrentY - event.getRawY();
} else
if (action == MotionEvent.ACTION_MOVE) {
mCurrentX = (int) (event.getRawX() + mDx);
mCurrentY = (int) (event.getRawY() + mDy);
mPopup.update(mCurrentX, mCurrentY, -1, -1);
}
return true;
}
};
tv.setOnTouchListener(otl);
mCurrentX = 20;
mCurrentY = 50;
cv.post(new Runnable() {
#Override
public void run() {
mPopup.showAtLocation(cv, Gravity.NO_GRAVITY, mCurrentX, mCurrentY);
}
});
}
}`
manifest
`<application android:label="#string/app_name" android:icon="#drawable/ic_launcher">
<activity android:name=".MainActivity"
android:theme="#android:style/Theme.Translucent.NoTitleBar"
android:label="#string/app_name"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name="PopupMainActivity"
android:label="#string/app_name"
android:theme="#style/Theme.FloatingWindow.Popup"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
android:clearTaskOnLaunch="true"
android:exported="true"
tools:ignore="ExportedActivity" />
</application>`
please help me on this. I want to implement a widget kind of component on the whole. Thanks!!
You have to start a foreground Service and draw using the WindowManager on top of everything else, manage your popup position, size, etc...
WindowManger windowManager = Context.getSystemService(Context.WINDOW_SERVICE);
You will also have to add this permission "android.permission.SYSTEM_ALERT_WINDOW" to your manifest.
A simpler solution would be to use a library called StandOut, it basically takes care of all that I mentioned above and provide extra features like:
Window decorators (titlebar, minimize/close buttons, border, resize handle)
Windows are moveable and resizable. You can bring-to-front, minimize,
and close
Minimized windows can be restored (the example APK demos this using
the notification panel)
Create multiple types of windows, and multiple windows of each type

Android setWindowLayoutMode not working before SDK 11

I'm trying to create a PopupWindow in Android that centers itself in the middle of the display and is dynamically sized to the content of the loaded view. In addition, any tap outside of the popup should dismiss the popup. In SDK versions 11 on, this code works just fine, however in SDK 10 (the minimum our app must support), the setWindowLayoutMode seemingly does nothing.
I've so far subclassed this logic, which seems clean and efficient to me, with the exception of the issue (bug?) with SDK 10. Any thoughts on what I am doing wrong? I see that setWindowLayoutMode has been around since version 3, so I'm having trouble believing it is simply not working as it is described to in the documentation. If an SDK bug is the case, how might I workaround the problem? I tried .measure() on the contentView with the screen dimensions as the restrictions with the intention of manually setting the window size, but the values it returned were wildly different from the expected results.
I can easily get the window centered by wrapping the TextView in a layout that I can set to match the screen dimensions, but then I lose the nice ACTION_OUTSIDE tap event, so I'd rather not stumble down that path if I can avoid it.
I should mention that the problem that is happening on SDK 10 is that the window simply doesn't appear... It is technically "appearing", either with dimensions of 0,0 or offscreen, as subsequent taps trigger the OnTouchListener, but it surely is not correctly displaying its contents.
public class InfoPopupWindow extends PopupWindow {
private View _parentView;
public InfoPopupWindow(Context context, View parentView) {
super(context);
LayoutInflater inflater = LayoutInflater.from(context);
View contentView = inflater.inflate(R.layout.window_info, null, false);
this.setContentView(contentView);
this.setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
// This combo of parameters sends outside events properly, and inside events as well.
this.setOutsideTouchable(true);
this.setBackgroundDrawable(new BitmapDrawable());
this.setTouchInterceptor(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
Log.d("InfoPopupWindow", "Outside Window Touch Event");
dismiss();
}
return true;
}
});
this.setAnimationStyle(R.style.PopupAnimation);
_parentView = parentView;
}
public void show() {
this.showAtLocation(_parentView, Gravity.CENTER, 0, 0);
}
}
R.layout.window_info
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/info_text"
android:id="#+id/textView"
android:background="#000000"
android:padding="15dp" />
Well, I never found the solution to the broken setWindowLayoutMode, but I was able to call this method from the constructor to get API 10 to correctly size the popup. The workaround was one I had tested before, but I had been misusing the MeasureSpec parameters. Hope this helps someone else out someday...
private void calculateAndSetContentViewSize(Context context, View contentView) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
int screenWidth, screenHeight;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {
Point windowSize = new Point();
display.getSize(windowSize);
screenWidth = windowSize.x;
screenHeight = windowSize.y;
} else {
screenWidth = display.getWidth();
screenHeight = display.getHeight();
}
int widthSpec = View.MeasureSpec.makeMeasureSpec(screenWidth, View.MeasureSpec.EXACTLY);
int heightSpec = View.MeasureSpec.makeMeasureSpec(screenHeight, View.MeasureSpec.UNSPECIFIED);
contentView.measure(widthSpec, heightSpec);
this.setWidth(contentView.getMeasuredWidth());
this.setHeight(contentView.getMeasuredHeight());
this.setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}

Categories

Resources