import android.app.Service;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
public class ServicoJanelaFlutuante extends Service {
private WindowManager gerenciadorJanela;
private View visaoFlutuante;
@Override
public void onCreate() {
super.onCreate();
gerenciadorJanela = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
visaoFlutuante = LayoutInflater.from(this).inflate(R.layout.layout_janela_flutuante, null);
WindowManager.LayoutParams parametros = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
);
parametros.gravity = Gravity.CENTER;
parametros.x = 50;
parametros.y = 200;
gerenciadorJanela.addView(visaoFlutuante, parametros);
}
@Override
public void onDestroy() {
super.onDestroy();
if (visaoFlutuante != null) {
gerenciadorJanela.removeView(visaoFlutuante);
}
}
@Override
public IBinder onBind(android.content.Intent intent) {
return null;
}
}
Explicação dos parâmetros principais: WRAP_CONTENT ajusta o tamanho ao conteúdo, TYPE_APPLICATION_OVERLAY permite exibição sobre outras apps (requer permissão no Android 8.0+), FLAG_NOT_FOCUSABLE impede que a janela capture foco, e TRANSLUCENT torna o fundo semitransparente.
Adicione funcionalidade de arrastar para tornar a janela interativa. Isso envolve capturar eventos de toque e atualizar a posição.
import android.app.Service;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
public class ServicoJanelaFlutuante extends Service {
private WindowManager gerenciadorJanela;
private View visaoFlutuante;
private WindowManager.LayoutParams parametrosLayout;
private int posicaoInicialX;
private int posicaoInicialY;
private float toqueInicialX;
private float toqueInicialY;
@Override
public void onCreate() {
super.onCreate();
gerenciadorJanela = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
visaoFlutuante = LayoutInflater.from(this).inflate(R.layout.layout_janela_flutuante, null);
parametrosLayout = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
);
parametrosLayout.gravity = Gravity.TOP | Gravity.START;
parametrosLayout.x = 0;
parametrosLayout.y = 150;
gerenciadorJanela.addView(visaoFlutuante, parametrosLayout);
visaoFlutuante.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent evento) {
switch (evento.getAction()) {
case MotionEvent.ACTION_DOWN:
posicaoInicialX = parametrosLayout.x;
posicaoInicialY = parametrosLayout.y;
toqueInicialX = evento.getRawX();
toqueInicialY = evento.getRawY();
return true;
case MotionEvent.ACTION_MOVE:
parametrosLayout.x = posicaoInicialX + (int) (evento.getRawX() - toqueInicialX);
parametrosLayout.y = posicaoInicialY + (int) (evento.getRawY() - toqueInicialY);
gerenciadorJanela.updateViewLayout(visaoFlutuante, parametrosLayout);
return true;
case MotionEvent.ACTION_UP:
return true;
}
return false;
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
if (visaoFlutuante != null) {
gerenciadorJanela.removeView(visaoFlutuante);
}
}
@Override
public IBinder onBind(android.content.Intent intent) {
return null;
}
}
Os eventos de toque (MotionEvent) são processados: ACTION_DOWN registra a posição inicial, ACTION_MOVE calcula o deslocamento e atualiza o layout, e ACTION_UP finaliza a ação. A chamada updateViewLayout redesenha a janela na nova posição.
Para uma janela com botões, defina um layout XML com componentes interativos e vincule-os a ações específicas, como captura de tela ou cliques de acessibilidade.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp">
<Button
android:id="@+id/btn_captura_tela"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Capturar Tela" />
<Button
android:id="@+id/btn_clique_acessibilidade"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Simular Clique" />
</LinearLayout>
No Service, encontre os botões e adicione listeners. Os métodos executarCapturaCompleta e executarCliqueAcessibilidade devem conter a lógica real, como usar MediaProjection ou AccessibilityService.
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
public class ServicoJanelaFlutuante extends Service {
private WindowManager gerenciadorJanela;
private View visaoFlutuante;
private WindowManager.LayoutParams parametrosLayout;
private int posicaoInicialX;
private int posicaoInicialY;
private float toqueInicialX;
private float toqueInicialY;
@Override
public void onCreate() {
super.onCreate();
gerenciadorJanela = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
visaoFlutuante = LayoutInflater.from(this).inflate(R.layout.layout_janela_flutuante, null);
parametrosLayout = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
);
parametrosLayout.gravity = Gravity.CENTER;
parametrosLayout.x = 0;
parametrosLayout.y = 0;
gerenciadorJanela.addView(visaoFlutuante, parametrosLayout);
visaoFlutuante.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent evento) {
if (evento.getAction() == MotionEvent.ACTION_DOWN) {
posicaoInicialX = parametrosLayout.x;
posicaoInicialY = parametrosLayout.y;
toqueInicialX = evento.getRawX();
toqueInicialY = evento.getRawY();
return true;
} else if (evento.getAction() == MotionEvent.ACTION_MOVE) {
parametrosLayout.x = posicaoInicialX + (int) (evento.getRawX() - toqueInicialX);
parametrosLayout.y = posicaoInicialY + (int) (evento.getRawY() - toqueInicialY);
gerenciadorJanela.updateViewLayout(visaoFlutuante, parametrosLayout);
return true;
}
return false;
}
});
Button btnCaptura = visaoFlutuante.findViewById(R.id.btn_captura_tela);
Button btnAcessibilidade = visaoFlutuante.findViewById(R.id.btn_clique_acessibilidade);
btnCaptura.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
executarCapturaCompleta();
}
});
btnAcessibilidade.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
executarCliqueAcessibilidade();
}
});
}
private void executarCapturaCompleta() {
// Implementar lógica de captura de tela usando MediaProjection ou similar
}
private void executarCliqueAcessibilidade() {
// Implementar lógica de clique via AccessibilityService
}
@Override
public void onDestroy() {
super.onDestroy();
if (visaoFlutuante != null) {
gerenciadorJanela.removeView(visaoFlutuante);
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Os botões são localizados com findViewById e cada um aciona um método correspondente. A estrutura do código permite extensão para funcionalidades mais complexas.