Skip to content

Commit c62a372

Browse files
committed
start handling async methods on the plugin side
1 parent 3989757 commit c62a372

File tree

7 files changed

+260
-69
lines changed

7 files changed

+260
-69
lines changed

Assets/UrbanAirship/Platforms/AirshipChannel.cs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,21 @@ internal AirshipChannel(IAirshipPlugin plugin)
3131
}
3232

3333
/// <summary>
34-
/// Returns the channel ID. If the channel ID is not yet created the function it will wait for it before returning.
34+
/// Waits for the channel ID asynchronously using a coroutine.
35+
/// If the channel ID is not yet created, this method will wait for it before completing.
3536
/// After the channel ID is created, this method functions the same as `getChannelId()`.
37+
/// This method does not block Unity's main thread.
3638
/// </summary>
37-
/// <returns>The channel ID.</returns>
38-
public string WaitForChannelId()
39+
/// <param name="onComplete">Callback invoked with the channel ID when the operation completes.</param>
40+
/// <param name="onError">Optional callback invoked if an error occurs.</param>
41+
/// <returns>A coroutine that can be started with StartCoroutine.</returns>
42+
public IEnumerator WaitForChannelId(Action<string> onComplete, Action<Exception> onError = null)
3943
{
40-
return plugin.Call<string>("waitForChannelId");
44+
yield return AirshipCoroutineHelper.RunAsync(
45+
() => plugin.Call<string>("waitForChannelId"),
46+
onComplete,
47+
onError
48+
);
4149
}
4250

4351
/// <summary>
@@ -88,14 +96,23 @@ public AttributeEditor EditAttributes()
8896
}
8997

9098
/// <summary>
91-
/// Gets the channel's subscription lists.
99+
/// Gets the channel's subscription lists asynchronously using a coroutine.
100+
/// This method does not block Unity's main thread.
92101
/// </summary>
93-
/// <returns>The subscription lists.</returns>
94-
public IEnumerable<string> GetSubscriptionLists()
102+
/// <param name="onComplete">Callback invoked with the subscription lists when the operation completes.</param>
103+
/// <param name="onError">Optional callback invoked if an error occurs.</param>
104+
/// <returns>A coroutine that can be started with StartCoroutine.</returns>
105+
public IEnumerator GetSubscriptionLists(Action<IEnumerable<string>> onComplete, Action<Exception> onError = null)
95106
{
107+
yield return AirshipCoroutineHelper.RunAsync(
108+
() => {
96109
string subscriptionListsAsJson = plugin.Call<string>("getChannelSubscriptionLists");
97110
JsonArray<string> jsonArray = JsonArray<string>.FromJson(subscriptionListsAsJson);
98111
return jsonArray.AsEnumerable();
112+
},
113+
onComplete,
114+
onError
115+
);
99116
}
100117

101118
/// <summary>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/* Copyright Airship and Contributors */
2+
3+
using System;
4+
using System.Collections;
5+
using UnityEngine;
6+
7+
namespace UrbanAirship {
8+
9+
/// <summary>
10+
/// Helper class to run blocking operations in coroutines.
11+
/// AndroidJavaObject.Call() must run on Unity's main thread, so we call it directly
12+
/// from the coroutine (which runs on the main thread). We yield first to let Unity
13+
/// process a frame, then execute the blocking call. The blocking happens on the main
14+
/// thread, but Unity has had a chance to process input/rendering first.
15+
/// </summary>
16+
internal static class AirshipCoroutineHelper {
17+
18+
/// <summary>
19+
/// Runs a blocking operation on the main thread. Yields first to let Unity process,
20+
/// then executes the blocking call. Since coroutines run on the main thread, this
21+
/// ensures AndroidJavaObject.Call() works correctly.
22+
/// </summary>
23+
/// <typeparam name="T">The return type</typeparam>
24+
/// <param name="operation">The blocking operation to run</param>
25+
/// <param name="onComplete">Callback invoked when the operation completes</param>
26+
/// <param name="onError">Optional callback invoked if an error occurs</param>
27+
/// <returns>A coroutine</returns>
28+
public static IEnumerator RunAsync<T>(Func<T> operation, Action<T> onComplete, Action<Exception> onError = null) {
29+
// Yield first to let Unity process a frame
30+
yield return null;
31+
32+
T result = default(T);
33+
Exception exception = null;
34+
35+
try {
36+
result = operation();
37+
} catch (Exception e) {
38+
exception = e;
39+
Debug.LogError($"[AirshipCoroutineHelper] Exception: {e.Message}\n{e.StackTrace}");
40+
}
41+
42+
// Yield again before invoking callbacks to ensure we're still on main thread
43+
yield return null;
44+
45+
// Handle result or error on main thread
46+
if (exception != null) {
47+
onError?.Invoke(exception);
48+
} else {
49+
onComplete?.Invoke(result);
50+
}
51+
}
52+
53+
/// <summary>
54+
/// Runs a blocking operation on the main thread. Yields first to let Unity process,
55+
/// then executes the blocking call. Since coroutines run on the main thread, this
56+
/// ensures AndroidJavaObject.Call() works correctly.
57+
/// </summary>
58+
/// <param name="operation">The blocking operation to run</param>
59+
/// <param name="onComplete">Callback invoked when the operation completes</param>
60+
/// <param name="onError">Optional callback invoked if an error occurs</param>
61+
/// <returns>A coroutine</returns>
62+
public static IEnumerator RunAsync(Action operation, Action onComplete = null, Action<Exception> onError = null) {
63+
// Yield first to let Unity process a frame
64+
yield return null;
65+
66+
Exception exception = null;
67+
68+
try {
69+
operation();
70+
} catch (Exception e) {
71+
exception = e;
72+
Debug.LogError($"[AirshipCoroutineHelper] Exception: {e.Message}\n{e.StackTrace}");
73+
}
74+
75+
// Yield again before invoking callbacks to ensure we're still on main thread
76+
yield return null;
77+
78+
// Handle result or error on main thread
79+
if (exception != null) {
80+
onError?.Invoke(exception);
81+
} else {
82+
onComplete?.Invoke();
83+
}
84+
}
85+
}
86+
}
87+

Assets/UrbanAirship/Platforms/AirshipMessageCenter.cs

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,58 +23,96 @@ internal AirshipMessageCenter(IAirshipPlugin plugin)
2323
}
2424

2525
/// <summary>
26-
/// Gets the number of unread messages for the message center.
26+
/// Gets the number of unread messages for the message center asynchronously using a coroutine.
27+
/// This method does not block Unity's main thread.
2728
/// </summary>
28-
/// <returns>The number of unread messages.</returns>
29-
public int GetUnReadCount()
29+
/// <param name="onComplete">Callback invoked with the unread count when the operation completes.</param>
30+
/// <param name="onError">Optional callback invoked if an error occurs.</param>
31+
/// <returns>A coroutine that can be started with StartCoroutine.</returns>
32+
public IEnumerator GetUnReadCount(Action<int> onComplete, Action<Exception> onError = null)
3033
{
31-
return plugin.Call<int>("getUnreadCount");
34+
yield return AirshipCoroutineHelper.RunAsync(
35+
() => plugin.Call<int>("getUnreadCount"),
36+
onComplete,
37+
onError
38+
);
3239
}
3340

3441
/// <summary>
35-
/// Gets the inbox messages.
42+
/// Gets the inbox messages asynchronously using a coroutine.
43+
/// This method does not block Unity's main thread.
3644
/// </summary>
37-
/// <value>An enumberable list of InboxMessage objects.</value>
38-
public IEnumerable<InboxMessage> GetMessages()
45+
/// <param name="onComplete">Callback invoked with the messages when the operation completes.</param>
46+
/// <param name="onError">Optional callback invoked if an error occurs.</param>
47+
/// <returns>A coroutine that can be started with StartCoroutine.</returns>
48+
public IEnumerator GetMessages(Action<IEnumerable<InboxMessage>> onComplete, Action<Exception> onError = null)
3949
{
50+
yield return AirshipCoroutineHelper.RunAsync(
51+
() => {
4052
var inboxMessages = new List<InboxMessage>();
41-
4253
string inboxMessagesAsJson = plugin.Call<string>("getMessages");
4354
InternalInboxMessage[] internalInboxMessages = JsonArray<InternalInboxMessage>.FromJson(inboxMessagesAsJson).values;
44-
4555
// TODO verify this as the proxy provide a the extras into a map
4656
// Unity's JsonUtility doesn't support embedded dictionaries - constructor will create the extras dictionary
4757
foreach (InternalInboxMessage internalInboxMessage in internalInboxMessages)
4858
{
4959
inboxMessages.Add(new InboxMessage(internalInboxMessage));
5060
}
51-
return inboxMessages;
61+
return (IEnumerable<InboxMessage>)inboxMessages;
62+
},
63+
onComplete,
64+
onError
65+
);
5266
}
5367

5468
/// <summary>
55-
/// Mark an inbox message as having been read.
69+
/// Mark an inbox message as having been read asynchronously using a coroutine.
70+
/// This method does not block Unity's main thread.
5671
/// </summary>
5772
/// <param name="messageId">The messageId for the message.</param>
58-
public void MarkMessageRead(string messageId)
73+
/// <param name="onComplete">Optional callback invoked when the operation completes.</param>
74+
/// <param name="onError">Optional callback invoked if an error occurs.</param>
75+
/// <returns>A coroutine that can be started with StartCoroutine.</returns>
76+
public IEnumerator MarkMessageRead(string messageId, Action onComplete = null, Action<Exception> onError = null)
5977
{
60-
plugin.Call("markMessageRead", messageId);
78+
yield return AirshipCoroutineHelper.RunAsync(
79+
() => plugin.Call("markMessageRead", messageId),
80+
onComplete,
81+
onError
82+
);
6183
}
6284

6385
/// <summary>
64-
/// Delete an inbox message.
86+
/// Delete an inbox message asynchronously using a coroutine.
87+
/// This method does not block Unity's main thread.
6588
/// </summary>
6689
/// <param name="messageId">The messageId for the message.</param>
67-
public void DeleteMessage(string messageId)
90+
/// <param name="onComplete">Optional callback invoked when the operation completes.</param>
91+
/// <param name="onError">Optional callback invoked if an error occurs.</param>
92+
/// <returns>A coroutine that can be started with StartCoroutine.</returns>
93+
public IEnumerator DeleteMessage(string messageId, Action onComplete = null, Action<Exception> onError = null)
6894
{
69-
plugin.Call("deleteMessage", messageId);
95+
yield return AirshipCoroutineHelper.RunAsync(
96+
() => plugin.Call("deleteMessage", messageId),
97+
onComplete,
98+
onError
99+
);
70100
}
71101

72102
/// <summary>
73-
/// Refreshes the inbox.
103+
/// Refreshes the inbox asynchronously using a coroutine.
104+
/// This method does not block Unity's main thread.
74105
/// </summary>
75-
public void RefreshInbox()
106+
/// <param name="onComplete">Optional callback invoked when the operation completes.</param>
107+
/// <param name="onError">Optional callback invoked if an error occurs.</param>
108+
/// <returns>A coroutine that can be started with StartCoroutine.</returns>
109+
public IEnumerator RefreshInbox(Action onComplete = null, Action<Exception> onError = null)
76110
{
77-
plugin.Call("refreshMessages");
111+
yield return AirshipCoroutineHelper.RunAsync(
112+
() => plugin.Call("refreshMessages"),
113+
onComplete,
114+
onError
115+
);
78116
}
79117

80118
/// <summary>

Assets/UrbanAirship/Platforms/AirshipPreferenceCenter.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,20 @@ public void Display(string preferenceCenterId)
3333
}
3434

3535
/// <summary>
36-
/// Gets the preference center config.
36+
/// Gets the preference center config asynchronously using a coroutine.
37+
/// This method does not block Unity's main thread.
3738
/// </summary>
3839
/// <param name="preferenceCenterId">The preference center Id.</param>
39-
/// <returns>The preference center config.</returns>
40-
public PreferenceCenterConfig GetConfig(string preferenceCenterId)
40+
/// <param name="onComplete">Callback invoked with the config when the operation completes.</param>
41+
/// <param name="onError">Optional callback invoked if an error occurs.</param>
42+
/// <returns>A coroutine that can be started with StartCoroutine.</returns>
43+
public IEnumerator GetConfig(string preferenceCenterId, Action<PreferenceCenterConfig> onComplete, Action<Exception> onError = null)
4144
{
42-
// TODO parse this from a Json into a PreferenceCenterConfig and return that
43-
return JsonUtility.FromJson<PreferenceCenterConfig> (plugin.Call<string>("getPreferenceCenterConfig", preferenceCenterId));
45+
yield return AirshipCoroutineHelper.RunAsync(
46+
() => JsonUtility.FromJson<PreferenceCenterConfig>(plugin.Call<string>("getPreferenceCenterConfig", preferenceCenterId)),
47+
onComplete,
48+
onError
49+
);
4450
}
4551

4652
/// <summary>

Assets/UrbanAirship/Platforms/AirshipPush.cs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,36 @@ public bool IsUserNotificationEnabled()
5656
}
5757

5858
/// <summary>
59-
/// Enables user notifications.
59+
/// Enables user notifications asynchronously using a coroutine.
60+
/// This method does not block Unity's main thread.
6061
/// </summary>
6162
/// <param name="fallback">Optional fallback.</param>
62-
/// <returns>The permission result.</returns>
63-
public bool EnableUserNotifications(PromptPermissionFallback? fallback)
63+
/// <param name="onComplete">Callback invoked with the permission result when the operation completes.</param>
64+
/// <param name="onError">Optional callback invoked if an error occurs.</param>
65+
/// <returns>A coroutine that can be started with StartCoroutine.</returns>
66+
public IEnumerator EnableUserNotifications(PromptPermissionFallback? fallback, Action<bool> onComplete, Action<Exception> onError = null)
6467
{
65-
return plugin.Call<bool>("enableUserNotifications", fallback);
68+
yield return AirshipCoroutineHelper.RunAsync(
69+
() => plugin.Call<bool>("enableUserNotifications", fallback),
70+
onComplete,
71+
onError
72+
);
6673
}
6774

6875
/// <summary>
69-
/// Gets the notification status.
76+
/// Gets the notification status asynchronously using a coroutine.
77+
/// This method does not block Unity's main thread.
7078
/// </summary>
71-
/// <returns>The notification status.</returns>
72-
public PushNotificationStatus GetNotificationStatus()
79+
/// <param name="onComplete">Callback invoked with the notification status when the operation completes.</param>
80+
/// <param name="onError">Optional callback invoked if an error occurs.</param>
81+
/// <returns>A coroutine that can be started with StartCoroutine.</returns>
82+
public IEnumerator GetNotificationStatus(Action<PushNotificationStatus> onComplete, Action<Exception> onError = null)
7383
{
74-
// TODO parse this and return a PushNotificationStatus
75-
return JsonUtility.FromJson<PushNotificationStatus> (plugin.Call<string>("getNotificationStatus"));
84+
yield return AirshipCoroutineHelper.RunAsync(
85+
() => JsonUtility.FromJson<PushNotificationStatus>(plugin.Call<string>("getNotificationStatus")),
86+
onComplete,
87+
onError
88+
);
7689
}
7790

7891
/// <summary>

0 commit comments

Comments
 (0)