Crear una ventana de superposición del sistema (siempre en la parte superior)

285

Estoy tratando de crear un botón / imagen de clic siempre superior que permanezca en la parte superior de todas las ventanas todo el tiempo.

La prueba de concepto es

He tenido éxito y tengo un servicio en ejecución ahora. El servicio muestra algo de texto en la esquina superior izquierda de la pantalla todo el tiempo, mientras que el usuario puede interactuar libremente con el resto de aplicaciones de manera normal.

Lo que estoy haciendo es una subclase ViewGroupy la agrego al administrador de la ventana raíz con una marca TYPE_SYSTEM_OVERLAY. Ahora quiero agregar un botón / imagen en la que se pueda hacer clic en lugar de este texto que pueda recibir eventos táctiles en sí mismo. Intenté anular "onTouchEvent" para todo ViewGrouppero no recibe ningún evento.

¿Cómo puedo recibir eventos solo en ciertas partes de mi grupo de vista siempre arriba? Amablemente sugiera.

public class HUD extends Service {
    HUDView mView;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(getBaseContext(),"onCreate", Toast.LENGTH_LONG).show();
        mView = new HUDView(this);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                0,
//              WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
//                      | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.RIGHT | Gravity.TOP;
        params.setTitle("Load Average");
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        wm.addView(mView, params);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Toast.makeText(getBaseContext(),"onDestroy", Toast.LENGTH_LONG).show();
        if(mView != null)
        {
            ((WindowManager) getSystemService(WINDOW_SERVICE)).removeView(mView);
            mView = null;
        }
    }
}

class HUDView extends ViewGroup {
    private Paint mLoadPaint;

    public HUDView(Context context) {
        super(context);
        Toast.makeText(getContext(),"HUDView", Toast.LENGTH_LONG).show();

        mLoadPaint = new Paint();
        mLoadPaint.setAntiAlias(true);
        mLoadPaint.setTextSize(10);
        mLoadPaint.setARGB(255, 255, 0, 0);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawText("Hello World", 5, 15, mLoadPaint);
    }

    @Override
    protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //return super.onTouchEvent(event);
        Toast.makeText(getContext(),"onTouchEvent", Toast.LENGTH_LONG).show();
        return true;
    }
}
coreSOLO
fuente
2
@coreSOLO, espero que obtengas tu respuesta, mis preguntas extrañamente similares no obtuvieron ninguna respuesta. :) +1
st0le
3
@ st0le Prometo que pasaré mi noche y día hasta que encuentre la solución y la comparta con ustedes :)
coreSOLO
1
@ st0le Ok, tal vez tú y yo podamos resolverlo juntos, darme un enlace de tu pregunta y hacerme saber cuánto progreso
pudiste
3
Es sorprendente que te onDraw()llamen. ¿No debería dispatchDraw()ser anulado en su lugar? Ver groups.google.com/forum/?fromgroups#!topic/android-developers/…
faizal

Respuestas:

158

Esta podría ser una solución estúpida. Pero funciona. Si puede mejorarlo, hágamelo saber.

OnCreate de su servicio: he usado WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCHflag. Este es el único cambio en el servicio.

@Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(getBaseContext(),"onCreate", Toast.LENGTH_LONG).show();
        mView = new HUDView(this);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.RIGHT | Gravity.TOP;
        params.setTitle("Load Average");
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        wm.addView(mView, params);
    }

Ahora, comenzará a obtener cada evento de clic. Por lo tanto, debe rectificar en su controlador de eventos.

En su evento táctil ViewGroup

@Override
public boolean onTouchEvent(MotionEvent event) {

    // ATTENTION: GET THE X,Y OF EVENT FROM THE PARAMETER
    // THEN CHECK IF THAT IS INSIDE YOUR DESIRED AREA


    Toast.makeText(getContext(),"onTouchEvent", Toast.LENGTH_LONG).show();
    return true;
}

También es posible que deba agregar este permiso a su manifiesto:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Sarwar Erfan
fuente
1
@pengwang: ¿qué errores estás recibiendo? Este código no es independiente, debe colocarlos en su proyecto con los nombres de archivo adecuados en el proyecto correspondiente.
Sarwar Erfan
44
@pengwang: Encuentre la implementación más simple aquí: docs.google.com/…
Sarwar Erfan
66
todo el código es correcto, encuentro mi error, olvido el enlace <uses-permission android: name = "android.permission.SYSTEM_ALERT_WINDOW" /> Sarwar Erfan, creo que editas tu respuesta, puede ser más maravilloso
pengwang
1
Esta respuesta es muy útil, excepto por una cosa. Como se usa en el código, WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, de acuerdo con la documentación, "no recibirá el gesto completo de subir / mover / bajar". En otras palabras, no es posible detectar deslizamientos o cualquier otra cosa que no sea un ACTION_DOWN. Bueno, sé que es posible detectar swipes porque Wave Launcher usa este concepto similar y son capaces de detectar swipes. ¿Alguien puede explicarme cómo funciona esto?
Brian
66
@SarwarErfan: utilicé esto, funciona perfectamente en 2.3, pero el evento onTouch no se invoca en Android 4.0, 1nd 4.1. Por favor ayuda
Sanjay Singh
66

Siguiendo la respuesta de @Sam Lu, Indeed 4.x bloquea ciertos tipos para que no escuchen eventos táctiles externos, pero algunos tipos, como TYPE_SYSTEM_ALERT, todavía hacen el trabajo.

Ejemplo

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
            PixelFormat.TRANSLUCENT);

    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
    View myView = inflater.inflate(R.layout.my_view, null);
    myView.setOnTouchListener(new OnTouchListener() {
       @Override
       public boolean onTouch(View v, MotionEvent event) {
           Log.d(TAG, "touch me");
           return true;
       }
     });

    // Add layout to window manager
    wm.addView(myView, params);

Permisos

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
amirlazarovich
fuente
55
Su fabricación ahorcado mi pantalla .. Cualquier idea
Viswanath Lekshmanan
Buena publicación. Esperemos que esto no esté deshabilitado en futuras versiones tampoco.
Cookster
Las animaciones no funcionarán en él ... :( Y no puede captar clics en ninguna vista en 4.2.X. ¿Alguna sugerencia?
Kamran Ahmed
Funciona, pero cuando estoy usando ese acceso directo (es decir, Alerta de nivel del sistema), no se muestran otros cuadros de diálogo. Por qué ?
Chimu
@Arju, ¿qué quieres decir? ¿Estás tratando de eliminarlo?
amirlazarovich 01 de
18

Comenzando con Android 4.x, equipo de Android fija un problema potencial de seguridad mediante la adición de una nueva función adjustWindowParamsLw()en la que se va a añadir FLAG_NOT_FOCUSABLE, FLAG_NOT_TOUCHABLEy quitar FLAG_WATCH_OUTSIDE_TOUCHbanderas de TYPE_SYSTEM_OVERLAYventanas.

Es decir, una TYPE_SYSTEM_OVERLAYventana no recibirá ningún evento táctil en la plataforma ICS y, por supuesto, su uso TYPE_SYSTEM_OVERLAYno es una solución viable para ICS y dispositivos futuros.

Sam Lu
fuente
44
¿Entonces cuales son las alternativas?
radhoo
13

Soy uno de los desarrolladores del SDK de Tooleap . También proporcionamos una forma para que los desarrolladores muestren siempre en las ventanas y botones superiores, y hemos lidiado con una situación similar.

Un problema que las respuestas aquí no han abordado es el de los "botones seguros" de Android.

Los botones asegurados tienen la filterTouchesWhenObscuredpropiedad que significa que no pueden interactuar con ellos, si se colocan debajo de una ventana, incluso si esa ventana no recibe ningún toque. Citando la documentación de Android:

Especifica si se deben filtrar los toques cuando la ventana de la vista está oculta por otra ventana visible. Cuando se establece en verdadero, la vista no recibirá toques cada vez que aparezca un brindis, diálogo u otra ventana sobre la ventana de la vista. Consulte la documentación de seguridad {@link android.view.View} para obtener más detalles.

Un ejemplo de dicho botón es el botón de instalación cuando intenta instalar aplicaciones de terceros. Cualquier aplicación puede mostrar dicho botón si agrega al diseño de vista la siguiente línea:

android:filterTouchesWhenObscured="true"

Si muestra una ventana siempre arriba sobre un "Botón asegurado", todas las partes del botón aseguradas que están cubiertas por una superposición no manejarán ningún toque, incluso si no se puede hacer clic en esa superposición. Por lo tanto, si planea mostrar dicha ventana, debe proporcionar una forma para que el usuario la mueva o la descarte. Y si una parte de su superposición es transparente, tenga en cuenta que su usuario podría estar confundido por qué un botón determinado en la aplicación subyacente no funciona para él de repente.

Danny
fuente
¿Puede responder mi pregunta? Stackoverflow.com/questions/28964771/…
Nauman Afzaal
hay alguna actividad en Android 10 que la vista de superposición no puede mostrar por encima de ellos, ¿puede decir la razón o ver mi pregunta aquí? Será muy útil stackoverflow.com/questions/59598264/…
Mateen Chaudhry
12

TRABAJAR SIEMPRE EN EL BOTÓN DE IMAGEN SUPERIOR

en primer lugar lo siento por mi inglés

Edito sus códigos y hago que el botón de imagen de trabajo que escucha su evento táctil no le dé control táctil a sus elementos de fondo.

También le da a los oyentes toques de otros elementos

alineaciones de botones son inferior e izquierda

puede cambiar las alineaciones pero necesita cambiar los cordinats en el evento táctil en el elemento if

import android.annotation.SuppressLint;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Toast;

public class HepUstte extends Service {
    HUDView mView;

    @Override
    public void onCreate() {
        super.onCreate();   

        Toast.makeText(getBaseContext(),"onCreate", Toast.LENGTH_LONG).show();

        final Bitmap kangoo = BitmapFactory.decodeResource(getResources(),
                R.drawable.logo_l);


        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                kangoo.getWidth(), 
                kangoo.getHeight(),
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                |WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                 PixelFormat.TRANSLUCENT);






        params.gravity = Gravity.LEFT | Gravity.BOTTOM;
        params.setTitle("Load Average");
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);



        mView = new HUDView(this,kangoo);

        mView.setOnTouchListener(new OnTouchListener() {


            @Override
            public boolean onTouch(View arg0, MotionEvent arg1) {
                // TODO Auto-generated method stub
                //Log.e("kordinatlar", arg1.getX()+":"+arg1.getY()+":"+display.getHeight()+":"+kangoo.getHeight());
                if(arg1.getX()<kangoo.getWidth() & arg1.getY()>0)
                {
                 Log.d("tıklandı", "touch me");
                }
                return false;
            }
             });


        wm.addView(mView, params);



        }



    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

}



@SuppressLint("DrawAllocation")
class HUDView extends ViewGroup {


    Bitmap kangoo;

    public HUDView(Context context,Bitmap kangoo) {
        super(context);

        this.kangoo=kangoo;



    }


    protected void onDraw(Canvas canvas) {
        //super.onDraw(canvas);


        // delete below line if you want transparent back color, but to understand the sizes use back color
        canvas.drawColor(Color.BLACK);

        canvas.drawBitmap(kangoo,0 , 0, null); 


        //canvas.drawText("Hello World", 5, 15, mLoadPaint);

    }


    protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
    }

    public boolean onTouchEvent(MotionEvent event) {
        //return super.onTouchEvent(event);
       // Toast.makeText(getContext(),"onTouchEvent", Toast.LENGTH_LONG).show();

        return true;
    }
}
Onur Öztürk
fuente
Esto funciona, pero ¿cómo puedo asegurarme de que se hizo clic en mi imagen y no algo alrededor de la imagen?
Liam W
@LiamW, las coordenadas del toque se comparan con kangaroo.getWidth () en este ejemplo. Puede deducir con qué compararlo en su propio código en función de dónde dibuja su imagen.
Yevgeny Simkin
¡Muy agradable! Funciona perfectamente.
Yevgeny Simkin
¿Cuál es la versión mínima de Android en la que funciona? Estoy trabajando en escribir esto, pero soy nuevo en Android, así que estoy teniendo tiempo para principiantes.
Noitidart
6

En realidad, puede probar WindowManager.LayoutParams.TYPE_SYSTEM_ERROR en lugar de TYPE_SYSTEM_OVERLAY. Puede sonar como un truco, pero le permite mostrar la vista encima de todo y aún así obtener eventos táctiles.

Ov3r1oad
fuente
1
Intenté todo para que funcionara, pero no tuve éxito ... pero agregar TYPE_SYSTEM_ERROR hizo el trabajo ... En realidad, TYPE_SYSTEM_OVERLAY estaba bloqueando el toque ..... + 1.
Robi Kumar Tomar
6

TYPE_SYSTEM_OVERLAY Esta constante ha quedado en desuso desde el nivel 26 de API. Utilice TYPE_APPLICATION_OVERLAY en su lugar. o ** para usuarios de Android 8 y superiores

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
    LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
}
arwTheDev
fuente
5

Prueba esto. Funciona bien en ICS. Si desea detener el servicio, simplemente haga clic en la notificación generada en la barra de estado.

 public class HUD extends Service
 {
    protected boolean foreground = false;
    protected boolean cancelNotification = false;
    private Notification notification;
    private View myView;
    protected int id = 0;
    private WindowManager wm;
    private WindowManager.LayoutParams params;
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate() {
        super.onCreate();
       // System.exit(0);
        Toast.makeText(getBaseContext(),"onCreate", Toast.LENGTH_SHORT).show();
        params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
        params.gravity=Gravity.TOP|Gravity.LEFT;
    wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    inflateview();
    foregroundNotification(1);
    //moveToForeground(1,n,true);
    }     
   @Override
    public void onDestroy() {
        super.onDestroy();
        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).cancel(0);
        Toast.makeText(getBaseContext(),"onDestroy", Toast.LENGTH_SHORT).show();
        if(myView != null)
        {
            ((WindowManager) getSystemService(WINDOW_SERVICE)).removeView(myView);
            myView = null;
        }
    }
    protected Notification foregroundNotification(int notificationId) 
   {    
    notification = new Notification(R.drawable.ic_launcher, "my Notification", System.currentTimeMillis());    
        notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_ONLY_ALERT_ONCE;   
        notification.setLatestEventInfo(this, "my Notification", "my Notification", notificationIntent());          
        ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)).notify(id, notification);            
        return notification;
    }
    private PendingIntent notificationIntent() {
        Intent intent = new Intent(this, stopservice.class);    
        PendingIntent pending = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);    
        return pending;
    }
    public void inflateview()
    {
         LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
            myView = inflater.inflate(R.layout.activity_button, null);
            myView.setOnTouchListener(new OnTouchListener() {
               @Override
               public boolean onTouch(View v, MotionEvent event) {
                   Toast.makeText(getBaseContext(),"onToasttt", Toast.LENGTH_SHORT).show();
                   return false;
               }
             });
            // Add layout to window manager
            wm.addView(myView, params); 
    }
}

ACTUALIZAR

Muestra aquí

Para crear una vista de superposición, al configurar los LayoutParams NO configure el tipo en TYPE_SYSTEM_OVERLAY.

Instead set it to TYPE_PHONE.

Use the following flags:

FLAG_NOT_TOUCH_MODAL

FLAG_WATCH_OUTSIDE_TOUCH
Viswanath Lekshmanan
fuente
Esto no funciona para mí, mi pantalla no interactúa con el fondo. Cualquier otro método que conozca ...
Vinuthan
@Vinuthan ¿Cuál es su resultado? ¿Responde
Viswanath Lekshmanan
No, no responde. He rellenado mi actividad por encima de la mitad de la pantalla para poder interactuar con el fondo, pero no puedo interactuar con el fondo.
Vinuthan
FLAG_WATCH_OUTSIDE_TOUCH le impide interactuar con el fondo
Richard Le Mesurier
1
TYPE_PHONE funcionó para mí, ahora se entregan toques, al menos puedo tocar un botón;) ¡gracias!
vir us
3

Aquí hay una solución simple, todo lo que necesita es inflar el diseño XML al igual que lo hace en los adaptadores de lista, solo haga un diseño XML para inflarlo. Aquí hay un código que todo lo que necesitas.

 public class HUD extends Service {
    View mView;

    LayoutInflater inflate;
    TextView t;
    Button b;

    @Override
    public void onCreate() {
        super.onCreate();   

        Toast.makeText(getBaseContext(),"onCreate", Toast.LENGTH_LONG).show();


        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);

        Display display = wm.getDefaultDisplay();  get phone display size
        int width = display.getWidth();  // deprecated - get phone display width
        int height = display.getHeight(); // deprecated - get phone display height 


        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                width, 
                height,
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                |WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                |WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                 PixelFormat.TRANSLUCENT);


        params.gravity = Gravity.LEFT | Gravity.CENTER;
        params.setTitle("Load Average");

        inflate = (LayoutInflater) getBaseContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        mView = inflate.inflate(R.layout.canvas, null);

        b =  (Button) mView.findViewById(R.id.button1);
        t = (TextView) mView.findViewById(R.id.textView1);
        b.setOnClickListener(new OnClickListener() {

        public void onClick(View v) {
            // TODO Auto-generated method stub
            t.setText("yes you click me ");
        }
       });

        wm.addView(mView, params);

        }



    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

}
usuario1668168
fuente
3

Encontré una biblioteca que hace exactamente eso: https://github.com/recruit-lifestyle/FloatingView

Hay un proyecto de muestra en la carpeta raíz. Lo ejecuté y funciona según sea necesario. Se puede hacer clic en el fondo, incluso si se trata de otra aplicación.

ingrese la descripción de la imagen aquí

AlikElzin-kilaka
fuente
Gracias por la recomendación, solo estaba tratando de encontrar este gran proyecto.
hasta el
1

Bueno, prueba mi código, al menos te da una cadena como superposición, puedes reemplazarlo con un botón o una imagen. No creerás que esta es mi primera aplicación para Android LOL. De todos modos, si tiene más experiencia con las aplicaciones de Android que yo, intente

  • cambiar los parámetros 2 y 3 en "new WindowManager.LayoutParams"
  • pruebe un enfoque de evento diferente
coreSOLO
fuente
i finalmente ni de encontrar tiempo para perseguir esto, por lo tanto, estoy todavía escasa en sus detalles ...
st0le
1

Si alguien sigue leyendo este hilo y no puede hacerlo funcionar, lamento decirle que esta forma de interceptar el evento de movimiento se considera un error y una corrección en Android> = 4.2.

El evento de movimiento que interceptó, aunque tiene acción como ACTION_OUTSIDE, devuelve 0 en getX y getY. Esto significa que no puede ver toda la posición de movimiento en la pantalla, ni puede hacer nada. Sé que el documento dijo que obtendrá x e y, pero la verdad es que NO. Parece que esto es para bloquear el registrador de claves.

Si alguien tiene una solución, deje su comentario.

ref: ¿Por qué ACTION_OUTSIDE devuelve 0 cada vez en KitKat 4.4.2?

https://code.google.com/p/android/issues/detail?id=72746

Nick Jian
fuente
1

Al utilizar el servicio puede lograr esto:

public class PopupService extends Service{

    private static final String TAG = PopupService.class.getSimpleName();
    WindowManager mWindowManager;
    View mView;
    String type ;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
//        registerOverlayReceiver();
        type = intent.getStringExtra("type");
        Utils.printLog("type = "+type);
        showDialog(intent.getStringExtra("msg"));
        return super.onStartCommand(intent, flags, startId);
    }

    private void showDialog(String aTitle)
    {
        if(type.equals("when screen is off") | type.equals("always"))
        {
            Utils.printLog("type = "+type);
            PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
            WakeLock mWakeLock = pm.newWakeLock((PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP), "YourServie");
            mWakeLock.acquire();
            mWakeLock.release();
        }

        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        mView = View.inflate(getApplicationContext(), R.layout.dialog_popup_notification_received, null);
        mView.setTag(TAG);

        int top = getApplicationContext().getResources().getDisplayMetrics().heightPixels / 2;

        LinearLayout dialog = (LinearLayout) mView.findViewById(R.id.pop_exit);
//        android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) dialog.getLayoutParams();
//        lp.topMargin = top;
//        lp.bottomMargin = top;
//        mView.setLayoutParams(lp);

        final EditText etMassage = (EditText) mView.findViewById(R.id.editTextInPopupMessageReceived);

        ImageButton imageButtonSend = (ImageButton) mView.findViewById(R.id.imageButtonSendInPopupMessageReceived);
//        lp = (LayoutParams) imageButton.getLayoutParams();
//        lp.topMargin = top - 58;
//        imageButton.setLayoutParams(lp);
        imageButtonSend.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Utils.printLog("clicked");
//                mView.setVisibility(View.INVISIBLE);
                if(!etMassage.getText().toString().equals(""))
                {
                    Utils.printLog("sent");
                    etMassage.setText("");
                }
            }
        });

        TextView close = (TextView) mView.findViewById(R.id.TextViewCloseInPopupMessageReceived);
        close.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                hideDialog();   
            }
        });

        TextView view = (TextView) mView.findViewById(R.id.textviewViewInPopupMessageReceived);
        view.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                hideDialog();   
            }
        });

        TextView message = (TextView) mView.findViewById(R.id.TextViewMessageInPopupMessageReceived);
        message.setText(aTitle);

        final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
        WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
        WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
//                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON ,
        PixelFormat.RGBA_8888);

        mView.setVisibility(View.VISIBLE);
        mWindowManager.addView(mView, mLayoutParams);
        mWindowManager.updateViewLayout(mView, mLayoutParams);

    }

    private void hideDialog(){
        if(mView != null && mWindowManager != null){
            mWindowManager.removeView(mView);
            mView = null;
        }
    }
}
Akshay Paliwal
fuente
1

La respuesta de @Sarwar Erfan ya no funciona ya que Android no permite agregar vistas con WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY a la ventana para que ya no se pueda tocar, ni siquiera con WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH.

Encontré solución a este problema. Puedes verlo en la siguiente pregunta

Al agregar la vista a la ventana con WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, no se obtiene un evento táctil

patilmandar2007
fuente
Su solución
recomendada
@AhmadShahwaiz Por favor revise el enlace nuevamente. He agregado un comentario para el problema que mencionó allí
Patilmandar2007
Sí, ahora está funcionando. Pero no obtener la respuesta de devolución de llamada después de llamar a startActivityForResult (intento, OVERLAY_REQ_CODE); en el método void onActivityResult protegido (int requestCode, int resultCode, Intent data).
Ahmad Shahwaiz el
@AhmadShahwaiz Los cambios mencionados en el enlace se llamarán usando la actividad donde recibimos la devolución de llamada en el método onActivityResult. Si está verificando el permiso anterior en el Servicio, es posible que deba agregar una actividad transparente ficticia para iniciar desde el servicio, que puede solicitar Permiso al usuario y luego en la actividad ficticia onActivityResult obtendrá el resultado del usuario. Después de que
deba
1

Esta es una vieja pregunta, pero recientemente Android tiene soporte para Bubbles. Pronto se lanzarán burbujas, pero actualmente los desarrolladores pueden comenzar a usarlas y están diseñadas para ser una alternativa al uso SYSTEM_ALERT_WINDOW. Aplicaciones como (Facebook Messenger y MusiXMatch usan el mismo concepto).

Burbujas

Las burbujas se crean a través de la API de notificaciones, envía su notificación de forma normal. Si desea que burbujee, debe adjuntarle algunos datos adicionales. Para obtener más información sobre Bubbles, puede ir a la Guía oficial para desarrolladores de Android en Bubbles .

Xenolion
fuente