Skip to content

Commit

Permalink
Refactored and corrected sorting. Replaced improper TreeMaps by corre…
Browse files Browse the repository at this point in the history
…ct lists of comparable objects.

Closes #36 and #38
  • Loading branch information
mh- committed Sep 1, 2020
1 parent 71cb4c1 commit d223bd8
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.tosl.coronawarncompanion.matchentries;

import org.tosl.coronawarncompanion.diagnosiskeys.DiagnosisKey;

public class DkAndMatchEntries implements Comparable<DkAndMatchEntries> {
public final DiagnosisKey dk;
public final MatchEntryContent.GroupedByDkMatchEntries matchEntries;

public DkAndMatchEntries(DiagnosisKey dk, MatchEntryContent.GroupedByDkMatchEntries matchEntries) {
this.dk = dk;
this.matchEntries = matchEntries;
}

@Override
public int compareTo(DkAndMatchEntries o) {
return this.matchEntries.getMinStartTimeStampUTC().compareTo(o.matchEntries.getMinStartTimeStampUTC());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,26 @@ public void add(Matcher.MatchEntry entry, DiagnosisKey dk) {
public static class GroupedByDkMatchEntries {
private final ArrayList<Matcher.MatchEntry> list = new ArrayList<>();
private int groupedByDkRpiCount = 0;
private Integer minStartTimeStampUTC = Integer.MAX_VALUE;

public int getGroupedByDkRpiCount() {
return groupedByDkRpiCount;
}

public Integer getMinStartTimeStampUTC() {
return minStartTimeStampUTC;
}

public ArrayList<Matcher.MatchEntry> getList() {
return list;
}

public void add(Matcher.MatchEntry entry) {
list.add(entry);
groupedByDkRpiCount++;
if (minStartTimeStampUTC > entry.startTimestampUTC) {
minStartTimeStampUTC = entry.startTimestampUTC;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import android.graphics.Color;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
Expand All @@ -53,12 +52,12 @@
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;

import static org.tosl.coronawarncompanion.tools.Utils.byteArrayToHexString;
import static org.tosl.coronawarncompanion.tools.Utils.getMillisFromSeconds;
Expand All @@ -78,21 +77,19 @@ public class MatchesRecyclerViewAdapter extends RecyclerView.Adapter<MatchesRecy
private static final int greenColor = Color.parseColor("#00FF00");
private final float textScalingFactor;

private final ArrayList<Pair<DiagnosisKey, MatchEntryContent.GroupedByDkMatchEntries>> mValues;
private final ArrayList<DkAndMatchEntries> mValues;
private final Context mContext;

private boolean showAllScans = false;

public MatchesRecyclerViewAdapter(DailyMatchEntries dailyMatchEntries, Context context) {
this.mContext = context;
this.mValues = new ArrayList<>();
TreeMap<Integer, Pair<DiagnosisKey, MatchEntryContent.GroupedByDkMatchEntries>> treeMap = new TreeMap<>();
// Sorted TreeMap <startTimestampUTC of the first entry, Pair <DK, matchEntries> >
for (Map.Entry<DiagnosisKey, MatchEntryContent.GroupedByDkMatchEntries> entry :
dailyMatchEntries.getMap().entrySet()) {
treeMap.put(entry.getValue().getList().get(0).startTimestampUTC, new Pair<>(entry.getKey(), entry.getValue()));
mValues.add(new DkAndMatchEntries(entry.getKey(), entry.getValue()));
}
mValues.addAll(treeMap.values());
Collections.sort(mValues);
DisplayMetrics metrics = this.mContext.getResources().getDisplayMetrics();
this.textScalingFactor = metrics.scaledDensity/metrics.density;
this.lineColor = resolveColorAttr(android.R.attr.textColorSecondary, context);
Expand All @@ -109,9 +106,9 @@ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
@SuppressWarnings("deprecation")
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mMatchEntriesPair = mValues.get(position);
DiagnosisKey dk = holder.mMatchEntriesPair.first;
MatchEntryContent.GroupedByDkMatchEntries groupedByDkMatchEntries = holder.mMatchEntriesPair.second;
holder.mDkAndMatchEntries = mValues.get(position);
DiagnosisKey dk = holder.mDkAndMatchEntries.dk;
MatchEntryContent.GroupedByDkMatchEntries groupedByDkMatchEntries = holder.mDkAndMatchEntries.matchEntries;

ArrayList<Matcher.MatchEntry> list = groupedByDkMatchEntries.getList();

Expand Down Expand Up @@ -277,9 +274,24 @@ public static MatchEntryDetails getMatchEntryDetails(ArrayList<Matcher.MatchEntr
result.minTxPower = Byte.MAX_VALUE;
result.maxTxPower = Byte.MIN_VALUE;

TreeMap<Integer, Integer> dataPointsInterimMap = new TreeMap<>();
class InterimDataPoint implements Comparable<InterimDataPoint> {
public final Integer timestampLocalTZ;
public final Integer attenuation;

// First step: Create a "flat" sorted list (TreeMap) from all scan records from all matchEntries
private InterimDataPoint(int timestampLocalTZ, int attenuation) {
this.timestampLocalTZ = timestampLocalTZ;
this.attenuation = attenuation;
}

@Override
public int compareTo(InterimDataPoint o) {
return timestampLocalTZ.compareTo(o.timestampLocalTZ);
}
}

ArrayList<InterimDataPoint> dataPointsInterimList = new ArrayList<>();

// First step: Create a "flat" sorted list from all scan records from all matchEntries
for (Matcher.MatchEntry matchEntry : list) { // process each matchEntry separately
for (ContactRecordsProtos.ScanRecord scanRecord : matchEntry.contactRecords.getRecordList()) {
byte[] aem = xorTwoByteArrays(scanRecord.getAem().toByteArray(), matchEntry.aemXorBytes);
Expand All @@ -296,7 +308,7 @@ public static MatchEntryDetails getMatchEntryDetails(ArrayList<Matcher.MatchEntr
int timestampLocalTZ = scanRecord.getTimestamp() + timeZoneOffset;

// store to temporary buffers:
dataPointsInterimMap.put(timestampLocalTZ, attenuation);
dataPointsInterimList.add(new InterimDataPoint(timestampLocalTZ, attenuation));

// if found, store max/min values
if (result.minTxPower > txPower) {
Expand All @@ -319,6 +331,7 @@ public static MatchEntryDetails getMatchEntryDetails(ArrayList<Matcher.MatchEntr
}
}
}
Collections.sort(dataPointsInterimList);

// reduce timestamp to "day0", to improve resolution within the float x value of the graph:
int timestampMinOffset= (minTimestampLocalTZ / (24*3600)) * (24*3600);
Expand All @@ -331,12 +344,12 @@ public static MatchEntryDetails getMatchEntryDetails(ArrayList<Matcher.MatchEntr
int lastTimestampLocalTZDay0 = 0;
int localMinAttenuation = Integer.MAX_VALUE;

int numLastScanRecord = dataPointsInterimMap.size() - 1;
int numLastScanRecord = dataPointsInterimList.size() - 1;
int i = 0;
for(Map.Entry<Integer, Integer> mapEntry : dataPointsInterimMap.entrySet()) {
// iterate over sorted TreeMap
int timestampLocalTZDay0 = mapEntry.getKey() - timestampMinOffset;
int attenuation = mapEntry.getValue();
for (InterimDataPoint interimDataPoint : dataPointsInterimList) {
// iterate over sorted ArrayList
int timestampLocalTZDay0 = interimDataPoint.timestampLocalTZ - timestampMinOffset;
int attenuation = interimDataPoint.attenuation;

// Second step: look for a break (>= pauseThresholdSeconds)
// suppress break detection at the very first entry
Expand Down Expand Up @@ -559,7 +572,7 @@ public static class ViewHolder extends RecyclerView.ViewHolder {
public final TextView mTextViewRiskDetails;
public final TextView mTextViewTxPower;
public final LineChart mChartView;
public Pair<DiagnosisKey, MatchEntryContent.GroupedByDkMatchEntries> mMatchEntriesPair;
public DkAndMatchEntries mDkAndMatchEntries;

public ViewHolder(View view) {
super(view);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@
public class RpiList {
private static final String TAG = "RpiList";

@SuppressWarnings("FieldCanBeLocal")
private final boolean addFakeMatches = false; // TODO: ONLY FOR TESTING, MUST BE SET TO FALSE FOR RELEASES!

private final Map<Integer, ListsPerDayUTC> mapOfDaysUTCAndListsOfRPIs; // daysSinceEpochUTC, ListsPerDayUTC
private final Map<Integer, Integer> mapOfDailyCountsLocalTZ; // daysSinceEpochLocalTZ, numberOfEntries

private final int timeZoneOffsetSeconds;
//private final Random rand;
private final Random rand;

public static class ListsPerDayUTC {
public final HashMap<RpiBytes, RpiEntry> rpiEntries = new HashMap<>(2048); // RpiEntries
Expand Down Expand Up @@ -124,7 +127,7 @@ public RpiList() {
mapOfDaysUTCAndListsOfRPIs = new HashMap<>();
mapOfDailyCountsLocalTZ = new TreeMap<>();
timeZoneOffsetSeconds = CWCApplication.getTimeZoneOffsetSeconds();
//rand = new Random(); // not very random, but sufficient for the use case here
rand = new Random(); // not very random, but sufficient for the use case here
}

public void addEntry(Integer daysSinceEpochUTC, byte[] rpiBytes, ContactRecordsProtos.ContactRecords contactRecords) {
Expand Down Expand Up @@ -188,6 +191,7 @@ public Integer getRpiCountForDaysSinceEpochLocalTZ(Integer daysSinceEpochLocalTZ
public RpiEntry searchForRpiOnDaySinceEpochUTCWith2HoursTolerance(Crypto.RpiWithInterval searchRpiWithInterval) {
int daysSinceEpochUTC = getDaysSinceEpochFromENIN(searchRpiWithInterval.intervalNumber);
RpiEntry matchingRpiEntry = null;
//noinspection ConstantConditions
if (searchRpiWithInterval != null) {
RpiBytes rpiBytes = new RpiBytes(searchRpiWithInterval.rpiBytes);

Expand Down Expand Up @@ -238,26 +242,28 @@ public RpiEntry searchForRpiOnDaySinceEpochUTCWith2HoursTolerance(Crypto.RpiWith
}
}

/*
// Add some random matches, for test purposes only!
// TODO: THIS MUST NOT BE ACTIVE FOR RELEASES!
if (rand.nextInt(100000) >= 99995) {
ListsPerDayUTC listsPerDayUTC = mapOfDaysUTCAndListsOfRPIs.get(daysSinceEpochUTC);
if (listsPerDayUTC != null) {
int numEntries = listsPerDayUTC.rpiEntries.size();
int randomPos = rand.nextInt(numEntries)+1;
// The following random selection process is not 100% correct, but sufficient.
Iterator<RpiEntry> it = listsPerDayUTC.rpiEntries.values().iterator();
while (it.hasNext() && randomPos > 0) {
matchingRpiEntry = it.next();
randomPos--;
if (addFakeMatches) {
// Add some random matches, for test purposes only!
// TODO: THIS MUST NOT BE ACTIVE FOR RELEASES!
if (rand.nextInt(100000) >= 99955) {
ListsPerDayUTC listsPerDayUTC = mapOfDaysUTCAndListsOfRPIs.get(daysSinceEpochUTC);
if (listsPerDayUTC != null) {

int numEntries = listsPerDayUTC.rpiEntries.size();
int randomPos = rand.nextInt(numEntries) + 1;
// The following random selection process is not 100% correct, but sufficient.
Iterator<RpiEntry> it = listsPerDayUTC.rpiEntries.values().iterator();
while (it.hasNext() && randomPos > 0) {
matchingRpiEntry = it.next();
randomPos--;
}
if (matchingRpiEntry != null) {
Log.d(TAG, "Reporting fake match: matchingRpiEntry.startTimeStampUTC: " +
matchingRpiEntry.startTimeStampUTC);
}
}
Log.d(TAG, "Reporting fake match: matchingRpiEntry.startTimeStampUTC: " +
matchingRpiEntry.startTimeStampUTC);
}
}
*/
}
return matchingRpiEntry;
}
Expand Down

0 comments on commit d223bd8

Please sign in to comment.