27

I am trying to create a button that is always on the screen.

  1. The button should be clickable and anything directly under the button should not be activated on a press.
  2. 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.
  3. 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.LayoutParams(
        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

,

Cameron McBride
  • 6,779
  • 11
  • 40
  • 52
  • Can you please post the final answer or update your question? also, what are the permissions you used to build it? – shaimagz Feb 15 '11 at 11:44

2 Answers2

18
  1. 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).

  2. 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.

  3. 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 use FLAG_WATCH_OUTSIDE_TOUCH.

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
hackbod
  • 90,665
  • 16
  • 140
  • 154
  • 3
    Wow thanks! Using type_system_alert with flags not_focusable | not_touch_modal has done the trick. The buttons or selectors dont show their clicked animation, but I have gotten around that by using OnClick the clicks and OnTouch to change the background for action_down and action_up. – Cameron McBride Jan 20 '11 at 23:49
  • 2
    "It should not be possible to stop home from working." Actually @pcm2a is correct. I have tested this on a stock Motorola Droid 3 and a TYPE_SYSTEM_ERROR or TYPE_SYSTEM_ALERT window with the aforementioned flags can consume key events including the HOME key (in fact even if the events are not consumed the default behavior does not occur). This is quite strange as I was under the impression this was the one key that could never be intercepted by any application. – Tom Oct 10 '11 at 02:38
  • @Tom I think when Dianne said it isn't possible to make the soft key buttons not work, she meant that it is impossible to prevent the user from clicking the soft key buttons. Of course you can have your window override the default behavior of the buttons, but you can't "disable" the home button (i.e. make it non-clickable), for example. This is because those buttons exist as part of a separate window owned by the system. – Alex Lockwood Jul 31 '13 at 17:27
  • BTW: @pcm2a is right. HW home button does not work with FLAG_NOT_FOCUSABLE on Android 4.0.4, tested on both emulator and real device ;-) – gingo Jan 21 '14 at 13:50
  • @pcm2a Your comment saved me from a MESS. My thanks to everybody who contributed. – Joel Dec 31 '16 at 22:37
18

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

amirlazarovich
  • 906
  • 7
  • 6