Creating a system overlay where the home buttons still work?
I am trying to create a button that is always on the screen.
- The button should be clickable and anything directly under the button should not be activated on a press.
- The activity or home screen running behind the button should still work, meaning the user should still be able to interact with the home screen or application.
- The soft key buttons should still work: home, back, menu, etc
The following code does #1 and #2 but the soft key buttons no longer work:
WindowManager.LayoutParams params = new WindowManager.LayoutPara开发者_开发问答ms(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
Changing it to this disables the overlay from clicks but #2 and #3 work:
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
Finally in this example the overlay and what is directly behind it gets clicked:
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
How can I change this so that the overlay is clickable, whats directly under it is not clickable, and everything outside of the overlay works including the home buttons?
An example application that performs all of this is Super Manager.
UPDATE: I've found that the following allows the home button to be used, but still not the other buttons:
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
,
You can't OR window types together. You will create some random other type. And to be honest, the window types you are using were really not intended for use by apps (that is why they have the word "system" in them).
What do you mean by "soft key buttons no longer work"? It should not be possible to stop home from working, if it is that is a big problem (I'd like to know the code to do this). The other keys are delivered to the current key focus; if you don't want to be focused, use
FLAG_NOT_FOCUSABLE
.The documentation for each of these flags should be pretty clear what it does, so pick the flags that do what you want.
FLAG_NOT_FOCUSABLE
because you don't want to take key events.FLAG_NOT_TOUCH_MODAL
because you don't want to block touch events that are outside of your window. You don't say you want to find out about presses outside of your window, so there is no reason to useFLAG_WATCH_OUTSIDE_TOUCH
.
I see that pcm2a didn't published a working solution so i might as well do it for him.
Note: Although i present a working solution, the use of such methods are not 100% right with respect to Android architecture design. I recommend asking someone from the Android dev team for further information (and please post their answer here for all of us to see).
Permissions
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Service
package com.vidmind.test.service.video;
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.VideoView;
public class TestVideoService extends Service {
/** Command to the service to display a message */
public static final int MSG_PLAY_VIDEO = 1;
public static final int MSG_VIDEO_LOOP_MODE = 2;
/** Bundle Strings */
public static final String KEY_VIDEO_URL = "KEY_VIDEO_URL";
public static final String KEY_VIDEO_LOOP_MODE = "KEY_VIDEO_LOOP_MODE";
// Binder given to clients
private static final String TAG = "TestVideoService";
final Messenger mMessenger = new Messenger(new IncomingHandler());
private LinearLayout mOverlay;
private VideoView mVideoView;
private boolean mVideoLoop = true;
/** ****************** Handler implementation class ****************** **/
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Bundle bundle;
switch (msg.what) {
case MSG_PLAY_VIDEO:
bundle = msg.getData();
String url = bundle.getString(KEY_VIDEO_URL);
play(url);
break;
case MSG_VIDEO_LOOP_MODE:
bundle = msg.getData();
boolean looping = bundle.getBoolean(KEY_VIDEO_LOOP_MODE);
setVideoLoop(looping);
break;
default:
super.handleMessage(msg);
}
}
}
/** ****************************************************************** **/
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Service has started");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Serice destroyed");
// Remove view from WindowManager
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.removeView(mOverlay);
mOverlay = null;
mVideoView = null;
}
/** ****************** Private Functions ****************** **/
/**
* Set video loop mode
* @param value
*/
private void setVideoLoop(boolean value) {
mVideoLoop = value;
}
/**
* Start playing the movie
* @param url
* @returns success/failure
*/
private boolean play(String url) {
boolean isSuccess = false;
if (mOverlay != null && mVideoView.isPlaying()) {
Log.w(TAG, "Cannot recreate video overlay");
return isSuccess;
}
// Create overlay video
createOverlay(mOverlay != null);
if (!mVideoView.isPlaying()) {
mVideoView.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.setLooping(mVideoLoop);
}
});
mVideoView.setVideoURI(Uri.parse(url));
mVideoView.requestFocus();
mVideoView.start();
isSuccess = true;
}
return isSuccess;
}
/**
* Create video overlay
*
* @param isCreated
*/
private void createOverlay(boolean isCreated) {
if (isCreated) return;
// Create System overlay video
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FILL_PARENT, 150,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.BOTTOM;
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
mOverlay = (LinearLayout) inflater.inflate(R.layout.main, null);
mVideoView = (VideoView) mOverlay.findViewById(R.id.video_player);
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mOverlay, params);
}
}
layout.main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<VideoView
android:id="@+id/video_player"
android:layout_width="120dp"
android:layout_height="120dp"/>
</LinearLayout>
Happy coding!
amir
精彩评论