OneSignal / OneSignal-Xamarin-SDK

OneSignal is a free push notification service for mobile apps. This plugin makes it easy to integrate your Xamarin app with OneSignal. https://onesignal.com
Other
104 stars 50 forks source link

NotificationExtenderService no Extender prop at OverrideSettings #231

Open lltwist opened 3 years ago

lltwist commented 3 years ago

Description:

I try to implement Android Notification Extender Service to change payload as it is described at https://documentation.onesignal.com/docs/service-extensions#notification-extender-service and also using this snippet https://github.com/OneSignal/OneSignal-Xamarin-SDK/issues/123#issuecomment-447792361

    [Service(Permission = "android.permission.BIND_JOB_SERVICE", Exported = false)]
    [IntentFilter(actions: new[] {"com.onesignal.NotificationExtender"})]
    public sealed class OneSignalNotificationExtenderService : NotificationExtenderService
    {
        static readonly OverrideSettings _displaySettings = new OverrideSettings() {Extender = new Extender()};

        protected override bool OnNotificationProcessing(OSNotificationReceivedResult p0)
        {
            var notification = Com.OneSignal.NotificationOpenedHandler.OSNotificationToNative(new OSNotification() {Payload = p0.Payload});

            DisplayNotification(_displaySettings);

            // task.Wait();
            return true;
        }

        protected override void OnHandleIntent(Intent intent)
        {
        }

        private sealed class ExtenderEx : Java.Lang.Object, NotificationCompat.IExtender
        {
            public NotificationCompat.Builder Extend(NotificationCompat.Builder builder)
            {
                return builder
                    .SetContentTitle("modified")
                    .SetContentText("modified")
                    // .SetDefaults(~(int) NotificationDefaults.Sound);
            }
        }
    }

.. and having such problems:

Environment

  1. Targeting Android 10.0 API 29
  2. AndroidX.Migration packages are installed

3.10.3 and older crash stacktrace

Java.Lang.NoSuchFieldError: no type "Landroid/support/v4/app/NotificationCompat$Extender;" found and so no field "extender" could be found in class "Lcom/onesignal/NotificationExtenderService$OverrideSettings;" or its superclasses ---> Java.Lang.ClassNotFoundException: Didn't find class "android.support.v4.app.NotificationCompat$Extender" on path: DexPathList[[zip file "/data/app/com.tawasal.campus.mobile-O1CPsJ29_qaKpD3YgqcOmw==/base.apk"],nativeLibraryDirectories=[/data/app/com.tawasal.campus.mobile-O1CPsJ29_qaKpD3YgqcOmw==/lib/arm, /data/app/com.tawasal.campus.mobile-O1CPsJ29_qaKpD3YgqcOmw==/base.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib]] --- End of inner exception stack trace --- at Java.Interop.JniEnvironment+InstanceFields.GetFieldID (Java.Interop.JniObjectReference type, System.String name, System.String signature) [0x0005b] in <8b3b636835d84984ba4604c1f57b1983>:0 at Java.Interop.JniType.GetInstanceField (System.String name, System.String signature) [0x0000c] in <8b3b636835d84984ba4604c1f57b1983>:0 at Java.Interop.JniPeerMembers+JniInstanceFields.GetFieldInfo (System.String encodedMember) [0x00036] in <8b3b636835d84984ba4604c1f57b1983>:0 at Java.Interop.JniPeerMembers+JniInstanceFields.SetValue (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniObjectReference value) [0x00006] in <8b3b636835d84984ba4604c1f57b1983>:0 at Com.OneSignal.Android.NotificationExtenderService+OverrideSettings.set_Extender (AndroidX.Core.App.NotificationCompat+IExtender value) [0x00007] in /Users/josh/Documents/repos/OneSignal-Xamarin-SDK/OneSignal.Android.Binding/obj/Release/generated/src/Com.OneSignal.Android.NotificationExtenderService.cs:54 at CampusMobile.Droid.Services.OneSignalNotificationExtenderService.OnNotificationProcessing (Com.OneSignal.Android.OSNotificationReceivedResult p0) [0x0001f] in /Users/m/dev/repo/campus-mobile-xf/CampusMobile/CampusMobile/CampusMobile.Android/Services/OneSignalNotificationExtenderService.cs:27 at Com.OneSignal.Android.NotificationExtenderService.n_OnNotificationProcessing_Lcom_onesignalOSNotificationReceivedResult (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_p0) [0x00011] in /Users/josh/Documents/repos/OneSignal-Xamarin-SDK/OneSignal.Android.Binding/obj/Release/generated/src/Com.OneSignal.Android.NotificationExtenderService.cs:295 at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.104(intptr,intptr,intptr) --- End of managed Java.Lang.NoSuchFieldError stack trace --- java.lang.NoSuchFieldError: no type "Landroid/support/v4/app/NotificationCompat$Extender;" found and so no field "extender" could be found in class "Lcom/onesignal/NotificationExtenderService$OverrideSettings;" or its superclasses at crc64fee1e6eafa1c956b.OneSignalNotificationExtenderService.n_onNotificationProcessing(Native Method) at crc64fee1e6eafa1c956b.OneSignalNotificationExtenderService.onNotificationProcessing(OneSignalNotificationExtenderService.java:30) at com.onesignal.NotificationExtenderService.processJsonObject(NotificationExtenderService.java:170) at com.onesignal.NotificationExtenderService.processIntent(NotificationExtenderService.java:155) at com.onesignal.NotificationExtenderService.onHandleWork(NotificationExtenderService.java:123) at com.onesignal.JobIntentService$CommandProcessor.doInBackground(JobIntentService.java:439) at com.onesignal.JobIntentService$CommandProcessor.doInBackground(JobIntentService.java:430) at android.os.AsyncTask$2.call(AsyncTask.java:333) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) at java.lang.Thread.run(Thread.java:764) Caused by: java.lang.ClassNotFoundException: Didn't find class "android.support.v4.app.NotificationCompat$Extender" on path: DexPathList[[zip file "/data/app/com.tawasal.campus.mobile-O1CPsJ29_qaKpD3YgqcOmw==/base.apk"],nativeLibraryDirectories=[/data/app/com.tawasal.campus.mobile-O1CPsJ29_qaKpD3YgqcOmw==/lib/arm, /data/app/com.tawasal.campus.mobile-O1CPsJ29_qaKpD3YgqcOmw==/base.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib]] at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:125)

akaitrade commented 2 years ago

Did you find a solution for this problem because i am having exactly the same problem and i can't find anything about this extender service ?

Also the new 4.0 SDK Beta does not have any explanations at all regarding this topic

tanaynigam commented 2 years ago

@lltwist Thanks for reporting the issue. We'll be looking into it shortly to see if this is possible with the current OneSignal 4.x.x SDK. If it is we will create a guide for this, if it requires some SDK changes this will take us a bit more to get a working solution.

Acilec commented 1 year ago

Any news on this?

Im using OneSignalSDK.Xamarin 4.1.3 and would like to receive remote push messages in the background.

tmijieux commented 7 months ago

It looks like NotificationExtender is not available in the code with the new version OneSignalSDK.Xamarin; OneSignalSDK.Xamarin.Core;

while it was in the old deprecated lib Com.OneSignal

This is a rather big issue with my app also because i had a feature where i sent a notification just to wake up the app without displaying any notification band and without playing any sound in the Android UI. Without a customizable service the default behavior of the OneSignal default service is to display the notification.

Is there an alternative? does the native lib paradigm changed so that it cannot be translated anymore in Xamarin ? If not would you be accepting pull request for this ?

@brismithers

It looks like there are a lot of issues that occurs when handling notification after a cold start that could be resolved with the customizable service.

brismithers commented 7 months ago

I believe since v4 of the SDK (and v5 of the .NET SDK, which supports both Xamarin and .NET 6+) rely on the service extension documented here for v4 and here for v5. Unfortunately we do not have a .NET-specific solution, but you should be able to create a native Notification Service Extension and implement your logic in java/kotlin.

The primary reason we do not have a .NET-specific solution is the native Android SDK instantiates the class defined in your AndroidManifest.xml using reflection (if interested). We have not yet been able to come up with an elegant way for the Android native/.NET SDK to instantiate and call your Notification Service Extension within the Xamarin runtime (xamarin creates dynamic package names are dynamic, here's some docs describing how you can define an Activity with a predictable name that can be referenced in the AndroidManifest.xml).

We would absolutely be receptive to ideas from the Xamarin/.NET community on how best to tackle this to make it easier to get control on cold start, as we realize this is a common scenario and "going native" is not ideal.

tmijieux commented 7 months ago

EDIT: TL:DR I heavily edited this because my first comment was messy, and will try to come up with a PR before commenting further.

This was the way it used to work

    <service android:name="com.my.app.NotificationExtender" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="false">
      <intent-filter>
    <action android:name="com.onesignal.NotificationExtender" />
      </intent-filter>
    </service>
using System;
using Android.App;
using Android.Content;
using Com.OneSignal.Android;
using Serilog;

namespace MyApp.Droid
{
    [Service(Name = "com.my.app.NotificationExtender")]
    public class NotificationExtender : NotificationExtenderService
    {
        protected override void OnHandleIntent(Intent intent) { }

        protected override bool OnNotificationProcessing(OSNotificationReceivedResult receivedResult)
        {
            if (receivedResult == null
                || receivedResult.Payload == null
                || receivedResult.Payload.AdditionalData == null)
            {
                return false;
            }
            var payload = receivedResult.Payload;
            bool ret = false;
            try
            {
                var discard = payload.AdditionalData.Get("silent");
                if (discard != null)
                {
                    ret = true;
                }
                payload.AdditionalData.Put("datetimeReceived", (string)DateTime.UtcNow.ToString("o"));
            }
            catch (Exception /*e*/)
            {
            }
            return ret;
        }
    }
}

we can specify a predictable name for the service with the name property of the Service attribute: https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/services/creating-a-service/

this class used to be mapped using the android binding project https://github.com/OneSignal/OneSignal-Android-SDK/blob/feb29f16af5b7ab9c731399d78b95030f66d77ea/OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationExtenderService.java#L116C31-L116C55

(we can see it in the jar that is in the aar) https://github.com/OneSignal/OneSignal-Xamarin-SDK/tree/3.10.6/OneSignal.Android.Binding/Jars

the only thing i believe we need is an abstract base class that implements the "new" INotificationServiceExtension in java world so that we can reference it from android binding project and generate ACW

and basically i believe that with this setup, even if you instanciate with introspection from javaworld the predictable name will exist as a "ACW" class in javaworld , instanciating this class should automatically instanciate the peer object in dotnet world and call dotnet methods when called from java.

tmijieux commented 7 months ago

Hi,@brismithers i submitted this PR https://github.com/OneSignal/OneSignal-Xamarin-SDK/pull/381 regarding what we discussed. Could you check if this would do the trick for you ?