Skip to content

Commit 5d086fa

Browse files
authored
Merge pull request #432 from cph-cachet/419-upgrade-to-carp_serializable-v-20
Upgrade to carp serializable v 20
2 parents 40ae20f + bb7581a commit 5d086fa

File tree

145 files changed

+1484
-1172
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

145 files changed

+1484
-1172
lines changed

apps/carp_mobile_sensing_app/README.md

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,43 +26,29 @@ Since the [`SensingBLoC`](https://github.com/cph-cachet/carp.sensing-flutter/blo
2626

2727
````dart
2828
class SensingBLoC {
29+
/// The [Sensing] layer used in the app.
30+
Sensing get sensing => Sensing();
31+
2932
/// What kind of deployment are we running? Default is local.
3033
DeploymentMode deploymentMode => ...
3134
32-
/// The URI of the CAWS server to use depending on the current [deploymentMode].
33-
String get uri => ...
34-
35-
/// The study id for the currently running deployment.
36-
/// Returns the study id cached locally on the phone (if available).
37-
/// Returns `null` if no study is deployed (yet).
38-
String? get studyId => ...
39-
40-
/// The study deployment id for the currently running deployment.
41-
/// Returns the deployment id cached locally on the phone (if available).
35+
/// The study for the currently running study deployment.
36+
/// The study is cached locally on the phone.
4237
/// Returns `null` if no study is deployed (yet).
43-
String? get studyDeploymentId => ...
44-
45-
/// The device role name for the currently running deployment.
46-
/// Returns the role name cached locally on the phone (if available).
47-
/// Returns `null` if no study is deployed (yet).
48-
String? get deviceRoleName => ...
38+
SmartphoneStudy? get study => ...
4939
5040
/// Use the cached study deployment?
51-
bool get useCachedStudyDeployment => _useCached;
41+
bool get useCachedStudyDeployment => ...
5242
5343
/// Should sensing be automatically resumed on app startup?
54-
bool get resumeSensingOnStartup => _resumeSensingOnStartup;
44+
bool get resumeSensingOnStartup => ...
5545
5646
/// The [SmartphoneDeployment] deployed on this phone.
57-
SmartphoneDeployment? get deployment => bloc.sensing.controller?.deployment;
47+
SmartphoneDeployment? get deployment => sensing.controller?.deployment;
5848
5949
/// What kind of deployment are we running. Default is local.
6050
DeploymentMode deploymentMode = DeploymentMode.local;
6151
62-
/// The preferred format of the data to be uploaded according to
63-
/// [NameSpace]. Default using the [NameSpace.CARP].
64-
String dataFormat = NameSpace.CARP;
65-
6652
StudyDeploymentModel? _model;
6753
6854
/// Get the view model for this study [deployment].
@@ -84,15 +70,13 @@ class SensingBLoC {
8470
/// Initialize the BLoC.
8571
Future<void> initialize({
8672
DeploymentMode deploymentMode = DeploymentMode.local,
87-
String? deploymentId,
8873
String dataFormat = NameSpace.CARP,
8974
bool useCachedStudyDeployment = true,
9075
bool resumeSensingOnStartup = false,
9176
}) async {
9277
await Settings().init();
9378
Settings().debugLevel = DebugLevel.debug;
9479
this.deploymentMode = deploymentMode;
95-
if (deploymentId != null) studyDeploymentId = deploymentId;
9680
this.dataFormat = dataFormat;
9781
_resumeSensingOnStartup = resumeSensingOnStartup;
9882
_useCached = useCachedStudyDeployment;
@@ -119,7 +103,7 @@ final bloc = SensingBLoC();
119103

120104
The BLoC basically plays three roles:
121105

122-
* it holds core business data like `studyId`, `deploymentId`, `deviceRoleName`, and the `deployment` configuration
106+
* it holds core business data like the `study` and `deployment` configuration
123107
* it can create view models such as the `StudyDeploymentViewModel` and the list of `ProbeViewModel`s and `DeviceViewModel`s
124108
* it provide a set of life cycle methods for sensing like `initialize`, `connectToDevice` and `start`.
125109

@@ -132,7 +116,7 @@ Configuration of sensing is done in the [`Sensing`](https://github.com/cph-cache
132116
This class also illustrates how the app can be run both in a "local" deployment mode and in different "CAWS" modes. Depending on the deployment mode (local or using CAWS), deployment is initialized using the [`SmartphoneDeploymentService`](https://pub.dev/documentation/carp_mobile_sensing/latest/runtime/SmartphoneDeploymentService-class.html) or the [`CarpDeploymentService`](https://pub.dev/documentation/carp_webservices/latest/carp_services/CarpDeploymentService-class.html), respectively.
133117

134118
In the case a local deployment is used, a protocol is fetched from the `LocalStudyProtocolManager`, which is then added to the local `SmartphoneDeploymentService`.
135-
In the case a CAWS deployment is used, the study deployment configuration will be fetched from the `CarpDeploymentService` based on the `studyDeploymentId` fetched from an invitation (this invitation is fetched as part of the `init` method of the main `App` class).
119+
In the case a CAWS deployment is used, the study deployment configuration will be fetched from the `CarpDeploymentService` based on the `study`, which again is part of an `invitation` (this invitation is fetched as part of the `init` method of the main `App` class).
136120

137121
Once, the right deployment service is configured, the `SmartPhoneClientManager` singleton is configured and the study is added (based on the deployment id and the role name of the phone) and deployed.
138122
When deployed, the runtime (`SmartphoneDeploymentController`) is configured and sampling can now be started or stopped. This part of `Sensing` is shown below:
@@ -142,12 +126,11 @@ When deployed, the runtime (`SmartphoneDeploymentController`) is configured and
142126
// (local or CAWS), add the study, and deploy it.
143127
await SmartPhoneClientManager().configure(
144128
deploymentService: deploymentService,
129+
askForPermissions: true,
145130
);
146-
study = await SmartPhoneClientManager().addStudy(
147-
bloc.studyDeploymentId!,
148-
bloc.deviceRoleName!,
149-
);
150-
await controller?.tryDeployment();
131+
132+
study = await SmartPhoneClientManager().addStudy(bloc.study!);
133+
await controller?.tryDeployment(useCached: bloc.useCachedStudyDeployment);
151134
await controller?.configure();
152135
```
153136

@@ -243,3 +226,14 @@ StreamBuilder<Measurement>(
243226
_StudyControllerLine('${viewModel.samplingSize}',
244227
heading: 'Sample Size')),
245228
`````
229+
230+
## Technical Notes
231+
232+
The CARP Mobile Sensing Demo app makes use of many of the CAMS Sampling Packages. Each of these have their own requirements to work, which entails modification on how to configure and build the app on iOS and Android. You should pay special attention to the requirements described in the README of each sampling package. This often entails editing and modifying:
233+
234+
* the `info.plist` on iOS
235+
* the `AndroidManifest.xml` file on Android
236+
* the `MainActivity.kt` or `MainActivity.java` on Android
237+
* the different `build.gradle` and `settings.gradle` files on Android
238+
239+
This example app also illustrates how these files should be configured.
Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,40 @@
1-
def localProperties = new Properties()
2-
def localPropertiesFile = rootProject.file('local.properties')
3-
if (localPropertiesFile.exists()) {
4-
localPropertiesFile.withReader('UTF-8') { reader ->
5-
localProperties.load(reader)
6-
}
7-
}
8-
9-
def flutterRoot = localProperties.getProperty('flutter.sdk')
10-
if (flutterRoot == null) {
11-
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12-
}
13-
14-
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15-
if (flutterVersionCode == null) {
16-
flutterVersionCode = '1'
1+
plugins {
2+
id "com.android.application"
3+
id "kotlin-android"
4+
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
5+
id "dev.flutter.flutter-gradle-plugin"
176
}
187

19-
def flutterVersionName = localProperties.getProperty('flutter.versionName')
20-
if (flutterVersionName == null) {
21-
flutterVersionName = '1.0'
22-
}
23-
24-
apply plugin: 'com.android.application'
25-
apply plugin: 'kotlin-android'
26-
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27-
288
android {
29-
compileSdkVersion 34
9+
namespace = "dk.cachet.carp_mobile_sensing_app"
10+
compileSdk = flutter.compileSdkVersion
11+
ndkVersion = flutter.ndkVersion
3012

3113
compileOptions {
32-
sourceCompatibility JavaVersion.VERSION_1_8
33-
targetCompatibility JavaVersion.VERSION_1_8
14+
sourceCompatibility = JavaVersion.VERSION_1_8
15+
targetCompatibility = JavaVersion.VERSION_1_8
16+
}
17+
18+
kotlinOptions {
19+
jvmTarget = JavaVersion.VERSION_1_8
3420
}
3521

3622
defaultConfig {
3723
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
38-
applicationId "dk.cachet.carp_mobile_sensing_app"
39-
minSdkVersion 28
40-
targetSdkVersion flutter.targetSdkVersion
41-
versionCode flutterVersionCode.toInteger()
42-
versionName flutterVersionName
24+
applicationId = "dk.cachet.carp_mobile_sensing_app"
25+
// You can update the following values to match your application needs.
26+
// For more information, see: https://flutter.dev/to/review-gradle-config.
27+
minSdk = 28
28+
targetSdk = flutter.targetSdkVersion
29+
versionCode = flutter.versionCode
30+
versionName = flutter.versionName
4331
}
4432

4533
buildTypes {
4634
release {
4735
// TODO: Add your own signing config for the release build.
4836
// Signing with the debug keys for now, so `flutter run --release` works.
49-
signingConfig signingConfigs.debug
37+
signingConfig = signingConfigs.debug
5038
}
5139
}
5240
}
@@ -57,5 +45,5 @@ dependencies {
5745
}
5846

5947
flutter {
60-
source '../..'
48+
source = "../.."
6149
}

apps/carp_mobile_sensing_app/android/app/src/main/AndroidManifest.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
<!-- The following permissions are used in the Connectivity Package -->
4040
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
4141

42-
4342
<!-- Legacy Bluetooth permissions, which is needed on devices with API 30 (Android Q) or older. -->
4443
<uses-permission
4544
android:name="android.permission.BLUETOOTH"
@@ -132,6 +131,7 @@
132131
<!-- Configuration of AppAuth - see also flutter_appauth plugin -->
133132
<activity
134133
android:name="net.openid.appauth.RedirectUriReceiverActivity"
134+
android:theme="@style/Theme.AppCompat.NoActionBar"
135135
android:exported="true"
136136
tools:node="replace">
137137
<intent-filter>
@@ -177,6 +177,18 @@
177177
</intent-filter>
178178
</activity>
179179

180+
<!-- Activity to show Permissions screen for Health Connect -->
181+
<activity-alias
182+
android:name="ViewPermissionUsageActivity"
183+
android:exported="true"
184+
android:targetActivity=".MainActivity"
185+
android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
186+
<intent-filter>
187+
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
188+
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
189+
</intent-filter>
190+
</activity-alias>
191+
180192
<!-- Don't delete the meta-data below.
181193
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
182194
<meta-data
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dk.cachet.carp_mobile_sensing_app;
22

3-
import io.flutter.embedding.android.FlutterActivity;
3+
import io.flutter.embedding.android.FlutterFragmentActivity;
44

5-
public class MainActivity extends FlutterActivity {
5+
// Need to extend FlutterFragmentActivity to work with the Health Connect API
6+
public class MainActivity extends FlutterFragmentActivity {
67
}

apps/carp_mobile_sensing_app/android/build.gradle

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,18 @@
1-
buildscript {
2-
ext.kotlin_version = '1.9.0'
3-
repositories {
4-
google()
5-
mavenCentral()
6-
}
7-
8-
dependencies {
9-
classpath 'com.android.tools.build:gradle:7.3.0'
10-
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11-
}
12-
}
13-
141
allprojects {
152
repositories {
163
google()
174
mavenCentral()
185
flatDir{
196
dirs "$rootDir/libs"
20-
}
21-
22-
}
7+
} }
238
}
249

25-
rootProject.buildDir = '../build'
10+
rootProject.buildDir = "../build"
2611
subprojects {
2712
project.buildDir = "${rootProject.buildDir}/${project.name}"
2813
}
2914
subprojects {
30-
project.evaluationDependsOn(':app')
15+
project.evaluationDependsOn(":app")
3116
}
3217

3318
tasks.register("clean", Delete) {
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
1-
include ':app'
1+
pluginManagement {
2+
def flutterSdkPath = {
3+
def properties = new Properties()
4+
file("local.properties").withInputStream { properties.load(it) }
5+
def flutterSdkPath = properties.getProperty("flutter.sdk")
6+
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7+
return flutterSdkPath
8+
}()
29

3-
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4-
def properties = new Properties()
10+
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
511

6-
assert localPropertiesFile.exists()
7-
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
12+
repositories {
13+
google()
14+
mavenCentral()
15+
gradlePluginPortal()
16+
}
17+
}
818

9-
def flutterSdkPath = properties.getProperty("flutter.sdk")
10-
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11-
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
19+
plugins {
20+
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
21+
id "com.android.application" version "7.3.0" apply false
22+
// id "org.jetbrains.kotlin.android" version "1.7.10" apply false
23+
id "org.jetbrains.kotlin.android" version "2.0.0" apply false
24+
}
25+
26+
include ":app"

apps/carp_mobile_sensing_app/lib/main.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
library mobile_sensing_app;
2-
31
import 'dart:async';
2+
import 'dart:convert';
43

54
import 'package:flutter/material.dart' hide TimeOfDay;
65
import 'package:flutter/services.dart';
@@ -53,8 +52,7 @@ void main() async {
5352

5453
// Initialize the bloc, setting the deployment mode.
5554
await bloc.initialize(
56-
deploymentMode: DeploymentMode.local,
57-
// deploymentId: testDeploymentId,
55+
deploymentMode: DeploymentMode.dev,
5856
useCachedStudyDeployment: false,
5957
resumeSensingOnStartup: false,
6058
);

apps/carp_mobile_sensing_app/lib/src/app.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ class LoadingPage extends StatelessWidget {
2828
await CarpBackend().initialize();
2929
await CarpBackend().authenticate();
3030

31-
// Check if there is a local deployment id.
32-
// If not, get a deployment id based on an invitation.
33-
if (bloc.studyDeploymentId == null) {
31+
// Check if there is a local study.
32+
// If not, get a study deployment based on an invitation.
33+
if (bloc.study == null) {
3434
await CarpBackend().getStudyInvitation(context);
3535
}
3636

37-
// Make sure that CarpService knows the study and deployment ids
38-
CarpService().app.studyId = bloc.studyId;
39-
CarpService().app.studyDeploymentId = bloc.studyDeploymentId;
37+
// Make sure that CarpService knows the study deployment.
38+
// This is useful when an app (like this one only handles one study at a time
39+
CarpService().study = bloc.study;
4040
}
4141

4242
await bloc.sensing.initialize();

apps/carp_mobile_sensing_app/lib/src/blocs/carp_backend.dart

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,7 @@ class CarpBackend {
4444
);
4545

4646
/// The CAWS app configuration.
47-
late final CarpApp _app = CarpApp(
48-
name: "CAWS @ DTU",
49-
uri: uri,
50-
studyId: bloc.studyId,
51-
studyDeploymentId: bloc.studyDeploymentId,
52-
);
47+
late final CarpApp _app = CarpApp(name: "CAWS @ DTU", uri: uri);
5348

5449
CarpApp get app => _app;
5550

@@ -92,12 +87,13 @@ class CarpBackend {
9287
await CarpParticipationService().getStudyInvitation(context);
9388
debug('CAWS Study Invitation: $invitation');
9489

95-
bloc.studyId = invitation?.studyId;
96-
bloc.studyDeploymentId = invitation?.studyDeploymentId;
97-
bloc.deviceRoleName = invitation?.assignedDevices?.first.device.roleName;
98-
info('Invitation received - '
99-
'study id: ${bloc.studyId}, '
100-
'deployment id: ${bloc.studyDeploymentId}, '
101-
'role name: ${bloc.deviceRoleName}');
90+
if (invitation != null) {
91+
bloc.study = SmartphoneStudy.fromInvitation(invitation);
92+
93+
info('Invitation received - '
94+
'study id: ${invitation.studyId}, '
95+
'deployment id: ${invitation.studyDeploymentId}, '
96+
'role name: ${invitation.deviceRoleName}');
97+
}
10298
}
10399
}

0 commit comments

Comments
 (0)