B4A Library Wearable DataLayer

What is it?
Not so recently a new branch of the Android platform was released for wearables (Watches and things) know simply as Android Wear. Wearables running Android Wear are made to be an extension for your existing mobile Android device. They bring notifications and app information / interaction out of your pocket and onto your wrist (and maybe other places if other wearable devices are released). I believe notifications will account for about 80% of the usage of these wearable devices. Wearable notification support has been possible for a while with the Notification Builder library, and the possibility to make apps that run on Android Wear has also been possible Tutorial. There was still a little issue; your mobile device and your wearable device couldn't talk to each other.

So, here is the Wearable DataLayer library. It allows you to communicate between the 2 devices. There are a couple of ways this works. There are:
  • Messages - these are 'send and forget' one way messages for small amounts of information (<100 KB)
  • DataMaps (Google call them DataItems, externally, but DataMaps internally o_O) - These are maps of data that are kept in sync on the Wear Network (a virtual connection between the devices). This data can be changed from either side and the data will sync to all other devices. DataMaps can hold Strings, Ints, Booleans, etc.
  • Assets - These are technically part of DataMaps but it is easier to explain them separately and you create them like so. Assets are basically used to transfer larger (anything > 100 KB) blocks of information as a File or Bitmaps. Assets are added to DataMaps and the system takes care of the transfer over bluetooth and caching, avoiding re-transmission.
This library has literally taken me an lifetime to make and I can only apologize for that, I hoped it would have been waaaaaay before now but sometimes life gets in the way of hobbies.

How To Install

First of all, copy the .jar and .xml to your additional libraries folder like any other library.

The use of this library requires the inclusion of Google Play Services. Similar to the Android-Support libraries, you have to download this with sdk manager and copy the library file(s) over to your additional libraries folder. A typical place for this would be

C:\Program Files\Android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\libs

Although this path may be different depending on your installation.

It also requires 'AdditionalRes'. Again the path may vary. This is just for the Version ID of Play Services.
#AdditionalRes: C:\Program Files\Android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res, com.google.android.gms

I will update that post a little in the future and also create / post a few simple examples to help you all out.

Note: I have edited a few bits since my last BIG testing session, hopefully it won't have broken anything. I've run a few tests but thought it was about time I got this online so I will fix any issues as they come up. Also, a lot has changed since the BETA so the examples for that won't work, though the principles are the same so you should be able to get the idea from it.

Documentation

WearableDataLayer
Author:
BarxDroid
Version: 1

  • Methods:
    • AddDynamicListener
      Adds a dynamic listener to receive message events.
      A dynamic listener is create at runtime and will be removed once the process is stopped or once RemoveListener is called.
    • Info
      NOTE: This method doesn't do anything and is to provide information only.
      A Message is used to 'Send and forget' small amounts (<100KB) of data.
      You can receive the messages either with dynamic or static receivers.
      Dynamic - Will only be received while the listener is registered.
      Register with .AddDynamicReceiver.
      You must also unregister once done using .RemoveDynamicReceiver
      static - More complex but means you can receive messages at any time
      Add the following text to the Manifest Editor
      <code>AddApplicationText(
      <service android:name="barxdroid.wearabledatalayer.ListenerService"
      android:label="Wearable Listener">
      <intent-filter>
      <action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
      </intent-filter>
      </service>)
      </code>
      Then add a service called 'WearListenerService to your project and in the service module, use code like this.
      <code>
      Sub Process_Globals
      Dim WL As WearableListener
      End Sub
      Sub Service_Start (StartingIntent As Intent)
      WL.Initialize("WL")
      WL.HandleIntent(StartingIntent)
      End Sub
      Sub WL_MessageReceived(SourceNodeID As String, RequestID As Int, msgPath As String, Data As String)
      ToastMessageShow(Data, False)
      End Sub
      </code>
    • IsInitialized As Boolean
    • RemoveListener
      Removes the dynamic listener so no more messages will be received
    • Send (NodeID As String, Timeout As Long, msgPath As String, Data As String)
      Sends a message to a specified node
      NodeID - The ID of the node to connect to.
      Timeout - The timeout before the message sending will fail in milliseconds.
      Path - Denotes a path identifier to specify a particular endpoint at the receiving node.
      Data - A ByteArray of data to pass. Do not pass >100KB. Pass Null if not required.
  • WearableAsset
    Methods:
    • CreateFromBitmap (bitmap As Bitmap) As Asset
      Creates an Asset to use from a Bitmap
    • CreateFromFile (Dir As String, Filename As String) As Asset
      Creates an Asset to use from a File
    • Info
      This method doesn't do anything, it is here purely for informational purposes.
      An asset is used to send a binary blob of data such as an image.
      You attach an asset to a DataItem.
      The system takes care of conserving bluetooth by caching large assets to avoid re-transmission.
  • WearableDataLayer
    Events:
    • BitmapResult (Tag As String, Result As Bitmap)
    • Connected ( As )
    • ConnectionFailed (ErrorCode As Int, Reason As String)
    • ConnectionSuspended (Reason As String)
    • DataChanged (ChangedItems As Map, DeletedItems As Map)
    • DataMapAdded (Success As Boolean)
    • DataMapDeleted (Path as string As , Success As Boolean)
    • DataMapResults (Success As Boolean, Results As Map)
    • FileResult (Tag As String, Dir As String, Filename As String, Success As Boolean)
    • LocalNodeIDResult (Success As Boolean, NodeID As String, NodeDisplayName As String)
    • MessageReceived (SourceNodeID As String, RequestID As Int, msgPath As String, Data As String)
    • MessageSent (Success As Boolean)
    • NodeResults (Results As List)
    • PeerConnected (ID As Int, DisplayName As String)
    Fields:
    • message As Message
    Methods:
    • AddDataMap (Path As String, dataMap As DataMap)
      Adds a Data Map to the Client to sync across the Wearable Data connection.
      DataMaps are synchronized across all devices
      Path - The path to store the DataMap under. e.g "/User"
      DataMap - The DataMap object to add
      Will call DatamapAdded() once complete returning the path for Identification and the success
      Also triggers DataChanged() event if new information is added
    • ClearCallingIdentity
    • Connect
      Connects the Google Play services client (required for the Data Layer to work)
      The _onConnected event will raise once the connection is successful.
      Do NOT attempt to use the Data Layer until the connection is successful
      Make sure you disconnect the client once done (probably best to do this in Activity_Pause()
    • DeleteDataMap (Path As String)
      Deletes a DataMap
      Path - The path which the DataMap resides.
      Calls DataMapDeleted() Event if present, with the success of the action
    • Disconnect
      Disconnects the Google Play services client
      Should always be called once you have finished with the Data Layer e.g. when the app closes or is paused.
    • GetAllDataMaps
      Returns all the present DataMaps as a Map.
      Each Map Key-Value pair is as follows
      Key - The path that the DataMap resides
      Value - The DataMap object

      The results will be returned in the DataLayer_DataMapResults() event
    • GetBitmapFromAsset (tag As String, asset As Asset)
      Gets a Bitmap from an Asset
      Tag - a Tag used to identify the request in the resulting callback
      Asset - the Asset object to extract the File from
      Result returned in BitmapResult() callback
    • GetConnectedNodes
      Get a list of the connected Nodes (Devices)
      The returned List will contains a Map for each node.
      The map will then contains 3 Key-Value pairs:
      ID - The ID of the node, this is used to reference the node when sending messages etc
      DisplayName - A HumanReadable name for the device (on my Samsung Gear Live this matched the ID so was of no use)
      ToString - A string representation of the full Node object, used mainly for my testing

      Result returned in NodeResults() Event
    • GetDataMap (Path As String, Tag As String)
      Gets an existing DataMap
      Path - the path that the DataMap resides
      Tag - a tag that is passed through to the results to make the result set identifiable
      If there is more than one DataMap with the same Path present on the Wear network. e.g. from different Nodes.
      All the DataMaps with that name will be returned.
      Use GetDataMap2 to specify a Node to narrow down a specific DataMap.
      Results are returned in DatamapResults() Event
    • GetDataMap2 (NodeID As String, Path As String, Tag As String)
      Similar to GetDataMap but allows you to specify a NodeID.
    • GetFileFromAsset (tag As String, asset As Asset, TargetDir As String, TargetFilename As String)
      Gets a File from an Asset
      Tag - A tag used to identify the request in the resulting callback
      Asset - The Asset object to extract the File from.
      TargetDir - The directory where the File will be created
      TargetFilename - The filename that the extracted File will be named
      Result returned in FileResult() callback
    • Initialize (Eventname As String)
      Initializes the object.
      Note: this library requires Android 4.3 (API18) or above
    • LocalNodeID
      Gets the NodeID of the local Device
      Result returned in LocalNodeIDResult() event
    • RestoreCallingIdentity
  • WearableDataMap
    Methods:
    • Clear
      Clears all previously added data items from the DataMap
    • ContainsKey (Key As String) As Boolean
      Checks if the given key is contained in the DataMap.
      Returns True if the key is present
    • Get (Key As String) As Object
      Returns the DataMap entry with the given Key as an Object
    • GetAsset (Key As String) As Asset
      Gets an Asset data item from the DataMap
      Key - The reference as set in in the PutAsset() method
    • GetBoolean (Key As String) As Boolean
      Gets a Boolean data item from the DataMap
      Key - The reference as set in in the PutBoolean() method
    • GetByte (Key As String) As Byte
      Gets a Byte data item from the DataMap
      Key - The reference as set in in the PutByte() method
    • GetByteArray (Key As String) As Byte[]
      Gets a Byte Array data item from the DataMap
      Key - The reference as set in in the PutByteArray() method
    • GetDouble (Key As String) As Double
      Gets a Double data item from the DataMap
      Key - The reference as set in in the PutDouble() method
    • GetFloat (Key As String) As Float
      Gets a Float data item from the DataMap
      Key - The reference as set in in the PutFloat() method
    • GetFloatArray (Key As String) As Float[]
      Gets a Float Array data item from the DataMap
      Key - The reference as set in in the PutFloatArray() method
    • GetInt (Key As String) As Int
      Gets an Int data item from the DataMap
      Key - The reference as set in in the PutInt() method
    • GetLong (Key As String) As Long
      Gets a Long data item from the DataMap
      Key - The reference as set in in the PutLong() method
    • GetLongArray (Key As String) As Long[]
      Gets a Long Array data item from the DataMap
      Key - The reference as set in in the PutLongArray() method
    • GetString (Key As String) As String
      Gets a String data item from the DataMap
      Key - The reference as set in in the PutString() method
    • GetStringArray (Key As String) As String[]
      Gets a StringArray data item from the DataMap
      Key - The reference as set in in the PutStringArray method
    • Initialize
      Initializes the object.
      No Eventname is required. The DataLayer Eventname is used.
    • IsInitialized As Boolean
    • PutAsset (Key As String, Asset As Asset)
      Adds an Asset data item to the DataMap
      Key - a key used to reference the data item
      Asset - the Asset object to pass.
    • PutBoolean (Key As String, Val As Boolean)
      Adds a Boolean data item to the DataMap
      Key - a key used to reference the data item
      Val - the value to set to.
    • PutByte (Key As String, Val As Byte)
      Adds a Byte data item to the DataMap
      Key - a key used to reference the data item
      Val - the value to set to.
    • PutByteArray (Key As String, Val() As Byte)
      Adds a Byte Array data item to the DataMap
      Key - a key used to reference the data item
      Val - the value to set to.
    • PutDouble (Key As String, Val As Double)
      Adds a Double data item to the DataMap
      Key - a key used to reference the data item
      Val - the value to set to.
    • PutFloat (Key As String, Val As Float)
      Adds a Float data item to the DataMap
      Key - a key used to reference the data item
      Val - the value to set to.
    • PutFloatArray (Key As String, Val() As Float)
      Adds a Float Array data item to the DataMap
      Key - a key used to reference the data item
      Val - the value to set to.
    • PutInt (Key As String, Val As Int)
      Adds an Int data item to the DataMap.
      Key - a key use to reference the data item
      Val - the value to set to.
    • PutLong (Key As String, Val As Long)
      Adds a Long data item to the DataMap
      Key - a key used to reference the data item
      Val - the value to set to.
    • PutLongArray (Key As String, Val() As Long)
      Adds a Long Array data item to the DataMap
      Key - a key used to reference the data item
      Val - the value to set to.
    • PutString (Key As String, Val As String)
      Adds a String data item to the DataMap
      Key - a key used to reference the data item
      Val - the value to set to.
    • PutStringArray (Key As String, Val() As String)
      Adds a String Array data item to the DataMap
      Key - a key used to reference the data item
      Val - the value to set to.
    • Remove (Key As String)
      Removes an item from the DataMap with the given Key
    • Size As Int
      Returns the number of Key-Value pairs currently in the DataMap
    • isEmpty As Boolean
      Return True if the DataMap is currently empty
    • toByteArray As Byte[]
      Returns the DataMap as a ByteArray
    • toString As String
      Returns a string representation of the DataMap
  • WearableListener
    Events:
    • DataChanged (ChangedItems As Map, DeletedItems As Map)
    • MessageReceived (SourceNodeID As String, RequestID As Int, msgPath As String, Data As String)
    Methods:
    • HandleIntent (StartingIntent As IntentWrapper) As Boolean
      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
    • Initialize (EventName As String)
      initilizes the object and set the EventName for callback events

To Be Continued.............
 

Attachments

  • WearableDataLayer.zip
    30.8 KB · Views: 616

barx

Well-Known Member
Licensed User
Longtime User
I haven't a device connected.
That will be the issue then, it needs a device cnnected. though the library shouldn't cause the app to crash, it should just report that the connection failed, I will need to look into that.
 

Jerez

Active Member
Licensed User
Longtime User
That will be the issue then, it needs a device cnnected. though the library shouldn't cause the app to crash, it should just report that the connection failed, I will need to look into that.

Now i've connected a device and still crash...maybe the google-play-services.jar version (last version REV 27)?
 

TpS

Member
Licensed User
Longtime User
I downloaded both examples in Post #2 but get the same error on both:
B4X:
LogCat connected to: 03157df3e167393e

--------- beginning of system
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
java.lang.IncompatibleClassChangeError: The method 'void com.google.android.gms.common.api.GoogleApiClient.connect()' was expected to be of type interface but instead was found to be of type virtual (declaration of 'java.lang.reflect.ArtMethod' appears in /system/framework/core-libart.jar)
    at barxdroid.wearabledatalayer.WearableDataLayer.Connect(WearableDataLayer.java:94)
    at barxdroid.wearable.example.dynamicmessage.main._activity_resume(main.java:316)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:159)
    at barxdroid.wearable.example.dynamicmessage.main.afterFirstLayout(main.java:104)
    at barxdroid.wearable.example.dynamicmessage.main.access$100(main.java:16)
    at barxdroid.wearable.example.dynamicmessage.main$WaitForLayout.run(main.java:76)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:145)
    at android.app.ActivityThread.main(ActivityThread.java:6837)
    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:1404)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
May it be a result of using the latest (Rev 29) of the Google Play Services or have I missed anything else?
The regular example in the "Creating and Packaging a Wear App" thread works fine but is quite useless if it´s not possible to connect to the Mobile device.
 

barx

Well-Known Member
Licensed User
Longtime User
I will try to look into the above errors over the coming week.

Please not though that all my stuff is packed up in boxes, I am attempting to move house tomorrow so will have to find everything atthe other end. One top of that I will not have internet connection until I can get it installed........

Going to be a fun Holidays Period.
 

TpS

Member
Licensed User
Longtime User
That would be great! A Wear application with a static info isn´t a very good one :)

Take your time and good luck with the moving, Happy Holidays!
 

StevieDk

Member
Licensed User
Longtime User
HI group.
I have just started to use barx library, that i feel have to be excellent for my purposes, but i am having a , probably my fault, problem.
When I run a very Little code, I am havin this java exception:

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean(Line: 42)

My code is as easy as :

Activity.LoadLayout("main")
btnSend.Enabled = False
DL.Initialize( "DL") ( When INitialize method is executed, is when i have the exception!!!)
End Sub

Where DL is Dim DL As WearableDataLayer at the GLobals sub.

I have updated Google_play_services to the very last versión, and I have confirmed that my API is over v18, well now is V20. I am very lost, can someone give me a clue?

Thank in advanced
 

Mark Thorndyke

Member
Licensed User
Longtime User
Hi,
Having some issues with the #AdditionalRes: C:\android-sdk\extras\google\google_play_services\libproject\google-play-services_lib\res, com.google.android.gms line and running into the below on compiling:

B4A version: 5.20
Parsing code. (0.00s)
Compiling code. (0.02s)
Compiling layouts code. (0.00s)
Generating R file. Error
C:\android-sdk\extras\google\google_play_services\libproject\google-play-services_lib\res\values-v21\appinvite_styles.xml:5: error: Error retrieving parent for item: No resource found that matches the given name '@android:style/Theme.Material.Light.DialogWhenLarge.NoActionBar'.

The appinvite_styles.xml file is good with the Theme.Material.Light.DialogWhenLarge.NoActionBar being used elsewhere in other tests elsewhere, even today.

All other things working fine with Android Wear and able to compile small apps but only issues when trying anything with the wearable datalayer (ditto the beta thread)

Any pointers muchly appreciated.
 

Mark Thorndyke

Member
Licensed User
Longtime User
Sorted as Android SDKs strangely became broken in places. Only became apparent after reinstalling, fixing then seeing the Android Support repository and library being unavailable. Ended up reinstalling from fresh and then getting the android-support-v4.jar back across.
I assume this may of happened during the Win8 to Win10 upgrade as ditto on the laptop.
One to keep an eye on in future

Now to continue with the above as now compiling ok

upload_2016-2-1_21-8-46.png


upload_2016-2-1_21-8-58.png
 

Mark Thorndyke

Member
Licensed User
Longtime User
Back to TpS in #103 - I'm having the same issues.
Have tried API 20 thru 23. I get a mixture of failing on the device or on the wearable depending on android-support-v4.jar used (may be a coincidence)
Both devices are 5.1.1. Sony Z5 and SW3
 

Mark Thorndyke

Member
Licensed User
Longtime User
I've finally got this working between devices. I had to do the following:
Revert to Google Play Services API 21 (5.0.1). (Keep a copy of the current version). Copying the v21 google-play-services.jar from this to your libraries. Making sure the android.jar is also pointing to v21

Hopefully this will help shine some more light for Barx to proceed

There will be a donation coming shortly (as mentioned in a previous thread) now things are clicking together

edit: v21 available here:
 

TpS

Member
Licensed User
Longtime User
I've finally got this working between devices. I had to do the following:
Revert to Google Play Services API 21 (5.0.1). (Keep a copy of the current version). Copying the v21 google-play-services.jar from this to your libraries. Making sure the android.jar is also pointing to v21

Hopefully this will help shine some more light for Barx to proceed

There will be a donation coming shortly (as mentioned in a previous thread) now things are clicking together

edit: v21 available here:
Nice one,
I´ll try this when I get the time. Thanks for the finding!
 

TpS

Member
Licensed User
Longtime User
I've finally got this working between devices. I had to do the following:
Revert to Google Play Services API 21 (5.0.1). (Keep a copy of the current version). Copying the v21 google-play-services.jar from this to your libraries. Making sure the android.jar is also pointing to v21

Hopefully this will help shine some more light for Barx to proceed

There will be a donation coming shortly (as mentioned in a previous thread) now things are clicking together

edit: v21 available here:
Awesome it works!

Got a bit pissed at first when I received the same error as before. :)
However I looked in the "base" libraries folder and had by mistake placed a google-play-services.jar in there and that is probably Superior to the one placed in the Additional Libraries.

Got Example 1 from post #2 running so I´m all happy now, Great thanks!
 

tpakis

Active Member
Licensed User
Longtime User
This is my first android Wear app!: https://play.google.com/store/apps/details?id=aithanasakis.toggle.wearable Thank you @barx for creating this library and for the instructions!

My main problem was on creating the service for the static listener on the mobile device and not on the watch, that will update the datamap. I post the service module for future reference for anyone having the same problem

B4X:
#Region  Service Attributes
    #StartAtBoot: True
#End Region

Sub Process_Globals

    Dim WL1 As WearableListener
    Dim dl As WearableDataLayer
    Dim datamap As WearableDataMap
    
End Sub
Sub Service_Create
    Log("service created")
    dl.Initialize("DL") 'Initialize the DataLayer object
    datamap.Initialize 'Initialize the DataMap object
End Sub

Sub Service_Start (StartingIntent As Intent)
   
    WL1.Initialize("WL")
    WL1.HandleIntent(StartingIntent)
     dl.Connect
End Sub

Sub Service_Destroy
dl.Disconnect
End Sub

Sub WL_MessageReceived(SourceNodeID As String, RequestID As Int, msgPath As String, data As String)
    dosomething(data)
    Log("Message Received")
End Sub

Sub DL_Connected()
  ' Get local node and connected nodes
  dl.LocalNodeID
  dl.GetConnectedNodes

dosomethingelse 
End Sub



Sub dosomethingelse
datamap.PutLong("Time", DateTime.Now)
dl.AddDataMap("/User", datamap)
End Sub
 

gmars

Member
Licensed User
Longtime User
I am having trouble getting the wearableListener to work on the phone side using datamaps. The datalayer method (DL_DataChanged) works fine, but my WL_DataChanged is not getting called. I have tried initializing it in the service's create routine and in it's start routine. My Manfest entry is as follows:

'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: http://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="20" android:targetSdkVersion="21"/>
<supports-screens android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.
AddApplicationText(
<meta-data android:name="com.google.android.gms.version"
android:value="@Integer/google_play_services_version" />
<meta-data android:name="com.google.android.wearable.beta.app"
android:resource="@xml/wearable_app_desc" />
)
'this creates the internal service
AddApplicationText(
<service android:name="barxdroid.wearabledatalayer.ListenerService"
android:label="Wearable Listener">
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>)

Any suggestions?

Thanks,

Gary
 
Last edited:

barx

Well-Known Member
Licensed User
Longtime User
Are the package names for the mobile and wearables apps the same?
 

gmars

Member
Licensed User
Longtime User
My bad. I did not realize that the service's name had to be "WearListenerService". I had given mine a different name.
 

Raywingit

Member
Licensed User
Longtime User
Hello, I'm new to this (January 2016), and am trying to get the CRWearable example of post #80 to run.
At the stage of compiling the first part, it appears to get through converting to java, then crashes:
B4A version: 5.80
Parsing code. (0.01s)
Compiling code. (0.08s)
Compiling layouts code. (0.02s)
Generating R file. (0.49s)
Compiling generated Java code. (2.46s)
Convert byte code - optimized dex. (17.79s)
Packaging files. Error

...........................Is this an acceptable outcome since it is supposed to fail?
In case it's an environment issue, I'm going back to a previous project to see if that runs OK.
 
Top