-
Notifications
You must be signed in to change notification settings - Fork 26
Android side
- Android part of the App is 100% written in Kotlin
- Dagger dependency injection is used to glue together different classes in the app
- Since the nature of the app (communicating to the remote watch) is inherently asynchronous, app employs heavy use of Kotlin coroutines and Flows.
Android app uses a bunch of FlutterBridge classes to implement pigeon communication (see Flutter to Native for more on Pigeon).
Bridge classes are located in the bridges package and are split into three categories:
-
Common bridges
These bridges can be called from either UI or background flutter isolate. Most bridges should be added into this category. New bridges are added to
CommonBridgesModule
. -
UI bridges
These bridges can only be called from UI flutter isolate. Unlike other two, you can inject
MainActivity
in its constructors to access UI-related code (such as requesting permissions). New bridges are added toUiBridgesModule
. -
Background bridges
These bridges are mostly used for Native -> background Flutter triggers. Unlike other two, these are usually Singletons (since background Flutter is also Singleton) and can be injected into other classes which allows other classes to trigger flutter methods. New bridges are added to
BackgroundBridgesModule
.
To create new bridge:
- Create new class in the respective subfolder of the
bridges
package. Class must implementFlutterBridge
package. - Add
@Inject
constructor - Add bridge to its respective module (see above list for name of the module for each bridge type). See existing module code for example.
- If your bridge needs Flutter -> Native callbacks, implement flutter interface, inject
BridgeLifecycleController
and then set it up withbridgeLifecycleController.setupControl(Pigeons.MyInterface::setup, this)
. Use this instead of directly callingMyInterface.setup
to ensure flutter interface will be automatically unregistered when bridge is disposed. - Write your bridge code ;)
All pigeon callbacks are being synchronously executed on the main thread. Pigeon does not support asynchronous calls yet on native. If you want to make asynchronous pigeon callback, you have to look up the raw name of the Pigeon method in the generated Pigeons.java
and then use BinaryMessenger.registerAsyncPigeonCallback
to register your callback instead of bridgeLifecycleController.setupControl
. Look up existing usages of this method for examples.
Whenever watch is connected or connecting, WatchService
is active. However, service class is just a shell that handles service lifecycle. Most of the Cobble app logic is contained in the handlers.
Handler is a class that observes some external event (such as new Bluetooth message from the watch or user timezone change) and react to them accordingly. To add new handler, create a class that implements CobbleHandler
, add @Inject
constructor to that class and register the class in the ServiceModule
, similarly to how previous handlers are added.
All handlers are started when the watch is connected (you can assume that watch is either Connected when handler instance is being created). Provided coroutine scope is stopped whenever watch is disconnected (including if it goes into connecting mode). At this point, handlers should stop all listeners and release all resources. If you have to close anything manually, you can do so in coroutineScope.coroutineContext.job.invokeOnCompletion {}
callback.