Skip to content

Commit

Permalink
Merge pull request #198 from adamfowleruk/feature-191
Browse files Browse the repository at this point in the history
Nullability modifications - requires review
  • Loading branch information
adamfowleruk authored Jun 1, 2021
2 parents d399ade + 6e2a514 commit d47ae6f
Show file tree
Hide file tree
Showing 111 changed files with 879 additions and 299 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
distribution: 'adopt'

- name: Publish to the Maven Central Repository
run: ./gradlew publish
run: ./gradlew publish --stacktrace --info
env:
MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
Expand Down
15 changes: 3 additions & 12 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions herald/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

group = 'io.heraldprox'
version = '2.0.0-beta3'
version = '2.0.0-beta4'

apply plugin: 'com.android.library'

Expand All @@ -17,7 +17,7 @@ android {
minSdkVersion 21
targetSdkVersion 30
versionCode 2
versionName "2.0.0-beta3"
versionName "2.0.0-beta4"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
Expand Down
6 changes: 5 additions & 1 deletion herald/src/main/java/io/heraldprox/herald/sensor/Device.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.heraldprox.herald.sensor;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import io.heraldprox.herald.sensor.datatype.TargetIdentifier;

import java.util.Date;
Expand All @@ -8,6 +11,7 @@ public class Device {
/// Device registration timestamp
public final Date createdAt;
/// Last time anything changed, e.g. attribute update
@Nullable
public Date lastUpdatedAt = null;
/// Ephemeral device identifier, e.g. peripheral identifier UUID
public final TargetIdentifier identifier;
Expand All @@ -18,7 +22,7 @@ public Device(TargetIdentifier identifier) {
this.identifier = identifier;
}

public Device(Device device, TargetIdentifier identifier) {
public Device(@NonNull Device device, TargetIdentifier identifier) {
this.createdAt = device.createdAt;
this.lastUpdatedAt = new Date();
this.identifier = identifier;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

package io.heraldprox.herald.sensor;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import io.heraldprox.herald.sensor.datatype.Data;
import io.heraldprox.herald.sensor.datatype.LegacyPayloadData;
import io.heraldprox.herald.sensor.datatype.PayloadData;
Expand All @@ -14,11 +17,14 @@
/// Payload data supplier, e.g. BeaconCodes in C19X and BroadcastPayloadSupplier in Sonar.
public interface PayloadDataSupplier {
/// Legacy payload supplier callback - for those transitioning their apps to Herald. Note: Device may be null if Payload in use is same for all receivers
@Nullable
LegacyPayloadData legacyPayload(PayloadTimestamp timestamp, Device device);

/// Get payload for given timestamp. Use this for integration with any payload generator, e.g. BeaconCodes or SonarBroadcastPayloadService
@NonNull
PayloadData payload(PayloadTimestamp timestamp, Device device);

/// Parse raw data into payloads
@NonNull
List<PayloadData> payload(Data data);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import android.content.Context;

import androidx.annotation.NonNull;

import io.heraldprox.herald.sensor.ble.BLESensorConfiguration;
import io.heraldprox.herald.sensor.ble.ConcreteBLESensor;
import io.heraldprox.herald.sensor.data.CalibrationLog;
Expand All @@ -22,16 +24,18 @@

/// Sensor array for combining multiple detection and tracking methods.
public class SensorArray implements Sensor {
@NonNull
private final Context context;
private final SensorLogger logger = new ConcreteSensorLogger("Sensor", "SensorArray");
private final List<Sensor> sensorArray = new ArrayList<>();

private final PayloadData payloadData;
public final static String deviceDescription = android.os.Build.MODEL + " (Android " + android.os.Build.VERSION.SDK_INT + ")";

@NonNull
private final ConcreteBLESensor concreteBleSensor;

public SensorArray(final Context context, PayloadDataSupplier payloadDataSupplier) {
public SensorArray(@NonNull final Context context, @NonNull PayloadDataSupplier payloadDataSupplier) {
this.context = context;
// Ensure logger has been initialised (should have happened in AppDelegate already)
ConcreteSensorLogger.context(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

import android.content.Context;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import io.heraldprox.herald.sensor.DefaultSensorDelegate;
import io.heraldprox.herald.sensor.data.ConcreteSensorLogger;
import io.heraldprox.herald.sensor.data.SensorLogger;
Expand All @@ -32,16 +35,18 @@
/// of encounters for on-device or centralised matching.
public class Interactions extends DefaultSensorDelegate {
private final SensorLogger logger = new ConcreteSensorLogger("Sensor", "Analysis.EncounterLog");
@Nullable
private final TextFile textFile;
private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.UK);
@NonNull
private List<Encounter> encounters = new ArrayList<>();

public Interactions() {
textFile = null;
dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
}

public Interactions(final Context context, final String filename) {
public Interactions(@NonNull final Context context, @NonNull final String filename) {
textFile = new TextFile(context, filename);
if (textFile.empty()) {
textFile.write("time,proximity,unit,payload");
Expand All @@ -57,15 +62,16 @@ public Interactions(final Context context, final String filename) {
}
}

public synchronized void append(Encounter encounter) {
public synchronized void append(@NonNull Encounter encounter) {
if (textFile != null) {
textFile.write(encounter.csvString());
}
encounters.add(encounter);
}

/// Get encounters from start date (inclusive) to end date (exclusive)
public synchronized List<Encounter> subdata(Date start, Date end) {
@NonNull
public synchronized List<Encounter> subdata(@NonNull Date start, @NonNull Date end) {
final long startTime = start.getTime();
final long endTime = end.getTime();
final List<Encounter> subdata = new ArrayList<>();
Expand All @@ -82,7 +88,8 @@ public synchronized List<Encounter> subdata(Date start, Date end) {
}

/// Get all encounters from start date (inclusive)
public synchronized List<Encounter> subdata(Date start) {
@NonNull
public synchronized List<Encounter> subdata(@NonNull Date start) {
final long startTime = start.getTime();
final List<Encounter> subdata = new ArrayList<>();
for (Encounter encounter : encounters) {
Expand All @@ -98,7 +105,7 @@ public synchronized List<Encounter> subdata(Date start) {
}

/// Remove all log records before date (exclusive). Use this function to implement data retention policy.
public synchronized void remove(Date before) {
public synchronized void remove(@NonNull Date before) {
final StringBuilder content = new StringBuilder();
final List<Encounter> subdata = subdata(before);
for (Encounter encounter : subdata) {
Expand Down Expand Up @@ -134,6 +141,7 @@ public InteractionsForTime(Date time, Map<PayloadData,List<Proximity>> context)
this.time = time;
this.context = context;
}
@NonNull
@Override
public String toString() {
return "InteractionsForTime{" +
Expand All @@ -142,10 +150,12 @@ public String toString() {
'}';
}
}
public final static List<InteractionsForTime> reduceByTime(List<Encounter> encounters) {
@NonNull
public final static List<InteractionsForTime> reduceByTime(@NonNull List<Encounter> encounters) {
return reduceByTime(encounters, TimeInterval.minute);
}
public final static List<InteractionsForTime> reduceByTime(List<Encounter> encounters, TimeInterval duration) {
@NonNull
public final static List<InteractionsForTime> reduceByTime(@NonNull List<Encounter> encounters, @NonNull TimeInterval duration) {
final List<InteractionsForTime> result = new ArrayList<>();
final long divisor = duration.value * 1000;
long currentTimeWindow = 0;
Expand Down Expand Up @@ -184,6 +194,7 @@ public InteractionsForTarget(Date lastSeenAt, TimeInterval duration, Sample prox
this.duration = duration;
this.proximity = proximity;
}
@NonNull
@Override
public String toString() {
return "InteractionsForTarget{" +
Expand All @@ -193,7 +204,8 @@ public String toString() {
'}';
}
}
public final static Map<PayloadData, InteractionsForTarget> reduceByTarget(List<Encounter> encounters) {
@NonNull
public final static Map<PayloadData, InteractionsForTarget> reduceByTarget(@NonNull List<Encounter> encounters) {
final Map<PayloadData, InteractionsForTarget> targets = new HashMap<>();
for (Encounter encounter : encounters) {
if (encounter.proximity.unit != ProximityMeasurementUnit.RSSI) {
Expand Down Expand Up @@ -222,10 +234,12 @@ public final static Map<PayloadData, InteractionsForTarget> reduceByTarget(List<
}

/// Histogram of exposure offers an esimate of exposure, while avoiding resolution of actual payload identity.
public final static Map<Double,TimeInterval> reduceByProximity(List<Encounter> encounters) {
@NonNull
public final static Map<Double,TimeInterval> reduceByProximity(@NonNull List<Encounter> encounters) {
return reduceByProximity(encounters, ProximityMeasurementUnit.RSSI, 1d);
}
public final static Map<Double,TimeInterval> reduceByProximity(List<Encounter> encounters, ProximityMeasurementUnit unit, Double bin) {
@NonNull
public final static Map<Double,TimeInterval> reduceByProximity(@NonNull List<Encounter> encounters, ProximityMeasurementUnit unit, Double bin) {
final Map<PayloadData,Date> targets = new HashMap<>();
final Map<Double,TimeInterval> histogram = new HashMap<>();
for (Encounter encounter : encounters) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

package io.heraldprox.herald.sensor.analysis;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class Sample {
protected long n = 0;
protected double m1 = 0, m2 = 0, m3 = 0, m4 = 0;
Expand Down Expand Up @@ -42,7 +45,7 @@ public void add(final double x, final long f) {
add(new Sample(x, f));
}

public void add(final Sample distribution) {
public void add(@NonNull final Sample distribution) {
if (0 == n) {
n = distribution.n;
m1 = distribution.m1;
Expand Down Expand Up @@ -86,6 +89,7 @@ public long count() {
return n;
}

@Nullable
public Double mean() {
if (n > 0) {
return m1;
Expand All @@ -94,6 +98,7 @@ public Double mean() {
}
}

@Nullable
public Double variance() {
if (n > 1) {
return m2 / (n - 1d);
Expand All @@ -102,6 +107,7 @@ public Double variance() {
}
}

@Nullable
public Double standardDeviation() {
if (n > 1) {
return StrictMath.sqrt(m2 / (n - 1d));
Expand All @@ -110,6 +116,7 @@ public Double standardDeviation() {
}
}

@Nullable
public Double min() {
if (n > 0) {
return min;
Expand All @@ -118,6 +125,7 @@ public Double min() {
}
}

@Nullable
public Double max() {
if (n > 0) {
return max;
Expand All @@ -127,13 +135,15 @@ public Double max() {
}

/// Estimate distance between this sample's distribution and another sample's distribution, 1 means identical and 0 means completely different.
public Double distance(final Sample sample) {
@Nullable
public Double distance(@NonNull final Sample sample) {
return bhattacharyyaDistance(this, sample);
}

/// Bhattacharyya distance between two distributions estimate the likelihood that the two distributions are the same.
/// bhattacharyyaDistance = 1 means the two distributions are identical; value = 0 means they are different.
private final static Double bhattacharyyaDistance(final Sample d1, final Sample d2) {
@Nullable
private final static Double bhattacharyyaDistance(@NonNull final Sample d1, @NonNull final Sample d2) {
final Double v1 = d1.variance();
final Double v2 = d2.variance();
final Double m1 = d1.mean();
Expand Down Expand Up @@ -161,6 +171,7 @@ private final static Double bhattacharyyaDistance(final Sample d1, final Sample
return Dbc;
}

@NonNull
@Override
public String toString() {
return "[count=" + count() + ",mean=" + mean() + ",sd=" + standardDeviation() + ",min=" + min() + ",max=" + max() + "]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package io.heraldprox.herald.sensor.analysis;

import androidx.annotation.NonNull;

import io.heraldprox.herald.sensor.datatype.Encounter;
import io.heraldprox.herald.sensor.datatype.PayloadData;
import io.heraldprox.herald.sensor.datatype.Proximity;
Expand All @@ -23,7 +25,7 @@ public class SocialDistance extends Interactions {
// MARK:- SensorDelegate

@Override
public void sensor(SensorType sensor, Proximity didMeasure, TargetIdentifier fromTarget) {
public void sensor(SensorType sensor, Proximity didMeasure, @NonNull TargetIdentifier fromTarget) {
final Encounter encounter = new Encounter(didMeasure, new PayloadData(fromTarget.value.getBytes()));
if (encounter.isValid()) {
append(encounter);
Expand All @@ -41,10 +43,10 @@ public void sensor(SensorType sensor, Proximity didMeasure, TargetIdentifier fro
/// or RSSI less than excludeRssiBelow in every minute.
/// - measuredPower defines RSSI at 1 metre
/// - excludeRssiBelow defines minimum RSSI to include in analysis
public Double scoreByProximity(Date start, Date end) {
public Double scoreByProximity(@NonNull Date start, @NonNull Date end) {
return scoreByProximity(start, end, -32d, -65d);
}
public Double scoreByProximity(Date start, Date end, double measuredPower, double excludeRssiBelow) {
public Double scoreByProximity(@NonNull Date start, @NonNull Date end, double measuredPower, double excludeRssiBelow) {
// Get encounters over time period
final List<Encounter> encounters = subdata(start, end);
// Get number of minutes in time period
Expand Down Expand Up @@ -84,10 +86,10 @@ public Double scoreByProximity(Date start, Date end, double measuredPower, doubl

/// Calculate social distance score based on number of different devices per 1 minute time window over duration
/// A score of 1.0 means 6 or more in every minute, score of 0.0 means no device in every minute.
public Double scoreByTarget(Date start, Date end) {
public Double scoreByTarget(@NonNull Date start, @NonNull Date end) {
return scoreByTarget(start, end, 6, -65);
}
public Double scoreByTarget(Date start, Date end, int maximumDeviceCount, double excludeRssiBelow) {
public Double scoreByTarget(@NonNull Date start, @NonNull Date end, int maximumDeviceCount, double excludeRssiBelow) {
// Get encounters over time period
final List<Encounter> encounters = subdata(start, end);
// Get number of minutes in time period
Expand Down
Loading

0 comments on commit d47ae6f

Please sign in to comment.