Skip to content

Commit

Permalink
Android: fixed server monitor widgets hanging out with the loading pr…
Browse files Browse the repository at this point in the history
…ogress indicator after opening the app
  • Loading branch information
entdark committed Jul 27, 2024
1 parent 089b276 commit 3513be5
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 27 deletions.
74 changes: 47 additions & 27 deletions JKChat.Android/Widgets/ServerMonitorAppWidget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 16,7 @@
using JKChat.Android.ValueConverters;
using JKChat.Android.Views.Main;
using JKChat.Core;
using JKChat.Core.Helpers;
using JKChat.Core.Services;
using JKChat.Core.ViewModels.Dialog;
using JKChat.Core.ViewModels.Dialog.Items;
Expand All @@ -39,11 40,13 @@ public class ServerMonitorAppWidget : AppWidgetProvider {
public const string WidgetIdExtraKey = nameof(ServerMonitorAppWidget) nameof(WidgetIdExtraKey);
public const string PlayersExtraKey = nameof(ServerMonitorAppWidget) nameof(PlayersExtraKey);

private readonly TasksQueue tasksQueue = new();

public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int []appWidgetIds) {
var serverAddresses = AppSettings.ServerMonitorServers;
foreach (var appWidgetId in appWidgetIds) {
if (serverAddresses.TryGetValue(appWidgetId, out string serverAddress)) {
Update(context, appWidgetManager, appWidgetId, serverAddress);
Update(context, appWidgetManager, appWidgetId, serverAddress, false);
} else {
UpdateEmpty(context, appWidgetManager, appWidgetId);
}
Expand All @@ -56,22 59,30 @@ public override void OnReceive(Context context, Intent intent) {
var componentName = new ComponentName(context, Java.Lang.Class.FromType(typeof(ServerMonitorAppWidget)));
if (intent.Action == UpdateAction) {
var appWidgetIds = appWidgetManager.GetAppWidgetIds(componentName);
//remove saved servers associated with widgets if OnDeleted failed to get called and do the job
var serversAddresses = AppSettings.ServerMonitorServers;
serversAddresses = serversAddresses.Where(kvp => appWidgetIds.Contains(kvp.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
AppSettings.ServerMonitorServers = serversAddresses;
OnUpdate(context, appWidgetManager, appWidgetIds);
} else if (intent.Action == RefreshAction
&& intent.Extras.GetString(ServerAddressExtraKey, null) is string serverAddress
&& intent.Extras.GetInt(WidgetIdExtraKey, -1) is int appWidgetId && appWidgetId != -1) {
Update(context, appWidgetManager, appWidgetId, serverAddress);
Update(context, appWidgetManager, appWidgetId, serverAddress, true);
}
}

public override async void OnAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
public override void OnAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
base.OnAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
var serverAddresses = AppSettings.ServerMonitorServers;
if (serverAddresses.TryGetValue(appWidgetId, out string serverAddress)) {
var server = await ServerListItemVM.FindExistingOrLoad(serverAddress, true, false).ConfigureAwait(false);
if (server != null) {
SetView(context, appWidgetManager, appWidgetId, newOptions, server);
}
tasksQueue.Enqueue(async () => {
var server = await ServerListItemVM.FindExistingOrLoad(serverAddress, true, false);
if (server != null) {
await MainThread.InvokeOnMainThreadAsync(() => {
SetView(context, appWidgetManager, appWidgetId, newOptions, server);
});
}
});
}
}

Expand All @@ -84,30 95,37 @@ public override void OnDeleted(Context context, int []appWidgetIds) {
base.OnDeleted(context, appWidgetIds);
}

private static async void Update(Context context, AppWidgetManager appWidgetManager, int appWidgetId, string serverAddress) {
setLoading(true);
var server = await ServerListItemVM.FindExistingOrLoad(serverAddress, true).ConfigureAwait(false);
if (server != null) {
setView();
}
bool refreshed = await server?.Refresh();
setLoading(false);
if (refreshed) {
setView();
}
private void Update(Context context, AppWidgetManager appWidgetManager, int appWidgetId, string serverAddress, bool showLoading) {
tasksQueue.Enqueue(async () => {
await setLoading(true);
var server = await ServerListItemVM.FindExistingOrLoad(serverAddress, true);
await Task.Delay(500);
if (server != null) {
await server.Refresh();
await setView(server);
} else {
await setLoading(false);
}
});

void setLoading(bool loading) {
var views = new RemoteViews(context.PackageName, Resource.Layout.server_monitor_widget);
views.SetViewVisibility(Resource.Id.loading_progressbar, loading ? ViewStates.Visible : ViewStates.Gone);
appWidgetManager.UpdateAppWidget(appWidgetId, views);
async Task setView(ServerListItemVM server) {
await MainThread.InvokeOnMainThreadAsync(() => {
var options = appWidgetManager.GetAppWidgetOptions(appWidgetId) ?? new();
SetView(context, appWidgetManager, appWidgetId, options, server);
});
}
void setView() {
var options = appWidgetManager.GetAppWidgetOptions(appWidgetId) ?? new();
SetView(context, appWidgetManager, appWidgetId, options, server);
async Task setLoading(bool loading) {
if (!showLoading)
return;
await MainThread.InvokeOnMainThreadAsync(() => {
var views = new RemoteViews(context.PackageName, Resource.Layout.server_monitor_widget);
views.SetViewVisibility(Resource.Id.loading_progressbar, loading ? ViewStates.Visible : ViewStates.Gone);
appWidgetManager.UpdateAppWidget(appWidgetId, views);
});
}
}

private static void SetView(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions, ServerListItemVM server) {
private static void SetView(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions, ServerListItemVM server, bool resetLoading = true) {
string serverAddress = server.Address;

var views = new RemoteViews(context.PackageName, Resource.Layout.server_monitor_widget);
Expand Down Expand Up @@ -147,7 165,9 @@ private static void SetView(Context context, AppWidgetManager appWidgetManager,
views.SetTextViewText(Resource.Id.map_textview, server.MapName);
views.SetTextViewText(Resource.Id.datetime_textview, $"{DateTime.Now:g}");
//to be safe
// views.SetViewVisibility(Resource.Id.loading_progressbar, ViewStates.Gone);
if (resetLoading) {
views.SetViewVisibility(Resource.Id.loading_progressbar, ViewStates.Gone);
}
appWidgetManager.UpdateAppWidget(appWidgetId, views);
}

Expand Down
52 changes: 52 additions & 0 deletions JKChat.Core/Helpers/TasksQueue.cs
Original file line number Diff line number Diff line change
@@ -0,0 1,52 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace JKChat.Core.Helpers {
//source: https://github.com/gentlee/SerialQueue
public class TasksQueue {
private readonly SpinLock spinLock = new(false);
private readonly WeakReference<Task> lastTaskRef = new(null);

public Task Enqueue(Action action) {
return Enqueue(() => {
action();
return Task.FromResult(true);
});
}

public Task<T> Enqueue<T>(Func<T> func) {
return Enqueue(() => Task.FromResult(func()));
}

public Task Enqueue(Func<Task> func) {
return Enqueue(async () => {
await func();
return true;
});
}

public Task<T> Enqueue<T>(Func<Task<T>> func) {
bool lockTaken = false;
try {
Task<T> resultTask;

spinLock.Enter(ref lockTaken);

if (lastTaskRef.TryGetTarget(out Task lastTask)) {
resultTask = lastTask.ContinueWith(_ => func(), TaskContinuationOptions.ExecuteSynchronously).Unwrap();
} else {
resultTask = Task.Run(func);
}

lastTaskRef.SetTarget(resultTask);

return resultTask;
} finally {
if (lockTaken)
spinLock.Exit(false);
}
}
}
}

0 comments on commit 3513be5

Please sign in to comment.