没有WindowManager.LayoutParams.TYPE_PHONE的粘性叠加

时间:2019-03-19 23:37:15

标签: java android kotlin

粘性是指不会通过调用启动器意图(intent.addCategory(Intent.CATEGORY_HOME)关闭的窗口。

以前,此操作是通过WindowManager.LayoutParams.TYPE_PHONE完成的,但是现在不建议使用此类型,并且会在api 28上引发异常:

  

WindowManager $ BadTokenException ...窗口类型2002的权限被拒绝

基于Facebook的Messenger在其聊天“ Heads”中进行聊天的行为仍然可能,因为假设Facebook由于已预先安装在许多rom上而没有获得系统应用程序许可。

使用WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY不起作用(即,按下主屏幕按钮也会隐藏叠加窗口)。

编辑:问题是当用户单击“主页”按钮/调用启动器意图时,如何将其覆盖窗口移除。 TYPE_APPLICATION_OVERLAY不是这种情况,TYPE_PHONE是这种情况,但已过时。

编辑2:显然,这确实适用于某些人,这是我正在运行的代码:

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 个答案:

答案 0 :(得分:1)

Ref:https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html

TYPE_PHONE

public static final int TYPE_APPLICATION_OVERLAY

在API级别26中不推荐使用此常数。

用于非系统应用程序。请改用TYPE_APPLICATION_OVERLAY。

窗口类型:电话。 These are non-application windows providing user interaction with the phone(尤其是来电)。这些窗口通常位于所有应用程序的上方,但位于状态栏的后面。在多用户系统中,所有用户的窗口上都会显示。

TYPE_APPLICATION_OVERLAY

public static final int TYPE_APPLICATION_OVERLAY

窗口类型:应用程序覆盖窗口显示在所有活动窗口上方(FIRST_APPLICATION_WINDOWLAST_APPLICATION_WINDOW之间的类型),但显示在状态栏或IME之类的关键系统窗口下方。

系统可以随时更改这些窗口的位置,大小或可见性,以减少用户的视觉混乱并管理资源。

需要Manifest.permission.SYSTEM_ALERT_WINDOW权限。

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

系统将调整这种窗口类型的进程的重要性,以减少低内存杀手杀死它们的机会。

在多用户系统中,仅在拥有用户的屏幕上显示。

我们必须在设备Oreo或更高版本上使用TYPE APPLICATION OVERLAY 我们已经有 Manoj Perumarath 给出的示例

您需要定义这样的windowlayout参数

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

阅读一次

答案 1 :(得分:0)

Manifest中添加权限

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

在启动器活动中,您应该请求canDrawOverlays权限`

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

然后在您的服务中

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

这不会随着用户按Home键而消失。

请确保将浮动窗口小部件定义为service,否则在内存不足/配置更改等问题时它将无法生存。 同时在清单中注册服务

   <service
            android:name=".MyWidget"
            android:enabled="true"
            android:exported="false"/>

答案 2 :(得分:0)

解决方案::请勿直接在其他应用程序上覆盖叠加层。

通常,使用TYPE_APPLICATION_OVERLAY的行为仍然与TYPE_PHONE相同,我的案例的问题在于,当来自另一个应用程序的活动位于前台时,我正在显示我的视图(windowManager.addView(rootView, params)),并且Android将视图附加到当前的前台活动,因此,当用户退出该活动时(例如,通过按下主屏幕按钮),系统也会杀死我的应用中的叠加层。对我来说,解决方案非常具体,我没有一般性的解决方案,但是有了此信息,人们应该可以找到解决方案。