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: 606

barx

Well-Known Member
Licensed User
Longtime User
Barx, would it be possible for you to add an example showing how to transfer files? I tried the following lines

Sub Process_Globals
'These global variables will be declared once when the application starts.
'These variables can be accessed from all modules.

Dim DL As WearableDataLayer 'The DataLayer
Dim WA As WearableAsset
Dim DM As WearableDataMap
End Sub

Sub DL_Connected()
DL.GetConnectedNodes 'returns the results in the _NodeResults event
WA.CreateFromFile("/sdcard", "example.txt")
DM.PutAsset("wear", WA)
DL.AddDataMap("/test", DM)
End Sub


and get the following when I compile

DM.PutAsset(\
javac 1.7.0_45
src\livonics\epidetect_wear\epidetectservice.java:114: error: inconvertible types
_dm.PutAsset("wear",(com.google.android.gms.wearable.Asset)(_wa));
^
required: Asset
found: WearableAsset

The Assets work a little strange. You don't actually assign anything to them like you have above. Instead they work like factory and just return the required object. I cannot create anything this morning as my watch is flat (but on charge) so I cannot test. This is the code I was testing with though so it should give you an idea.

B4X:
    File.WriteString(File.DirInternal, "time.txt", DateTime.Date(DateTime.Now) & " - " & DateTime.Time(DateTime.Now))
    If File.Exists(File.DirInternal, "time.txt") Then
        DataMap.PutAsset("TimeFile", Asset.CreateFromFile(File.DirInternal, "time.txt"))
    End If

If this doesn't help I do plan on adding a few more examples soon. Planned examples so far are:

dynamic message listeners,
file transfer
http operations

Hope this helps
 

smishra

Member
Licensed User
Longtime User
I am able to transfer files from my watch to my mobile phone using the library! Thanks Barx!
 

CryoGenID

Active Member
Licensed User
Longtime User
Hello,

and here I am again (sorry for giving you nightmares ;-) )...
I have just tried out your demo (with the static message) and I have the problem that it is not working here :-(
The app gets installed on the device (Nexus 7 with Android 5.0.2) and on the wearable (Moto360). When I manually run it on the Moto, I can see the toastMessae "Example started".
On the device, the send button is not greyed out (and I can see on the debug pane that a device was found) but when I send a message, I don't see it on the Moto...
Btw: the Nexus has currently no WiFi access... But it wouldn't need any Wifi/Internet access for the demo to work right? The messages are exchanged locally via BT right?

Here is the debug output:
B4X:
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
Google Play Services Client Connected
DataListener and NodeListener registered
NodeID - ecca0048-8593-4efc-88c6-ff0227677XXX
NodeDisplayName - ecca0048-8593-4efc-88c6-ff0227677XXX
Message Sent: true

Did I do anything wrong?

Thanks a lot and best regards,

Chris
 

barx

Well-Known Member
Licensed User
Longtime User
Hmmmmmm, I will install a fresh copy tomorrow to test it again. Maybe it was my mistake. I will update once tested...
 

barx

Well-Known Member
Licensed User
Longtime User
That would be perfect thank you :)

Best regards,

Christian

Well, it seems to work here :confused:

Can anybody else confirm or deny if it works if they have tried it?

Chris, do you have your watch setup to debug? if so, plugin and see what the log says. I get this when I type a message and press send:

Internal ListenerService created

** Service (wearlistenerservice) Start **

Message Received
 

CryoGenID

Active Member
Licensed User
Longtime User
Hello Barx,

thanks a lot for your reply.

I have now tried to get the watch into debug mode... It now shows "Debugging" on its homescreen, the App on the tablet also says that
the BT-Debugging is activated (on host and device).
To get that BT-Debugging working I used these two commands:
B4X:
adb forward tcp:4444 localabstract:/adb-hub
adb connect localhost:4444
But I don't get any response inside B4A... I have added a "Log"-statement into the wearable part of the application, that should be displayed inside
B4A right?
My config now is MOTO360 -> BT to Nexus 7 -> Nexus7 via USB to Windows (VM on a Mac)

The debug messages from the Nexus 7 are displayed fine inside B4A, but nothing from the watch :-(

Do you have any idea what I possibly did wrong?

Thanks a lot and best regards,

Chris
 

barx

Well-Known Member
Licensed User
Longtime User

barx

Well-Known Member
Licensed User
Longtime User
Another simple example added to Post #2. Sorry they are slow in coming....
 

Ph1lJ

Member
Licensed User
Longtime User
Hi barx
I'm trying to get the 1st Example running, after compiling both, and starting the app on the watch ok (A samsung gear live) I then start the tablet app (Android 4.4.2) and I get the following error reported (see attached image) - the watch does normally interface well with this tablet.
I'm using the WearableDataLayer (v1.0)

Here is what the manifest on the tablet is actually set to


B4X:
'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="14" android:targetSdkVersion="20"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.

'AddPermission(android.permission.READ_EXTERNAL_STORAGE)

SetApplicationAttribute(android:theme, "@android:style/Theme.DeviceDefault.Light")
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"/>
)


Hope someone can help
 

Attachments

  • IMG_0502.JPG
    IMG_0502.JPG
    109.2 KB · Views: 253

Ph1lJ

Member
Licensed User
Longtime User
So is know one else having the same problem that I mentioned in #31 ?

Please help
 

barx

Well-Known Member
Licensed User
Longtime User
So is know one else having the same problem that I mentioned in #31 ?

Please help

Sorry Ph1l, hadn't seen this issue, Looks to me like the version of google play service you have in the Android/SDK folder is newer than the one you have in your additional libraries folder. Try copying again from android sdk to additional libs folder. Should sort it out ;)

Either that or manuall set the version id in the manifest.

I prefer the first option ;)
 

Ph1lJ

Member
Licensed User
Longtime User
Sorry Ph1l, hadn't seen this issue, Looks to me like the version of google play service you have in the Android/SDK folder is newer than the one you have in your additional libraries folder. Try copying again from android sdk to additional libs folder. Should sort it out ;)

Either that or manuall set the version id in the manifest.

I prefer the first option ;)

Ok your suggestion, fixed that problem - however the first app does nothing - the watch doesn't seem to be receiving anything from the tablet - when running the tablet app.
When I start the watch app, I get a little msgbox saying "Eample Started" - but that is all, I can see nothing sent.

I should also tell you that the watch automatically updated it's self to Software version 5.0.2.
 

GabrielM

Member
Licensed User
Longtime User
Hi Barx

Got a Sony sw2 to test your lib, but got stuck at first example compilation with the following error:

B4X:
B4A version 4.30
Parsing code.                           0.03
Compiling code.                         0.05
Compiling layouts code.                 0.02
Generating R file.                      0.36
Compiling generated Java code.          Error
B4A line: 39
DL.Initialize(\
javac 1.8.0_25
src\barxdroid\wearable\example\staticmessage\main.java:326: error: cannot access DataApi
mostCurrent._dl.Initialize(processBA,"DL");
               ^
  class file for com.google.android.gms.wearable.DataApi not found

I have copied the Google Play services v23 to Additional Libraries but no joy so far, getting same error.

Could it be the java version 1.8xx I am using ?

The wearable side of code does compile ok though.


*** Thanks *** Found the issue: the google jar with same name was saved in the ..//B4A/Libraries and it seems was picked before the one in Additional Libraries folder. All sorted now, Cheers.
 
Last edited:

Ph1lJ

Member
Licensed User
Longtime User
Can anyone help with issue #34 or #33, I'm hoping to use this in the very near future.

Here's hoping
 

barx

Well-Known Member
Licensed User
Longtime User
Can anyone help with issue #34 or #33, I'm hoping to use this in the very near future.

Here's hoping

Can you post the logs from both devices when you send a message?
 

Ph1lJ

Member
Licensed User
Longtime User
Hi Barx
Here is the LOG response (see attached), this time using a new LG G watch, again it upgraded it's self to 5.0.2 - no response other than the initial 'Start Example' message,just like the Samsung Gear Live. I also installed api 22 - but no joy
 

Attachments

  • LOG of G watch no static message.png
    LOG of G watch no static message.png
    34.6 KB · Views: 201

barx

Well-Known Member
Licensed User
Longtime User
So the watch and Client are connected then, hence the node ID.

I will update my watch and see if I suffer the same...
 

barx

Well-Known Member
Licensed User
Longtime User
What about a log from the watch?
 
Top