B4A Library WatchFace Library

With this library you can create WatchFaces for Android Wear devices with B4A.

digitalwatchface_preview.pnganalogwatchface_preview.png

The example watchface should work well on round and square displays with or without "flat tire".

To use this library you should be familiar with creating wear apps with B4A. It will also help if you read the complete android developer documentation for watch faces.

Installation:
Copy all files from the WatchFaceLibX_X.zip file to your additional libraries folder (.xml, .jar, .aar)
You need to install the Support Repository and Google Repository from SDK Manager.

The dependencies are removed from the library in the latest version, since they don't work anymore.

Add the following dependencies to your watchface source code:

B4X:
#AdditionalJar: androidx.wear:wear
#AdditionalJar: wearable-2.5.0.aar
#AdditionalJar: com.google.android.wearable:wearable, ReferenceOnly
#AdditionalJar: com.android.support:support-v4, ReferenceOnly
#AdditionalJar: com.android.support:percent, ReferenceOnly
#AdditionalJar: com.android.support:support-annotations, ReferenceOnly
#AdditionalJar: com.android.support:recyclerview-v7, ReferenceOnly

Documentation:
WatchFace
Author:
Markus Stipp
Version: 1
  • WFEngine
    Fields:
    • AMBIENT_PEEK_MODE_HIDDEN As Int
    • AMBIENT_PEEK_MODE_VISIBLE As Int
    • BACKGROUND_VISIBILITY_INTERRUPTIVE As Int
    • BACKGROUND_VISIBILITY_PERSISTENT As Int
    • PEEK_MODE_NONE As Int
    • PEEK_MODE_SHORT As Int
    • PEEK_MODE_VARIABLE As Int
    • PEEK_OPACITY_MODE_OPAQUE As Int
    • PEEK_OPACITY_MODE_TRANSLUCENT As Int
    • PROTECT_HOTWORD_INDICATOR As Int
    • PROTECT_STATUS_BAR As Int
    • PROTECT_WHOLE_SCREEN As Int
    • TAPTYPE_TAP As Int
    • TAPTYPE_TOUCH As Int
    • TAPTYPE_TOUCH_CANCEL As Int
  • Methods:
    • ChinSize As Int
      Returns the chin size (flat tire size) of a round watch like the Moto360.
    • SetWatchFaceStyle
      Updates WatchFace style.

      Uses the following properties:

      AcceptsTapEvents
      AmbientPeekMode
      BackgroundVisiblility
      CardPeekMode
      HideHotwordIndicator
      HideStatusBar
      StatusBarGravity
      HotworkIndicatorGravity
      ShowSystemUiTime
      PeekOpacityMode
      ShowUnreadCountIndicator
      ViewProtectionMode
  • Properties:
    • AcceptsTapEvents As Boolean
      Sets whether this watchface accepts tap events. The default is false.
      Setting this to true enables the TapCommand event.

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • AmbientPeekMode As Int
      Sets how the first, peeking card will be displayed while the watch is in ambient, black and white mode.

      Must be either AMBIENT_PEEK_MODE_VISIBLE or AMBIENT_PEEK_MODE_HIDDEN

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • BackgroundVisibility As Int
      Set how to display background of the first, peeking card.

      Must be either BACKGROUND_VISIBILITY_INTERRUPTIVE or BACKGROUND_VISIBILITY_PERSISTENT

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • BurnInProtection As Boolean [read only]
      Returns if the device needs burn in protections. On such devices don't create
      big white backgrounds.
    • CardPeekMode As Int
      Sets how far into the screen the first card will peek while the watch face is displayed.

      Must be either PEEK_MODE_VARIABLE or PEEK_MODE_SHORT

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • Height As Int [read only]
      Returns the Height of the screen
    • HideHotwordIndicator As Boolean
      Hides the hotword indicator.

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • HideStatusBar As Boolean
      Hides the status icons (battery state, lack of connection etc.)

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • HotwordIndicatorGravity As Int
      Sets position of hotword (OK Google) on the screen.

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • IsAmbientMode As Boolean [read only]
      Returns if the watchface is in ambient mode.
    • IsRound As Boolean [read only]
      Returns true if the device has a round display.
    • IsVislible As Boolean [read only]
      Returns if the watchface is visible.
    • LowBitAmbient As Boolean [read only]
      Returns if the device has reduced colors in ambient mode. Disable antialias of your
      drawings to reduce CPU usage.
    • MuteMode As Boolean [read only]
      Returns if the device is muted.
    • NotificationCount As Int [read only]
      Returns the total number of Notification cards.
    • OuterBounds As RectWrapper [read only]
      Returns a Rect object that has the complete screen size without any insets etc.
    • PeekCardBounds As RectWrapper [read only]
      Returns a Rect object for the first PeekCard Bounds.
    • PeekOpacityMode As Int
      Sets whether the first, peeking card should be opaque when the watch face is displayed.

      Must be either PEEK_OPACITY_MODE_OPAQUE or PEEK_OPACITY_MODE_TRANSLUCENT

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • ShowSystemUiTime As Boolean
      Sets if the system will draw the system-style time over the watch face.

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • ShowUnreadCountIndicator As Boolean
      Sets whether to add an indicator of how many unread cards there are in the stream.
      The indicator will be displayed next to status icons (battery state, lack of connection).

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • StatusBarGravity As Int
      Sets position of status icons (battery state, lack of connection) on the screen.

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • Tag As Object
      Property to store any information in the engine.
    • UnreadCount As Int [read only]
      Returns the number of unread Notification cards.
    • ViewProtectionMode As Int
      Adds background color to UI elements of the home screen, so they are readable on the watch face. This should be used if the watch face color is close to being white.

      Must be any combination of PROTECT_STATUS_BAR, PROTECT_HOTWORD_INDICATOR and PROTECT_WHOLE_SCREEN

      This property changes not immediately but only when SetWatchFaceStyle() is called!
    • Width As Int [read only]
      Returns the Width of the screen
  • WFManager
    Events:
    • AmbientModeChanged (Engine as WFEngine As , AmbientMode as Boolean As )
    • Created (Engine As WFEngine)
    • Draw (Engine as WFEngine As , Canvas as Canvas As , Bounds as Rect As )
    • EngineDestroyed (Engine As WFEngine)
    • SizeChanged (Engine As WFEngine, Width as Int As , Height as Int As )
    • TapCommand (Engine as WFEngine As , TapType As Int, XPos as Int As , YPos as Int As , EventTime as Long As )
    • VisibilityChanged (Engine As WFEngine, Visible As Boolean)
  • Methods:
    • Initialize (EventName As String, IntervalMs As Int)
      Initialize the WatchFaceManager object.

      EventName: The name prefix for the events
      IntervalMs: Timer interval in milliseconds. This will setup a timer which will call the Draw event so the WatchFace gets updated. This timer will fire only in interactive mode and when the WatchFace is visible.
    • InvalidateEngine
      Invalidate the Engine. This will cause a redraw of the WatchFace.
    • IsInitialized As Boolean
      Check if the manager is initialized
  • Permissions:
    • android.permission.WAKE_LOCK
  • Properties:
    • Interval As Int
      Set or get the timer interval. The timer fires only in interactive mode.
      In ambient mode a refresh is done every minute.
    • WFEngine As WFEngine [read only]
      Returns the WatchFace engine. This may be null if the WatchFace engine is not fully initialized yet.

Examples:
There is an example provided which implements an analog clock and a digital clock watchface.

The WatchFaceWear app is the watchface implementation for the wear device. If you have a device with USB port you can directly deploy this app on the device with B4A. If not then you have to create a companion app which is provided with the WatchFaceMobile app. To create the WatchFaceMobile app copy the generated WatchFaceWear.apk to the resouce/raw folder and name it "watchfacewear.apk" (all lower case). Then compile and deploy to your mobile phone.
The WatchFaces should show up in the wear app and directly on the wear device.

The example requires the ABExtDrawing library.

History:
V0.1
  • initial release
V0.2
  • more complete implementation
V1.0
  • First official release
V2.0
  • Should work with AndroidX
Since the library is too large now you can download it here:
WatchfaceLib2_0.zip
 

Attachments

  • WatchFaceMobile2_0.zip
    7.6 KB · Views: 745
  • WatchfaceWear2_0.zip
    34 KB · Views: 747
Last edited:

Wolli013

Well-Known Member
Licensed User
Longtime User
Sorry what is wrong?

B4A version: 6.50
Parsing code. (0.01s)
Compiling code. (0.06s)
Compiling layouts code. (0.00s)
Organizing libraries. (1.56s)
Generating R file. Error
invalid resource directory name: D:\WatchfaceWear\Objects\bin\extra\res1\res values-notround-v23
invalid resource directory name: D:\WatchfaceWear\Objects\bin\extra\res1\res values-round-v23
invalid resource directory name: D:\WatchfaceWear\Objects\bin\extra\res1\res values-sw180dp-notround-v23
invalid resource directory name: D:\WatchfaceWear\Objects\bin\extra\res1\res values-sw210dp-round-v23
 

corwin42

Expert
Licensed User
Longtime User
Sorry what is wrong?

B4A version: 6.50
Parsing code. (0.01s)
Compiling code. (0.06s)
Compiling layouts code. (0.00s)
Organizing libraries. (1.56s)
Generating R file. Error
invalid resource directory name: D:\WatchfaceWear\Objects\bin\extra\res1\res values-notround-v23
invalid resource directory name: D:\WatchfaceWear\Objects\bin\extra\res1\res values-round-v23
invalid resource directory name: D:\WatchfaceWear\Objects\bin\extra\res1\res values-sw180dp-notround-v23
invalid resource directory name: D:\WatchfaceWear\Objects\bin\extra\res1\res values-sw210dp-round-v23

This is one of the errors I never have seen here so I can only give some tipps what to check.

Try to update everything to the latest versions:

In SDK Manager:
- Android SDK Build Tools (I use 25.0.2, deinstall all other versions)
- Google Repository 41
- Support Repository 41

In Configure Paths be sure you have android.jar of platform 25 selected.
 

Wolli013

Well-Known Member
Licensed User
Longtime User
Thank you is running
In SDK Manager:
- Android SDK Build Tools (I use 25.0.2, deinstall all other versions) was wrong

Top Librarie !!!
 

BWinGR

New Member
Licensed User
Longtime User
Looking for some direction on this. When I load the library, I get the following:

Maven artifact not found: com.google.android.support/wearable

I'm obviously missing something, but I can't seem to track it down myself.
 

corwin42

Expert
Licensed User
Longtime User
Looking for some direction on this. When I load the library, I get the following:

Maven artifact not found: com.google.android.support/wearable

I'm obviously missing something, but I can't seem to track it down myself.

Do you have the Google Repository installed in SDK Manager?
 

NeoTechni

Well-Known Member
Licensed User
Longtime User
How do you get a settings icon?

I'm trying:
B4X:
AddApplicationText(<activity
  android:name=".WATCHFACE_NAMEWearableConfigActivity"
  android:label="$LABEL$">
  <intent-filter>
  <action android:name="PACKAGE_NAME.ACTIVITY_NAME" />
  <category android:name="com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
  <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</activity>)
 

NeoTechni

Well-Known Member
Licensed User
Longtime User
I got it! I'm not sure which of the 2 manifest edits did it, but here it is:

AddApplicationText(<activity
android:name=".analogwatchfaceWearableConfigActivity"
android:label="$LABEL$">
<intent-filter>
<action android:name="com.omnicorp.lcar.wear.MAIN" />
<category android:name="com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>)

AddServiceText(analogwatchface,
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/watch_face" />
<meta-data
android:name="com.google.android.wearable.watchface.preview"
android:resource="@drawable/analogwatchface_preview" />
<meta-data
android:name="com.google.android.wearable.watchface.preview_circular"
android:resource="@drawable/analogwatchface_preview" />

<meta-data
android:name=
"com.google.android.wearable.watchface.wearableConfigurationAction"
android:value=
"com.omnicorp.lcar.wear.MAIN" />

<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
<category
android:name=
"com.google.android.wearable.watchface.category.WATCH_FACE" />
</intent-filter>
)

It's crashing at the moment though, hopefully changing main to MAIN fixed it.
 

NeoTechni

Well-Known Member
Licensed User
Longtime User
It did not. No matter what I change .analogwatchfaceWearableConfigActivity to

java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.omnicorp.lcar.wear/com.omnicorp.lcar.wear.analogwatchfaceWearableConfigActivity}: java.lang.ClassNotFoundException: Didn't find class "com.omnicorp.lcar.wear.analogwatchfaceWearableConfigActivity" on path: DexPathList[[zip file "/data/app/com.omnicorp.lcar.wear-1/base.apk"],nativeLibraryDirectories=[/data/app/com.omnicorp.lcar.wear-1/lib/arm, /vendor/lib, /system/lib]]
 

NeoTechni

Well-Known Member
Licensed User
Longtime User
I started with lowercase and it still failed

#Region Watchfaces

AddApplicationText(<activity
android:name=".analogwatchfaceWearableConfigActivity"
android:label="$LABEL$">
<intent-filter>
<action android:name="com.omnicorp.lcar.wear.main" />
<category android:name="com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>)

SetServiceAttribute(analogwatchface, android:label, "LCARS UI")
SetServiceAttribute(analogwatchface, android:permission, "android.permission.BIND_WALLPAPER")

AddServiceText(analogwatchface,
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/watch_face" />
<meta-data
android:name="com.google.android.wearable.watchface.preview"
android:resource="@drawable/analogwatchface_preview" />
<meta-data
android:name="com.google.android.wearable.watchface.preview_circular"
android:resource="@drawable/analogwatchface_preview" />

<meta-data
android:name="com.google.android.wearable.watchface.wearableConfigurationAction"
android:value="com.omnicorp.lcar.wear.main" />

<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
<category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
</intent-filter>
)

CreateResource(xml, watch_face.xml,
<?xml version="1.0" encoding="UTF-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android" />)

#End Region

Google's documentation is garbage
https://developer.android.com/training/wearables/watch-faces/configuration.html
They could do a lot more to show what is the variables to swap, and what isn't (ie: colored text)
 

NeoTechni

Well-Known Member
Licensed User
Longtime User
This was the trick, inside #Region Watchfaces

AddApplicationText(<activity android:name=".main" android:label="$LABEL$">
<intent-filter>
<action android:name="com.omnicorp.lcar.wear.CONFIG_DIGITAL" />
<category android:name="com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>)

and in AddServiceText(analogwatchface,

<meta-data
android:name="com.google.android.wearable.watchface.wearableConfigurationAction"
android:value="com.omnicorp.lcar.wear.CONFIG_DIGITAL" />

com.omnicorp.lcar.wear = your package name
main = your activity name
CONFIG_DIGITAL = unique name given to the watchface

I could have sworn I tried that...

I wish Google would quit making things so deliberately obtuse. I would have made it:

<meta-data
android:name="com.google.android.wearable.watchface.wearableConfigurationAction"
android:value="packagename.activityname" />
 

Star-Dust

Expert
Licensed User
Longtime User
I ha e tested on smartwatch LG w100
 

Peter Simpson

Expert
Licensed User
Longtime User
Very nice library, very nice library indeed @corwin42, thank you :)

So since my Windows work has died down a little bit in the last week or two, I though that I would look through the forum to see what was going on. Then I came across your library, again. I completely forgot about it ;)

Anyway, four and a half hours later and I created an app to match the app that I have on my phone, click here to see more.

I'll put an actual screen shot in the share your creations section of the forum.

What it looks like on my device.
IMG_20171122_085322.jpg


Cheers...
 
Last edited:

corwin42

Expert
Licensed User
Longtime User
Unfortunately my Sony SmartWatch 3 didn't get the AW2 update so I'm currently not planning to add any 2.0 features like complications or providers.
Maybe some time in the future.
 
Top