carp_mobile_sensing 0.2.1

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • new61

CARP Mobile Sensing Framework in Flutter

This library contains the software architecture for the CARP sensing framework implemented in Flutter. Supports cross-platform (iOS and Android) sensing.

Usage

To use this plugin, add carp_mobile_sensing as a dependency in your pubspec.yaml file.

This plugin relies on json_serialization: ^1.0.0 which again rely on Dart 2.1. This mean that (at the time of writing) you should use the dev channel in Flutter. This can be set using the following Flutter command:

flutter channel dev

Note that there are two issues with Android to consider:

  • Issue #1 - make sure your app's android build.graddle has a minSdkVersion 19 (instead of 16 ).
  • Issue #2 - update the he file build.graddle in flutter_blue and change the JDK parameters to 26 (instead of 27).

Documentation

The Dart API doc describes the different libraries and classes.

The wiki contains detailed documentation on the CARP Mobile Sensing Framework, including the domain model, its built-in probes, and how to extend it.

Below is a very simple / minimum example.

Example

// Import package
import 'package:carp_mobile_sensing/carp_mobile_sensing.dart';

... {
  // Instantiate a new study
  Study study = new Study("1234", "bardram", name: "Test study #1");

  // Setting the data endpoint to print to the console
  study.dataEndPoint = new DataEndPoint(DataEndPointType.PRINT);

  // Create a task to hold measures
  Task task = new Task("Simple Task");

  // Create a battery and location measures and add them to the task
  // Both are listening on events from changes from battery and location
  task.addMeasure(new BatteryMeasure(ProbeRegistry.BATTERY_MEASURE));
  task.addMeasure(new LocationMeasure(ProbeRegistry.LOCATION_MEASURE));

  // Create an executor that can execute this study, initialize it, and start it.
  StudyExecutor executor = new StudyExecutor(study);
  executor.initialize();
  executor.start();
}

There is a very simple example app which shows how a study can be created with different taks and measures. This app just prints the sensing data to a console screen on the phone.

Features and bugs

Please read about existing issues and file new feature requests and bug reports at the issue tracker.

License

This software is copyright (c) 2018 Copenhagen Center for Health Technology (CACHET) at the Technical University of Denmark (DTU). This software is made available 'as-is' in a MIT license.

0.2.1 October 2018

  • re-organization of github location and outline
  • improvements to FileDataManager to avoid race conditions
  • improved API documentation

0.2.0 October 2018

  • refactor of organization of classes into libraries
  • complete API documentation

0.1.1 October 2018

  • small improvements incl. documentation

0.1.0 October 2018

  • removal of all remote backend code to separate packages

0.0.1 September 2018

  • Initial version by Jakob E. Bardram
  • Transferring the old implementation to this carp.sensing-flutter framework
  • General refactor and clean-up

example/lib/main.dart

/*
 * Copyright 2018 Copenhagen Center for Health Technology (CACHET) at the
 * Technical University of Denmark (DTU).
 * Use of this source code is governed by a MIT-style license that can be
 * found in the LICENSE file.
 */
import 'package:flutter/material.dart';
import 'package:carp_mobile_sensing/carp_mobile_sensing.dart';

void main() => runApp(new CARPMobileSensingApp());

class CARPMobileSensingApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'CARP Mobile Sensing Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new ConsolePage(title: 'CARP Mobile Sensing Demo'),
    );
  }
}

class ConsolePage extends StatefulWidget {
  ConsolePage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  Console createState() => new Console();
}

class Console extends State<ConsolePage> {
  String _log = "";
  Sensing sensing;

  void log(String msg) {
    setState(() {
      _log += "$msg\n";
    });
  }

  void clearLog() {
    setState(() {
      _log += "";
    });
  }

  void restart() {
    log("-------------------------------------\nSensing restarted...");
    sensing.start();
  }

  @override
  void initState() {
    super.initState();
    sensing = new Sensing(this);
    sensing.start();
  }

  @override
  void dispose() {
    sensing.stop();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new SingleChildScrollView(
        child: new Text(
          _log,
          style: TextStyle(fontFamily: 'RobotoMono'),
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: restart,
        tooltip: 'Restart study & probes',
        child: new Icon(Icons.cached),
      ),
    );
  }
}

/// This class implements the sensing incl. setting up a [Study] with [Task]s and [Measure]s.
/// Note that it implements a [ProbeListener] and hence listen on any data created by the probes.
/// This is used to write to a log, which is displayed in a simple scrollable text view.
class Sensing implements ProbeListener {
  Console console;
  StudyExecutor executor;

  Sensing(this.console);

  /// Callback method called by [ProbeListener]s
  void notify(Datum datum) {
    console.log(datum.toString());
  }

  /// (Re)start sensing.
  void start() async {
    // if an executor/study is already running, stop the old one before creating a new study
    if (executor != null) {
      stop();
    }

    console.log("Setting up '${study.name}'...");

    // specify the [DataEndPoint] for this study.
    study.dataEndPoint = getDataEndpoint(DataEndPointType.PRINT);

    // add tasks to the study
    // note that in this version, don't start the sensors (accelerometer, etc. - they simpley generate too much data....
    //study.tasks.add(sensorTask);
    study.tasks.add(pedometerTask);
    study.tasks.add(hardwareTask);
    study.tasks.add(appTask);
    study.tasks.add(commTask);
    study.tasks.add(locationTask);

    // print the study to the console
    console.log(study.toString());

    // create a new executor
    executor = new StudyExecutor(study);
    // add `this` as a [ProbeListener] so we can update the log.
    // see the [notify()] method
    executor.addProbeListener(this);
    executor.initialize();
    executor.start();
    console.log("Sensing started ...");
  }

  /// Stop sensing.
  void stop() async {
    executor.stop();
    _study = null;
    console.log("Sensing stopped ...");
  }

  Study _study;
  Study get study {
    if (_study == null) {
      _study = new Study("1234", "user@dtu.dk", name: "Test study #1");
    }
    return _study;
  }

  /// Return a [DataEndPoint] of the specified type.
  DataEndPoint getDataEndpoint(String type) {
    assert(type != null);
    switch (type) {
      case DataEndPointType.PRINT:
        return new DataEndPoint(DataEndPointType.PRINT);
      case DataEndPointType.FILE:
        final FileDataEndPoint fileEndPoint =
            new FileDataEndPoint(DataEndPointType.FILE);
        fileEndPoint.bufferSize = 500 * 1000;
        fileEndPoint.zip = true;
        fileEndPoint.encrypt = false;
        return fileEndPoint;
      default:
        return new DataEndPoint(DataEndPointType.PRINT);
    }
  }

  Task _sensorTask;

  /// A task collecting sensor data from four sensors:
  /// - the accelerometer
  /// - the gyroscope
  /// - the light sensor
  ///
  /// Note that these sensors collects *a lot of data* and should be used *very* carefully.
  Task get sensorTask {
    if (_sensorTask == null) {
      _sensorTask = new Task("Sensor task");

      SensorMeasure am = new SensorMeasure(ProbeRegistry.ACCELEROMETER_MEASURE);
      am.name = 'Accelerometer';
      am.frequency = 8 * 1000; // once every 8 second
      am.duration = 500; // 500 ms
      _sensorTask.addMeasure(am);

      SensorMeasure gm = new SensorMeasure(ProbeRegistry.GYROSCOPE_MEASURE);
      gm.name = 'Gyroscope';
      gm.frequency = 8 * 1000; // once every 8 second
      gm.duration = 100; // 100 ms
      _sensorTask.addMeasure(gm);

      SensorMeasure lm = new SensorMeasure(ProbeRegistry.LIGHT_MEASURE);
      lm.name = 'Light';
      lm.frequency = 8 * 1000; // once every 8 second
      lm.duration = 100; // 500 ms
      _sensorTask.addMeasure(lm);
    }
    return _sensorTask;
  }

  Task _pedometerTask;

  /// A task collecting pedometer (step count) data on a regular basis.
  Task get pedometerTask {
    if (_pedometerTask == null) {
      _pedometerTask = new Task("Pedometer task");

      SensorMeasure pm = new SensorMeasure(ProbeRegistry.PEDOMETER_MEASURE);
      pm.name = 'Pedometer';
      pm.frequency = 5 * 1000; // Sample once every 5 seconds
      _pedometerTask.addMeasure(pm);
    }
    return _pedometerTask;
  }

  Task _hardwareTask;

  /// A task with three types of hardware measures:
  /// - free memory
  /// - battery
  /// - screen activity (lock, on, off)
  Task get hardwareTask {
    if (_hardwareTask == null) {
      _hardwareTask = new Task("Hardware Task");

      _hardwareTask.addMeasure(PollingProbeMeasure(ProbeRegistry.MEMORY_MEASURE,
          name: 'Polling of availabel memory', frequency: 2 * 1000));
      _hardwareTask.addMeasure(
          BatteryMeasure(ProbeRegistry.BATTERY_MEASURE, name: 'Battery'));
      _hardwareTask.addMeasure(ScreenMeasure(ProbeRegistry.SCREEN_MEASURE,
          name: 'Screen Lock/Unlock'));
    }
    return _hardwareTask;
  }

  Task _connectivityTask;

  /// A task with three types of connectivity measures:
  /// - connectivity (wifi, ...)
  /// - nearby bluetooth devices
  Task get connectivityTask {
    if (_connectivityTask == null) {
      _connectivityTask = new Task("Connectivity Task");

      _connectivityTask.addMeasure(ConnectivityMeasure(
          ProbeRegistry.CONNECTIVITY_MEASURE,
          name: 'Connectivity'));
      _connectivityTask.addMeasure(BluetoothMeasure(
          ProbeRegistry.BLUETOOTH_MEASURE,
          name: 'Nearby Bluetooth Devices'));
    }
    return _connectivityTask;
  }

  Task _appTask;

  /// A task collecting app information about installed apps on the device
  Task get appTask {
    if (_appTask == null) {
      _appTask = new Task("Application Task");
      PollingProbeMeasure am =
          new PollingProbeMeasure(ProbeRegistry.APPS_MEASURE);
      am.name = "Apps";
      am.frequency = 5 * 1000;
      _appTask.addMeasure(am);
    }
    return _appTask;
  }

  Task _commTask;

  /// A task collecting information about communication. So far only collecting sms info:
  /// - messages (sms) log from this device
  /// - an event every time a sms is recieved
  ///
  /// Works only on Android.
  Task get commTask {
    if (_commTask == null) {
      _commTask = new Task("Communication Task");

      TextMessageMeasure tm_1 = new TextMessageMeasure(
          ProbeRegistry.TEXT_MESSAGE_LOG_MEASURE,
          name: "Text Message Log");
      tm_1.collectBodyOfMessage = false;
      _commTask.addMeasure(tm_1);

      TextMessageMeasure tm_2 = new TextMessageMeasure(
          ProbeRegistry.TEXT_MESSAGE_MEASURE,
          name: "Text Messages");
      tm_2.collectBodyOfMessage = true;
      _commTask.addMeasure(tm_2);
    }
    return _commTask;
  }

  Task _locationTask;

  /// A task collecting location information.
  Task get locationTask {
    if (_locationTask == null) {
      _locationTask = new Task("Location Task");
      _locationTask.addMeasure(
          LocationMeasure(ProbeRegistry.LOCATION_MEASURE, name: 'Location'));
    }
    return _locationTask;
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  carp_mobile_sensing: ^0.2.1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter packages get

Alternatively, your editor might support flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:carp_mobile_sensing/carp_mobile_sensing.dart';
  
Version Uploaded Documentation Archive
0.2.1 Oct 21, 2018 Go to the documentation of carp_mobile_sensing 0.2.1 Download carp_mobile_sensing 0.2.1 archive
0.2.0 Oct 14, 2018 Go to the documentation of carp_mobile_sensing 0.2.0 Download carp_mobile_sensing 0.2.0 archive
0.1.1 Oct 13, 2018 Go to the documentation of carp_mobile_sensing 0.1.1 Download carp_mobile_sensing 0.1.1 archive
0.1.0 Oct 9, 2018 Go to the documentation of carp_mobile_sensing 0.1.0 Download carp_mobile_sensing 0.1.0 archive
Popularity:
Describes how popular the package is relative to other packages. [more]
32
Health:
Code health derived from static analysis. [more]
95
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
80
Overall:
Weighted score of the above. [more]
61
Learn more about scoring.

We analyzed this package on Oct 21, 2018, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.0.0
  • pana: 0.12.4
  • Flutter: 0.9.5

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Health suggestions

Fix lib/probes/sensors/sensor_probes.dart. (-1.99 points)

Analysis of lib/probes/sensors/sensor_probes.dart reported 4 hints:

line 17 col 9: The value of the field '_startTimer' isn't used.

line 18 col 9: The value of the field '_stopTimer' isn't used.

line 101 col 9: The value of the field '_startTimer' isn't used.

line 102 col 9: The value of the field '_stopTimer' isn't used.

Fix lib/domain/domain.g.dart. (-1 points)

Analysis of lib/domain/domain.g.dart reported 2 hints:

line 317 col 11: The function '_$CARPDatumFromJson' isn't used.

line 329 col 22: The function '_$CARPDatumToJson' isn't used.

Fix lib/probes/sensors/light_probe.dart. (-1 points)

Analysis of lib/probes/sensors/light_probe.dart reported 2 hints:

line 14 col 9: The value of the field '_startTimer' isn't used.

line 15 col 9: The value of the field '_stopTimer' isn't used.

Fix lib/domain/serialization.dart. (-0.50 points)

Analysis of lib/domain/serialization.dart reported 1 hint:

line 23 col 3: The method '_registerFromJson' isn't used.

Fix lib/probes/sensors/pedometer_probe.dart. (-0.50 points)

Analysis of lib/probes/sensors/pedometer_probe.dart reported 1 hint:

line 18 col 9: The value of the field '_periodicTimer' isn't used.

Maintenance suggestions

The description is too short. (-20 points)

Add more detail about the package, what it does and what is its target use case. Try to write at least 60 characters.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev <3.0.0
archive ^2.0.4 2.0.4
battery ^0.2.3 0.2.3
connectivity ^0.3.2 0.3.2
device_apps ^1.0.0 1.0.0
device_info ^0.2.1 0.2.1
flutter 0.0.0
flutter_blue ^0.4.1 0.4.1
json_annotation ^1.0.0 1.2.0
light ^0.0.3 0.0.3
location ^1.4.1 1.4.1
path_provider ^0.4.1 0.4.1
pedometer ^0.0.3 0.0.3
screen_state ^0.0.1 0.0.1
sensors ^0.3.4 0.3.5
sms ^0.2.4 0.2.4
system_info ^0.1.0 0.1.0
uuid ^1.0.0 1.0.3
Transitive dependencies
args 1.5.0
charcode 1.1.2
collection 1.14.11
convert 2.0.2
crypto 2.0.6
file_utils 0.1.1
fixnum 0.10.8
globbing 0.2.0
meta 1.1.6
path 1.6.2
protobuf 0.9.1 0.10.4
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
build_runner any
flutter_test
json_serializable ^1.0.0