Skip to content

Conversation

matejdro
Copy link
Contributor

@matejdro matejdro commented Oct 5, 2025

PR adds support for the PebbleKit Android.

I recommend checking by commits: First PKJSLifecycleManager is generalized to allow non-JS companion apps, then basic AppMessage sending is implemented, then few bugs are fixed that prevented PebbleKit from functioning. Finally start/stop listeners and the provider is added.

To alleviate provider conflict issues somewhat, I have decided to not include the provider into the library's manifest. Instead a README entry is added to allow every implementing app to decide whether to use it or not.

I did not implement Data logging or sports/golf apps. I do not have any experience with this, so I have no way of testing whether these would work or not.

P.S.: I have noticed the message sending is quite a bit more sluggish than it used to be with the old Pebble app. Is that due to the use of the BLE vs BT Classic? EDIT:I've made a separate ticket for that.

when consumeAsFlow is called, it immediately hogs all the messages, which means that the Buffer in the Channel above has no effect - if nobody is collecting the flow, the messages are lost. This is fixed by moving away from SharedFlow to regular flow and by using receive instead of consume.

A side effect of this is that this flow cannot be collected by more than one consumer at once, but from what I can see, there is no use case for that?
This allows PebbleKit Android to distinguish between incoming signed and unsigned values. It should not affect JS, because it is a normal number behind the scenes
@matejdro
Copy link
Contributor Author

matejdro commented Oct 5, 2025

Hm, not sure why build failed, it seems it failed in unrelated files. Any pointers?

@ericmigi
Copy link
Contributor

ericmigi commented Oct 6, 2025

Thanks @matejdro! This is effectively an implementation of your plan here right? https://docs.google.com/document/d/1BcX7W9HEBays5puwcRQh1GzClyHc014IRzEIuk7lLuk/edit?tab=t.0. Any major deltas from the plan?

@matejdro
Copy link
Contributor Author

matejdro commented Oct 6, 2025

No, this is an implementation of the old system (hence the "classic" in the name) to get compatibility with the old apps. I will start work on the new system soon, but it's more complex, so it will take a bit more.

LibPebble might not be initialised yet at the point when the first requests arrive, so we now handle this case more gracefully. Updating has been moved to its own class PebbleKitProviderNotifier, which is created after the LibPebble fully inits
@ericmigi
Copy link
Contributor

ericmigi commented Oct 6, 2025

Ah my bad, missed that!

this fixes the following scenario:

1. App A is active, with PBJS active
2. App B is activated and immediately sends an AppMessage
3. Afterward, AppRunStateStart is sent for the app switch
4. Packet from the step 2 is dropped, because PBJS for the app A is still active
# Conflicts:
#	libpebble3/src/androidMain/kotlin/io/rebble/libpebblecommon/js/WebViewJsRunner.kt
Copy link
Member

@sjp4 sjp4 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Looks good on first pass - left a few comments, and also would like @crc-32 to take a look at how it generalises the PKJS code

device: CompanionAppDevice,
appInfo: PbwAppInfo
): CompanionApp? {
// PebbleKit iOS is not supported (yet?)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PebbleKit on iOS works directly through bluetooth without going through the Pebble app (i.e. this being a no-op is expected)

android:exported="true" />
```

Note that by doing so, your app will not be able to be installed alongside other apps that also do this.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth noting that (unless we change libpebble so that it doesn't enable the gatt server by default) installing any other app using libpebble will break other apps (see otherPebbleCompanionAppsInstalled), so this might not really be a big problem. Leaving it like this is fine though

private val logger = Logger.withTag(PebbleKitClassicStartListeners::class.simpleName!!)
}

private val context: Context = getKoin().get()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could probably be provided in LibPebbleModule.android and use constructor injection


private val context: Context = getKoin().get()
private val libPebble: LibPebble = getKoin().get()
private val connectionScope: LibPebbleCoroutineScope = getKoin().get()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming: this isn't a connection scope - it's a global scope

fun init() {
val watches: Watches = getKoin().get<LibPebble>()
val libPebbleCoroutineScope: LibPebbleCoroutineScope = getKoin().get()
val context: Context = getKoin().get()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: could use constructor injection


private val runningApp: MutableStateFlow<CompanionApp?> = MutableStateFlow(null)
@Deprecated("Use more generic currentCompanionAppSession instead and cast if necessary")
override val currentPKJSSession: StateFlow<PKJSApp?> = PKJSStateFlow(runningApp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@crc-32 can you take a look as you're more familiar with the PKJS code (and AppMessage stuff)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants