Создание системного оверлея, где все еще работают кнопки домой? - PullRequest
27 голосов
/ 20 января 2011

Я пытаюсь создать кнопку, которая всегда на экране.

  1. Кнопка должна быть кликабельной и что-нибудь прямо под кнопкой не должен быть активирован на прессе.
  2. Активность или домашний экран работает за кнопкой все равно должно работать, это означает, что пользователь все еще должен быть способен взаимодействовать с домом экран или приложение.
  3. программная клавиша кнопки все равно должны работать: дома, назад, меню и т. д.

Следующий код работает # 1 и # 2, но кнопки программных клавиш больше не работают:

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

Изменение этого параметра отключает оверлей от кликов, но # 2 и # 3 работают:

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,

Наконец, в этом примере щелкается оверлей и то, что находится за ним:

WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,

Как я могу изменить это так, чтобы наложение было активным, что непосредственно под ним не было активным, и все, что находилось за пределами наложения, работало, включая кнопки «Домой»?

Примером приложения, которое выполняет все это, является Super Manager.

ОБНОВЛЕНИЕ: я обнаружил, что следующее позволяет использовать кнопку домой, но все же не другие кнопки:

        WindowManager.LayoutParams.TYPE_SYSTEM_ALERT | 
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
            WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH

Ответы [ 2 ]

18 голосов
/ 04 мая 2011

Я вижу, что pcm2a не опубликовал работающее решение, поэтому я мог бы сделать это для него. Примечание : хотя я представляю работающее решение, использование таких методов не на 100% верно в отношении дизайна архитектуры Android.Я рекомендую попросить кого-нибудь из команды разработчиков Android получить дополнительную информацию (и, пожалуйста, опубликуйте свой ответ здесь, чтобы все могли его увидеть).

Разрешения

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

Сервис

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>

Удачного кодирования! амир

18 голосов
/ 20 января 2011
  1. Вы не можете ИЛИ типы окон вместе.Вы создадите некоторый случайный другой тип.И, честно говоря, используемые вами типы окон на самом деле не предназначены для использования приложениями (поэтому в них есть слово «система»).

  2. Что вы имеете в видупо кнопкам софт-клавиш больше не работают?Не должно быть возможности остановить работу дома, если это большая проблема (я хотел бы знать код для этого).Другие ключи доставляются в текущий фокус ключа;если вы не хотите быть сосредоточенным, используйте FLAG_NOT_FOCUSABLE.

  3. Документация для каждого из этих флагов должна быть достаточно ясной, что это делает, поэтому выберите флаги, которые делают то, чтохочу.FLAG_NOT_FOCUSABLE потому что вы не хотите принимать ключевые события.FLAG_NOT_TOUCH_MODAL потому что вы не хотите блокировать сенсорные события, которые находятся за пределами вашего окна.Вы не говорите, что хотите узнать о прессах за пределами вашего окна, поэтому нет смысла использовать FLAG_WATCH_OUTSIDE_TOUCH.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...