Java Question Help setting up a reference. Not sure if my implementation or the manifest is wrong

DonManfred

Expert
Licensed User
i have build a class, based on firebasenotifications source, like this

B4X:
/*
* Copyright 2010 - 2020 Anywhere Software (www.b4x.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package anywheresoftware.b4a.objects;

import com.amazon.device.messaging.ADMMessageHandlerBase;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.ShortName;

@Hide
public class AmazonNotificationsService extends ADMMessageHandlerBase {
    protected AmazonNotificationsService(String className) {
        super(className);
    }

    private static boolean firstMessage = true;
    private static Handler handler;
    private static Class<?> ServiceClass;
    private static Class<?> ReceiverClass;
   
    public SampleADMMessageReceiver amazonmessagingbr;
   
    @Override
    public void onCreate() {
        super.onCreate();
        try {
            handler = new Handler();
            ServiceClass = Class.forName(getPackageName() + ".amazonmessaging");
            ReceiverClass = Class.forName(getPackageName() + ".amazonmessaging$amazonmessaging_BR");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            BA.LogError("AmazonMessaging not found.");
        }
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.amazon.device.messaging.intent.REGISTRATION");
        filter.addAction("com.amazon.device.messaging.intent.RECEIVE");
        filter.addCategory(getPackageName());
        amazonmessagingbr = new SampleADMMessageReceiver();
        registerReceiver(amazonmessagingbr, filter);
    }
    public static Intent createIntent(Service me, String event, String value) {
        Intent i;
        i = new Intent(me, ReceiverClass);
        i.setAction("b4a_amazonmessaging");
        i.putExtra("event", event);
        return i;
    }

    // @Hide
    // public static class InstanceService extends FirebaseInstanceIdService {
    // @Override
    // public void onTokenRefresh() {
    // sendBroadcast(createIntent(this, "b4a_tokenrefresh"));
    // }
    // }
    // @DependsOn(values={"com.google.firebase:firebase-messaging",
    // "com.google.firebase:firebase-installations"})
    @ShortName("AmazonMessaging")
    // @Events(values={"TokenRefresh (Token As String)", "MessageArrived (Message
    // As RemoteMessage)"})
    public static class AmazonMessageWrapper {
        private BA ba;
        private String eventName;

        public void Initialize(BA ba, String EventName) {
            this.ba = ba;
            this.eventName = EventName.toLowerCase(BA.cul);
        }
        // /**
        // * Should be called from Service_Start. Returns true if the intent was
        // handled.
        // */
        // public boolean HandleIntent(Intent Intent) {
        // if (Intent == null || "b4a_firebasemessaging".equals(Intent.getAction())
        // == false)
        // return false;
        // Intent.setExtrasClassLoader(RemoteMessage.class.getClassLoader());
        // String event = Intent.getStringExtra("event");
        // if (event.equals("b4a_tokenrefresh"))
        // ba.raiseEventFromUI(this, eventName + "_tokenrefresh", getToken());
        // else if (event.equals("b4a_messagereceived")) {
        // RemoteMessage rm = Intent.getParcelableExtra("message");
        // ba.raiseEventFromUI(this, eventName + "_messagearrived",
        // AbsObjectWrapper.ConvertToWrapper(new RemoteMessageWrapper(), rm));
        // }
        // return true;
        // }
        // /**
        // * Returns the device token. The token can change from time to time. The
        // TokenRefresh event is raised when the token changes.
        // *The token is only needed when sending messages to a specific device.
        // */
        // public String getToken() {
        // return BA.returnString(FirebaseInstanceId.getInstance().getToken());
        // }
        // /**
        // * Subscribes to the specified topic.
        // *Example:<code>
        // *fm.SubscribeToTopic("general")</code>
        // */
        // public void SubscribeToTopic(String Topic) {
        // getObject().subscribeToTopic(Topic);
        // }
        // /**
        // * Unsubscribes from a topic.
        // */
        // public void UnsubscribeFromTopic(String Topic) {
        // getObject().unsubscribeFromTopic(Topic);
        // }
        //
        // }
        // /**
        // * Holds the push message data.
        // */
        // @ShortName("RemoteMessage")
        // public static class RemoteMessageWrapper extends
        // AbsObjectWrapper<RemoteMessage> {
        // /**
        // * Gets the collapse key (if set).
        // */
        // public String getCollapseKey() {
        // return BA.returnString(getObject().getCollapseKey());
        // }
        // /**
        // * Returns the sender id or the topic name. In the later case the value
        // will start with /topics/
        // */
        // public String getFrom() {
        // return BA.returnString(getObject().getFrom());
        // }
        // /**
        // * Gets the message id.
        // */
        // public String getMessageId() {
        // return getObject().getMessageId();
        // }
        // /**
        // * Returns the time the message was sent.
        // */
        // public long getSentTime() {
        // return getObject().getSentTime();
        // }
        // /**
        // * Returns a Map with the key / values set as the message data.
        // */
        // public Map GetData() {
        // Map m = new Map();
        // m.Initialize();
        // if (getObject().getData() != null) {
        // for (Entry<String, String> e : getObject().getData().entrySet()) {
        // m.Put(e.getKey(), e.getValue());
        // }
        // }
        // return m;
        // }
        // // public String getNotificationBody() {
        // // Notification n = getObject().getNotification();
        // // if (n == null)
        // // return "";
        // // return BA.returnString(n.getBody());
        // // }
        // // public String getNotificationTitle() {
        // // Notification n = getObject().getNotification();
        // // if (n == null)
        // // return "";
        // // return BA.returnString(n.getTitle());
        // // }
        //
        // }
    }

    @Override
    protected void onMessage(Intent intent) {
        BA.Log("AmazonNotificationsService.OnMessage("+intent+")");
        // TODO Auto-generated method stub
        if (intent == null || "b4a_amazonmessaging".equals(intent.getAction()) == false) {

        } else {
            BA.applicationContext.sendBroadcast(createIntent(this, "b4a_onmessage",intent.getAction()));
        }
    }

    @Override
    protected void onRegistered(String arg0) {
        BA.Log("AmazonNotificationsService.OnRegistered("+arg0+")");
        // TODO Auto-generated method stub
        BA.applicationContext.sendBroadcast(createIntent(this, "b4a_onregistered",arg0));
    }

    @Override
    protected void onRegistrationError(String arg0) {
        // TODO Auto-generated method stub
        BA.Log("AmazonNotificationsService.OnRegistrationError("+arg0+")");
        BA.applicationContext.sendBroadcast(createIntent(this, "b4a_onerror",arg0));

    }

    @Override
    protected void onUnregistered(String arg0) {
        // TODO Auto-generated method stub
        BA.Log("AmazonNotificationsService.OnUnRegistered("+arg0+")");
        BA.applicationContext.sendBroadcast(createIntent(this, "b4a_onunregistered",arg0));

    }
}
i´m not sure about
B4X:
            ServiceClass = Class.forName(getPackageName() + ".amazonmessaging");

            ReceiverClass = Class.forName(getPackageName() + ".amazonmessaging$amazonmessaging_BR");
as i do not see them used somewhere.

but when i try to build the manifest like this

B4X:
AddPermission(android.permission.ACCESS_NETWORK_STATE)
AddPermission(android.permission.INTERNET)
AddPermission(android.permission.WAKE_LOCK)

SetManifestAttribute(xmlns:amazon,"http://schemas.amazon.com/apk/res/android")
AddApplicationText(<amazon:enable-feature android:name="com.amazon.device.messaging" android:required="true"/>)
AddApplicationText(
<service
  android:name="anywheresoftware.b4a.objects.AmazonNotificationsService"
  android:permission="android.permission.BIND_JOB_SERVICE"
  android:exported="false" />
)

AddApplicationText(
<receiver
     android:name="anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessagingbr"
     android:permission="com.amazon.device.messaging.permission.SEND" >
     <intent-filter>
        <action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
        <action android:name="com.amazon.device.messaging.intent.RECEIVE" />
        <category android:name="${applicationId}" />
     </intent-filter>
     <category android:name="${applicationId}" />
</receiver>
)
AddManifestText(
<permission android:name="${applicationId}.permission.RECEIVE_ADM_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.RECEIVE_ADM_MESSAGE" />
<uses-permission android:name="com.amazon.device.messaging.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />  
)
it does not work. I´m not sure here if the implementation or the manifest is wrong

I´m fighing with this error

Logger connected to: Amazon AFTT
--------- beginning of system
Successfully loaded all classes
Successfully loaded all methods
Successfully loaded all fields
Successfully loaded all classes
Successfully loaded all methods
Successfully loaded all fields
Successfully loaded all classes
Successfully loaded all methods
Successfully loaded all fields
--------- beginning of main
Shutting down VM
java.lang.RuntimeException: Unable to instantiate receiver anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessaging_BR: java.lang.ClassNotFoundException: Didn't find class "anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessaging_BR" on path: DexPathList[[zip file "/data/app/de.donmanfred.admtest-1/base.apk", zip file "/system/priv-app/DeviceMessagingAndroidSDK/DeviceMessagingAndroidSDK.apk", zip file "/system/priv-app/MetricsApi/MetricsApi.apk", zip file "/system/priv-app/com.amazon.dp.logger/com.amazon.dp.logger.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2750)
at android.app.ActivityThread.access$1700(ActivityThread.java:160)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1398)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5597)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:984)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
Caused by: java.lang.ClassNotFoundException: Didn't find class "anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessaging_BR" on path: DexPathList[[zip file "/data/app/de.donmanfred.admtest-1/base.apk", zip file "/system/priv-app/DeviceMessagingAndroidSDK/DeviceMessagingAndroidSDK.apk", zip file "/system/priv-app/MetricsApi/MetricsApi.apk", zip file "/system/priv-app/com.amazon.dp.logger/com.amazon.dp.logger.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2745)
... 9 more
Suppressed: java.lang.ClassNotFoundException: anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessaging_BR
at java.lang.Class.classForName(Native Method)
at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
... 11 more
Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available
--------- beginning of crash
FATAL EXCEPTION: main
Process: de.donmanfred.admtest, PID: 22697
java.lang.RuntimeException: Unable to instantiate receiver anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessaging_BR: java.lang.ClassNotFoundException: Didn't find class "anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessaging_BR" on path: DexPathList[[zip file "/data/app/de.donmanfred.admtest-1/base.apk", zip file "/system/priv-app/DeviceMessagingAndroidSDK/DeviceMessagingAndroidSDK.apk", zip file "/system/priv-app/MetricsApi/MetricsApi.apk", zip file "/system/priv-app/com.amazon.dp.logger/com.amazon.dp.logger.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2750)
at android.app.ActivityThread.access$1700(ActivityThread.java:160)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1398)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5597)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:984)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
Caused by: java.lang.ClassNotFoundException: Didn't find class "anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessaging_BR" on path: DexPathList[[zip file "/data/app/de.donmanfred.admtest-1/base.apk", zip file "/system/priv-app/DeviceMessagingAndroidSDK/DeviceMessagingAndroidSDK.apk", zip file "/system/priv-app/MetricsApi/MetricsApi.apk", zip file "/system/priv-app/com.amazon.dp.logger/com.amazon.dp.logger.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2745)
... 9 more
Any hints on how to setup the manifest correctly are highly welcome ;-)
 
Last edited:

OliverA

Expert
Licensed User
anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessagingbr
Maybe as easy as missing an underscore?
B4X:
anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessaging_br
 

OliverA

Expert
Licensed User
Class.forName(getPackageName() + ".amazonmessaging")
getPackageName returns the Apps package name. I doubt that the package name is
anywheresoftware.b4a.objects.AmazonNotificationsService
therefore
android:name="anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessagingbr"
should be more like
android:name="yourpackagename.whateveritis.amazonmessaging$amazonmessaging_br"
 

OliverA

Expert
Licensed User
Why not try a
B4X:
BA.Log(getPackageName());
and see what it returns?
B4X:
try {
            handler = new Handler();
            ServiceClass = Class.forName(getPackageName() + ".amazonmessaging");
            ReceiverClass = Class.forName(getPackageName() + ".amazonmessaging$amazonmessaging_BR");
            BA.Log("****************************" + getPackageName() + "*******************************");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            BA.LogError("AmazonMessaging not found.");
        }
 

OliverA

Expert
Licensed User
BTW, looking at the AndroidManifest.xml file of a test project that uses FCM, the entries for the service(s) are
B4X:
</activity>
        <service android:name=".firebasemessaging">
        </service>
        <receiver android:name=".firebasemessaging$firebasemessaging_BR">
        </receiver>
so it looks like you don't even have to prepend the package name but just use
B4X:
android:name=".amazonmessaging$amazonmessaging_BR"
Edit: Capitalized BR (I think the stuff is case sensitive in the manifest).
 

OliverA

Expert
Licensed User
Ok, I looked at the source of FCM (https://github.com/AnywhereSoftware...b4a/objects/FirebaseNotificationsService.java)(yeah for the github posting of the frameworks!) and @Erel's mentioning of "Add a Service named FirebaseMessaging to your app (must be this name): " (I added the bold) and I'm starting to realize the following:

In order to use what you have, you have to create a Service in B4A with the name AmazonMessaging (capitalization does not matter, B4A lower cases everything). Looks like B4A creates (for each Service module you create) a inner Java class named yourservicename_BR that extends android.content.BroadcastReceiver. So in your case, the source that would be generated for your AmazonMessaging B4A service would include an inner class named amazonmessaging_BR. Why do you have to create a Service with the exact name of AmazonMessaging (again, is case insensitive)? Because of the following lines of code

B4X:
ServiceClass = Class.forName(getPackageName() + ".amazonmessaging");
ReceiverClass = Class.forName(getPackageName() + ".amazonmessaging$amazonmessaging_BR");
This code points directly to your Service that you created and the inner class that will be created for you by B4A. So, then in order to address these two items correctly in the manifest, you need to use your package name that you set up for your B4A project (or, I guess, just a period).

So either
B4X:
android:name= "yourpackagename.whateveritis.amazonmessaging$amazonmessaging_BR"
(the BR needs to be capitalized! We're interfacing with Java land now, so case sensitivity matters!) or
B4X:
android:name= ".amazonmessaging$amazonmessaging_BR"
should work. Now what else you have to do to actually make ADM work, I have no clue.

Edit: Fixed link
 

DonManfred

Expert
Licensed User
I´ll try your suggestion. Even if i think i already tried this variants. I´ll report the results including some codesnippets (or manifest) to prove what i have done.

Class.forName(getPackageName() + ".amazonmessaging")
I can also upload a generated manifest later. Here, if i remember correctly, we can see that the getPackagename() is replaced by my apps packagename (de.donmanfred.admtest in my example) in the manifest.
 

DonManfred

Expert
Licensed User
For now i´ve added a broadcastreceiver to the b4a project and use this as a reference in manifest.
B4X:
AddApplicationText(
<receiver
     android:name="${applicationId}.amazonmessaging$amazonmessaging_BR"
     android:permission="com.amazon.device.messaging.permission.SEND" >
     <intent-filter>
        <action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
        <action android:name="com.amazon.device.messaging.intent.RECEIVE" />
        <category android:name="${applicationId}" />
     </intent-filter>
     <category android:name="${applicationId}" />
</receiver>
)
At least it does not crash anymore. Need to investigate more now. Means; sending a Notification to this appinstance using the RegistrationId i got...

But at least i am a step further now
 

OliverA

Expert
Licensed User
BTW, I just got done writing this BEFORE the above post, but the the above post appeared first. The points raised here are still valid though:

Actually, this is a little bit more convoluted. I looked at Amazon's site for the integration of ADM into an app (https://developer.amazon.com/docs/adm/integrate-your-app.html) and it looks like you are on the right track, but if you're going to follow FCM's sample, I was right too, just in the wrong place. Ok. What is SampleADMMessageReceiver? I think you're close, but I don't think you need to initialize it here. I would need to see the code for that though. You need to model AmazonMessageWrapper totally after the FirebaseMessageWrapper. It needs the HandleIntent method, since that is how the B4A events are called. Just like FirebaseNotificationsService's onMessageReceived method, your AmazonNotificationsService onMessage method should process the incoming message, fill an intent and fire the intent in the same manner as it is done in FirebaseNotificationsService. That intent will then raise B4A's auto-generated _BR class's onReceive event, which will raise the B4A's Service_Start. In Service_Start, you would then call HandleIntent with Service_Start's intent as parameter. That in turn would call your AmazonMessageWrapper's HandleIntent method that, depending on the content of the intent, raise the various event subs in your B4A code. What a ride!

So, this
B4X:
AddApplicationText(
<service
  android:name="anywheresoftware.b4a.objects.AmazonNotificationsService"
  android:permission="android.permission.BIND_JOB_SERVICE"
  android:exported="false" />
)

AddApplicationText(
<receiver
     android:name="anywheresoftware.b4a.objects.AmazonNotificationsService.amazonmessaging$amazonmessagingbr"
     android:permission="com.amazon.device.messaging.permission.SEND" >
     <intent-filter>
        <action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
        <action android:name="com.amazon.device.messaging.intent.RECEIVE" />
        <category android:name="${applicationId}" />
     </intent-filter>
     <category android:name="${applicationId}" />
</receiver>
)
is actually setting up the Java side of the service and receiver end required by ADM. As to the way you are setting up your receiver, maybe you should not embed the receiver in the notification service, but leave it separate. So if SampleADMMessageReceiver code is in SampleADMMessageReceiver.java, located in the same place as AmazonNotificationsService.java and is modeled sort of on this code
B4X:
public class Receiver extends ADMMessageReceiver
{ 
     public Receiver()
     {   // This is needed for backward compatibility
        super(MyADMLegacyMessageHandler.class);
        // Where available, prefer using the new job based
        if (ADMLatestAvailable) {
            registerJobServiceClass(MyADMMessageHandler.class, <JOB_ID>)
        }
     }
     // Nothing else is required here; your broadcast receiver automatically
     // forwards intents to your service for processing.
}
Then you could make the receiver android:name to
B4X:
<receiver
     android:name="anywheresoftware.b4a.objects.SampleADMMessageReceiver"
You would still have to create the two entries, one for the service and one for the receiver on B4A's side and that is where
B4X:
        <service android:name=".amazonmessaging">
        </service>
        <receiver android:name=".amazonmessaging$amazonmessaging_BR">
        </receiver>
would come into play. I hope this makes sense.

Edit: fixing some code tags
 

DonManfred

Expert
Licensed User
this is the source as of now.

Note that the sources, except the AmazonNotificationsService, are basically from the example app amazon provided.

The manifest lines for this are

B4X:
AddPermission(android.permission.ACCESS_NETWORK_STATE)
AddPermission(android.permission.INTERNET)
AddPermission(android.permission.WAKE_LOCK)

SetManifestAttribute(xmlns:amazon,"http://schemas.amazon.com/apk/res/android")
AddApplicationText(<amazon:enable-feature android:name="com.amazon.device.messaging" android:required="true"/>)
AddApplicationText(
<service
  android:name="anywheresoftware.b4a.objects.AmazonNotificationsService"
  android:permission="android.permission.BIND_JOB_SERVICE"
  android:exported="false" />
)

AddApplicationText(
<receiver
     android:name="${applicationId}.amazonmessaging$amazonmessaging_BR"
     android:permission="com.amazon.device.messaging.permission.SEND" >
     <intent-filter>
        <action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
        <action android:name="com.amazon.device.messaging.intent.RECEIVE" />
        <category android:name="${applicationId}" />
     </intent-filter>
     <category android:name="${applicationId}" />
</receiver>
)
AddManifestText(
<permission android:name="${applicationId}.permission.RECEIVE_ADM_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.RECEIVE_ADM_MESSAGE" />
<uses-permission android:name="com.amazon.device.messaging.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />   
)
And the resulting manifest is as follow

XML:
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="de.donmanfred.admtest"
    android:versionCode="1"
    android:versionName=""
    android:installLocation="internalOnly"
    xmlns:amazon="http://schemas.amazon.com/apk/res/android">
    
    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="28"/>
    <supports-screens android:largeScreens="true"
        android:normalScreens="true"
        android:smallScreens="true"
        android:anyDensity="true"/>
    <permission android:name="de.donmanfred.admtest.permission.RECEIVE_ADM_MESSAGE" android:protectionLevel="signature" />
    <uses-permission android:name="de.donmanfred.admtest.permission.RECEIVE_ADM_MESSAGE" />
    <uses-permission android:name="com.amazon.device.messaging.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_UPDATES"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <application
        android:icon="@drawable/icon"
        android:label="B4A Example"
        android:theme="@style/DarkTheme">
        
        <activity android:name="com.google.android.gms.common.api.GoogleApiActivity"
          android:theme="@android:style/Theme.Translucent.NoTitleBar"
          android:exported="false"/>
            <meta-data
          android:name="com.google.android.gms.version"
          android:value="@integer/google_play_services_version" />
        <amazon:enable-feature android:name="com.amazon.device.messaging" android:required="true"/>
        <service
          android:name="anywheresoftware.b4a.objects.AmazonNotificationsService"
          android:permission="android.permission.BIND_JOB_SERVICE"
          android:exported="false" />
        <receiver
             android:name="de.donmanfred.admtest.amazonmessaging$amazonmessaging_BR"
             android:permission="com.amazon.device.messaging.permission.SEND" >
             <intent-filter>
                <action android:name="com.amazon.device.messaging.intent.REGISTRATION" />
                <action android:name="com.amazon.device.messaging.intent.RECEIVE" />
                <category android:name="de.donmanfred.admtest" />
             </intent-filter>
             <category android:name="de.donmanfred.admtest" />
        </receiver>
        <activity
            android:windowSoftInputMode="stateHidden"
            android:launchMode="singleTop"
            android:name=".main"
            android:label="B4A Example"
            android:screenOrientation="unspecified">
            <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            
        </activity>
        <service android:name=".starter">
        </service>
        <receiver android:name=".starter$starter_BR">
        </receiver>
        <service android:name=".amazonmessaging">
        </service>
        <receiver android:name=".amazonmessaging$amazonmessaging_BR">
        </receiver>
        <service android:name=".httputils2service">
        </service>
        <receiver android:name=".httputils2service$httputils2service_BR">
        </receiver>
    </application>
</manifest>
You can see example project and compiled library here:
 

Attachments

OliverA

Expert
Licensed User
No clue how far you folks are along with this. I took a stab at this (even though I'm pretty clueless). I used the source for B4A's FirebaseNotification/Messaging class, the info provided by Amazon, and someone else's implementation of a ADM handler (inclusive inner class for the receiver) and cooked something up. I do not know if it works (it compiles without errors, but I don't have ADM on the phone to test anything). I'm attaching the source of your B4A app (as changed by me) and of your library (with changes). BTW, @DonManfred was a lot closer to the whole receiver thing in the Manifest file than I was. I've included an updated ADM.b4x_excluded file in the library source. Note that the source tree for the library is set up for SLC (since that is what I have and I have once or twice used successfully for some smaller stuff).

Resources used:
 

Attachments

DonManfred

Expert
Licensed User
i´ll check your source tomorrow. To late for today ;-)
Note that the source tree for the library is set up for SLC
should be no problem for me then. Even if i use Eclipse as Editor i am using SLC to compile everything i wrap. :cool:

Edit: Looking at the source (could not resist 😂)

Ahh, i see what you have done here. Cool. I guess this is a good way for getting success ;-)

I see that you are still using the broadcast receiver lib in your example. I found out that it is not needed as we are setting the receiver up in Manifest.
In my testproject here i recently remove the hole Broadcastreceiver in the service. And i am still able to get informed when the service start.
With your addition i guess it will be really smooth now... Hardly can´t wait for tomorrow to test it ;-)
 
Last edited:

OliverA

Expert
Licensed User
Ahh, i see what you have done here. Cool. I guess this is a good way for getting success ;-)
a) Thanks!
b) Truthfully, I cobbled some code together between the three links I mentioned above and the source you provided.

I see that you are still using the broadcast receiver lib in your example
The whole point of my rewrite in the App itself was to get rid if it, since I had no clue what it did and with the rewrite of the library I did not think it was necessary. I guess I somehow failed to remove everything pertaining to it.

Since we're at it, some points/questions about the code (AmazonNotificationsService.java) provided:

1) In the ExtractMessage method of the AmazonMessageWrapper class (in AmazonNotificationsService.java), I'm hard coding the msgKey value. This should be extracted from the strings.xml (?) file, but I had no clue how to do that.
2) Even though AmazonMessageWrapper has Wrapper in its name, it is not a Wrapper in the typical B4A library sense. I did not know what this class should implement/extend in order to make it a "proper" B4A Java class that has getObject, setObject, and whatever a "proper" B4A Java class would have.
3) I'm guessing that the Firebase Messaging this is based on is using intents to wake a B4A service via its background receiver. If so, I learned a new trick.
4) onMessage uses Runnable to create a new Thread to sent an intent to the B4A service. But since a B4A service runs overall in the same thread as Main, does this provide some sort of queuing of the intents? B4A still processes one intent after another, in the same thread as Main?
 

DonManfred

Expert
Licensed User
1) In the ExtractMessage method of the AmazonMessageWrapper class (in AmazonNotificationsService.java), I'm hard coding the msgKey value. This should be extracted from the strings.xml (?) file, but I had no clue how to do that.
B4X:
CreateResource(values, strings.xml,
<resources>
    <string name="app_name">ADMMessenger</string>
    <string name="menu_settings">Settings</string>
    <string name="title_activity_main">MainActivity</string>
    <string name="json_data_msg_key">message</string>
    <string name="json_data_time_key">timeStamp</string>
    <string name="json_data_consolidation_key">collapse_key</string>
    <string name="intent_msg_action">com.amazon.sample.admmessenger.ON_MESSAGE</string>
    <string name="intent_msg_category">com.amazon.sample.admmessenger.MSG_CATEGORY</string>
    <string name="server_address">http://webhook.basic4android.de/</string>
    <string name="server_port">80</string>
</resources>

)
this can be read like this
B4X:
    public String FetchManifestdata(final Context context,String field) {
        ApplicationInfo ai;
        try {
            ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
            Object value = (Object)ai.metaData.get(field);
            return (String)value;
        } catch (NameNotFoundException e) {
            // TODO Auto-generated catch block
            //e.printStackTrace();
            return "";
        }
    }
See SampleADMMessageHandler
B4X:
    /** {@inheritDoc} */
    @Override
    protected void onMessage(final Intent intent) {
        Log.i(TAG, "SampleADMMessageHandler:onMessage");
        BA.Log("SampleADMMessageHandler:onMessage");

        /* String to access message field from data JSON. */
        final String msgKey = FetchManifestdata(BA.applicationContext,"json_data_msg_key");

        /* String to access timeStamp field from data JSON. */
        final String timeKey = FetchManifestdata(BA.applicationContext,"json_data_time_key");

        /* Intent action that will be triggered in onMessage() callback. */
        final String intentAction = FetchManifestdata(BA.applicationContext,"intent_msg_action");

        /* Extras that were included in the intent. */
        final Bundle extras = intent.getExtras();
 

OliverA

Expert
Licensed User
Curiosity: what is the error?
 

DonManfred

Expert
Licensed User
Curiosity: what is the error?
I´m not at my pc at home (at work as of now). Can´t remember. I saw this error with my wrap too. Something in the setup seems to be not correct.

In my latest wrap version it was working and i also could catch the message in the startingintent of the service.
 
Top