AccessibilityService 覆盖的 WindowManagerBadTokenException

WindowManagerBadTokenException for AccessibilityService Overlay

我正在使用 Xamarin 和 Visual Studio 创建无障碍服务,遵循此 Codelab (Java + Android SDK):https://codelabs.developers.google.com/codelabs/developing-android-a11y-service/#0

无障碍服务已启动,运行 可以在我的 android 设备的“设置”>“无障碍”中找到它。

服务:

    [Service(Label = "Input Utility", Permission = Manifest.Permission.BindAccessibilityService)]
    [IntentFilter(new string[] { "android.accessibilityservice.AccessibilityService" })]
    [MetaData("android.accessibilityservice", Resource = "@xml/config")]
    public class TapService : AccessibilityService
    {
        FrameLayout mLayout;
        public override void OnAccessibilityEvent(AccessibilityEvent e)
        {
            
        }

        public override void OnInterrupt()
        {
            
        }

        public override bool OnUnbind(Intent intent)
        {
            return base.OnUnbind(intent);
        }

        protected override void OnServiceConnected()
        {
            
            base.OnServiceConnected();

            IWindowManager wm = Application.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
            mLayout = new FrameLayout(this);
            WindowManagerLayoutParams lp = new WindowManagerLayoutParams();
            lp.Type = WindowManagerTypes.AccessibilityOverlay;
            lp.Format = Format.Translucent;
            lp.Flags |= WindowManagerFlags.NotFocusable;
            lp.Width = WindowManagerLayoutParams.WrapContent;
            lp.Height = WindowManagerLayoutParams.WrapContent;
            lp.Gravity = GravityFlags.Top;
            LayoutInflater inflater = (LayoutInflater)Application.Context.GetSystemService(Context.LayoutInflaterService);
            inflater.Inflate(Resource.Layout.InputBar, mLayout);
            wm.AddView(mLayout, lp);

        }
    }

布局文件(InputBar.xml):

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <Button
        android:id="@+id/power"
        android:text="Power"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/volume_up"
        android:text="Volume"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/scroll"
        android:text="Scroll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/swipe"
        android:text="Swipe"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>


</LinearLayout>

元数据文件(config.xml):

<?xml version="1.0" encoding="utf-8" ?> 
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
   android:settingsActivity="true"
   android:accessibilityFeedbackType="feedbackGeneric"
   android:accessibilityFlags="flagDefault"
   android:canPerformGestures="true"
   android:canRetrieveWindowContent="true" />

但是,当我尝试在 wm.AddView(mLayout, lp); 处将叠加层添加到屏幕时出现错误,它给了我这个错误:Android.Views.WindowManagerBadTokenException Message=Unable to add window -- token null is not valid; is your activity running?.

我知道在官方代码实验室中它告诉我像这样启动:

我怀疑这可能是我遇到的错误的来源,但我不在 android 工作室,我正在使用 Visual Studio 并且不知道该怎么做。

要重现我的错误,只需克隆我的 github 存储库,运行 它,然后在 运行ning 期间,转到 android 的设置 > 辅助功能模拟器并启用“输入服务”辅助功能服务。

Github 存储库:https://github.com/tatapuchi/XamarinAccessibilityService

要测试此功能,请标记确保 Settings.CanDrawOverlays 已启用。尝试在启动服务之前检测状态。

using Android.Provider;
...

if (!Settings.CanDrawOverlays(this))
{
    StartActivityForResult(new Intent(Settings.ActionManageOverlayPermission, Android.Net.Uri.Parse("package:" + PackageName)), 0);
}
else
{
    StartService(new Intent(this, typeof(DemoService)));
}

...
public override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);

    if (requestCode == 0)
    {
        if (!Settings.CanDrawOverlays(this))
        {
        }
        else
        {
            StartService(new Intent(this, typeof(DemoService)));
        }
    }
}

更新:

示例相关代码如下:

public class MainActivity : AppCompatActivity
{
    public AccessibilityServiceBinder binder;
    public AccessibilityServiceConnection accessibilityServiceConnection;
    public bool isBound = false;
    bool isBoundAC = false;
    Intent ServiceIntent;

    protected override void OnStart()
    {
        base.OnStart();
        if (!isBoundAC)
        {
            accessibilityServiceConnection = new AccessibilityServiceConnection(this);
            isBoundAC = BindService(ServiceIntent, accessibilityServiceConnection, Bind.AutoCreate);
            if (isBoundAC)
            {
                Toast.MakeText(this, "AccessibilityService is Bound", ToastLength.Long);
            }
        }
    }

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        SetContentView(Resource.Layout.activity_main);

        ServiceIntent = new Intent(this, typeof(TapService));
        ServiceIntent.SetPackage("com.companyname.app19_3_1");

        FindViewById<Button>(Resource.Id.btn).Click += async delegate
        {
            if (!Settings.CanDrawOverlays(this))
            {
                StartActivityForResult(new Intent(Settings.ActionManageOverlayPermission, Android.Net.Uri.Parse("package:" + PackageName)), 0);
            }
        };
    }
}

public class AccessibilityServiceBinder : Binder
{
    TapService service;
    public AccessibilityServiceBinder(TapService service)
    {
        this.service = service;
    }
    public TapService GetAccessibilityService()
    {
        return service;
    }
}

public class AccessibilityServiceConnection : Java.Lang.Object, IServiceConnection
{
    MainActivity activity;
    AccessibilityServiceBinder binder;
    public AccessibilityServiceBinder Binder
    {
        get
        {
            return (AccessibilityServiceBinder)binder;
        }
    }
    public AccessibilityServiceConnection(MainActivity activity)
    {
        this.activity = activity;
    }
    public void OnServiceConnected(ComponentName name, IBinder service)
    {
        var accessibilityServiceBinder = service as AccessibilityServiceBinder;
        if (accessibilityServiceBinder != null)
        {
            activity.binder = accessibilityServiceBinder;
            activity.isBound = true;
            this.binder = accessibilityServiceBinder;
        }
    }
    public void OnServiceDisconnected(ComponentName name)
    {
        activity.isBound = false;
    }
}


[Service(Label = "Input Utility", Permission = Manifest.Permission.BindAccessibilityService, Enabled = true)]
[IntentFilter(new string[] { "android.accessibilityservice.AccessibilityService" })]
[MetaData("android.accessibilityservice", Resource = "@xml/config")]
public class TapService : AccessibilityService
{
    FrameLayout mLayout;
    ...
    protected override void OnServiceConnected()
    {
        base.OnServiceConnected();

        IWindowManager wm = GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
        WindowManagerLayoutParams lp = new WindowManagerLayoutParams();
        lp.Type = WindowManagerTypes.AccessibilityOverlay;
        lp.Format = Format.Translucent;
        lp.Flags |= WindowManagerFlags.NotFocusable;
        lp.Width = WindowManagerLayoutParams.WrapContent;
        lp.Height = WindowManagerLayoutParams.WrapContent;
        lp.Gravity = GravityFlags.Top;
        LayoutInflater inflater = (LayoutInflater)GetSystemService(Context.LayoutInflaterService);
        View view = inflater.Inflate(Resource.Layout.layoutfloat, null);
        wm.AddView(view, lp);
    }
}