#android #xamarin #xamarin.android
#Android #xamarin #xamarin.android
Вопрос:
Я создаю службу специальных возможностей с использованием Xamarin и Visual Studio, следуя этой кодовой таблице (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?
.
Я понимаю, что в официальной codelab он говорит мне о запуске следующим образом:
Я подозреваю, что это может быть источником ошибки, которую я получаю, однако я не на Android studio, я использую Visual Studio и не знаю, что с этим делать.
Чтобы воспроизвести мою ошибку, просто клонируйте мой репозиторий github, запустите его, затем, пока он запущен, перейдите в Настройки> Доступность вашего эмулятора Android и включите службу специальных возможностей «Служба ввода».
Репозиторий Github: https://github.com/tatapuchi/XamarinAccessibilityService
Комментарии:
1. Параметры запуска — это настройка активности запуска. Чтобы устранить эту проблему, попробуйте получить LayoutInflater из системной службы. Проверьте код:
IWindowManager wm = Application.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>(); ... //LayoutInflater inflater = LayoutInflater.From(this); LayoutInflater inflater = (LayoutInflater)Application.Context.GetSystemService(Context.LayoutInflaterService); inflater.Inflate(Resource.Layout.InputBar, mLayout); wm.AddView(mLayout, lp);
2. @JarvanZhang-MSFT Я пытался использовать этот код, но я все еще получаю одно и то же сообщение об ошибке, должен ли я изменить действие при запуске или что-то в этом роде? Я не знаю, как это сделать (установка MainActivity MainLauncher в false приводит к тому, что приложение не работает)
Ответ №1:
Чтобы протестировать эту функцию, пожалуйста, убедитесь 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);
}
}
Комментарии:
1. Я не уверен, что здесь есть «Настройки», я не могу найти для этого правильную директиву using, также я явно не запускаю службу таким образом, я думаю, что это делается полностью с помощью атрибутов, потому что класс TapService и мой файл метаданных — это все, что есть. Я даже ничего не добавил к своей MainActivity 🙁 . Я также не могу найти директиву using для startActivityForResult :/ .
2. Параметр «Настройки» взят из Android. Библиотека поставщика, добавьте
using Android.Provider;
пространство имен для использования метода. И я обновил код, пожалуйста, проверьте это.3. Хорошо, спасибо за разъяснение. Я последовал инструкциям, добавив директиву using и поместил первый бит в мой onCreate() непосредственно перед вызовом LoadApplication(); . Я также добавил второй бит в свой onRequestPermissionsResult(); теперь, когда я запускаю, мне предлагается перейти на системную страницу «Отображение поверх других приложений». Однако эта страница продолжает загружаться бесконечно и ничего не делает, когда я перехожу в Настройки> Специальные возможности, чтобы включить свою службу, я получаю ту же ошибку. Я что-то делаю не так?
4. Я тестировал функцию в обычном сервисе, поэтому вместо этого я использую тип ApplicationOverlay. Попробуйте использовать этот тип для тестирования в вашем проекте.
5. Спасибо, что поделились соответствующим кодом, я тестирую WindowManger в AccessibilityService и обновлю его позже.