Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a system overlay where the home buttons still work?

Tags:

android

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

,

like image 426
Cameron McBride Avatar asked Jan 20 '11 18:01

Cameron McBride


People also ask

What is screen overlay setting?

Screen overlay is a feature of modern Android smartphones and tablets that allows compatible applications to appear on top of others.

What is resource overlay?

A runtime resource overlay (RRO) is a package that changes the resource values of a target package at runtime. For example, an app installed on the system image might change its behavior based upon the value of a resource.

What is Android system overlay?

An overlay is a focused view that handles tasks that are too complex for a complication, tile, or notification. Overlays are immersive. In some ways they are similar to a mobile app's main user interface.


2 Answers

  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.

like image 83
hackbod Avatar answered Oct 08 '22 06:10

hackbod


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

like image 18
amirlazarovich Avatar answered Oct 08 '22 04:10

amirlazarovich