diff --git a/Source/Fuse.Android.Permissions/AndroidPermissionsInternal.uno b/Source/Fuse.Android.Permissions/AndroidPermissionsInternal.uno
index 0aabc65f2..80127222d 100644
--- a/Source/Fuse.Android.Permissions/AndroidPermissionsInternal.uno
+++ b/Source/Fuse.Android.Permissions/AndroidPermissionsInternal.uno
@@ -769,5 +769,10 @@ namespace Fuse.Android.Permissions.Internal
{
return new PlatformPermission("android.permission.WRITE_VOICEMAIL");
}
+ [TargetSpecificImplementation]
+ internal static extern PlatformPermission _post_notification()
+ {
+ return new PlatformPermission("android.permission.POST_NOTIFICATIONS");
+ }
}
}
diff --git a/Source/Fuse.Android.Permissions/AndroidPermissionsInternal.uxl b/Source/Fuse.Android.Permissions/AndroidPermissionsInternal.uxl
index 09eaf6cb5..d78d5003f 100644
--- a/Source/Fuse.Android.Permissions/AndroidPermissionsInternal.uxl
+++ b/Source/Fuse.Android.Permissions/AndroidPermissionsInternal.uxl
@@ -611,5 +611,9 @@
+
+
+
+
diff --git a/Source/Fuse.Android.Permissions/Permissions.uno b/Source/Fuse.Android.Permissions/Permissions.uno
index c874ee5ab..df9409809 100644
--- a/Source/Fuse.Android.Permissions/Permissions.uno
+++ b/Source/Fuse.Android.Permissions/Permissions.uno
@@ -271,6 +271,7 @@ namespace Fuse.Android.Permissions
public static PlatformPermission WRITE_SYNC_SETTINGS { get { return Internal.Android._write_sync_settings(); } }
public static PlatformPermission WRITE_USER_DICTIONARY { get { return Internal.Android._write_user_dictionary(); } }
public static PlatformPermission WRITE_VOICEMAIL { get { return Internal.Android._write_voicemail(); } }
+ public static PlatformPermission POST_NOTIFICATIONS { get { return Internal.Android._post_notification(); } }
}
}
diff --git a/Source/Fuse.Android.Permissions/Permissions.uxl b/Source/Fuse.Android.Permissions/Permissions.uxl
index 12c77e1e8..9ffa31bbe 100644
--- a/Source/Fuse.Android.Permissions/Permissions.uxl
+++ b/Source/Fuse.Android.Permissions/Permissions.uxl
@@ -611,5 +611,9 @@
+
+
+
+
diff --git a/Source/Fuse.PushNotifications/Android/Impl.uno b/Source/Fuse.PushNotifications/Android/Impl.uno
index 2b5ba0218..aff549626 100644
--- a/Source/Fuse.PushNotifications/Android/Impl.uno
+++ b/Source/Fuse.PushNotifications/Android/Impl.uno
@@ -5,6 +5,7 @@ using Fuse;
using Fuse.Controls;
using Fuse.Triggers;
using Fuse.Resources;
+using Fuse.Android.Permissions;
using Fuse.Platform;
using Uno.Compiler.ExportTargetInterop;
@@ -16,6 +17,7 @@ namespace Fuse.PushNotifications
"android.app.Activity",
"android.os.AsyncTask",
"android.app.Notification",
+ "android.app.PendingIntent",
"android.content.Context",
"android.content.Intent",
"android.media.RingtoneManager",
@@ -33,7 +35,7 @@ namespace Fuse.PushNotifications
"com.google.android.gms.common.ConnectionResult",
"com.google.android.gms.common.GoogleApiAvailability"
)]
- [Require("Gradle.Dependency.Implementation", "com.google.firebase:firebase-messaging:20.0.1")]
+ [Require("Gradle.Dependency.Implementation", "com.google.firebase:firebase-messaging:23.2.0")]
extern(Android)
internal class AndroidImpl
{
@@ -71,20 +73,30 @@ namespace Fuse.PushNotifications
// Set up FCM
final Activity activity = com.fuse.Activity.getRootActivity();
activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- // Check device for Play Services APK. If check succeeds, proceed with FCM registration.
- if (@{CheckPlayServices():Call()}) {
-
- String _regID = com.google.firebase.iid.FirebaseInstanceId.getInstance().getToken();
- if (_regID != null) {
- @{getRegistrationIdSuccess(string):Call(_regID)};
+ @Override
+ public void run() {
+ // Check device for Play Services APK. If check succeeds, proceed with FCM registration.
+ if (@{CheckPlayServices():Call()}) {
+ com.google.firebase.messaging.FirebaseMessaging.getInstance().getToken().addOnCompleteListener(new com.google.android.gms.tasks.OnCompleteListener() {
+ @Override
+ public void onComplete(com.google.android.gms.tasks.Task task) {
+ if (!task.isSuccessful()) {
+ @{getRegistrationIdError(string):Call(task.getException().toString())};
+ return;
+ }
+ // Get new FCM registration token
+ String token = task.getResult();
+ @{getRegistrationIdSuccess(string):Call(token)};
}
- } else {
- @{getRegistrationIdError(string):Call("Google Play Services need to be updated")};
- }
+ });
+ } else {
+ @{getRegistrationIdError(string):Call("Google Play Services need to be updated")};
}
- });
+ }
+ });
+ #if (!@(Project.Android.PushNotifications.RegisterOnLaunch:IsSet)) || @(Project.Android.PushNotifications.RegisterOnLaunch:Or(0))
+ @{RegisterForPushNotifications():Call()};
+ #endif
@}
@@ -110,6 +122,38 @@ namespace Fuse.PushNotifications
return true;
@}
+ [Foreign(Language.Java)]
+ internal static void RegisterForPushNotifications()
+ @{
+ // This is only necessary for API level >= 33 (TIRAMISU)
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
+ @{RequestPermission():Call()};
+ }
+ @}
+
+ static void RequestPermission()
+ {
+ Permissions.Request(new PlatformPermission[] { Permissions.Android.POST_NOTIFICATIONS}).Then(OnPermissions, OnRejected);
+ }
+
+ static void OnPermissions(PlatformPermission[] grantedPermissions)
+ {
+ if(grantedPermissions.Length == 1)
+ {
+ // Success
+ return;
+ }
+ else
+ {
+ getRegistrationIdError("Push Notification is not granted");
+ }
+ }
+
+ static void OnRejected(Exception e)
+ {
+ getRegistrationIdError(e.Message);
+ }
+
[Foreign(Language.Java), ForeignFixedName]
static void RegistrationIDUpdated(string regid)
@{
@@ -429,7 +473,17 @@ namespace Fuse.PushNotifications
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setAction(PushNotificationReceiver.ACTION);
intent.replaceExtras(payload);
- android.app.PendingIntent pendingIntent = android.app.PendingIntent.getActivity(context, id, intent, android.app.PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent pendingIntent = null;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S)
+ {
+ pendingIntent = android.app.PendingIntent.getActivity
+ (context, id, intent, android.app.PendingIntent.FLAG_IMMUTABLE | android.app.PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+ else
+ {
+ pendingIntent = android.app.PendingIntent.getActivity
+ (context, id, intent, android.app.PendingIntent.FLAG_UPDATE_CURRENT);
+ }
android.app.NotificationManager notificationManager = (android.app.NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
/// Setup Default notification channel properties to fallback on
diff --git a/Source/Fuse.PushNotifications/Android/PushNotificationReceiver.java b/Source/Fuse.PushNotifications/Android/PushNotificationReceiver.java
index 0d3fbd28c..011634005 100644
--- a/Source/Fuse.PushNotifications/Android/PushNotificationReceiver.java
+++ b/Source/Fuse.PushNotifications/Android/PushNotificationReceiver.java
@@ -52,9 +52,9 @@ public void onMessageReceived(RemoteMessage message)
JSONObject jsonObj = new JSONObject(jsonStr);
- jsonStr = jsonObj.toString();
+ jsonStr = jsonObj.toString();
- bundle = jsonStrToBundle(jsonStr);
+ bundle = jsonStrToBundle(jsonStr);
} catch (JSONException je) {
Log.d("onMessageReceived", "BAD JSON");
@@ -69,87 +69,87 @@ public void onMessageReceived(RemoteMessage message)
}
}
- public static Bundle jsonStrToBundle(String jsonStr) {
- Bundle bundle = new Bundle();
-
- try {
- JSONObject jsonObject = new JSONObject(jsonStr.trim());
- bundle = handleJSONObject(jsonObject);
- } catch (JSONException notObject) {
- try {
- JSONArray jsonArr = new JSONArray(jsonStr.trim());
- bundle = handleJSONArray(jsonArr);
- } catch (JSONException badJSON) {
- Log.d("jsonStrToBundle", "BAD JSON");
- }
- }
-
- return bundle;
- }
-
-
- public static Bundle handleJSONArray(JSONArray jsonArray) {
- Bundle bundle = new Bundle();
-
- for (int i = 0; i < jsonArray.length(); i++) {
-
- try {
- Object jsonArrayValue = jsonArray.get(i);
-
- if (jsonArrayValue instanceof JSONObject) {
- bundle.putBundle("" + i, handleJSONObject((JSONObject) jsonArrayValue));
- } else if (jsonArrayValue instanceof JSONArray) {
- bundle.putBundle("" + i, handleJSONArray((JSONArray) jsonArrayValue));
- } else if (jsonArrayValue instanceof String) {
- bundle.putString("" + i, "" + jsonArrayValue);
- } else if (jsonArrayValue instanceof Boolean) {
- bundle.putBoolean("" + i, (boolean) jsonArrayValue);
- } else if (jsonArrayValue instanceof Integer) {
- bundle.putInt("" + i, (int) jsonArrayValue);
- } else if (jsonArrayValue instanceof Double) {
- bundle.putDouble("" + i, (double) jsonArrayValue);
- } else if (jsonArrayValue instanceof Long) {
- bundle.putLong("" + i, (long) jsonArrayValue);
- }
- } catch (JSONException je) {
- Log.d("handleJSONArray", "BAD JSON VALUE IN JSON ARRAY, AT POSITION: " + i);
- }
- }
-
- return bundle;
- }
-
- public static Bundle handleJSONObject(JSONObject jsonObject) {
- Bundle bundle = new Bundle();
-
- Iterator keys = jsonObject.keys();
-
- while(keys.hasNext()) {
- String keyStr = keys.next();
-
- try {
- Object keyValue = jsonObject.get(keyStr);
-
- if (keyValue instanceof JSONObject) {
- bundle.putBundle(keyStr, handleJSONObject((JSONObject) keyValue));
- } else if (keyValue instanceof JSONArray) {
- bundle.putBundle(keyStr, handleJSONArray((JSONArray) keyValue));
- } else if (keyValue instanceof String) {
- bundle.putString(keyStr, "" + keyValue);
- } else if (keyValue instanceof Boolean) {
- bundle.putBoolean(keyStr, (boolean) keyValue);
- } else if (keyValue instanceof Integer) {
- bundle.putInt(keyStr, (int) keyValue);
- } else if (keyValue instanceof Double) {
- bundle.putDouble(keyStr, (double) keyValue);
- } else if (keyValue instanceof Long) {
- bundle.putLong(keyStr, (long) keyValue);
- }
- } catch (JSONException je) {
- Log.d("handleJSONObject", "BAD JSON VALUE IN JSON OBJECT, AT KEY: " + keyStr);
- }
- }
-
- return bundle;
- }
+ public static Bundle jsonStrToBundle(String jsonStr) {
+ Bundle bundle = new Bundle();
+
+ try {
+ JSONObject jsonObject = new JSONObject(jsonStr.trim());
+ bundle = handleJSONObject(jsonObject);
+ } catch (JSONException notObject) {
+ try {
+ JSONArray jsonArr = new JSONArray(jsonStr.trim());
+ bundle = handleJSONArray(jsonArr);
+ } catch (JSONException badJSON) {
+ Log.d("jsonStrToBundle", "BAD JSON");
+ }
+ }
+
+ return bundle;
+ }
+
+
+ public static Bundle handleJSONArray(JSONArray jsonArray) {
+ Bundle bundle = new Bundle();
+
+ for (int i = 0; i < jsonArray.length(); i++) {
+
+ try {
+ Object jsonArrayValue = jsonArray.get(i);
+
+ if (jsonArrayValue instanceof JSONObject) {
+ bundle.putBundle("" + i, handleJSONObject((JSONObject) jsonArrayValue));
+ } else if (jsonArrayValue instanceof JSONArray) {
+ bundle.putBundle("" + i, handleJSONArray((JSONArray) jsonArrayValue));
+ } else if (jsonArrayValue instanceof String) {
+ bundle.putString("" + i, "" + jsonArrayValue);
+ } else if (jsonArrayValue instanceof Boolean) {
+ bundle.putBoolean("" + i, (boolean) jsonArrayValue);
+ } else if (jsonArrayValue instanceof Integer) {
+ bundle.putInt("" + i, (int) jsonArrayValue);
+ } else if (jsonArrayValue instanceof Double) {
+ bundle.putDouble("" + i, (double) jsonArrayValue);
+ } else if (jsonArrayValue instanceof Long) {
+ bundle.putLong("" + i, (long) jsonArrayValue);
+ }
+ } catch (JSONException je) {
+ Log.d("handleJSONArray", "BAD JSON VALUE IN JSON ARRAY, AT POSITION: " + i);
+ }
+ }
+
+ return bundle;
+ }
+
+ public static Bundle handleJSONObject(JSONObject jsonObject) {
+ Bundle bundle = new Bundle();
+
+ Iterator keys = jsonObject.keys();
+
+ while(keys.hasNext()) {
+ String keyStr = keys.next();
+
+ try {
+ Object keyValue = jsonObject.get(keyStr);
+
+ if (keyValue instanceof JSONObject) {
+ bundle.putBundle(keyStr, handleJSONObject((JSONObject) keyValue));
+ } else if (keyValue instanceof JSONArray) {
+ bundle.putBundle(keyStr, handleJSONArray((JSONArray) keyValue));
+ } else if (keyValue instanceof String) {
+ bundle.putString(keyStr, "" + keyValue);
+ } else if (keyValue instanceof Boolean) {
+ bundle.putBoolean(keyStr, (boolean) keyValue);
+ } else if (keyValue instanceof Integer) {
+ bundle.putInt(keyStr, (int) keyValue);
+ } else if (keyValue instanceof Double) {
+ bundle.putDouble(keyStr, (double) keyValue);
+ } else if (keyValue instanceof Long) {
+ bundle.putLong(keyStr, (long) keyValue);
+ }
+ } catch (JSONException je) {
+ Log.d("handleJSONObject", "BAD JSON VALUE IN JSON OBJECT, AT KEY: " + keyStr);
+ }
+ }
+
+ return bundle;
+ }
}
diff --git a/Source/Fuse.PushNotifications/Common.uno b/Source/Fuse.PushNotifications/Common.uno
index 0bace2a44..4106825a8 100644
--- a/Source/Fuse.PushNotifications/Common.uno
+++ b/Source/Fuse.PushNotifications/Common.uno
@@ -12,14 +12,13 @@ namespace Fuse.PushNotifications
[ForeignInclude(Language.Java,
"android.util.Log",
- "com.google.firebase.iid.FirebaseInstanceId",
"java.util.ArrayList",
"java.util.List",
"android.graphics.Color"
)]
[Require("Gradle.Dependency.ClassPath", "com.google.gms:google-services:4.3.2")]
[Require("Gradle.AllProjects.Repository", "maven {url 'https://maven.google.com'}")]
- [Require("Gradle.Dependency.Implementation", "com.google.firebase:firebase-analytics:17.2.1")]
+ [Require("Gradle.Dependency.Implementation", "com.google.firebase:firebase-analytics:21.3.0")]
[Require("Gradle.BuildFile.End", "apply plugin: 'com.google.gms.google-services'")]
public static class PushNotify
{
@@ -165,7 +164,12 @@ namespace Fuse.PushNotifications
iOSImpl.RegisterForPushNotifications();
}
- public extern(!iOS) static void Register() { }
+ public extern(Android) static void Register()
+ {
+ AndroidImpl.RegisterForPushNotifications();
+ }
+
+ public extern(!iOS && !Android) static void Register() { }
public extern(iOS) static bool IsRegisteredForRemoteNotifications()
{
diff --git a/Source/Fuse.PushNotifications/Docs/Guide.md b/Source/Fuse.PushNotifications/Docs/Guide.md
index fbf765af5..9105acc14 100644
--- a/Source/Fuse.PushNotifications/Docs/Guide.md
+++ b/Source/Fuse.PushNotifications/Docs/Guide.md
@@ -61,6 +61,11 @@ If you wish to disable auto-registration you can place the following in your uno
"RegisterOnLaunch": false
}
},
+ "Android": {
+ "PushNotifications": {
+ "RegisterOnLaunch": false
+ }
+ },
```
You must then register for push notifications by calling `register()` from JS. This option is useful as when the notifications are registered the OS may ask the user for permission to use push notifications and this may be undesirable on launch.
diff --git a/Source/Fuse.PushNotifications/Fuse.PushNotifications.unoproj b/Source/Fuse.PushNotifications/Fuse.PushNotifications.unoproj
index 60547e486..0a50387f6 100644
--- a/Source/Fuse.PushNotifications/Fuse.PushNotifications.unoproj
+++ b/Source/Fuse.PushNotifications/Fuse.PushNotifications.unoproj
@@ -12,7 +12,8 @@
"../Fuse.Elements/Fuse.Elements.unoproj",
"../Fuse.Platform/Fuse.Platform.unoproj",
"../Fuse.Reactive/Fuse.Reactive.unoproj",
- "../Fuse.Scripting/Fuse.Scripting.unoproj"
+ "../Fuse.Scripting/Fuse.Scripting.unoproj",
+ "../Fuse.Android.Permissions/Fuse.Android.Permissions.unoproj"
],
"Includes": [
"Docs/**:File",