skip to Main Content

By sticky I mean a window that doesn’t get closed by calling the launcher intent (intent.addCategory(Intent.CATEGORY_HOME).

Previously this was done with WindowManager.LayoutParams.TYPE_PHONE, but this type is now deprecated and throws an exception on api 28:

WindowManager$BadTokenException … permission denied for window type 2002

The behavious is still possible since Facebook’s Messenger does it with its chat “Heads”, based on the assumption that facebook doesn’t get system app permissions since it’s pre-installed on a lot of roms.

Using WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY doesn’t work (i.e. pressing the home button also hides the overlay window).

Edit: The question is how to have an overlay window that doesn’t get removed when user clicks the home button / calling the launcher intent. It’s not the case for TYPE_APPLICATION_OVERLAY, it was the case for TYPE_PHONE but that’s deprecated.

Edit 2: Apparently this does work for some people, this is the code I’m running:

class MyClass {

    var params: WindowManager.LayoutParams = WindowManager.LayoutParams(
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
            else WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG,
            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
            PixelFormat.TRANSLUCENT
    ).apply {
        windowAnimations = android.R.style.Animation_Dialog
        gravity = Gravity.CENTER or Gravity.CENTER
        x = 0
        y = 0
    }
    var windowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager

    init {
        val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        rootView = layoutInflater.inflate(R.layout.view_overlay, null)
        windowManager.addView(rootView, params)
    }

}

3

Answers


  1. Chosen as BEST ANSWER

    Solution: Don't inflate your overlay directly over other apps.

    Normally, using TYPE_APPLICATION_OVERLAY still behaves as TYPE_PHONE, the issue with my case was that I was displaying my view (windowManager.addView(rootView, params)) when an activity from another app was in the foreground, and Android attaches the view to the current foreground activity, so when the user exits that activity (by pressing the home button for example), the system also kills the overlay from my app. A solution for me was very specific, I don't have a general solution but with this information should help people finding one.


  2. In Manifest add permission

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    

    In the launcher activity you should ask for canDrawOverlays Permissions`

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && 
     !Settings.canDrawOverlays(this)) {
    
    
            //If the draw over permission is not available open the settings screen
            //to grant the permission.
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, CODE_DRAW_OVER_OTHER_APP_PERMISSION);
        } else {
            initializeView();
        }
    

    Then inside your service

     //Inflate the floating view layout
        mFloatingView = 
        LayoutInflater.from(this).inflate(R.layout.layout_floating_widget, null);
    
        int LAYOUT_TYPE;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            LAYOUT_TYPE = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            LAYOUT_TYPE = WindowManager.LayoutParams.TYPE_PHONE;
        }
        //Add the view to the window.
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                LAYOUT_FLAG ,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);
    
        //Specify the view position
        params.gravity = Gravity.TOP | Gravity.LEFT;        //Initially view will be added to top-left corner
        params.x = 0;
        params.y = 100;
    
        //Add the view to the window
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        mWindowManager.addView(mFloatingView, params);
    

    This won’t get dismissed as the user press Home Key.

    Make sure that your floating widget is defined as service otherwise it can’t survive when there is memory low/ configuration change kind of issues.
    Also register the service inside the manifest

       <service
                android:name=".MyWidget"
                android:enabled="true"
                android:exported="false"/>
    
    Login or Signup to reply.
  3. Ref : https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html

    TYPE_PHONE

    public static final int TYPE_APPLICATION_OVERLAY
    

    This constant was deprecated in API level 26.

    for non-system apps. Use TYPE_APPLICATION_OVERLAY instead.

    Window type: phone. These are non-application windows providing user interaction with the phone (in particular incoming calls). These windows are normally placed above all applications, but behind the status bar. In multiuser systems shows on all users’ windows.

    TYPE_APPLICATION_OVERLAY

    public static final int TYPE_APPLICATION_OVERLAY
    

    Window type: Application overlay windows are displayed above all activity windows (types between FIRST_APPLICATION_WINDOW and LAST_APPLICATION_WINDOW) but below critical system windows like the status bar or IME.

    The system may change the position, size, or visibility of these windows at anytime to reduce visual clutter to the user and also manage resources.

    Requires Manifest.permission.SYSTEM_ALERT_WINDOW permission.

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    

    The system will adjust the importance of processes with this window type to reduce the chance of the low-memory-killer killing them.

    In multi-user systems shows only on the owning user’s screen.

    we have to use TYPE APPLICATION OVERLAY on device Oreo or later
    we have already example as Manoj Perumarath gived

    you need to defined windowlayout params like this

    //if device is Oreo or latter if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSLUCENT);
    
    //or else
    final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_PHONE,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSLUCENT);
    

    Manifest.xml

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    //...other stuff
    <service
    android:name=".serviceClass"
    android:enabled="true"
    android:exported="false"/>
    

    MainActivity

    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, APP_PERMISSION_REQUEST);
    }
    else
    {
     //start service
    }
    

    Read once

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search