Criando uma janela de sobreposição do sistema (sempre no topo)

285

Estou tentando criar um botão sempre op-top / imagem clicável que fica no topo de todas as janelas o tempo todo.

A prova de conceito é

Fui bem-sucedido e tenho um serviço em execução agora. O serviço exibe algum texto no canto superior esquerdo da tela o tempo todo, enquanto o usuário pode interagir livremente com o restante dos aplicativos da maneira normal.

O que estou fazendo é subclasse ViewGroupe adicione-o ao gerenciador de janelas raiz com sinalizador TYPE_SYSTEM_OVERLAY. Agora, quero adicionar um botão / imagem clicável no lugar deste texto que possa receber eventos de toque em si mesmo. Tentei substituir "onTouchEvent" no todo, ViewGroupmas ele não recebe nenhum evento.

Como posso receber eventos apenas em certas partes do meu grupo de visualizações sempre no topo? Por favor, sugira.

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
fonte
2
@coreSOLO, espero que você receba sua resposta, minhas perguntas estranhamente semelhantes não receberam nenhuma resposta. :) +1
st0le
3
@ st0le Eu prometo que vou passar o meu dia e noite até que eu encontrar a solução e compartilhá-lo com você :)
coreSOLO
1
@ st0le Ok, talvez eu e você pode cavar isso juntos, me dar um link da sua pergunta e deixe-me saber quanto progresso você fosse capaz de fazer ...
coreSOLO
não é muito: - \ stackoverflow.com/questions/3732935/…
st0le
3
É surpreendente que você onDraw()esteja sendo chamado. Em dispatchDraw()vez disso, não deve ser substituído? Consulte groups.google.com/forum/?fromgroups#!topic/android-developers/…
faizal 07/07/2014

Respostas:

158

Esta pode ser uma solução estúpida. Mas funciona. Se você pode melhorar, por favor me avise.

Ao criar o seu serviço: usei a WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCHflag. Esta é a única alteração no serviço.

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

Agora, você começará a receber todos os eventos de clique. Portanto, você precisa corrigir no seu manipulador de eventos.

No seu evento de toque no 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;
}

Você também pode precisar adicionar esta permissão ao seu manifesto:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Sarwar Erfan
fonte
1
@pengwang: que erros você está recebendo? Este código não é independente, você precisa colocá-los em seu projeto com nomes de arquivos apropriados no projeto apropriado.
Sarwar Erfan
4
@pengwang: Encontre a implementação mais simples aqui: docs.google.com/…
Sarwar Erfan
6
todo o código está correto, eu acho o meu erro, esqueço o link <usa-permissão android: name = "android.permission.SYSTEM_ALERT_WINDOW" /> Sarwar Erfan eu acho que você edita sua resposta, ela pode se tornar mais maravilhosa
pengwang
1
Esta resposta é muito útil, exceto por uma coisa. Conforme usado no código, o WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, de acordo com a documentação, "não receberá o gesto completo de subir / mover / descer". Em outras palavras, não é possível detectar furtos ou qualquer outra coisa que não seja um ACTION_DOWN. Bem, eu sei que é possível detectar furtos porque o Wave Launcher usa esse conceito semelhante e eles são capazes de detectar furtos. Alguém pode me explicar como isso funciona?
21711 Brian
6
@SarwarErfan - usei isso perfeitamente na versão 2.3, mas o evento onTouch não é geeting ativado no Android 4.0, 1º e 4,1. Por favor ajude
Sanjay Singh
66

Seguindo a resposta de @Sam Lu, o Indeed 4.x impede que certos tipos ouçam eventos de toque externos, mas alguns tipos, como TYPE_SYSTEM_ALERT , ainda fazem o trabalho.

Exemplo

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

Permissões

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
amirlazarovich
fonte
5
Sua fazendo minha tela enforcado .. Qualquer idéia
Viswanath Lekshmanan
Ótimo post. Espero que isso também não esteja desativado em versões futuras.
Cookster
Animações não funcionará sobre ele ... :( E é incapaz de cliques de captura de qualquer modo em que em 4.2.x Alguma sugestão?
Kamran Ahmed
Funciona, mas quando estou usando esse atalho (por exemplo, Alerta no nível do sistema), outras caixas de diálogo não são exibidas. Por quê ?
Chimu
@Arju o que você quer dizer? você está tentando removê-lo?
Amirlazarovich
18

Começando com Android 4.x, equipe do Android corrigiu um problema de segurança em potencial, adicionando uma nova função adjustWindowParamsLw()na qual ele irá adicionar FLAG_NOT_FOCUSABLE, FLAG_NOT_TOUCHABLEe remover FLAG_WATCH_OUTSIDE_TOUCHbandeiras paraTYPE_SYSTEM_OVERLAY janelas.

Ou seja, uma TYPE_SYSTEM_OVERLAYjanela não receberá nenhum evento de toque na plataforma ICS e, é claro, usar TYPE_SYSTEM_OVERLAYnão é uma solução viável para o ICS e dispositivos futuros.

Sam Lu
fonte
4
Então quais são as alternativas?
radhoo
1
Veja esta pergunta sobre o ICS: stackoverflow.com/questions/9656185/type-system-overlay-in-ics
clemp6r:
13

Sou um dos desenvolvedores do Tooleap SDK . Também fornecemos uma maneira de os desenvolvedores exibirem sempre nas janelas e botões superiores e lidamos com uma situação semelhante.

Um problema que as respostas aqui não abordaram é o dos "Botões protegidos" do Android.

Os botões protegidos têm a filterTouchesWhenObscuredpropriedade, o que significa que eles não podem ser interagidos, se colocados sob uma janela, mesmo que essa janela não receba nenhum toque. Citando a documentação do Android:

Especifica se é necessário filtrar toques quando a janela da vista é obscurecida por outra janela visível. Quando definida como true, a vista não receberá toques sempre que uma janela, caixa de diálogo ou outra janela aparecer acima da janela da vista. Consulte a documentação de segurança {@link android.view.View} para obter mais detalhes.

Um exemplo desse botão é o botão de instalação quando você tenta instalar aplicativos de terceiros. Qualquer aplicativo pode exibir esse botão se adicionar ao layout da visualização a seguinte linha:

android:filterTouchesWhenObscured="true"

Se você exibir uma janela sempre na parte superior sobre um "Botão protegido", todas as partes protegidas do botão cobertas por uma sobreposição não terão nenhum toque, mesmo que essa sobreposição não seja clicável. Portanto, se você planeja exibir essa janela, deve fornecer uma maneira de o usuário movê-la ou descartá-la. E se uma parte da sua sobreposição for transparente, leve em consideração que o usuário pode ficar confuso por que um determinado botão no aplicativo subjacente não está funcionando para ele de repente.

Danny
fonte
Você pode por favor responder à minha pergunta: stackoverflow.com/questions/28964771/…
Nauman Afzaal 11/15/15
há alguma atividade no android 10 que a exibição de sobreposição não pode aparecer acima deles, por favor, diga o motivo ou veja minha pergunta aqui será muito útil stackoverflow.com/questions/59598264/…
Mateen Chaudhry -
12

TRABALHANDO SEMPRE NO BOTÃO SUPERIOR DA IMAGEM

primeiro de tudo desculpa pelo meu ingles

edito seus códigos e faço com que o botão da imagem de trabalho que ouça seu evento de toque não dê controle de toque a seus elementos de segundo plano.

também dá ouvintes de toque para fora de outros elementos

os alinhamentos dos botões estão na parte inferior e esquerda

você pode alterar alterações, mas precisa alterar cordinats no evento touch no 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
fonte
Isso funciona, mas como posso garantir que minha imagem foi clicada e não algo em torno dela?
Liam W
@LiamW, as coordenadas do toque são comparadas com kangaroo.getWidth () neste exemplo. Você pode deduzir com o que compará-lo em seu próprio código, com base em onde está desenhando sua imagem.
Yevgeny Simkin 27/10
Muito agradável! Funciona perfeitamente.
Yevgeny Simkin 27/10
Qual é a versão mínima do Android em que isso funciona? Estou trabalhando para escrever isso, mas sou novo no Android, então estou tendo um tempo para iniciantes.
Noitidart 10/08/2015
6

Na verdade, você pode tentar o WindowManager.LayoutParams.TYPE_SYSTEM_ERROR em vez de TYPE_SYSTEM_OVERLAY. Pode parecer um hack, mas permite exibir a visualização em cima de tudo e ainda receber eventos de toque.

Ov3r1oad
fonte
1
Eu tentei de tudo para fazê-lo funcionar, mas sem sucesso .... mas a adição de TYPE_SYSTEM_ERROR fez o trabalho ... Na verdade, TYPE_SYSTEM_OVERLAY estava bloqueando o toque ..... + 1.
Robi Kumar Tomar
6

TYPE_SYSTEM_OVERLAY Essa constante foi descontinuada desde o nível 26 da API. Use TYPE_APPLICATION_OVERLAY. ou ** para usuários abaixo e acima do android 8

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

Tente isso. Funciona bem no ICS. Se você deseja interromper o serviço, basta clicar na notificação gerada na barra de status.

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

ATUALIZAR

Amostra aqui

Para criar uma exibição de sobreposição, ao configurar o LayoutParams, NÃO defina o tipo como TYPE_SYSTEM_OVERLAY.

Instead set it to TYPE_PHONE.

Use the following flags:

FLAG_NOT_TOUCH_MODAL

FLAG_WATCH_OUTSIDE_TOUCH
Viswanath Lekshmanan
fonte
Isso não funciona para mim. Minha tela não interage com o plano de fundo. Qualquer outro método que você conhece ...
Vinuthan
@Vinuthan Qual é a sua saída, ela responde
Viswanath Lekshmanan
Não, não responde. Coloquei minha atividade acima da metade da tela para poder interagir com o plano de fundo, mas não consigo interagir com o plano de fundo.
21413 Vinuthan
FLAG_WATCH_OUTSIDE_TOUCH impede que você interaja com o plano de fundo
Richard Le Mesurier
1
TYPE_PHONE funcionou para mim - agora os toques são entregues - pelo menos eu posso tocar em um botão;) obrigado!
vir us 23/12
3

Aqui está uma solução simples: tudo o que você precisa é aumentar o layout XML da mesma forma que faz nos adaptadores de lista, basta criar o layout XML para aumentá-lo. Aqui está o código que você precisa.

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

}
user1668168
fonte
3

Encontrei uma biblioteca que faz exatamente isso: https://github.com/recruit-lifestyle/FloatingView

Há um projeto de amostra na pasta raiz. Eu o executei e funciona conforme necessário. O fundo é clicável - mesmo que seja outro aplicativo.

insira a descrição da imagem aqui

AlikElzin-kilaka
fonte
Obrigado pela recomendação, eu estava apenas tentando encontrar esse ótimo projeto.
upupming
1

Bem, tente meu código, pelo menos ele fornece uma string como sobreposição, você pode muito bem substituí-lo por um botão ou uma imagem. Você não vai acreditar que este é o meu primeiro aplicativo para Android LOL. De qualquer forma, se você tem mais experiência com aplicativos Android do que eu, tente

  • alterando os parâmetros 2 e 3 em "new WindowManager.LayoutParams"
  • tente alguma abordagem de evento diferente
coreSOLO
fonte
eu finalmente não encontrei tempo para prosseguir com isso, por isso ainda sou escasso em seus detalhes ...
st0le
1

Se alguém ainda estiver lendo este tópico e não conseguir fazê-lo funcionar, lamento informar que essa maneira de interceptar evento de movimento é considerada um bug e correção no android> = 4.2.

O evento de movimento que você interceptou, embora tenha ação como ACTION_OUTSIDE, retorne 0 em getX e getY. Isso significa que você não pode ver toda a posição do movimento na tela, nem fazer nada. Eu sei que o documento disse que ele receberá x e y, mas a verdade é que NÃO VAI. Parece que isso é para bloquear o key logger.

Se alguém tiver uma solução alternativa, deixe seu comentário.

ref: Por que ACTION_OUTSIDE retorna 0 todas as vezes no KitKat 4.4.2?

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

Nick Jian
fonte
1

usando o serviço, você pode conseguir isso:

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
fonte
1

A resposta de @Sarwar Erfan não funciona mais, pois o Android não permite adicionar visualização com WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY à janela para ser tocável mais, nem mesmo com WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH.

Encontrei solução para este problema. Você pode conferir na seguinte pergunta

Ao adicionar a exibição à janela com WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, ele não está recebendo o evento de toque

patilmandar2007
fonte
Sua solução referida também não funciona em Marshmallow
Ahmad Shahwaiz
Comentário @AhmadShahwaiz Por favor ligação seleção novamente eu adicionei para o problema que você mencionou não
patilmandar2007
Sim, agora está funcionando. Mas não obtendo a resposta de retorno de chamada após chamar startActivityForResult (intent, OVERLAY_REQ_CODE); no método void onActivityResult protegido (int requestCode, int resultCode, Intent data).
Ahmad Shahwaiz 7/11
@AhmadShahwaiz As alterações mencionadas no link devem ser chamadas usando a atividade em que obtemos retorno de chamada no método onActivityResult. Se você estiver verificando a permissão acima no Serviço, talvez seja necessário adicionar uma atividade fictícia transparente a ser iniciada a partir do serviço, que pode solicitar permissão ao usuário e, na atividade fictícia onActivityResult, você obterá o resultado do usuário. Depois que você vai precisar para notificar o seu serviço para adicionar vista a janela ou não, dependendo da resposta do usuário
patilmandar2007
1

Esta é uma pergunta antiga, mas recentemente o Android tem suporte para Bubbles. Em breve, o Bubbles será lançado, mas atualmente os desenvolvedores podem começar a usá-los. Eles foram projetados para serem uma alternativa ao uso SYSTEM_ALERT_WINDOW. Aplicativos como (Facebook Messenger e MusiXMatch usam o mesmo conceito).

Bolhas

As bolhas são criadas por meio da API de notificação, você envia sua notificação normalmente. Se você quiser que borbulhe, precisará anexar alguns dados extras. Para obter mais informações sobre o Bubbles, acesse o Guia oficial do desenvolvedor do Android sobre Bubbles .

Xenolion
fonte