Client manager and study controller
SmartPhoneClientManager is a singleton and the main entry point to CAMS. It holds the SmartphoneStudy objects you add, and one SmartphoneStudyController per study. Its state is created, configured or disposed.
CAMS persists its state. Once a study is added, deployed and resumed, it is restored and resumed after an app restart, so the flow below runs once per study.
Where in the repo
Section titled “Where in the repo”| File | What is in it |
|---|---|
runtime/client_manager.dart |
SmartPhoneClientManager |
runtime/client_repository.dart |
SmartphoneClientRepository: the studies and their stored state |
runtime/study_controller.dart |
SmartphoneStudyController: runs one study and owns its DataManager |
Deploy the protocol (optional)
Section titled “Deploy the protocol (optional)”A study protocol is deployed via a DeploymentService.
// Use the on-phone deployment service.DeploymentService deploymentService = SmartphoneDeploymentService();
// Create a study deployment using the protocol.var status = await deploymentService.createStudyDeployment(protocol);Configure, add and deploy
Section titled “Configure, add and deploy”Runtime management of studies is handed by the client manager SmartPhoneClientManager.
The most simple configuration of the client manager is:
await SmartPhoneClientManager().configure();However, the client manager can be configured in different ways:
registration: unique device registration for this phone.deploymentService: deployment backend (default is localSmartphoneDeploymentService).dataCollectorFactory: custom collector factory (default uses the standardDeviceController).enableNotifications: show task notifications (trueby default).enableBackgroundMode: run sampling in background (trueby default, Android only).backgroundNotificationTitle/backgroundNotificationText: text for Android background notification.askForPermissions: request package permissions automatically (trueby default).
final client = SmartPhoneClientManager();await client.configure();final client = SmartPhoneClientManager();
await client.configure( deploymentService: deploymentService, enableNotifications: true, enableBackgroundMode: true, backgroundNotificationTitle: 'CAMS is running', backgroundNotificationText: 'Collecting sensing data in background', askForPermissions: false, // app handles permissions manually);Once the client is configured, you can deploy studies to it. This is done by getting a study deployment from the deployment service, based on the protocol. In order to do this, we need to configure the client to use this deployment service. If the protocol has been added as shown above, this will add and deploy the study on the client:
// Use the on-phone deployment service.DeploymentService deploymentService = SmartphoneDeploymentService();
// Create a study deployment using a protocol.var status = await deploymentService.createStudyDeployment(protocol);
// Create and configure a client manager to use the deploymentService.SmartPhoneClientManager client = SmartPhoneClientManager();await client.configure(deploymentService: deploymentService);
// Add a study based on the deployed protocol to the clientfinal study = await client.addStudy( SmartphoneStudy( studyDeploymentId: status.studyDeploymentId, deviceRoleName: phone.roleName, ),);
/// Deploy the study to the client.SmartPhoneClientManager().tryDeployment( study.studyDeploymentId, study.deviceRoleName,);This will configure the client manager and add a new study based on the specified protocol. However, if using the local SmartphoneDeploymentService, deploying and adding a study based on a protocol can be written more compact like this:
// Create and configure a client manager for this phone.await SmartPhoneClientManager().configure();
// Create a study based on the protocol.var study = await SmartPhoneClientManager().addStudyFromProtocol(protocol);
/// Deploy the study.await SmartPhoneClientManager().tryDeployment( study.studyDeploymentId, study.deviceRoleName,);This can be written even more compact:
SmartPhoneClientManager().configure().then( (_) => SmartPhoneClientManager() .addStudyFromProtocol(protocol) .then( (study) => SmartPhoneClientManager().tryDeployment( study.studyDeploymentId, study.deviceRoleName, ), ),);Note that the client manager can handle multiple studies - just add more studies to the client:
var study_1 = await client.addStudyFromProtocol(protocol_1);var study_2 = await client.addStudyFromProtocol(protocol_2);var study_3 = await client.addStudyFromProtocol(protocol_3);Resume and pause
Section titled “Resume and pause”Data sampling can be controlled by the resume and pause methods:
// Resume data sampling for all studies added to the client.SmartPhoneClientManager().resume();
// .. and pause all data sampling for all studies again.SmartPhoneClientManager().pause();Calling resume() or pause() on the client manager will resume or pause all studies added to the client. If you want more fine-grained control over each study, you can control each study using its SmartphoneStudyController:
SmartphoneStudyController? controller = client.getStudyController(study);controller?.resume();You can stop a study by calling stop which will permanently stop the study - once stopped, and study cannot be (re)started. Call dispose to dispose of the client - typically in the Flutter dispose method.
// Permanently stop the study.// This will mark the study as stopped and remove it from the client manager.SmartPhoneClientManager().stopStudy( study.studyDeploymentId, study.deviceRoleName,);
// Dispose of the client (e.g. in Flutter dispose())client.dispose();Listen to measurements
Section titled “Listen to measurements”All sampling data is available in the measurements stream. This stream is available on both controller and client:
// Listening on the measurements stream. client.measurements.listen((measurement) { // Do something with the measurement, e.g. print the json. print(toJsonString(measurement)); });Since it is a standard Dart Stream, you can subscribe, filter, map, etc. as needed.
// subscribe to the stream of measurementsvar subscription = controller?.measurements.listen((Measurement measurement) { // do something w. the measurement ...});
...
// Cancel the subscription.await subscription?.cancel();// listen on ambient light measurements onlycontroller?.measurements .where((measurement) => measurement.data.jsonType == SensorSamplingPackage.AMBIENT_LIGHT) .listen((measurement) => print(measurement));// map measurements to JSON and then printcontroller?.measurements .map((measurement) => measurement.toJson()) .listen((json) => print(json));