Eu estou trabalhando em um aplicativo onde eu preciso para exibir uma janela com algumas informações ON na tela de bloqueio (proteção) sem desbloquear o telefone. Achei que provavelmente poderia fazer isso com WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
Mas sempre que meu aplicativo trava, ocorre o seguinte erro:
android.view.WindowManager $ BadTokenException: Não é possível adicionar a janela android.view.ViewRootImpl$W@40ec8528 - permissão negada para este tipo de janela
Todas essas postagens ( aqui , aqui e aqui ) dão a mesma resposta. Para adicionar a seguinte permissão no arquivo Manifest.
android.permission.SYSTEM_ALERT_WINDOW
Solução que implementei, mas ainda estou recebendo o mesmo erro. Alguma ideia do que estou fazendo de errado?
Aqui estão as permissões em meu arquivo de manifesto:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.droidpilot.keyguardwindow" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
E este é o código que uso para adicionar a janela à tela de bloqueio
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
LayoutInflater mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mView = mInflater.inflate(R.layout.lock_screen_notif, null);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
PixelFormat.TRANSLUCENT
);
wm.addView(mView, params);
Alguém tem alguma ideia?
PS Estou testando em um HTC Desire 620 DS com Android 4.4.2
fonte
se você usa
apiLevel >= 19
, não useWindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
que obtém o seguinte erro:
Em vez disso, use:
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
fonte
TYPE_SYSTEM_ALERT
funcionando bem no Android 5.1 com oandroid.permission.SYSTEM_ALERT_WINDOW
concedido no manifesto.android.permission.SYSTEM_ALERT_WINDOW
comLayoutParams.TYPE_TOAST
?Eu acho que você deve diferenciar o alvo (antes e depois do Oreo)
int LAYOUT_FLAG; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE; } params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, LAYOUT_FLAG, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
fonte
Build.VERSION_CODES.O
&WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
. Minha versão alvo é 26Fiz o possível para tentar todos os exemplos disponíveis para esse problema. Finalmente obtive a resposta para isso, não sei o quanto é confiável, mas meu aplicativo não está travando agora.
windowManager = (WindowManager)getSystemService(WINDOW_SERVICE); //here is all the science of params final LayoutParams myParams = new LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, LayoutParams.TYPE_SYSTEM_ERROR, WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, PixelFormat.TRANSLUCENT );
Em seu arquivo de manifesto, apenas dê a permissão
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Além disso, você também pode verificar o nível da API se for> = 23, então
if(Build.VERSION.SDK_INT >= 23) { if (!Settings.canDrawOverlays(Activity.this)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, 1234); } } else { Intent intent = new Intent(Activity.this, Service.class); startService(intent); }
Espero que ajude alguém em algum lugar. Exemplo completo https://anam-android-codes.blogspot.in/?m=1
fonte
Para o nível de API do Android 8.0.0, você deve usar
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
ao invés de
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
ou
SYSTEM_ALERT
.fonte
tente este código funcionando perfeitamente
int layout_parms; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { layout_parms = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { layout_parms = WindowManager.LayoutParams.TYPE_PHONE; } yourparams = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, layout_parms, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
fonte
Pesquisa
em sua configuração e habilite seu aplicativo. Para Android 8 Oreo, tente
fonte
Em primeiro lugar, certifique-se de que tem permissão para adicionar no arquivo de manifesto.
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Verifique se o aplicativo atraiu a permissão de outros aplicativos ou não? Essa permissão está disponível por padrão para API <23. Mas para API> 23, você deve pedir permissão em tempo de execução.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, 1); }
Use este código:
public class ChatHeadService extends Service { private WindowManager mWindowManager; private View mChatHeadView; WindowManager.LayoutParams params; public ChatHeadService() { } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); Language language = new Language(); //Inflate the chat head layout we created mChatHeadView = LayoutInflater.from(this).inflate(R.layout.dialog_incoming_call, null); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT); params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; params.x = 0; params.y = 100; mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); mWindowManager.addView(mChatHeadView, params); } else { params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT); params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; params.x = 0; params.y = 100; mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); mWindowManager.addView(mChatHeadView, params); } TextView tvTitle=mChatHeadView.findViewById(R.id.tvTitle); tvTitle.setText("Incoming Call"); //Set the close button. Button btnReject = (Button) mChatHeadView.findViewById(R.id.btnReject); btnReject.setText(language.getText(R.string.reject)); btnReject.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //close the service and remove the chat head from the window stopSelf(); } }); //Drag and move chat head using user's touch action. final Button btnAccept = (Button) mChatHeadView.findViewById(R.id.btnAccept); btnAccept.setText(language.getText(R.string.accept)); LinearLayout linearLayoutMain=mChatHeadView.findViewById(R.id.linearLayoutMain); linearLayoutMain.setOnTouchListener(new View.OnTouchListener() { private int lastAction; private int initialX; private int initialY; private float initialTouchX; private float initialTouchY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //remember the initial position. initialX = params.x; initialY = params.y; //get the touch location initialTouchX = event.getRawX(); initialTouchY = event.getRawY(); lastAction = event.getAction(); return true; case MotionEvent.ACTION_UP: //As we implemented on touch listener with ACTION_MOVE, //we have to check if the previous action was ACTION_DOWN //to identify if the user clicked the view or not. if (lastAction == MotionEvent.ACTION_DOWN) { //Open the chat conversation click. Intent intent = new Intent(ChatHeadService.this, HomeActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); //close the service and remove the chat heads stopSelf(); } lastAction = event.getAction(); return true; case MotionEvent.ACTION_MOVE: //Calculate the X and Y coordinates of the view. params.x = initialX + (int) (event.getRawX() - initialTouchX); params.y = initialY + (int) (event.getRawY() - initialTouchY); //Update the layout with new X & Y coordinate mWindowManager.updateViewLayout(mChatHeadView, params); lastAction = event.getAction(); return true; } return false; } }); } @Override public void onDestroy() { super.onDestroy(); if (mChatHeadView != null) mWindowManager.removeView(mChatHeadView); }
}
fonte
mude o sinalizador Windowmanger "TYPE_SYSTEM_OVERLAY" para "TYPE_APPLICATION_OVERLAY" em seu projeto para torná-lo compatível com Android O
WindowManager.LayoutParams.TYPE_PHONE
paraWindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
fonte
Eu consegui finalmente exibir uma janela na tela de bloqueio com o uso de em
TYPE_SYSTEM_OVERLAY
vez deTYPE_KEYGUARD_DIALOG
. Isso funciona conforme o esperado e adiciona a janela na tela de bloqueio.O problema com isso é que a janela é adicionada em cima de tudo o que for possível. Ou seja, a janela aparecerá até na parte superior do seu teclado / bloqueio de padrão no caso de telas de bloqueio seguras. No caso de uma tela de bloqueio desprotegida, ela aparecerá na parte superior da bandeja de notificação se você abri-la na tela de bloqueio.
Para mim, isso é inaceitável. Espero que isso possa ajudar alguém a enfrentar esse problema.
fonte
TYPE_SYSTEM_OVERLAY
. Meu setOnDismissListener em AlertDialog não está sendo chamado.Acabei de adicionar a visualização simples ao
WindowManager
com as seguintes etapas:// 1. Show view private void showCustomPopupMenu() { windowManager = (WindowManager)getSystemService(WINDOW_SERVICE); // LayoutInflater layoutInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); // View view = layoutInflater.inflate(R.layout.dummy_layout, null); ViewGroup valetModeWindow = (ViewGroup) View.inflate(this, R.layout.dummy_layout, null); int LAYOUT_FLAG; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE; } WindowManager.LayoutParams params=new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, LAYOUT_FLAG, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); params.gravity= Gravity.CENTER|Gravity.CENTER; params.x=0; params.y=0; windowManager.addView(valetModeWindow, params); } // 2. Get permissions by asking if (!Settings.canDrawOverlays(this)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, 1234); }
Com isso, você pode adicionar uma visão ao WM.
Testado em Pie.
fonte
LayoutParams.TYPE_PHONE está obsoleto. Eu atualizei meu código assim.
parameters = if (Build.VERSION.SDK_INT > 25) { LayoutParams( minHW * 2 / 3, minHW * 2 / 3, LayoutParams.TYPE_APPLICATION_OVERLAY, LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT) }else { LayoutParams( minHW * 2 / 3, minHW * 2 / 3, LayoutParams.TYPE_PHONE, LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT) }
LayoutParams.TYPE_APPLICATION_OVERLAY requer nível de API 26 ou superior.
fonte
O principal motivo para a permissão negada é que não temos permissão para desenhar sobre outros aplicativos, temos que fornecer a permissão para desenhar sobre outros aplicativos que pode ser feito pelo seguinte código
Solicitar código para permissão
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
adicione isso em sua MainActivity
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) { askPermission(); }
private void askPermission() { Intent intent= new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName())); startActivityForResult(intent,ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE); }
Adicione isto também
@RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE){ if(!Settings.canDrawOverlays(this)){ askPermission(); } } }
fonte
Lutei para encontrar a solução de trabalho com
ApplicationContext
eTYPE_SYSTEM_ALERT
encontrei soluções confusas. Caso queira que a caixa de diálogo seja aberta a partir de qualquer atividade, mesmo a caixa de diálogo é um singleton que você deve usargetApplicationContext()
, e se quiser que a caixa de diálogo seja,TYPE_SYSTEM_ALERT
você precisará dos seguintes passos :primeiro obtenha a instância da caixa de diálogo com o tema correto, também é necessário gerenciar a compatibilidade da versão como fiz no seguinte trecho:
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext(), R.style.Theme_AppCompat_Light);
Depois de definir o título, a mensagem e os botões, você deve construir o diálogo como:
AlertDialog alert = builder.create();
Agora o
type
joga o rolo principal aqui, já que esse é o motivo da falha, tratei a compatibilidade da seguinte maneira:if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { alert.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY - 1); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); }
Nota : se você estiver usando uma caixa de diálogo personalizada
AppCompatDialog
como abaixo:AppCompatDialog dialog = new AppCompatDialog(getApplicationContext(), R.style.Theme_AppCompat_Light);
você pode definir diretamente seu tipo para a
AppCompatDialog
instância da seguinte maneira:if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY - 1); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); }
Não se esqueça de adicionar a permissão do manifesto:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
fonte
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT); params.gravity = Gravity.START | Gravity.TOP; params.x = left; params.y = top; windowManager.addView(view, params); } else { WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, PixelFormat.TRANSLUCENT); params.gravity = Gravity.START | Gravity.TOP; params.x = left; params.y = top; windowManager.addView(view, params); }
fonte
Certifique-se de que
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
foi adicionado para a versão do sistema operacional <Oreo, pois está obsoleto.fonte