Pracuję nad aplikacji gdzie potrzebne do wyświetlania okna z jakiejś informacji ON ekranie blokady (funkcja blokowania klawiatury) bez odblokowywania telefonu. Pomyślałem, że prawdopodobnie mógłbym to zrobić za pomocą WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
Ale za każdym razem, gdy moja aplikacja ulega awarii z następującym błędem:
android.view.WindowManager $ BadTokenException: Nie można dodać okna android.view.ViewRootImpl$W@40ec8528 - odmowa uprawnień dla tego typu okna
Te posty ( tutaj , tutaj i tutaj ) dają tę samą odpowiedź. Aby dodać następujące uprawnienie w pliku manifestu.
android.permission.SYSTEM_ALERT_WINDOW
Rozwiązanie, które wdrożyłem, ale nadal pojawia się ten sam błąd. Masz pojęcie, co robię źle?
Oto uprawnienia w moim pliku manifestu:
<?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" />
I to jest kod, którego używam, aby dodać okno do ekranu blokady
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);
Anybody got any idea?
P.S. I'm testing on an HTC Desire 620 DS running Android 4.4.2
źródło
if you use
apiLevel >= 19
, don't useWindowManager.LayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
which gets the following error:
Use this instead:
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
źródło
TYPE_SYSTEM_ALERT
working fine on Android 5.1 with theandroid.permission.SYSTEM_ALERT_WINDOW
granted in the manifest.android.permission.SYSTEM_ALERT_WINDOW
withLayoutParams.TYPE_TOAST
?I guess you should differentiate the target (before and after 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);
źródło
Build.VERSION_CODES.O
&WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
. My targetSdkVersion is 26I tried my best to try all the examples available for this issue. Finally I got the answer for this I don know how much is it reliable but my app is not crashing now.
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 );
In your manifest file just give the permission
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Addition to this you can also check for the API level if its >=23 then
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); }
I hope it helps someone somewhere. Full example https://anam-android-codes.blogspot.in/?m=1
źródło
For Android API level of 8.0.0, you should use
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
instead of
LayoutParams.TYPE_TOAST or TYPE_APPLICATION_PANEL
or
SYSTEM_ALERT
.źródło
try this code working perfectly
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);
źródło
Search
in your setting and enable your app. For Android 8 Oreo, try
źródło
Firstly you make sure you have add permission in manifest file.
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Check if the application has draw over other apps permission or not? This permission is by default available for API<23. But for API > 23 you have to ask for the permission in runtime.
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 This code:
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); }
}
źródło
change your flag Windowmanger flag "TYPE_SYSTEM_OVERLAY" to "TYPE_APPLICATION_OVERLAY" in your project to make compatible with Android O
WindowManager.LayoutParams.TYPE_PHONE
toWindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
źródło
I did manage to finally display a window on the lock screen with the use of
TYPE_SYSTEM_OVERLAY
instead ofTYPE_KEYGUARD_DIALOG
. This works as expected and adds the window on the lock screen.The problem with this is that the window is added on top of everything it possibly can. That is, the window will even appear on top of your keypad/pattern lock in case of secure lock screens. In the case of an unsecured lock screen, it will appear on top of the notification tray if you open it from the lock screen.
For me, that is unacceptable. I hope that this may help anybody else facing this problem.
źródło
TYPE_SYSTEM_OVERLAY
. My setOnDismissListener on AlertDialog is not getting invoked.I just added simple view to
WindowManager
with the following steps:// 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); }
With this you can add a view to WM.
Tested in Pie.
źródło
LayoutParams.TYPE_PHONE is deprecated. I have updated my code like these.
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 requires api level 26 or above.
źródło
The Main Reason for the denying permission is that we don’t have permission for drawing over another apps,We have to provide the permission for the Drawing over other apps that can be done by the following code
Request code for Permission
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
add this in your 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); }
Add this also
@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(); } } }
źródło
I struggled to find the working solution with
ApplicationContext
andTYPE_SYSTEM_ALERT
and found confusing solutions, In case you want the dialog should be opened from any activity even the dialog is a singleton you have to usegetApplicationContext()
, and if want the dialog should beTYPE_SYSTEM_ALERT
you will need the following steps:first get the instance of dialog with correct theme, also you need to manage the version compatibility as I did in my following snippet:
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext(), R.style.Theme_AppCompat_Light);
After setting the title, message and buttons you have to build the dialog as:
AlertDialog alert = builder.create();
Now the
type
plays the main roll here, since this is the reason of crash, I handled the compatibility as following: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); }
Note: if you are using a custom dialog with
AppCompatDialog
as below:AppCompatDialog dialog = new AppCompatDialog(getApplicationContext(), R.style.Theme_AppCompat_Light);
you can directly define your type to the
AppCompatDialog
instance as following: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); }
Don't forget to add the manifest permission:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
źródło
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); }
źródło
Make sure
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
is added for OS version < Oreo as it's been deprecated.źródło