Java Question Send Map via intent?

barx

Well-Known Member
Licensed User
Longtime User
Hi

For WearableDataLayer library I'm trying to use the method similar to Notification Listener for the static listeners. Basically I have a service (ListenerService) internal to the library. This is bound so it gets the listener triggers. then in the service, dependent on whether it is a message received or a dataMap changed I build an intent to call a pre determined service created by the dev in b4a (WearListenerService).

Now, if the service is dealing with a message received I add the info to the intent to send. This is not an issue as it is all Strings and Ints but for the Data changed I would like to send 2 Maps (ChangedItems, DeletedItems).

Is this possible?
Am I going about this wrong?
Should I even be sending this much data with an intent?

thanks

Current code available if desired but I didn't want to fill the post with a load of code ;)
 

barx

Well-Known Member
Licensed User
Longtime User
p.s. when I try to pass the Map I get error

...the type Intent is not applicable for the arguments (String, Map)
 

barx

Well-Known Member
Licensed User
Longtime User
Here is my current code for the onDataChanged event

B4X:
@Override
    public void onDataChanged(DataEventBuffer events) {
      
        Map changedItems = new Map();
        Map deletedItems = new Map();
        changedItems.Initialize();
        deletedItems.Initialize();
      
        for (DataEvent event : events) {
            if (event.getType() == DataEvent.TYPE_CHANGED) {
                DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
                changedItems.Put(dataMapItem.getUri().getPath(), dataMapItem.getDataMap());
            } else if (event.getType() == DataEvent.TYPE_DELETED) {
                DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
                deletedItems.Put(dataMapItem.getUri().getPath(), dataMapItem.getDataMap());
            }
        }
      
        try {
            Intent intent;
            intent = new Intent(this, Class.forName(getPackageName() + B4A_SERVICE));
            intent.setAction(INTENT_ACTION);
            intent.putExtra("success", events.getStatus().isSuccess());
            intent.putExtra("ChangedItems", changedItems);
            intent.putExtra("DeletedItems", deletedItems);
            startService(intent);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }      
    }
 
Last edited:

barx

Well-Known Member
Licensed User
Longtime User
OK. The notification listener lib seems to store the object in a class variable but I cannot see exactly how it works due to how it is viewed in the decompiler:oops:
 

thedesolatesoul

Expert
Licensed User
Longtime User
Interesting.
Basically he stores it in a map inside the service.
He only puts an integer index out in int the intent.
Once you receive the intent you use the index to retrieve whatever object you need to.

Also note that the methods used to store and retrieve the object are actually static.
 

barx

Well-Known Member
Licensed User
Longtime User
Just been thinking about this myself and how I would access the same var from 2 classes. But then I look again. They are 2 child classes of the parent wrapper. Var belongs to wrapper. Both children can access it. Interesting!
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
All you need to do is ask...

B4X:
package anywheresoftware.b4a.objects;

import java.util.HashMap;

import android.app.Service;
import android.content.Intent;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import anywheresoftware.b4a.AbsObjectWrapper;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;

@Hide
public class NotificationListenerWrapper extends NotificationListenerService{
   private static final HashMap<Integer, StatusBarNotification> sbns = new HashMap<Integer, StatusBarNotification>();
   public static int index;
   public NotificationListener nl;
   @Override
   public void onCreate() {
     super.onCreate();
     BA.Log("Notification internal service created");
     try {
       Class.forName(getPackageName() + ".notificationservice");
     } catch (ClassNotFoundException e) {
       e.printStackTrace();
       BA.LogError("NotificationService not found.");
     }
   }
   private Intent createIntent(String event, StatusBarNotification sbn) throws ClassNotFoundException {
     Intent i;
     i = new Intent(this, Class.forName(getPackageName() + ".notificationservice"));
     i.setAction("b4a_notificationlistener");
     addSbnToIntent(sbn, i);
     i.putExtra("event", event);
     return i;
   }
   public static void addSbnToIntent(StatusBarNotification sbn, Intent i) {
     index++;
     i.putExtra("sbn", index);
     sbns.put(index, sbn);
   }
   public static StatusBarNotification getSbnFromIntent(Intent i) {
     int sbnI = i.getIntExtra("sbn", -1);
     StatusBarNotification sbn = sbns.get(sbnI);
     sbns.remove(sbnI);
     return sbn;
   }
   @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
     super.onStartCommand(intent, flags, startId);
     if (intent.hasExtra("b4a_cancel_all"))
       cancelAllNotifications();
     else if (intent.hasExtra("b4a_cancel")) {
       StatusBarNotification sbn = getSbnFromIntent(intent);
       cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId());
     }
     else if (intent.hasExtra("b4a_getactive")) {
       StatusBarNotification[] all = getActiveNotifications();
       if (all != null) {
         for (StatusBarNotification sbn : all) {
           try {
             startService(createIntent("posted", sbn));
           } catch (ClassNotFoundException e) {
             throw new RuntimeException(e);
           }
         }
       }
     }
     return Service.START_NOT_STICKY;
   }
   @Override
   public void onNotificationPosted(StatusBarNotification arg0) {
     try {
       startService(createIntent("posted", arg0));
     } catch (ClassNotFoundException e) {
       BA.printException(e, true);
     }
   }

   @Override
   public void onNotificationRemoved(StatusBarNotification arg0) {
     try {
       startService(createIntent("removed", arg0));
     } catch (ClassNotFoundException e) {
       BA.printException(e, true);
     }
   }
   /**
    * NotificationListener allows you to access the device notifications.
    *This is only supported by Android 4.3+.
    *See the tutorial in the forum for more information.
    */
   @Events(values={"NotificationPosted (SBN As StatusBarNotification)",
       "NotificationRemoved (SBN As StatusBarNotification)"})
   @Version(1.10f)
   @ShortName("NotificationListener")
   public static class NotificationListener {
     @Hide
     public BA ba;
     @Hide
     public String eventName;
     /**
      * Initializes the object and sets the subs that will handle the events.
      */
     public void Initialize(BA ba, String EventName) {
       this.ba = ba;
       this.eventName = EventName.toLowerCase(BA.cul);
     }
     /**
      * Handles the intent with the notifications information.
      *Returns true if the intent was handled.
      */
     public boolean HandleIntent(IntentWrapper StartingIntent) {
       if (StartingIntent.IsInitialized() == false)
         return false;
       if (StartingIntent.getAction().equals("b4a_notificationlistener")) {
         String event = (String) StartingIntent.GetExtra("event");
         StatusBarNotification sbn = NotificationListenerWrapper.getSbnFromIntent(StartingIntent.getObject());
         if (event.equals("posted")) {
           ba.raiseEvent(this, eventName + "_notificationposted", AbsObjectWrapper.ConvertToWrapper(
               new StatusBarNotificationWrapper(), sbn));
         }
         else if (event.equals("removed")) {
           ba.raiseEvent(this, eventName + "_notificationremoved", AbsObjectWrapper.ConvertToWrapper(
               new StatusBarNotificationWrapper(), sbn));
         }
         
         return true;
       }
       return false;
     }
     /**
      * Clears all non-ongoing notifications.
      */
     public void ClearAll() {
       Intent i = new Intent(BA.applicationContext, NotificationListenerWrapper.class);
       i.putExtra("b4a_cancel_all", true);
       BA.applicationContext.startService(i);
     }
     /**
      * Clears the given notification (if it is not an ongoing notification).
      */
     public void ClearNotification(StatusBarNotificationWrapper SBN) {
       Intent i = new Intent(BA.applicationContext, NotificationListenerWrapper.class);
       i.putExtra("b4a_cancel", true);
       NotificationListenerWrapper.addSbnToIntent(SBN.getObject(), i);
       BA.applicationContext.startService(i);
     }
     /**
      * Causes the listener to repost all the active notifications.
      */
     public void GetActiveNotifications() {
       Intent i = new Intent(BA.applicationContext, NotificationListenerWrapper.class);
       i.putExtra("b4a_getactive", true);
       BA.applicationContext.startService(i);
     }
   }
   
   @ShortName("StatusBarNotification")
   public static class StatusBarNotificationWrapper extends AbsObjectWrapper<StatusBarNotification> {
     /**
      * Returns the notification package name.
      */
     public String getPackageName() {
       return getObject().getPackageName();
     }
     /**
      * Returns the notification id.
      */
     public int getId() {
       return getObject().getId();
     }
     /**
      * Returns the notification ticker text field.
      */
     public String getTickerText() {
       CharSequence cs = getObject().getNotification().tickerText;
       return cs == null ? "" : cs.toString();
     }
     /**
      * Returns the internal notification object.
      */
     public NotificationWrapper getNotification() {
       NotificationWrapper nw = new NotificationWrapper();
       nw.setObject(getObject().getNotification());
       return nw;
     }
   }

}
 

thedesolatesoul

Expert
Licensed User
Longtime User
Just been thinking about this myself and how I would access the same var from 2 classes. But then I look again. They are 2 child classes of the parent wrapper. Var belongs to wrapper. Both children can access it. Interesting!
The children are not accessing it. Look at sbns in Erel's code above.
And the two static methods used to access it: addSbnToIntent and getSbnFromIntent.
 

barx

Well-Known Member
Licensed User
Longtime User
Not got time to test tonight but if anyone has a spare few minutes, care to cast and eye over this to see if I have the right idea???

Thanks

B4X:
package BarxDroid.WearableDataLayer;

import android.content.Intent;

import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.Author;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.objects.IntentWrapper;
import anywheresoftware.b4a.objects.collections.List;
import anywheresoftware.b4a.objects.collections.Map;

import com.google.android.gms.wearable.DataEvent;
import com.google.android.gms.wearable.DataEventBuffer;
import com.google.android.gms.wearable.DataMapItem;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.WearableListenerService;

@Author("BarxDroid")
@BA.Hide
public class ListenerService extends WearableListenerService{
   private static final String INTENT_ACTION = "barxdroid_wearlistener";
   private static final String B4A_SERVICE = ".WearListenerService";
   private static BA mBA;
   private static String mEventname;
   private static List collection = new List();
   private static int collectionId = 0;
  
   public void onCreate() {
     super.onCreate();
     BA.Log("Internal ListenerService created");
     collection.Initialize();
     try {
       Class.forName(getPackageName() + B4A_SERVICE);
     }
     catch(ClassNotFoundException e) {
       BA.Log("WearListenerService not found");
     }
   }
  
   /**
    * Wearable Listener is used to access the messages and data changed on the Wear network when implementing Static listeners
    */
   @ShortName("WearableListener")
   @Events(values={"MessageReceived(SourceNodeID As String, RequestID As Int, msgPath As String, Data As String)", "DataChanged(ChangedItems As Map, DeletedItems As Map)"})
   public class WearableListener {
    
     /**
      * initilizes the object and set the EventName for callback events
      */
     public void Initialize(BA ba, String EventName) {
       mBA = ba;
       mEventname = EventName;
     }
    
     /**
      * Used to handle the starting intent when using static listeners.
      * Will call the following events:
      *
      * _MessageReceived - When a message is received
      * _DataChanged - When a DataMap is changed on the Wear Network
      */
     public boolean HandleIntent(IntentWrapper StartingIntent) {
       if (!StartingIntent.IsInitialized()) {
         return false;
       }
      
       if (StartingIntent.getAction().equals("barxdroid_wearlistener")) {
         String event = (String)StartingIntent.GetExtra("event");
         if (event.equals("messageReceived")) {
           String sourceNodeID = (String)StartingIntent.GetExtra("SourceNodeID");
           int requestID = (Integer)StartingIntent.GetExtra("RequestID");
           String msgPath = (String)StartingIntent.GetExtra("msgPath");
           String data = (String)StartingIntent.GetExtra("Data");
          
           if (mBA.subExists(mEventname + "_messagereceived")) {
             mBA.raiseEvent(this, mEventname + "_messagereceived", new Object[] {sourceNodeID, requestID, msgPath, data});
           }
         } else if (event.equals("dataChanged")) {
           int ChangedItemsID = (Integer)StartingIntent.GetExtra("ChangedItems");
           int DeletedItemsID = (Integer)StartingIntent.GetExtra("DeletedItems");
           Map ChangedItems = (Map)collection.Get(ChangedItemsID);
           Map DeletedItems = (Map)collection.Get(DeletedItemsID);
          
           if (mBA.subExists(mEventname + "_datachanged")) {
             mBA.raiseEvent(this, mEventname + "_datachanged", new Object[] {ChangedItems, DeletedItems});
           }
         }
         return true;
       }
       return false;
     }
   }
  
   @Override
  public void onMessageReceived(MessageEvent message) {
     try {
       Intent intent;
       intent = new Intent(this, Class.forName(getPackageName() + B4A_SERVICE));
       intent.setAction(INTENT_ACTION);
       intent.putExtra("event", "messageReceived");
       intent.putExtra("SourceNodeID", message.getSourceNodeId());
       intent.putExtra("RequestID", message.getRequestId());
       intent.putExtra("msgPath", message.getPath());
       intent.putExtra("Data", new String(message.getData()));
       startService(intent);
     } catch (ClassNotFoundException e) {
       e.printStackTrace();
     }
  }
  
   @Override
   public void onDataChanged(DataEventBuffer events) {
    
     Map changedItems = new Map();
     Map deletedItems = new Map();
     changedItems.Initialize();
     deletedItems.Initialize();
    
     for (DataEvent event : events) {
       if (event.getType() == DataEvent.TYPE_CHANGED) {
         DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
         changedItems.Put(dataMapItem.getUri().getPath(), dataMapItem.getDataMap());
       } else if (event.getType() == DataEvent.TYPE_DELETED) {
         DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
         deletedItems.Put(dataMapItem.getUri().getPath(), dataMapItem.getDataMap());
       }
     }
    
     try {
       Intent intent;
       intent = new Intent(this, Class.forName(getPackageName() + B4A_SERVICE));
       intent.setAction(INTENT_ACTION);
       intent.putExtra("event", "dataChanged");
       intent.putExtra("success", events.getStatus().isSuccess());
       collection.Add(changedItems.getObject());
       intent.putExtra("ChangedItems", collectionId);
       collectionId++;
       collection.Add(deletedItems.getObject());
       intent.putExtra("DeletedItems", collectionId);
       collectionId++;
       startService(intent);
     } catch (ClassNotFoundException e) {
       e.printStackTrace();
     }    
   }
}
 

warwound

Expert
Licensed User
Longtime User
A crude solution is to subclass the android Application class.
Create a few getters and setters that'll allow you to pass any instances of any objects from any class to any class.

Remember to avoid memory leaks by holding onto references only until the value has been retrieved via it's getter.
 
Top