Finish foreground service thread - c#

I have a foreground service with a thread that is continuously checking data from a server.
So the service works well and it doesn't closes when I close the app, but when I start the app again the service creates another thread. Is posible abort the thread of the previous instance?
This is my MainActivity
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static Activity ActivityCurrent { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Window.SetStatusBarColor(Android.Graphics.Color.Rgb(0, 120, 71));
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
System.Diagnostics.Debug.WriteLine("Iniciado");
LoadApplication(new App());
ActivityCurrent = this;
DependencyService.Get<IServicio>().Start();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
This is my service class
[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeDataSync)]
public class Servicio : Service, IServicio
{
Uri link = new Uri(new Url().Dir + "/estado_alarma.php");
public override IBinder OnBind(Intent intent)
{
throw new NotImplementedException();
}
[return: GeneratedEnum]
Thread refrescar;
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int starId)
{
if (intent.Action == "START_SERVICE")
{
System.Diagnostics.Debug.WriteLine("Se ha iniciado el servicio");
RegisterNotification();
}
else if (intent.Action == "STOP_SERVICE")
{
StopForeground(true);
StopSelfResult(starId);
}
return StartCommandResult.NotSticky;
}
private void RegisterNotification()
{
NotificationChannel channel = new NotificationChannel("ServicioChannel", "Demo de servicio", NotificationImportance.Max);
NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(channel);
Notification notification = new Notification.Builder(this, "ServicioChannel").SetContentTitle("Servicio Trabajando").SetOngoing(true).Build();
StartForeground(200, notification);
_ = IniciarServicio();
}
public void Start()
{
Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(Servicio));
startService.SetAction("START_SERVICE");
MainActivity.ActivityCurrent.StartService(startService);
}
public void Stop()
{
Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(Servicio));
startService.SetAction("STOP_SERVICE");
MainActivity.ActivityCurrent.StartService(startService);
}
public int IniciarServicio()
{
refrescar = new Thread(EsperaAlarmaAsync);
refrescar.Start();
return 1;
}
public async void EsperaAlarmaAsync()
{
WebClient cliente = new WebClient();
byte[] respuesta;
string resultado;
var a = true;
while (a)
{
respuesta = cliente.DownloadData(link);
resultado = System.Text.Encoding.ASCII.GetString(respuesta);
cliente.Dispose();
if (resultado == "Alerta")
{
setNotificationVolumeToMax();
Device.BeginInvokeOnMainThread(() =>
{
App.Current.MainPage = new Alerta();
var powerManager = (PowerManager)GetSystemService(PowerService);
var wakeLock = powerManager.NewWakeLock(WakeLockFlags.ScreenDim | WakeLockFlags.AcquireCausesWakeup, "Ejemplo");
wakeLock.Acquire();
Intent x = new Intent(MainActivity.ActivityCurrent.Intent);
StartActivity(x);
});
a = false;
}
Thread.Sleep(3000);
}
refrescar.Abort();
}
private void setNotificationVolumeToMax()
{
try
{
var audioManager = (AudioManager)GetSystemService(Context.AudioService);
int max_volume = audioManager.GetStreamMaxVolume(Android.Media.Stream.Music);
audioManager.SetStreamVolume(Android.Media.Stream.Music, max_volume, 0);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
When the server return 'Alerta' it opens the app

Related

Call Android Function From Xamarin.Forms

I have made an Xamarin.Android Widget with a Button and TextView. I have an instance of product (latestProduct) declared in App.cs. A new product is assigned to latestProduct when a new product is added.
The user is able to save a new record by clicking a Button (BtnAdd) on a Xamarin.Forms page. I am trying to update the Widget TextView when the user clicks this Button.
MyPage:
void BtnAdd_Clicked(object sender, EventArgs e) {
Product product = new Product
{
ProductSaveTime = DateTime.Now
};
App.Database.SaveProduct(product);
App.latestProduct = product;
DependencyService.Get<IUpdateDataService>().UpdateWidgetUI();
}
App.cs:
public static Product latestProduct;
IUpdateDataService.cs:
public interface IUpdateDataService
{
void UpdateWidgetUI();
}
WidgetClass.cs:
public class WidgetClass : AppWidgetProvider, IUpdateDataService
{
public static String SaveClick = "Save Product";
public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
var me = new ComponentName(context, Java.Lang.Class.FromType(typeof(WidgetClass)).Name);
appWidgetManager.UpdateAppWidget(me, BuildRemoteViews(context, appWidgetIds));
}
private RemoteViews BuildRemoteViews(Context context, int[] appWidgetIds)
{
var widgetView = new RemoteViews(context.PackageName, Resource.Layout.widget);
UpdateData(widgetView);
RegisterClicks(context, appWidgetIds, widgetView);
return widgetView;
}
private void UpdateData(RemoteViews widgetView)
{
CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
widgetView.SetTextViewText(Resource.Id.textView1, "Last Product: " + App.latestProduct.ProductSaveTime.ToString("g", currentCulture));
}
private void RegisterClicks(Context context, int[] appWidgetIds, RemoteViews widgetView)
{
var intent = new Intent(context, typeof(WidgetClass));
intent.SetAction(AppWidgetManager.ActionAppwidgetUpdate);
intent.PutExtra(AppWidgetManager.ExtraAppwidgetIds, appWidgetIds);
widgetView.SetOnClickPendingIntent(Resource.Id.buttonSave, GetPendingSelfIntent(context, SaveClick));
}
private PendingIntent GetPendingSelfIntent(Context context, string action)
{
var intent = new Intent(context, typeof(WidgetClass));
intent.SetAction(action);
return PendingIntent.GetBroadcast(context, 0, intent, 0);
}
public override void OnReceive(Context context, Intent intent)
{
base.OnReceive(context, intent);
if (SaveClick.Equals(intent.Action))
{
Product product = new Product
{
ProductSaveTime = DateTime.Now
};
App.latestProduct = product;
RemoteViews remoteViews = new RemoteViews(context.PackageName, Resource.Layout.widget);
UpdateData(remoteViews);
AppWidgetManager appWidgetManager = AppWidgetManager.GetInstance(context);
ComponentName componentName = new ComponentName(context, Java.Lang.Class.FromType(typeof(WidgetClass)).Name);
appWidgetManager.UpdateAppWidget(componentName, remoteViews);
}
}
public void UpdateWidgetUI()
{
var widgetView = new RemoteViews(context.PackageName, Resource.Layout.widget);
UpdateData(widgetView);
AppWidgetManager appWidgetManager = AppWidgetManager.GetInstance(context);
ComponentName componentName = new ComponentName(context, Java.Lang.Class.FromType(typeof(WidgetClass)).Name);
appWidgetManager.UpdateAppWidget(componentName, remoteViews);
}
}
When BtnAdd is clicked, how can I use DependecyService to call UpdateData()? The problem I have is 'context' does not exist in the current context in UpdateWidgetUI();
You could make your MainActivity as a static variable.
like:
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static MainActivity Instance;
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Instance = this;
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
}
then you want use the context,you could do like:
public void UpdateWidgetUI()
{
var widgetView = new RemoteViews(MainActivity.Instance.PackageName, Resource.Layout.widget);
UpdateData(widgetView);
AppWidgetManager appWidgetManager = AppWidgetManager.GetInstance(MainActivity.Instance);
ComponentName componentName = new ComponentName(MainActivity.Instance, Java.Lang.Class.FromType(typeof(WidgetClass)).Name);
appWidgetManager.UpdateAppWidget(componentName, remoteViews);
}

How to integrate a rabbitmq Client in a BackgroundService in a Xamarin Forms Android App?

Xamarin Forms App
I have two pages in my App: News and Announcements.
On these pages I want to display messages received from a rabbitmq Server.
I implemented a Service in Android that is executed after the login is completed. The communication between the Shared Code and the Android-Code is realized with Xamarin.MessagingCenter.
The Problem is that my rabbitmq Client currently doesn't receive any messages.
The Server is running on a VM and the App runs in an Emulator.
Code
Here is my Code
DataTransferTaskService in Android
[Service]
class DataTransferTaskService : Service
{
static User user = new User { Groups = new List<string>() { "Test","Test2" } };
CancellationTokenSource _cts;
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
// From shared code or in your PCL
_cts = new CancellationTokenSource();
PS();
return StartCommandResult.NotSticky;
}
void PS()
{
var factory = new ConnectionFactory() { HostName = "10.0.0.3", UserName = "test", Password = "test", Port = 5672 };
var connection = factory.CreateConnection();
var channel = connection.CreateModel();
channel.ExchangeDeclare(exchange: "Kastner", type: ExchangeType.Direct);
var queueName = channel.QueueDeclare().QueueName;
foreach (string g in user.Groups)
{
channel.QueueBind(queue: queueName,
exchange: "Kastner",
routingKey: g);
}
var consumer = new EventingBasicConsumer(channel);
System.Diagnostics.Debug.WriteLine("vor event");
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
var obj = JObject.Parse(message);
News n;
Announcement a;
System.Diagnostics.Debug.WriteLine("vor if");
if (obj.Properties().Select(p => p.Name).FirstOrDefault() == "NewsId")
{
n = JsonConvert.DeserializeObject<News>(message);
MessagingCenter.Send<object, News>(this, "NewsMessage", n);
}
else
{
a = JsonConvert.DeserializeObject<Announcement>(message);
MessagingCenter.Send<object, Announcement>(this, "AnnouncementMessage", a);
}
};
channel.BasicConsume(queue: queueName,
autoAck: true,
consumer: consumer);
}
}
MainActivity.cs in Android
[Activity(Label = "DA_MessageBrokerApp", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
FFImageLoading.Forms.Platform.CachedImageRenderer.Init(enableFastRenderer: true);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
CrossMediaManager.Current.Init(this);
LoadApplication(new App());
WireUpDataTransferTask();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
void WireUpDataTransferTask()
{
MessagingCenter.Subscribe<NewsViewModel>(this, "StartDataTransferMessage", (sender) =>
{
var intent = new Intent(this, typeof(DataTransferTaskService));
StartService(intent);
});
}
}
NewsViewModel in Shared Code
public class NewsViewModel : BaseViewModel
{
static User user = new User { Groups = new List<string>() { "Test","Test2" } };
private News _selectedItem;
public ObservableCollection<News> Items { get; }
public Command LoadItemsCommand { get; }
public Command AddItemCommand { get; }
public Command<News> ItemTapped { get; }
public NewsViewModel()
{
Title = "News";
Items = new ObservableCollection<News>();
MessagingCenter.Subscribe<object, News>(this, "NewsMessage", async (sender, arg) =>
{
await Task.Run(() =>DataStoreNews.AddItemAsync(arg));
await ExecuteLoadItemsCommand();
});
MessagingCenter.Send<NewsViewModel>(this, "StartDataTransferMessage");
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
ItemTapped = new Command<News>(OnItemSelected);
}
void AddNewItem(News news)
{
if (!Items.Contains(news))
Items.Add(news);
}
async Task ExecuteLoadItemsCommand()
{
IsBusy = true;
try
{
Items.Clear();
var items = await DataStoreNews.GetItemsAsync(true);
foreach (var item in items)
{
Items.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
public void OnAppearing()
{
IsBusy = true;
SelectedItem = null;
}
public News SelectedItem
{
get => _selectedItem;
set
{
SetProperty(ref _selectedItem, value);
OnItemSelected(value);
}
}
async void OnItemSelected(News item)
{
if (item == null)
return;
// This will push the ItemDetailPage onto the navigation stack
await Shell.Current.GoToAsync($"{nameof(NewsDetailPage)}?{nameof(NewsDetailViewModel.NewsId)}={item.NewsId}");
}
}
BaseViewModel in Shared Code
public class BaseViewModel : INotifyPropertyChanged
{
public IDataStore<News> DataStoreNews => DependencyService.Get<IDataStore<News>>();
public IDataStore<Announcement> DataStoreMessage => DependencyService.Get<IDataStore<Announcement>>();
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Problem
In the PS() method in the Service Class is the Method where I execute the Code of the rabbitmq Client. But the event that the client receives a message is never raised. I already tried the connection with annother Test Console App and there I received messages. Did I do something wrong with the Service or why is this not working?
Edit: I removed the usings of connection and channel in the DataTransferTaskService class, because I found out that these caused some problems, but it is still not working.

The parameter came from broadcast receiver constructor is always nullable

I want to update my main activity UI in my foreground service with broadcast receiver. In BroadcastReceiver.OnReceive method,I passed the instance of main activity,but it is always nullable. How can I fix it? Many thanks in advance!
In my broadcast receiver:
public override void OnReceive(Context context, Intent intent)
{
Core.Music music = intent.GetParcelableExtra("selectedMusic") as Core.Music;
mMainActivity.mTxtSongName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Title);
mMainActivity.mTxtAuthorName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Author);
System.Threading.ThreadPool.QueueUserWorkItem(o =>
{
string imageUrl = music.Url.Replace(#"\", "").Replace("http", "https");
var task = Core.MusicHelper.GetSongPic(imageUrl, 35, 35);
var pic = task.Result;
if (pic != null)
{
mMainActivity.RunOnUiThread(() =>
{
mMainActivity.mImageViewSongPic.SetImageBitmap(pic);
});
}
});
}
In my service:
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
Core.Music music = intent.GetParcelableExtra("selectedMusic") as Core.Music;
BroadcastStarted(music);
//To start the service
return StartCommandResult.NotSticky;
}
In my MainActivity.OnResume:
IntentFilter filter = new IntentFilter();
mReceive = new Service.Boradcast.MusicChangedBroadcastReceiver() { mMainActivity=this};
RegisterReceiver(mReceive,filter);
how do you defined mMainActivity ?
however the simplest example of a reference to MainActivity is to use static
define in your MainActivity OnCreate() method:
public static MainActivity Instance;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
...
Instance = this;
}
then call in your broadcast receiver
public override void OnReceive(Context context, Intent intent)
{
Core.Music music = intent.GetParcelableExtra("selectedMusic") as Core.Music;
MainActivity.Instance.mTxtSongName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Title);
MainActivity.Instance.mTxtAuthorName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Author);
System.Threading.ThreadPool.QueueUserWorkItem(o =>
{
string imageUrl = music.Url.Replace(#"\", "").Replace("http", "https");
var task = Core.MusicHelper.GetSongPic(imageUrl, 35, 35);
var pic = task.Result;
if (pic != null)
{
MainActivity.Instance.RunOnUiThread(() =>
{
MainActivity.Instance.mImageViewSongPic.SetImageBitmap(pic);
});
}
});
}
Or pass MainActivity as a parameter to the constructor :
in your MainActivity.OnResume():
IntentFilter filter = new IntentFilter();
mReceive = new Service.Boradcast.MusicChangedBroadcastReceiver(this);
RegisterReceiver(mReceive,filter);
then in your broadcast receiver:
[BroadcastReceiver]
public class MusicChangedBroadcastReceiver: BroadcastReceiver
{
public MainActivity mMainActivity;
public MusicChangedBroadcastReceiver()
{
}
public MusicChangedBroadcastReceiver(MainActivity activity)
{
this.mMainActivity= activity;
}
public override void OnReceive(Context context, Intent intent)
{
Core.Music music = intent.GetParcelableExtra("selectedMusic") as Core.Music;
mMainActivity.mTxtSongName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Title);
mMainActivity.mTxtAuthorName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Author);
System.Threading.ThreadPool.QueueUserWorkItem(o =>
{
string imageUrl = music.Url.Replace(#"\", "").Replace("http", "https");
var task = Core.MusicHelper.GetSongPic(imageUrl, 35, 35);
var pic = task.Result;
if (pic != null)
{
mMainActivity.RunOnUiThread(() =>
{
mMainActivity.mImageViewSongPic.SetImageBitmap(pic);
});
}
});
}
}

Android - Get value from Service and using it in Widget

I am developing a simple C# Android app, which runs in Background and does a specific calculation, I have a also a widget with a TexTview in which I want to display the Calculation result.
Service.cs
Intent intent = new Intent("TEST");
intent.PutExtra("alltotale", total.ToString());
this.SendBroadcast(intent);
Then in BroadcastReceiver class, I have:
Receiver.cs
public override void OnReceive(Context context, Intent intent)
{
string nh = intent.GetStringExtra("alltotale");
Toast.MakeText(context, nh, ToastLength.Short).Show();
}
Now I want to re-use this value and display it in the widget TextView but it is not working, I am getting nothing in my Widget.
AppWidget.cs
[BroadcastReceiver(Label = "Widget1")]
[IntentFilter(new string[] { "android.appwidget.action.APPWIDGET_UPDATE" })]
[MetaData("android.appwidget.provider", Resource = "#xml/appwidgetprovider")]
public class AppWidget : AppWidgetProvider
{
Intent intent;
public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
var me = new ComponentName(context, Java.Lang.Class.FromType(typeof(AppWidget)).Name);
appWidgetManager.UpdateAppWidget(me, BuildRemoteViews(context, appWidgetIds));
}
private RemoteViews BuildRemoteViews(Context context, int[] appWidgetIds)
{
var widgetView = new RemoteViews(context.PackageName, Resource.Layout.Widget);
SetTextViewText(widgetView);
RegisterClicks(context, appWidgetIds, widgetView);
return widgetView;
}
private void SetTextViewText(RemoteViews widgetView)
{
string oo = intent.GetStringExtra("alltotale");
widgetView.SetTextViewText(Resource.Id.widgetMedium, "Dabboussi");
widgetView.SetTextViewText(Resource.Id.widgetSmall, oo);
}
private static string AnnouncementClick = "AnnouncementClickTag";
private void RegisterClicks(Context context, int[] appWidgetIds, RemoteViews widgetView)
{
var intent = new Intent(context, typeof(AppWidget));
intent.SetAction(AppWidgetManager.ActionAppwidgetUpdate);
intent.PutExtra(AppWidgetManager.ExtraAppwidgetIds, appWidgetIds);
// Register click event for the Background
var piBackground = PendingIntent.GetBroadcast(context, 0, intent, PendingIntentFlags.UpdateCurrent);
widgetView.SetOnClickPendingIntent(Resource.Id.widgetAnnouncementIcon, GetPendingSelfIntent(context, AnnouncementClick));
}
private PendingIntent GetPendingSelfIntent(Context context, string action)
{
var intent = new Intent(context, typeof(AppWidget));
intent.SetAction(action);
return PendingIntent.GetBroadcast(context, 0, intent, 0);
}
public override void OnReceive(Context context, Intent intent)
{
base.OnReceive(context, intent);
var data = intent.GetStringExtra("alltotale");
if (data != null)
{
var updateViews = new RemoteViews(context.PackageName, Resource.Layout.Widget);
SetTextViewText(updateViews);
ComponentName thisWidget = new ComponentName(context, Java.Lang.Class.FromType(typeof(AppWidget)).Name);
AppWidgetManager manager = AppWidgetManager.GetInstance(context);
manager.UpdateAppWidget(thisWidget, updateViews);
}
if (AnnouncementClick.Equals(intent.Action))
{
Toast.MakeText(context, "OPEN", ToastLength.Short).Show();
// Open another app
}
}
}
So, the value is being displayed in the Toast message in background but nothing in the widget textview. Why is that and how can I solve it ?
Edit
I have thought that maybe ISharedPreferences would do the trick, so I did the following:
Service.cs
Intent priceIntent = new Intent("com.xamarin.example.TEST");
priceIntent.PutExtra("alltotale", total.ToString());
this.SendBroadcast(priceIntent);
ISharedPreferences prSer = PreferenceManager.GetDefaultSharedPreferences(this);
ISharedPreferencesEditor prEd = prSer.Edit();
prEd.PutInt("PriceTot", Convert.ToInt32(total));
then in the AppWidget.cs file I did the following:
[BroadcastReceiver(Label = "Widget1")]
[IntentFilter(new string[] { "android.appwidget.action.APPWIDGET_UPDATE", "com.xamarin.example.TEST" })]
[MetaData("android.appwidget.provider", Resource = "#xml/appwidgetprovider")]
public class AppWidget : AppWidgetProvider
{
Intent intent;
public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
var me = new ComponentName(context, Java.Lang.Class.FromType(typeof(AppWidget)).Name);
appWidgetManager.UpdateAppWidget(me, BuildRemoteViews(context, appWidgetIds));
}
private RemoteViews BuildRemoteViews(Context context, int[] appWidgetIds)
{
var widgetView = new RemoteViews(context.PackageName, Resource.Layout.Widget);
//SetTextViewText(widgetView);
RegisterClicks(context, appWidgetIds, widgetView);
return widgetView;
}
private void SetTextViewText(RemoteViews widgetView)
{
widgetView.SetTextViewText(Resource.Id.widgetMedium, "Don Dabboussi");
widgetView.SetTextViewText(Resource.Id.widgetSmall, "KK");
}
private static string AnnouncementClick = "AnnouncementClickTag";
private void RegisterClicks(Context context, int[] appWidgetIds, RemoteViews widgetView)
{
var intent = new Intent(context, typeof(AppWidget));
intent.SetAction(AppWidgetManager.ActionAppwidgetUpdate);
intent.PutExtra(AppWidgetManager.ExtraAppwidgetIds, appWidgetIds);
// Register click event for the Background
var piBackground = PendingIntent.GetBroadcast(context, 0, intent, PendingIntentFlags.UpdateCurrent);
//widgetView.SetOnClickPendingIntent(Resource.Id.widgetAnnouncementIcon, GetPendingSelfIntent(context, AnnouncementClick));
}
private PendingIntent GetPendingSelfIntent(Context context, string action)
{
var intent = new Intent(context, typeof(AppWidget));
intent.SetAction(action);
return PendingIntent.GetBroadcast(context, 0, intent, 0);
}
public override void OnReceive(Context context, Intent intent)
{
base.OnReceive(context, intent);
ISharedPreferences prSer = PreferenceManager.GetDefaultSharedPreferences(context);
var data = prSer.GetInt("PriceTot", 0);
if (data != 0)
{
var updateViews = new RemoteViews(context.PackageName, Resource.Layout.Widget);
SetTextViewText(updateViews);
ComponentName thisWidget = new ComponentName(context, Java.Lang.Class.FromType(typeof(AppWidget)).Name);
AppWidgetManager manager = AppWidgetManager.GetInstance(context);
manager.UpdateAppWidget(thisWidget, updateViews);
}
if (AnnouncementClick.Equals(intent.Action))
{
Toast.MakeText(context, "OPEN", ToastLength.Short).Show();
// Open another app
}
}
But that didn't worked, there are no text in the textviews in my widget (no dabboussi and no kk). Why is that ?
You put a string named alltotale and you ask for a string named widgetTotale, so it is normal if it is not working. You have to change widgetTotale by alltotale
You need to get AppWidgetManager and call UpdateAppWidget() method to update the widget.
For example:
public override void OnReceive(Context context, Intent intent)
{
base.OnReceive(context, intent);
var data = intent.GetStringExtra("alltotale");
if(data != null)
{
var updateViews = new RemoteViews(context.PackageName, Resource.Layout.widget);
SetTextViewText(updateViews, intent);
// Push update for this widget to the home screen
ComponentName thisWidget = new ComponentName(context, Java.Lang.Class.FromType(typeof(MyWidget)).Name);
AppWidgetManager manager = AppWidgetManager.GetInstance(context);
manager.UpdateAppWidget(thisWidget, updateViews);
}
}
private void SetTextViewText(RemoteViews widgetView, Intent intent)
{
string oo = intent.GetStringExtra("alltotale");
widgetView.SetTextViewText(Resource.Id.widgetMedium, "Dabboussi");
widgetView.SetTextViewText(Resource.Id.widgetSmall, oo);
}
And you could refer to this official demo for more information.

How to bind to AccessibilityService in Mono (Xamarin)

I've tried and got OnServiceConnected event in ServiceConnection class. But casting IBinder to my class (it inherits Binder, that inherits IBinder) returns null if I try to do that as "var myObject = IBinderObject as myBinderClass;" or casting error if I try to do that as "var myObject = (myBinderClass) IBinderObject;".
Please, any working example...
MainActivity code:
namespace MyApp.Droid
{
[Activity(Label = "MyApp", Icon = "#drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
AccessibilityServiceBinder binder;
AccessibilityServiceConnection accessibilityServiceConnection;
bool isBound = false, isBoundAC = false, isConfigurationChange = false;
protected Intent ServiceIntent;
AccessibilityServiceClass ServiceClass;
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 bundle)
{
base.OnCreate(bundle);
ServiceIntent = new Intent(this, typeof(AccessibilityServiceClass));
ServiceIntent.SetPackage("android.accessibilityservice.AccessibilityService");
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
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;
//accessibilityServiceBinder is always null here
if (accessibilityServiceBinder != null)
{
activity.binder = accessibilityServiceBinder;
activity.isBound = true;
this.binder = accessibilityServiceBinder;
}
}
public void OnServiceDisconnected(ComponentName name)
{
activity.isBound = false;
}
}
public override Java.Lang.Object OnRetainNonConfigurationInstance()
{
base.OnRetainNonConfigurationInstance();
isConfigurationChange = true;
return accessibilityServiceConnection;
}
}
}
Service code:
namespace MyApp.Droid
{
[Service(Label = "MyApp", Permission = Manifest.Permission.BindAccessibilityService), IntentFilter(new[] { "android.accessibilityservice.AccessibilityService" }), MetaData("android.accessibility-service", Resource = "#xml/accessibilityserviceconfig")]
public class AccessibilityServiceClass : Android.AccessibilityServices.AccessibilityService
{
public override void OnAccessibilityEvent(AccessibilityEvent e)
{
Toast.MakeText(this, "OnAccessibilityEvent", ToastLength.Short);
}
public override void OnInterrupt()
{
Toast.MakeText(this, "OnInterrupt", ToastLength.Short);
}
protected override void OnServiceConnected()
{
Toast.MakeText(this, "OnServiceConnected", ToastLength.Short);
}
}
public class AccessibilityServiceBinder : Binder
{
AccessibilityServiceClass service;
public AccessibilityServiceBinder(AccessibilityServiceClass service)
{
this.service = service;
}
public AccessibilityServiceClass GetAccessibilityService()
{
return service;
}
}
}
You aren't overriding the OnBind event of your service, thus the binder is null as you haven't provided an instance.
Add this to your service:
AccessibilityServiceBinder binder;
public override IBinder OnBind (Intent intent)
{
if(binder == null)
binder = new AccessibilityServiceBinder (this);
return binder;
}
For more info read the Xamarin docs on bound services, they're very well explained: https://developer.xamarin.com/guides/android/application_fundamentals/services/part_2_-_bound_services/
EDIT: Total wrong answer (I will delete it in some minutes). It seems it's not possible to bind to an Accessibility service. More info here: android bind to AccessibilityService

Categories