From 3b4e70ca3fbc8b8ec84bdb7184ddb39804a4a4d8 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Tue, 22 Nov 2016 22:26:53 -0600 Subject: [PATCH 01/18] Updating build configuration --- build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index b0998b5..309523a 100755 --- a/build.gradle +++ b/build.gradle @@ -45,11 +45,11 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:25.0.0' - compile 'com.android.support:recyclerview-v7:25.0.0' - compile 'com.android.support:cardview-v7:25.0.0' - compile 'com.google.android.gms:play-services-location:9.8.00' - compile 'com.google.android.gms:play-services-maps:9.8.00' + compile 'com.android.support:appcompat-v7:25.0.1' + compile 'com.android.support:recyclerview-v7:25.0.1' + compile 'com.android.support:cardview-v7:25.0.1' + compile 'com.google.android.gms:play-services-location:10.0.0' + compile 'com.google.android.gms:play-services-maps:10.0.0' compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'commons-io:commons-io:2.4' compile 'commons-codec:commons-codec:1.10' From 791502d82b93a38d9e027779f14fac6b3ed7c534 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Thu, 1 Dec 2016 07:27:35 -0600 Subject: [PATCH 02/18] Cleaned up conditions for uploading. * Bumped dependency version numbers. * Added method for dynamically setting user ID. --- build.gradle | 10 +++++----- .../transmitters/HttpTransmitter.java | 20 ++++++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index e3e915d..308c884 100755 --- a/build.gradle +++ b/build.gradle @@ -48,11 +48,11 @@ android { compile 'com.android.support:appcompat-v7:25.0.1' compile 'com.android.support:recyclerview-v7:25.0.1' compile 'com.android.support:cardview-v7:25.0.1' - compile 'com.google.android.gms:play-services-location:10.0.0' - compile 'com.google.android.gms:play-services-maps:10.0.0' - compile 'com.google.android.gms:play-services-nearby:10.0.0' - compile 'com.google.android.gms:play-services-places:10.0.0' - compile 'com.google.android.gms:play-services-awareness:10.0.0' + compile 'com.google.android.gms:play-services-location:10.0.1' + compile 'com.google.android.gms:play-services-maps:10.0.1' + compile 'com.google.android.gms:play-services-nearby:10.0.1' + compile 'com.google.android.gms:play-services-places:10.0.1' + compile 'com.google.android.gms:play-services-awareness:10.0.1' compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'commons-io:commons-io:2.4' compile 'commons-codec:commons-codec:1.10' diff --git a/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java b/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java index 93f74d3..ab65bc8 100755 --- a/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java +++ b/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java @@ -136,13 +136,20 @@ else if (!options.containsKey(HttpTransmitter.USER_ID)) { Generators.getInstance(this.mContext).addNewDataPointListener(this); } - private boolean shouldAttemptUpload() { + private boolean shouldAttemptUpload(boolean force) { + if (force) { + return true; + } if (this.mWifiOnly) { - return DeviceInformation.wifiAvailable(this.mContext); + if (DeviceInformation.wifiAvailable(this.mContext) == false) { + return false; + } } if (this.mChargingOnly) { - return DeviceInformation.isPluggedIn(this.mContext); + if (DeviceInformation.isPluggedIn(this.mContext) == false) { + return false; + } } return true; @@ -156,7 +163,7 @@ public void transmit(boolean force) { this.mLastAttempt = 0; } - if (now - this.mLastAttempt < this.mUploadInterval || !this.shouldAttemptUpload()) { + if (now - this.mLastAttempt < this.mUploadInterval || !this.shouldAttemptUpload(force)) { return; } @@ -314,7 +321,6 @@ private int transmitHttpPayload(String payload) { builder = builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"payload\""), RequestBody.create(null, payload)); - RequestBody requestBody = builder.build(); if (this.mUserAgent == null) { @@ -591,6 +597,10 @@ else if (value instanceof Bundle) { } } + public void setUserId(String userId) { + this.mUserId = userId; + } + public static class IncompleteConfigurationException extends RuntimeException { public IncompleteConfigurationException(String message) { super(message); From bb50e09c088990901259d0125c9af44eab2de24b Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Sun, 4 Dec 2016 23:35:20 -0600 Subject: [PATCH 03/18] Added functionality and infrastructure for customizing data generators --- .../generators/Generators.java | 27 +++++++++++++++++ .../generators/device/Location.java | 30 ++++++++++++++++--- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java index fb9e412..c48087b 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generators.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java @@ -240,6 +240,33 @@ public void broadcastLatestDataPoints() { } } + public Generator getGenerator(String className) { + Log.e("BB", "GENERATOR FIND START"); + for (String name : this.mActiveGenerators) { + Log.e("BB", "GENERATOR NAME: " + name); + } + Log.e("BB", "GENERATOR FIND END"); + + if (this.mActiveGenerators.contains(className)) { + try { + Class probeClass = (Class) Class.forName(className); + + Method getInstance = probeClass.getDeclaredMethod("getInstance", Context.class); + return (Generator) getInstance.invoke(null, this.mContext); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + + return null; + } + private static class GeneratorsHolder { public static Generators instance = new Generators(); } diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Location.java b/src/com/audacious_software/passive_data_kit/generators/device/Location.java index 814c5ad..5b3abf4 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/Location.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/Location.java @@ -73,8 +73,11 @@ public class Location extends Generator implements GoogleApiClient.ConnectionCal private static Location sInstance = null; private GoogleApiClient mGoogleApiClient = null; private android.location.Location mLastLocation = null; + private long mUpdateInterval = 60000; public static Location getInstance(Context context) { + Log.e("BB", "SHARED LOCATION INSTANCE: " + Location.sInstance); + if (Location.sInstance == null) { Location.sInstance = new Location(context.getApplicationContext()); } @@ -114,7 +117,6 @@ else if (Location.useGoogleLocationServices(me.mContext)) me.mGoogleApiClient = builder.build(); me.mGoogleApiClient.connect(); } - } else { @@ -130,6 +132,13 @@ else if (Location.useGoogleLocationServices(me.mContext)) Generators.getInstance(this.mContext).registerCustomViewClass(Location.GENERATOR_IDENTIFIER, Location.class); } + private void stopGenerator() { + if (this.mGoogleApiClient != null) { + this.mGoogleApiClient.disconnect(); + this.mGoogleApiClient = null; + } + } + public static boolean useGoogleLocationServices(Context context) { SharedPreferences prefs = Generators.getInstance(context).getSharedPreferences(context); @@ -206,13 +215,15 @@ public void run() { return actions; } - @Override public void onConnected(Bundle bundle) { final LocationRequest request = new LocationRequest(); request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); - request.setInterval(60000); + Log.e("BB", "USING INTERVAL: " + this.mUpdateInterval); + + request.setFastestInterval(this.mUpdateInterval); + request.setInterval(this.mUpdateInterval); if (this.mGoogleApiClient != null && this.mGoogleApiClient.isConnected()) { if (ContextCompat.checkSelfPermission(this.mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { @@ -223,6 +234,8 @@ public void onConnected(Bundle bundle) { @Override public void onConnectionSuspended(int i) { + Log.e("BB", "DISCONNECTING"); + if (this.mGoogleApiClient != null && this.mGoogleApiClient.isConnected()) LocationServices.FusedLocationApi.removeLocationUpdates(this.mGoogleApiClient, this); } @@ -239,6 +252,8 @@ public void onLocationChanged(android.location.Location location) { long now = System.currentTimeMillis(); + Log.e("BB", "LOCATION UPDATE"); + Bundle bundle = new Bundle(); bundle.putDouble(Location.LATITUDE_KEY, location.getLatitude()); @@ -380,7 +395,7 @@ public android.location.Location getLastKnownLocation() { android.location.Location last = null; - if (ContextCompat.checkSelfPermission(this.mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && + if (ContextCompat.checkSelfPermission(this.mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this.mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { Log.e("FC", "LOCATION PERMISSIONS GRANTED..."); @@ -402,4 +417,11 @@ public android.location.Location getLastKnownLocation() { public static void broadcastLatestDataPoint(Context context) { Generators.getInstance(context).transmitData(Location.GENERATOR_IDENTIFIER, new Bundle()); } + + public void setUpdateInterval(long interval) { + this.mUpdateInterval = interval; + + this.stopGenerator(); + this.startGenerator(); + } } From 1c1cc4942e8337c244b8e5f0b0f4328163a729c8 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Tue, 13 Dec 2016 13:38:28 -0600 Subject: [PATCH 04/18] Overhauled data generator data storage. * Added text-messaging data generator. --- AndroidManifest.xml | 1 + build.gradle | 5 +- res/drawable-hdpi/ic_text_messages.png | Bin 0 -> 1363 bytes res/drawable-mdpi/ic_text_messages.png | Bin 0 -> 1244 bytes res/drawable-xhdpi/ic_text_messages.png | Bin 0 -> 1426 bytes res/drawable-xxhdpi/ic_text_messages.png | Bin 0 -> 1634 bytes res/drawable-xxxhdpi/ic_text_messages.png | Bin 0 -> 1588 bytes res/drawable/ic_location_heatmap_marker.xml | 6 + res/layout/card_generator_location_google.xml | 30 +- res/layout/card_generator_phone_calls.xml | 9 +- res/layout/card_generator_screen_state.xml | 9 +- res/layout/card_generator_text_messages.xml | 134 +++++ res/values/databases.xml | 15 + res/values/generators.xml | 27 + res/values/strings.xml | 6 +- .../passive_data_kit/PassiveDataKit.java | 15 + .../passive_data_kit/PhoneUtililties.java | 30 ++ .../activities/DataStreamActivity.java | 30 +- .../generators/DataPointsAdapter.java | 117 +++-- .../generators/Generator.java | 85 +++- .../generators/Generators.java | 84 ++-- .../generators/communication/PhoneCalls.java | 348 ++++++++----- .../communication/TextMessages.java | 469 ++++++++++++++++++ .../generators/device/Location.java | 282 ++++++++--- .../generators/device/ScreenState.java | 308 +++++++----- .../generators/services/GoogleAwareness.java | 6 + .../generators/wearables/MicrosoftBand.java | 8 +- .../transmitters/HttpTransmitter.java | 22 +- 28 files changed, 1554 insertions(+), 492 deletions(-) create mode 100755 res/drawable-hdpi/ic_text_messages.png create mode 100755 res/drawable-mdpi/ic_text_messages.png create mode 100755 res/drawable-xhdpi/ic_text_messages.png create mode 100755 res/drawable-xxhdpi/ic_text_messages.png create mode 100755 res/drawable-xxxhdpi/ic_text_messages.png create mode 100755 res/drawable/ic_location_heatmap_marker.xml create mode 100755 res/layout/card_generator_text_messages.xml create mode 100755 res/values/databases.xml create mode 100755 src/com/audacious_software/passive_data_kit/PhoneUtililties.java create mode 100755 src/com/audacious_software/passive_data_kit/generators/communication/TextMessages.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3b7277a..5dd65d6 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -4,6 +4,7 @@ + diff --git a/build.gradle b/build.gradle index 308c884..c571e90 100755 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.android.tools.build:gradle:2.2.3' } } @@ -17,7 +17,7 @@ repositories { android { compileSdkVersion 25 - buildToolsVersion "25" + buildToolsVersion "25.0.2" useLibrary 'org.apache.http.legacy' @@ -53,6 +53,7 @@ android { compile 'com.google.android.gms:play-services-nearby:10.0.1' compile 'com.google.android.gms:play-services-places:10.0.1' compile 'com.google.android.gms:play-services-awareness:10.0.1' + compile 'com.google.maps.android:android-maps-utils:0.4' compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'commons-io:commons-io:2.4' compile 'commons-codec:commons-codec:1.10' diff --git a/res/drawable-hdpi/ic_text_messages.png b/res/drawable-hdpi/ic_text_messages.png new file mode 100755 index 0000000000000000000000000000000000000000..ef5d391720158ab483952d80eb3716bc78560ea4 GIT binary patch literal 1363 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBBuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFl%InM3hAM`dB6B=jtVb)aX^@765fKFxc2v6eK2Rr0UTq__OB&@Hb09I0xZL0)vRD^GUf^&XRs)DJWnQpS7v4w)UrJkXw zrG=4+j)IYap_#scrM{twu7RPIfu)s!p#l^r0c|TvNwW%aaf8|g1^l#~=$>Fbx5 zm+O@q>*W`v>l<2HTIw4Z=^Gj80#)c1SLT%@R_NvxE5l51Ni9w;$}A|!%+FH*nV6WA zUs__T1av9H3%LbwWAlok!2}F2{ffi_eM3D1ke6TzeSPsO&CP|YE-nd5MYtEM!Nnn! z1*!T$sm1xFMajU3OH&3}Rbb^@l$uzQUlfv`p92fUfQdzUH`+b07r4*z=(Yi43VS5;M;nE(9+AX4eH0@yc=SH=ZyTpP219 zVP3qF^$Q*Wc}a)kf7l-FJQ2vI^=a{fmq+Dh{fJm-V)lVMi({#(jmCA`hHt0+7OB5F z?jXZ)WYUES_OFwzHb`ud@A$mX*3av-VSwxpkynm$cD}KkQrC3Y(9z*fq5_v&m`)$( zvZcu$nRPM?N>5A(DwSziwwC|Pi98>cU$>UO5H&ht@j^i0H}l8e2QN+CXCa`f;?Tgz Y&|$avvSHx8^`K(T)78&qol`;+0G3eStpET3 literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_text_messages.png b/res/drawable-mdpi/ic_text_messages.png new file mode 100755 index 0000000000000000000000000000000000000000..cdabac0688c8a10667449788a6b863bfd66d3edc GIT binary patch literal 1244 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuk*h0bFQqR!T z(!$6@N5ROz&`jUJQs2--*TB%qz|zXVPyq^*fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$DAddqG<*}2 zGxI=#nqXbNzE+-j#U+V($*G<$wn{*A^fEJ3tPGqD9Zk(GjhrkkTnr6e&72$^EuCD= zoQ*Baoh@9QjbVCS@{>z*Q}aq-dQ%X3jdAJ)C5YStpv^9+MVV!(DQ-pixe8#9TV>*Q zivdpapn6kqyTuTvUVWfr^g+>!6x}c(U>X83;fWW>fhYgeJYbqH0w(TXtP`#>Ffe*~ zx;TbZ+)DcM|Gz!6P(soIz8~z#8yqq<72dHW$!bM@VCs-={2{@7g56=yAN#KdofV#? zH?ei=F@J5;(MU;1{5PFVtl`X`^G(j5Bm~|wCz&enEj}U^vocLx;5yUC5U;(Br`g{G z%$m(%{Eq3PslqgtB;ll52d)UNBXtfjj$Ql(sq>aQ{9%6;#b;t1;n~TY^FUbOHq%Bi zf%(Uj&U>t6U10Q7Tj0NfYUE@o_A@_Xym`4-OyjR}dbvKs@eAjvf6flNZdy*?H|Yy- zGjCLyB%$H&(%4Bk3NVmJT0yI;;;I>?5hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s0qE{2A#W=@WdmQJo_ z&c+tz&K9oD#xT7u`N^fZsd*(Zy(tL2PB`^~5=3qR&}Ns^qRg_?6t|-MTm`VltunE> z#n{cw+0@(=r+HAlDcIZs(QAfNuRhQ*`k?4Vif)(?Fb#p2@Wcz`z>|M!9x%-p0TXv? zcen%t0~3R%i(^Q|tv9o5y_p>a+VUrBs71MRWGr^&TD@TFFK&^I+q{>y?mu+Q&GqOp zucPx5RyOsvZdK@*n&fb#Luh}|^W6sO)vs0aZ1iqkl&;{osk5`Pyu9q&`pw^DXY=0RpCp2|8NJTJ7008Y z*SC8XOvyA1-nOZ=XLa6zcvH5xnMdEx)_c%0@w2FvA}7ZUws0SoH4V|R2Y6?&ri)qY z@iR<2P;^7*NwmHM`(cq7=7f*!3irz&?UfNM$x@tOD0fEbp!PC@t^>J6Pd78R9Q=Qz z$y4o|>CaqY9Sd2TS^b;DUhViC$P)4JPd;nJ;~H(Imfmx-xjwwkx%q6*N(RgA>zgZ& uAL@2U{IJbynwd=4y(F%8>u=X^888@b-*=@^|NAjeDe39z=d#Wzp$P!!LIQvQ literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_text_messages.png b/res/drawable-xxhdpi/ic_text_messages.png new file mode 100755 index 0000000000000000000000000000000000000000..31ceb298ecdf4b0a0d7026f783c30b3b040e8db7 GIT binary patch literal 1634 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuk*h0bFQqR!T z(!$6@N5ROz&`jUJQs2--*TB%qz|zXVPyq^*fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$DAddqG<*}2 zGxI=#nqXbNzE+-j#U+V($*G<$wn{*A^fEJ3tPG6Z+?-9#jh!qlTnr6e&72$^EuCD= zoQ*Baoh@9QjbVCS@{>z*Q}aq-dQ%X3O>pW3C5YStpv^9+MVV!(DQ-pixe8#9TV>*Q ziy=<)pn6kqyTu5nUVWfr^g+>!6x}c(U>X83;fWW>fhYgeJYbqH0w(Uds|z$47?>`2 zx;TbZ+~Q& z)KQ}C&<1yL(SU$;5!WAm{rf=U24@yu+<&18F0})zci402)Luv{knDLiKepfefz}ST zuxQR(Over`XqPNyo)fLDxsHF=la(@}=?70P5i1d}VX~F`e)@6($DZzN$HVhZ2W}L9 zvw7(aKE5}b;-1q`iV0pC5A8O)>1mUOZRxT-V%e~3P8oD$}fd>#6Pg+NQ%2XOt8&t7EK=3-!K81s%z7ebX!y zUXg0#{_nHp27WEC00-5xaz2joFQ56>Z04LLxNd44cib(OyR60YxM#6!Hgk+WaI|ID zk*^UGGL#b^)fryxI@hp%-pRnsQYM@HS8Ij0waT+SKge)HrQUuPXYc*nD|bxSG7Hjs zy=JHK#k|8m>)SSq{C;*->}*|USeB{Y7d4A3$2xVIxPR$|ruqi0Sp8jGsP@m?os+_x z|8FT=GuPE~{XxynYr)mVk-op}w$9#hZ)MSvC(qWs>U{iQ;ro8;oJ`YeFN;nIUGZ4m zH$B*`%W`5^>Q;sHORIPs{g#Ga)O;2bC3>@Ij?Zc(mai|kcE8&+$EN97Nz~o)+AjH& sKa;kP{CnEt!c095LGy85}Sb4q9e0P9C@Y5)KL literal 0 HcmV?d00001 diff --git a/res/drawable-xxxhdpi/ic_text_messages.png b/res/drawable-xxxhdpi/ic_text_messages.png new file mode 100755 index 0000000000000000000000000000000000000000..352d5d59b85ccf114024983db4b35b82019a8ee7 GIT binary patch literal 1588 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGok|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuk*h0bFQqR!T z(!$6@N5ROz&`jUJQs2--*TB%qz|zXVPyq^*fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$DAddqG<*}2 zGxI=#nqXbNzE+-j#U+V($*G<$wn{*A^fEJ3tPG6Z+?-9#+?*^eTnr6e&72$^EuCD= zoQ*Baoh@9QjbVCS@{>z*Q}aq-dQ;$f%?)ts1to~w0-((~)nY%Yz#8Ia0xn8)7j?jb#ja!}$94<_mN*NFBAFw;N{$QVR?2^_EjcH+G z(*%23)mp_ggIqQUu>^i#4rWzk6iiZNv9-LPsNZya*XEP&?!4FgZS$e*Fzdba{cUI8 zT!~scjoCVj!-0Wm!FE=YDW6ZVrg~W};t7b=+CP=|>C#OrbTrQ@F{w?hOr4VV{fPS0 zS)a1>ne;UIr#e>Y9NA{H*yd(0L;aKWTI?^@n{cLII@e;tSo*Zr$Sr^ACqK)p`}|Y5 zcC6945H7M#qu;v!o8PjRno^f5^&I1;EG`jcz5jOOga)m<7#KMm4%mr)xTmVGy2)bgoHe@|+?LpXa@r{;QM`W<)4zr1L>U=5Knj7%U)b?# z{jkoj6p<*ts`@{wse$9i%-_uiW_*-j+QCxD@4x^w4rnNY@J5BYiHGkVm@!)~G6txs zCN%E3#9>tqpbf23GuX(6e~oS z*{6MY;qeRoUqpKOE1xl*2GUP$J~tdbHLGaCKCh?kMTb + + + + diff --git a/res/layout/card_generator_location_google.xml b/res/layout/card_generator_location_google.xml index 716536d..979c932 100755 --- a/res/layout/card_generator_location_google.xml +++ b/res/layout/card_generator_location_google.xml @@ -2,6 +2,7 @@ @@ -42,24 +43,29 @@ android:textColor="@android:color/white" android:layout_marginRight="8dp"/> - + android:layout_height="200dp"> - + - + android:layout_gravity="bottom|right" + android:layout_marginRight="6dp" + android:layout_marginBottom="104dp" /> + \ No newline at end of file diff --git a/res/layout/card_generator_phone_calls.xml b/res/layout/card_generator_phone_calls.xml index f66de04..2a2725b 100755 --- a/res/layout/card_generator_phone_calls.xml +++ b/res/layout/card_generator_phone_calls.xml @@ -41,7 +41,8 @@ android:textColor="@android:color/white" android:layout_marginRight="8dp"/> - @@ -150,6 +151,12 @@ android:layout_marginBottom="8dp"/> + \ No newline at end of file diff --git a/res/layout/card_generator_screen_state.xml b/res/layout/card_generator_screen_state.xml index f09f805..7af3c2d 100755 --- a/res/layout/card_generator_screen_state.xml +++ b/res/layout/card_generator_screen_state.xml @@ -41,7 +41,8 @@ android:textColor="@android:color/white" android:layout_marginRight="8dp"/> - @@ -138,6 +139,12 @@ + \ No newline at end of file diff --git a/res/layout/card_generator_text_messages.xml b/res/layout/card_generator_text_messages.xml new file mode 100755 index 0000000..03c022d --- /dev/null +++ b/res/layout/card_generator_text_messages.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/values/databases.xml b/res/values/databases.xml new file mode 100755 index 0000000..a6d7f01 --- /dev/null +++ b/res/values/databases.xml @@ -0,0 +1,15 @@ + + CREATE TABLE metadata(key TEXT, value TEXT, last_updated INTEGER); + + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, latitude REAL, longitude REAL, altitude REAL, bearing REAL, speed REAL, provider TEXT, location_timestamp INTEGER, accuracy REAL); + + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, state TEXT); + + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, duration INTEGER, call_type TEXT, number TEXT, post_dial_digits TEXT, via_number TEXT, is_new INTEGER, pulled_externally INTEGER, country_iso TEXT, data_usage INTEGER, geocoded_location TEXT, is_video INTEGER, presentation TEXT, is_read INTEGER); + + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, direction TEXT, length INTEGER, body TEXT, number_name TEXT, number TEXT); + \ No newline at end of file diff --git a/res/values/generators.xml b/res/values/generators.xml index c4d60a2..b5e805f 100755 --- a/res/values/generators.xml +++ b/res/values/generators.xml @@ -3,6 +3,7 @@ com.audacious_software.passive_data_kit.generators.device.Location com.audacious_software.passive_data_kit.generators.device.ScreenState com.audacious_software.passive_data_kit.generators.communication.PhoneCalls + com.audacious_software.passive_data_kit.generators.communication.TextMessages com.audacious_software.passive_data_kit.generators.wearables.MicrosoftBand com.audacious_software.passive_data_kit.generators.services.GoogleAwareness @@ -45,6 +46,30 @@ Screen State + On + Off + Doze + Unknown + Legend: + No screen state changes have been observed yet. + + + SMS Text Messages + Incoming + Outgoing + Other + + #3F51B5 + #4CAF50 + #9E9E9E + + Latest Text Message + Length + Direction + %1$d chars. + + No text messages have been sent or received on this device. + Please all the app permission to access your text messages to gather and report your messaging activity statistics. Phone Calls @@ -63,4 +88,6 @@ Direction %1$.2f min. + No phone calls have been made or received on this device. + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 61e918e..6474efa 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -14,10 +14,6 @@ Today - On - Off - Doze - Unknown - Legend: + Never diff --git a/src/com/audacious_software/passive_data_kit/PassiveDataKit.java b/src/com/audacious_software/passive_data_kit/PassiveDataKit.java index 8a8efc1..285f97e 100755 --- a/src/com/audacious_software/passive_data_kit/PassiveDataKit.java +++ b/src/com/audacious_software/passive_data_kit/PassiveDataKit.java @@ -5,9 +5,13 @@ import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; import com.audacious_software.passive_data_kit.generators.Generators; +import java.io.File; import java.util.ArrayList; public class PassiveDataKit { + private static final String STORAGE_PATH = "passive-data-kit"; + private static final String GENERATORS_PATH = "generators"; + private Context mContext = null; private boolean mStarted = false; @@ -30,6 +34,17 @@ public static ArrayList diagnostics(Context context) return actions; } + public static File getGeneratorsStorage(Context context) { + File path = new File(context.getFilesDir(), PassiveDataKit.STORAGE_PATH); + path = new File(path, PassiveDataKit.GENERATORS_PATH); + + if (path.exists() == false) { + path.mkdirs(); + } + + return path; + } + private static class PassiveDataKitHolder { public static PassiveDataKit instance = new PassiveDataKit(); } diff --git a/src/com/audacious_software/passive_data_kit/PhoneUtililties.java b/src/com/audacious_software/passive_data_kit/PhoneUtililties.java new file mode 100755 index 0000000..0213eb1 --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/PhoneUtililties.java @@ -0,0 +1,30 @@ +package com.audacious_software.passive_data_kit; + +import android.content.Context; + +/** + * Created by cjkarr on 12/13/2016. + */ + +public class PhoneUtililties { + public static String normalizedPhoneNumber(String phoneNumber) + { + if (phoneNumber == null) { + return null; + } + + phoneNumber = phoneNumber.replaceAll("[^\\d.]", ""); + + while (phoneNumber.length() > 10) { + phoneNumber = phoneNumber.substring(1); + } + + while (phoneNumber.length() < 10) { + phoneNumber += "0"; + } + + + return phoneNumber; + } +} + diff --git a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java index 184173a..a4a1300 100755 --- a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java +++ b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java @@ -20,7 +20,7 @@ import java.util.ArrayList; -public class DataStreamActivity extends AppCompatActivity implements Generators.NewDataPointListener { +public class DataStreamActivity extends AppCompatActivity implements Generators.GeneratorUpdatedListener { private DataPointsAdapter mAdapter = null; protected void onCreate(Bundle savedInstanceState) { @@ -30,6 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { this.getSupportActionBar().setSubtitle(this.getResources().getQuantityString(R.plurals.activity_data_stream_subtitle, 0, 0)); this.mAdapter = new DataPointsAdapter(); + this.mAdapter.setContext(this.getApplicationContext()); RecyclerView listView = (RecyclerView) this.findViewById(R.id.list_view); @@ -41,20 +42,33 @@ protected void onCreate(Bundle savedInstanceState) { protected void onResume() { super.onResume(); - Generators.getInstance(this).addNewDataPointListener(this); + Generators.getInstance(this).addNewGeneratorUpdatedListener(this); - Generators.getInstance(this).broadcastLatestDataPoints(); + final int count = this.mAdapter.getItemCount(); + + Handler mainHandler = new Handler(Looper.getMainLooper()); + + final DataStreamActivity me = this; + + mainHandler.post(new Runnable() { + @Override + public void run() { + me.getSupportActionBar().setSubtitle(me.getResources().getQuantityString(R.plurals.activity_data_stream_subtitle, count, count)); + } + }); } protected void onPause() { super.onPause(); - Generators.getInstance(this).removeNewDataPointListener(this); + Generators.getInstance(this).removeGeneratorUpdatedListener(this); } @Override - public void onNewDataPoint(String identifier, Bundle data) { - this.mAdapter.updateDataPoint(identifier, data); + public void onGeneratorUpdated(String identifier, Bundle data) { + Log.e("PDK", "GOT GENERATOR UPDATE: " + identifier + " -- " + data); + + this.mAdapter.notifyDataSetChanged(); final int count = this.mAdapter.getItemCount(); @@ -68,5 +82,9 @@ public void run() { me.getSupportActionBar().setSubtitle(me.getResources().getQuantityString(R.plurals.activity_data_stream_subtitle, count, count)); } }); + +// RecyclerView listView = (RecyclerView) this.findViewById(R.id.list_view); +// listView.setAdapter(this.mAdapter); +// listView.invalidate(); } } diff --git a/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java b/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java index b711460..fd1679d 100755 --- a/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java +++ b/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java @@ -1,9 +1,6 @@ package com.audacious_software.passive_data_kit.activities.generators; import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; @@ -14,15 +11,17 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; public class DataPointsAdapter extends RecyclerView.Adapter { - private ArrayList mDataPoints = new ArrayList<>(); + private Context mContext = null; @Override public DataPointViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - Class generatorClass = Generators.getInstance(null).fetchCustomViewClass(viewType); + Class generatorClass = Generators.getInstance(this.mContext).fetchCustomViewClass(viewType); try { Method fetchView = generatorClass.getDeclaredMethod("fetchView", ViewGroup.class); @@ -51,20 +50,24 @@ public DataPointViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { } @Override - public void onBindViewHolder(DataPointViewHolder holder, int position) { - Bundle dataPoint = this.mDataPoints.get(position); - Class generatorClass = Generators.getInstance(null).fetchCustomViewClass(dataPoint.getBundle(Generator.PDK_METADATA).getString(Generator.IDENTIFIER)); + public void onBindViewHolder(final DataPointViewHolder holder, int position) { + List> activeGenerators = Generators.getInstance(holder.itemView.getContext()).activeGenerators(); + + this.sortGenerators(this.mContext, activeGenerators); + + Class generatorClass = activeGenerators.get(position); try { - Method bindViewHolder = generatorClass.getDeclaredMethod("bindViewHolder", DataPointViewHolder.class, Bundle.class); - bindViewHolder.invoke(null, holder, dataPoint); + Method bindViewHolder = generatorClass.getDeclaredMethod("bindViewHolder", DataPointViewHolder.class); + bindViewHolder.invoke(null, holder); } catch (Exception e) { e.printStackTrace(); try { generatorClass = Generator.class; - Method bindViewHolder = generatorClass.getDeclaredMethod("bindViewHolder", DataPointViewHolder.class, Bundle.class); - bindViewHolder.invoke(null, holder, dataPoint); + Method bindViewHolder = generatorClass.getDeclaredMethod("bindViewHolder", DataPointViewHolder.class); + + bindViewHolder.invoke(null, holder); } catch (NoSuchMethodException e1) { Logger.getInstance(holder.itemView.getContext()).logThrowable(e1); } catch (InvocationTargetException e1) { @@ -77,51 +80,63 @@ public void onBindViewHolder(DataPointViewHolder holder, int position) { @Override public int getItemCount() { - return this.mDataPoints.size(); + return Generators.getInstance(null).activeGenerators().size(); } - public int getItemViewType (int position) { - Bundle dataPoint = this.mDataPoints.get(position); - Class generatorClass = Generators.getInstance(null).fetchCustomViewClass(dataPoint.getBundle(Generator.PDK_METADATA).getString(Generator.IDENTIFIER)); - - return generatorClass.hashCode(); - } - - public void updateDataPoint(String identifier, Bundle data) { - ArrayList toDelete = new ArrayList<>(); - - Handler mainHandler = new Handler(Looper.getMainLooper()); - final DataPointsAdapter me = this; - - for (Bundle bundle : this.mDataPoints) { - if (bundle.getBundle(Generator.PDK_METADATA).getString(Generator.IDENTIFIER).equals(identifier)) { - toDelete.add(bundle); + private void sortGenerators(final Context context, List> generators) { + Collections.sort(generators, new Comparator>() { + @Override + public int compare(Class one, Class two) { + long oneUpdated = 0; + + try { + Method oneGenerated = one.getDeclaredMethod("latestPointGenerated", Context.class); + + oneUpdated = (long) oneGenerated.invoke(null, context); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + long twoUpdated = 0; + + try { + Method twoGenerated = two.getDeclaredMethod("latestPointGenerated", Context.class); + + twoUpdated = (long) twoGenerated.invoke(null, context); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + if (oneUpdated < twoUpdated) { + return 1; + } else if (oneUpdated > twoUpdated) { + return -1; + } + + return 0; } - } - - Collections.reverse(toDelete); + }); + } - for (Bundle delete : toDelete) { - final int position = this.mDataPoints.indexOf(delete); + public int getItemViewType (int position) { + List> activeGenerators = Generators.getInstance(this.mContext).activeGenerators(); - this.mDataPoints.remove(position); + this.sortGenerators(this.mContext, activeGenerators); -// mainHandler.post(new Runnable() { -// @Override -// public void run() { -// me.notifyItemRemoved(position); -// } -// }); - } + Class generatorClass = activeGenerators.get(position); - this.mDataPoints.add(0, data); + return generatorClass.hashCode(); + } - mainHandler.post(new Runnable() { - @Override - public void run() { -// me.notifyItemInserted(0); - me.notifyDataSetChanged(); - } - }); + public void setContext(Context context) { + this.mContext = context; } } diff --git a/src/com/audacious_software/passive_data_kit/generators/Generator.java b/src/com/audacious_software/passive_data_kit/generators/Generator.java index dedb648..abb0938 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generator.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generator.java @@ -1,6 +1,9 @@ package com.audacious_software.passive_data_kit.generators; +import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -13,6 +16,7 @@ import java.text.DateFormat; import java.util.Calendar; import java.util.Date; +import java.util.List; @SuppressWarnings("unused") public abstract class Generator @@ -25,6 +29,15 @@ public abstract class Generator public static final String MEDIA_ATTACHMENT_KEY = "attachment"; public static final String MEDIA_CONTENT_TYPE_KEY = "attachment-type"; public static final String MEDIA_ATTACHMENT_GUID_KEY = "attachment-guid"; + public static final String LATITUDE = "latitude"; + public static final String LONGITUDE = "longitude"; + + private static final String TABLE_SQLITE_MASTER = "sqlite_master"; + + private static final String TABLE_METADATA = "metadata"; + private static String TABLE_METADATA_LAST_UPDATED = "last_updated"; + private static String TABLE_METADATA_KEY = "key"; + private static String TABLE_METADATA_VALUE = "value"; protected Context mContext = null; @@ -41,10 +54,6 @@ public static void stop(Context context) { // Do nothing - override in subclasses. } - public static void broadcastLatestDataPoint(Context context) { - // Do nothing - override in subclasses. - } - public static boolean isEnabled(Context context) { return false; @@ -55,12 +64,18 @@ public static boolean isRunning(Context context) return false; } + public static long latestPointGenerated(Context context) { + return 0; + } + public static View fetchView(ViewGroup parent) { return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_generic, parent, false); } - public static void bindViewHolder(DataPointViewHolder holder, Bundle dataPoint) { - String identifier = dataPoint.getBundle(Generator.PDK_METADATA).getString(Generator.IDENTIFIER); + public static void bindViewHolder(DataPointViewHolder holder) { + Class currentClass = new Object() { }.getClass().getEnclosingClass(); + + String identifier = currentClass.getCanonicalName(); TextView generatorLabel = (TextView) holder.itemView.findViewById(R.id.label_generator); @@ -87,4 +102,62 @@ public static String formatTimestamp(Context context, double timestamp) { return context.getString(R.string.format_full_timestamp_pdk, date, time); } + + public abstract List fetchPayloads(); + + protected int getDatabaseVersion(SQLiteDatabase db) { + String where = "type = ? AND name = ?"; + String[] args = { "table", Generator.TABLE_METADATA }; + + Cursor c = db.query(Generator.TABLE_SQLITE_MASTER, null, where, args, null, null, null); + + if (c.getCount() > 0) { + // Do nothing - table exists... + } else { + db.execSQL(this.mContext.getString(R.string.pdk_generator_create_version_table)); + } + + c.close(); + + String versionWhere = Generator.TABLE_METADATA_KEY + " = ?"; + String[] versionArgs = { "version" }; + + c = db.query(Generator.TABLE_METADATA, null, versionWhere, versionArgs, null, null, Generator.TABLE_METADATA_LAST_UPDATED + " DESC"); + + int version = 0; + + if (c.moveToNext()) { + version = Integer.parseInt(c.getString(c.getColumnIndex(Generator.TABLE_METADATA_VALUE))); + } + + c.close(); + + return version; + } + + protected void setDatabaseVersion(SQLiteDatabase db, int newVersion) { + boolean keyExists = false; + + String versionWhere = Generator.TABLE_METADATA_KEY + " = ?"; + String[] versionArgs = { "version" }; + + Cursor c = db.query(Generator.TABLE_METADATA, null, versionWhere, versionArgs, null, null, Generator.TABLE_METADATA_LAST_UPDATED + " DESC"); + + if (c.getCount() > 0) { + keyExists = true; + } + + c.close(); + + ContentValues values = new ContentValues(); + values.put(Generator.TABLE_METADATA_KEY, "version"); + values.put(Generator.TABLE_METADATA_VALUE, "" + newVersion); + values.put(Generator.TABLE_METADATA_LAST_UPDATED, System.currentTimeMillis()); + + if (keyExists) { + db.update(Generator.TABLE_METADATA, values, versionWhere, versionArgs); + } else { + db.insert(Generator.TABLE_METADATA, null, values); + } + } } diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java index c48087b..42b4cd9 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generators.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.List; public class Generators { private Context mContext = null; @@ -26,9 +27,9 @@ public class Generators { private ArrayList mGenerators = new ArrayList<>(); private HashSet mActiveGenerators = new HashSet<>(); private SharedPreferences mSharedPreferences = null; - private HashSet mNewDataPointListeners = new HashSet<>(); private HashMap> mGeneratorMap = new HashMap<>(); private SparseArray> mViewTypeMap = new SparseArray<>(); + private HashSet mGeneratorUpdatedListeners = new HashSet<>(); public void start() { if (!this.mStarted) @@ -148,27 +149,11 @@ public ArrayList diagnostics() { return actions; } - public void transmitData(String identifier, Bundle data) { - double now = (double) System.currentTimeMillis(); - now = now / 1000; // Convert to seconds... - - Bundle metadata = new Bundle(); - metadata.putString(Generator.IDENTIFIER, identifier); - metadata.putDouble(Generator.TIMESTAMP, now); - metadata.putString(Generator.GENERATOR, this.getGeneratorFullName(identifier)); - metadata.putString(Generator.SOURCE, this.getSource()); - data.putBundle(Generator.PDK_METADATA, metadata); - - for (Generators.NewDataPointListener listener : this.mNewDataPointListeners) { - listener.onNewDataPoint(identifier, data); - } - } - - private String getSource() { + public String getSource() { return "unknown-user-please-set-me"; } - private String getGeneratorFullName(String identifier) { + public String getGeneratorFullName(String identifier) { String pdkName = this.mContext.getString(R.string.pdk_name); String pdkVersion = this.mContext.getString(R.string.pdk_version); String appName = this.mContext.getString(this.mContext.getApplicationInfo().labelRes); @@ -186,10 +171,6 @@ private String getGeneratorFullName(String identifier) { return identifier + ": " + appName + "/" + version + " " + pdkName + "/" + pdkVersion; } - public void removeNewDataPointListener(Generators.NewDataPointListener listener) { - this.mNewDataPointListeners.remove(listener); - } - public void registerCustomViewClass(String identifier, Class generatorClass) { this.mGeneratorMap.put(identifier, generatorClass); this.mViewTypeMap.put(generatorClass.hashCode(), generatorClass); @@ -215,31 +196,6 @@ public Class fetchCustomViewClass(int viewType) { return generatorClass; } - public void broadcastLatestDataPoints() { - for (String className : this.mGenerators) - { - try { - Class generatorClass = (Class) Class.forName(className); - - Log.e("PDK", "CLASS " + generatorClass); - - if (generatorClass != null) { - Method broadcast = generatorClass.getDeclaredMethod("broadcastLatestDataPoint", Context.class); - - broadcast.invoke(null, this.mContext); - } - } catch (NoSuchMethodException e) { - - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } - } - } - public Generator getGenerator(String className) { Log.e("BB", "GENERATOR FIND START"); for (String name : this.mActiveGenerators) { @@ -267,6 +223,26 @@ public Generator getGenerator(String className) { return null; } + public List> activeGenerators() { + ArrayList> active = new ArrayList<>(); + + for (String className : this.mActiveGenerators) { + try { + active.add((Class) Class.forName(className)); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + + return active; + } + + public void notifyGeneratorUpdated(String identifier, Bundle bundle) { + for (GeneratorUpdatedListener listener : this.mGeneratorUpdatedListeners) { + listener.onGeneratorUpdated(identifier, bundle); + } + } + private static class GeneratorsHolder { public static Generators instance = new Generators(); } @@ -284,11 +260,15 @@ private void setContext(Context context) { this.mContext = context.getApplicationContext(); } - public void addNewDataPointListener(Generators.NewDataPointListener listener) { - this.mNewDataPointListeners.add(listener); + public void addNewGeneratorUpdatedListener(Generators.GeneratorUpdatedListener listener) { + this.mGeneratorUpdatedListeners.add(listener); + } + + public void removeGeneratorUpdatedListener(Generators.GeneratorUpdatedListener listener) { + this.mGeneratorUpdatedListeners.remove(listener); } - public interface NewDataPointListener { - void onNewDataPoint(String identifier, Bundle data); + public interface GeneratorUpdatedListener { + void onGeneratorUpdated(String identifier, Bundle data); } } diff --git a/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java b/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java index 4294ab8..93677c0 100755 --- a/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java +++ b/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java @@ -1,11 +1,13 @@ package com.audacious_software.passive_data_kit.generators.communication; import android.Manifest; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; import android.graphics.Typeface; import android.os.Build; import android.os.Bundle; @@ -20,6 +22,7 @@ import android.view.ViewGroup; import android.widget.TextView; +import com.audacious_software.passive_data_kit.PassiveDataKit; import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; import com.audacious_software.passive_data_kit.activities.generators.RequestPermissionActivity; import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; @@ -27,19 +30,17 @@ import com.audacious_software.passive_data_kit.generators.Generators; import com.audacious_software.pdk.passivedatakit.R; import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.components.Description; -import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.formatter.IValueFormatter; -import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.ViewPortHandler; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; +import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -50,21 +51,6 @@ public class PhoneCalls extends Generator { private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.communication.PhoneCalls.ENABLED"; private static final boolean ENABLED_DEFAULT = true; - private static final String SAMPLE_INTERVAL = "com.audacious_software.passive_data_kit.generators.communication.PhoneCalls.SAMPLE_INTERVAL"; - private static final long SAMPLE_INTERVAL_DEFAULT = 30000; // 300000; - - private static final String LAST_INCOMING_COUNT = "com.audacious_software.passive_data_kit.generators.communication.PhoneCalls.LAST_INCOMING_COUNT"; - private static final String LAST_OUTGOING_COUNT = "com.audacious_software.passive_data_kit.generators.communication.PhoneCalls.LAST_OUTGOING_COUNT"; - private static final String LAST_MISSED_COUNT = "com.audacious_software.passive_data_kit.generators.communication.PhoneCalls.LAST_MISSED_COUNT"; - private static final String LAST_TOTAL_COUNT = "com.audacious_software.passive_data_kit.generators.communication.PhoneCalls.LAST_TOTAL_COUNT"; - - private static final String LAST_CALL_TIMESTAMP = "com.audacious_software.passive_data_kit.generators.communication.PhoneCalls.LAST_CALL_TIMESTAMP"; - private static final String LAST_CALL_DURATION = "com.audacious_software.passive_data_kit.generators.communication.PhoneCalls.LAST_CALL_DURATION"; - private static final String LAST_CALL_TYPE = "com.audacious_software.passive_data_kit.generators.communication.PhoneCalls.LAST_CALL_TYPE"; - - private static final String LAST_SAMPLE = "com.audacious_software.passive_data_kit.generators.communication.PhoneCalls.LAST_SAMPLE"; - private static final long LAST_SAMPLE_DEFAULT = 0; - private static final String CALL_DATE_KEY = "call_timestamp"; private static final String CALL_DURATION_KEY = "duration"; private static final String CALL_IS_NEW_KEY = "is_new"; @@ -94,9 +80,33 @@ public class PhoneCalls extends Generator { private static final String CALL_PRESENTATION_PAYPHONE = "payphone"; private static final String CALL_PRESENTATION_UNKNOWN = "unknown"; + private static int DATABASE_VERSION = 2; + + private static final String TABLE_HISTORY = "history"; + private static final String HISTORY_OBSERVED = "observed"; + private static final String HISTORY_DURATION = "duration"; + private static final String HISTORY_NUMBER = "number"; + private static final String HISTORY_IS_NEW = "is_new"; + private static final String HISTORY_PULLED_EXTERNALLY = "pulled_externally"; + private static final String HISTORY_POST_DIAL_DIGITS = "post_dial_digits"; + private static final String HISTORY_COUNTRY_ISO = "country_iso"; + private static final String HISTORY_DATA_USAGE = "data_usage"; + private static final String HISTORY_GEOCODED_LOCATION = "geocoded_location"; + private static final String HISTORY_VIDEO = "is_video"; + private static final String HISTORY_VIA_NUMBER = "via_number"; + private static final String HISTORY_PRESENTATION = "presentation"; + private static final String HISTORY_IS_READ = "is_read"; + private static final String HISTORY_CALL_TYPE = "call_type"; + private static PhoneCalls sInstance = null; private Handler mHandler = null; + private Context mContext = null; + + private static final String DATABASE_PATH = "pdk-phone-calls.sqlite"; + + private SQLiteDatabase mDatabase = null; + private long mSampleInterval = 60000; public static PhoneCalls getInstance(Context context) { if (PhoneCalls.sInstance == null) { @@ -108,6 +118,8 @@ public static PhoneCalls getInstance(Context context) { public PhoneCalls(Context context) { super(context); + + this.mContext = context.getApplicationContext(); } public static void start(final Context context) { @@ -115,12 +127,14 @@ public static void start(final Context context) { } private void startGenerator() { - Log.e("PDK", "START PHONE CALL GENERATOR"); - final PhoneCalls me = this; if (this.mHandler != null) { - this.mHandler.getLooper().quitSafely(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + this.mHandler.getLooper().quitSafely(); + } else { + this.mHandler.getLooper().quit(); + } this.mHandler = null; } @@ -128,9 +142,6 @@ private void startGenerator() { final Runnable checkLogs = new Runnable() { @Override public void run() { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(me.mContext); - SharedPreferences.Editor e = prefs.edit(); - boolean approved = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -143,28 +154,30 @@ public void run() { Log.e("PDK", "TODO: Fetch Call Logs..."); - long now = System.currentTimeMillis(); - if (approved) { - long totalIncoming = prefs.getLong(PhoneCalls.LAST_INCOMING_COUNT, 0); - long totalOutgoing = prefs.getLong(PhoneCalls.LAST_OUTGOING_COUNT, 0); - long totalMissed = prefs.getLong(PhoneCalls.LAST_MISSED_COUNT, 0); - long total = prefs.getLong(PhoneCalls.LAST_TOTAL_COUNT, 0); + long lastObserved = 0; - long lastSample = prefs.getLong(PhoneCalls.LAST_SAMPLE, PhoneCalls.LAST_SAMPLE_DEFAULT); + Cursor lastCursor = me.mDatabase.query(PhoneCalls.TABLE_HISTORY, null, null, null, null, null, PhoneCalls.HISTORY_OBSERVED + " DESC"); - String where = CallLog.Calls.DATE + " > ?"; - String[] args = {"" + lastSample}; + if (lastCursor.moveToNext()) { + lastObserved = lastCursor.getLong(lastCursor.getColumnIndex(PhoneCalls.HISTORY_OBSERVED)); + } - long latestCallTimestamp = -1; - String latestCallType = null; - long latestCallDuration = -1; + lastCursor.close(); + + String where = CallLog.Calls.DATE + " > ?"; + String[] args = {"" + lastObserved}; Cursor c = me.mContext.getContentResolver().query(CallLog.Calls.CONTENT_URI, null, where, args, CallLog.Calls.DATE); while (c.moveToNext()) { - Bundle bundle = new Bundle(); + ContentValues values = new ContentValues(); + values.put(PhoneCalls.HISTORY_OBSERVED, c.getLong(c.getColumnIndex(CallLog.Calls.DATE))); + values.put(PhoneCalls.HISTORY_DURATION, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); + values.put(PhoneCalls.HISTORY_NUMBER, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); + values.put(PhoneCalls.HISTORY_IS_NEW, (c.getInt(c.getColumnIndex(CallLog.Calls.NEW)) != 0)); + Bundle bundle = new Bundle(); bundle.putLong(PhoneCalls.CALL_DATE_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DATE))); bundle.putLong(PhoneCalls.CALL_DURATION_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); bundle.putString(PhoneCalls.CALL_NUMBER_KEY, c.getString(c.getColumnIndex(CallLog.Calls.NUMBER))); @@ -186,6 +199,8 @@ public void run() { } bundle.putBoolean(PhoneCalls.CALL_PULLED_EXTERNALLY_KEY, ((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) == CallLog.Calls.FEATURES_PULLED_EXTERNALLY)); + + values.put(PhoneCalls.HISTORY_PULLED_EXTERNALLY, ((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) == CallLog.Calls.FEATURES_PULLED_EXTERNALLY)); case 24: if (typeInt == CallLog.Calls.REJECTED_TYPE) { type = PhoneCalls.CALL_TYPE_REJECTED; @@ -195,6 +210,9 @@ public void run() { bundle.putString(PhoneCalls.CALL_POST_DIAL_DIGITS_KEY, c.getString(c.getColumnIndex(CallLog.Calls.POST_DIAL_DIGITS))); bundle.putString(PhoneCalls.CALL_VIA_NUMBER_KEY, c.getString(c.getColumnIndex(CallLog.Calls.VIA_NUMBER))); + + values.put(PhoneCalls.HISTORY_POST_DIAL_DIGITS, c.getString(c.getColumnIndex(CallLog.Calls.POST_DIAL_DIGITS))); + values.put(PhoneCalls.HISTORY_VIA_NUMBER, c.getString(c.getColumnIndex(CallLog.Calls.VIA_NUMBER))); case 21: if (typeInt == CallLog.Calls.VOICEMAIL_TYPE) { type = PhoneCalls.CALL_TYPE_VOICEMAIL; @@ -203,57 +221,53 @@ public void run() { bundle.putString(PhoneCalls.CALL_COUNTRY_ISO_KEY, c.getString(c.getColumnIndex(CallLog.Calls.COUNTRY_ISO))); bundle.putLong(PhoneCalls.CALL_DATA_USAGE_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DATA_USAGE))); bundle.putString(PhoneCalls.CALL_GEOCODED_LOCATION_KEY, c.getString(c.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION))); - bundle.putBoolean(PhoneCalls.CALL_VIDEO_KEY, ((features & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO)); + values.put(PhoneCalls.HISTORY_COUNTRY_ISO, c.getString(c.getColumnIndex(CallLog.Calls.COUNTRY_ISO))); + values.put(PhoneCalls.HISTORY_DATA_USAGE, c.getLong(c.getColumnIndex(CallLog.Calls.DATA_USAGE))); + values.put(PhoneCalls.HISTORY_GEOCODED_LOCATION, c.getString(c.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION))); + values.put(PhoneCalls.HISTORY_VIDEO, ((features & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO)); + // bundle.putString(PhoneCalls.CALL_TRANSCRIPTION_KEY, c.getString(c.getColumnIndex(CallLog.Calls.TRANSCRIPTION))); case 19: switch (c.getInt(c.getColumnIndex(CallLog.Calls.NUMBER_PRESENTATION))) { case CallLog.Calls.PRESENTATION_ALLOWED: bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_ALLOWED); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_ALLOWED); break; case CallLog.Calls.PRESENTATION_RESTRICTED: bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_RESTRICTED); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_RESTRICTED); break; case CallLog.Calls.PRESENTATION_PAYPHONE: bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_PAYPHONE); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_PAYPHONE); break; case CallLog.Calls.PRESENTATION_UNKNOWN: bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_UNKNOWN); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_UNKNOWN); break; } case 14: bundle.putBoolean(PhoneCalls.CALL_IS_READ_KEY, (c.getInt(c.getColumnIndex(CallLog.Calls.IS_READ)) != 0)); + values.put(PhoneCalls.HISTORY_IS_READ, (c.getInt(c.getColumnIndex(CallLog.Calls.IS_READ)) != 0)); case 1: if (typeInt == CallLog.Calls.INCOMING_TYPE) { type = PhoneCalls.CALL_TYPE_INCOMING; - totalIncoming += 1; } else if (typeInt == CallLog.Calls.OUTGOING_TYPE) { type = PhoneCalls.CALL_TYPE_OUTGOING; - totalOutgoing += 1; } else if (typeInt == CallLog.Calls.MISSED_TYPE) { type = PhoneCalls.CALL_TYPE_MISSED; - totalMissed += 1; } } bundle.putString(PhoneCalls.CALL_TYPE_KEY, type); - - if (bundle.getLong(PhoneCalls.CALL_DATE_KEY, 0) > latestCallTimestamp) { - latestCallTimestamp = bundle.getLong(PhoneCalls.CALL_DATE_KEY, 0); - latestCallDuration = bundle.getLong(PhoneCalls.CALL_DURATION_KEY, 0); - latestCallType = type; - } - - Log.e("PDK", "------"); - for (int i = 0; i < c.getColumnCount(); i++) { - Log.e("PDK", "CALL LOG: " + c.getColumnName(i) + " --> " + c.getString(i)); - } + values.put(PhoneCalls.HISTORY_CALL_TYPE, type); String[] sensitiveFields = { PhoneCalls.CALL_NUMBER_KEY, PhoneCalls.CALL_POST_DIAL_DIGITS_KEY, - PhoneCalls.CALL_VIA_NUMBER_KEY + PhoneCalls.CALL_VIA_NUMBER_KEY, }; for (String field : sensitiveFields) { @@ -262,38 +276,49 @@ public void run() { } } - Generators.getInstance(me.mContext).transmitData(PhoneCalls.GENERATOR_IDENTIFIER, bundle); - } - - Log.e("PDK", "------"); + String[] valueSensitiveFields = { + PhoneCalls.HISTORY_NUMBER, + PhoneCalls.HISTORY_POST_DIAL_DIGITS, + PhoneCalls.HISTORY_VIA_NUMBER, + }; - total += c.getCount(); + for (String field : valueSensitiveFields) { + if (values.containsKey(field)) { + values.put(field, new String(Hex.encodeHex(DigestUtils.sha256(values.getAsString(field))))); + } + } - c.close(); + me.mDatabase.insert(PhoneCalls.TABLE_HISTORY, null, values); - if (latestCallType != null) { - e.putLong(PhoneCalls.LAST_CALL_TIMESTAMP, latestCallTimestamp); - e.putLong(PhoneCalls.LAST_CALL_DURATION, latestCallDuration); - e.putString(PhoneCalls.LAST_CALL_TYPE, latestCallType); + Generators.getInstance(me.mContext).notifyGeneratorUpdated(PhoneCalls.GENERATOR_IDENTIFIER, bundle); } - e.putLong(PhoneCalls.LAST_INCOMING_COUNT, totalIncoming); - e.putLong(PhoneCalls.LAST_OUTGOING_COUNT, totalOutgoing); - e.putLong(PhoneCalls.LAST_MISSED_COUNT, totalMissed); - e.putLong(PhoneCalls.LAST_TOTAL_COUNT, total); - - e.putLong(PhoneCalls.LAST_SAMPLE, now); - e.apply(); + c.close(); } - long sampleInterval = prefs.getLong(PhoneCalls.SAMPLE_INTERVAL, PhoneCalls.SAMPLE_INTERVAL_DEFAULT); - if (me.mHandler != null) { - me.mHandler.postDelayed(this, sampleInterval); + me.mHandler.postDelayed(this, me.mSampleInterval); } } }; + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, PhoneCalls.DATABASE_PATH); + + this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); + + int version = this.getDatabaseVersion(this.mDatabase); + + switch (version) { + case 0: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_phone_calls_create_history_table)); + case 1: + this.mDatabase.delete(PhoneCalls.TABLE_HISTORY, null, null); + } + + this.setDatabaseVersion(this.mDatabase, PhoneCalls.DATABASE_VERSION); + Runnable r = new Runnable() { @Override public void run() { @@ -363,92 +388,136 @@ public void run() { return actions; } - public static void bindViewHolder(DataPointViewHolder holder, final Bundle dataPoint) { + public static void bindViewHolder(DataPointViewHolder holder) { final Context context = holder.itemView.getContext(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - double timestamp = dataPoint.getBundle(Generator.PDK_METADATA).getDouble(Generator.TIMESTAMP); + long lastTimestamp = 0; + long lastDuration = 0; + String callType = null; - TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date); + long totalIncoming = 0; + long totalOutgoing = 0; + long totalMissed = 0; + long total = 0; - dateLabel.setText(Generator.formatTimestamp(context, timestamp)); + PhoneCalls generator = PhoneCalls.getInstance(holder.itemView.getContext()); - PieChart pieChart = (PieChart) holder.itemView.findViewById(R.id.chart_phone_calls); - pieChart.getLegend().setEnabled(false); + Cursor c = generator.mDatabase.query(PhoneCalls.TABLE_HISTORY, null, null, null, null, null, PhoneCalls.HISTORY_OBSERVED + " DESC"); - pieChart.setEntryLabelColor(android.R.color.transparent); - pieChart.getDescription().setEnabled(false); - pieChart.setDrawHoleEnabled(false); + while (c.moveToNext()) { + if (lastTimestamp == 0) { + lastTimestamp = c.getLong(c.getColumnIndex(PhoneCalls.HISTORY_OBSERVED)); + lastDuration = c.getLong(c.getColumnIndex(PhoneCalls.HISTORY_DURATION)); + } - long totalIncoming = prefs.getLong(PhoneCalls.LAST_INCOMING_COUNT, 0); - long totalOutgoing = prefs.getLong(PhoneCalls.LAST_OUTGOING_COUNT, 0); - long totalMissed = prefs.getLong(PhoneCalls.LAST_MISSED_COUNT, 0); - long total = prefs.getLong(PhoneCalls.LAST_TOTAL_COUNT, 0); + total += 1; - List entries = new ArrayList<>(); + String type = c.getString(c.getColumnIndex(PhoneCalls.HISTORY_CALL_TYPE)); - if (totalIncoming > 0) { - entries.add(new PieEntry(totalIncoming, context.getString(R.string.generator_phone_calls_incoming_label))); - } + if (PhoneCalls.CALL_TYPE_INCOMING.equals(type)) { + totalIncoming += 1; + } else if (PhoneCalls.CALL_TYPE_OUTGOING.equals(type)) { + totalOutgoing += 1; + } else if (PhoneCalls.CALL_TYPE_MISSED.equals(type)) { + totalOutgoing += 1; + } - if (totalOutgoing > 0) { - entries.add(new PieEntry(totalOutgoing, context.getString(R.string.generator_phone_calls_outgoing_label))); + if (callType == null) { + callType = type; + } } - if (totalMissed > 0) { - entries.add(new PieEntry(totalMissed, context.getString(R.string.generator_phone_calls_missed_label))); - } + c.close(); - long other = total - (totalIncoming + totalOutgoing + totalMissed); + View cardContent = holder.itemView.findViewById(R.id.card_content); + View cardEmpty = holder.itemView.findViewById(R.id.card_empty); + TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date); - if (other > 0) { - entries.add(new PieEntry(other, context.getString(R.string.generator_phone_calls_other_label))); - } + if (total > 0) { + cardContent.setVisibility(View.VISIBLE); + cardEmpty.setVisibility(View.GONE); - PieDataSet set = new PieDataSet(entries, " "); + dateLabel.setText(Generator.formatTimestamp(context, lastTimestamp)); - int[] colors = { - R.color.generator_phone_call_incoming, - R.color.generator_phone_call_outgoing, - R.color.generator_phone_call_missed, - R.color.generator_phone_call_other - }; + PieChart pieChart = (PieChart) holder.itemView.findViewById(R.id.chart_phone_calls); + pieChart.getLegend().setEnabled(false); - set.setColors(colors, context); + pieChart.setEntryLabelColor(android.R.color.transparent); + pieChart.getDescription().setEnabled(false); + pieChart.setDrawHoleEnabled(false); - PieData data = new PieData(set); - data.setValueTextSize(14); - data.setValueTypeface(Typeface.DEFAULT_BOLD); - data.setValueTextColor(0xffffffff); + List entries = new ArrayList<>(); - data.setValueFormatter(new IValueFormatter() { - @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return "" + ((Float) value).intValue(); + if (totalIncoming > 0) { + entries.add(new PieEntry(totalIncoming, context.getString(R.string.generator_phone_calls_incoming_label))); } - }); - pieChart.setData(data); - pieChart.invalidate(); + if (totalOutgoing > 0) { + entries.add(new PieEntry(totalOutgoing, context.getString(R.string.generator_phone_calls_outgoing_label))); + } - long latest = prefs.getLong(PhoneCalls.LAST_CALL_TIMESTAMP, 0); - long duration = prefs.getLong(PhoneCalls.LAST_CALL_DURATION, 0); - String direction = prefs.getString(PhoneCalls.LAST_CALL_TYPE, ""); + if (totalMissed > 0) { + entries.add(new PieEntry(totalMissed, context.getString(R.string.generator_phone_calls_missed_label))); + } + + long other = total - (totalIncoming + totalOutgoing + totalMissed); + + if (other > 0) { + entries.add(new PieEntry(other, context.getString(R.string.generator_phone_calls_other_label))); + } - TextView latestField = (TextView) holder.itemView.findViewById(R.id.field_latest_call); - TextView durationField = (TextView) holder.itemView.findViewById(R.id.field_duration); - TextView directionField = (TextView) holder.itemView.findViewById(R.id.field_direction); + PieDataSet set = new PieDataSet(entries, " "); - Date lateDate = new Date(latest); - String day = android.text.format.DateFormat.getMediumDateFormat(context).format(lateDate); - String time = android.text.format.DateFormat.getTimeFormat(context).format(lateDate); + int[] colors = { + R.color.generator_phone_call_incoming, + R.color.generator_phone_call_outgoing, + R.color.generator_phone_call_missed, + R.color.generator_phone_call_other + }; - latestField.setText(context.getString(R.string.format_full_timestamp_pdk, day, time)); - durationField.setText(context.getString(R.string.generator_phone_calls_duration_format, ((float) duration) / 60)); - directionField.setText(direction); + set.setColors(colors, context); - dateLabel.setText(Generator.formatTimestamp(context, latest / 1000)); + PieData data = new PieData(set); + data.setValueTextSize(14); + data.setValueTypeface(Typeface.DEFAULT_BOLD); + data.setValueTextColor(0xffffffff); + + data.setValueFormatter(new IValueFormatter() { + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return "" + ((Float) value).intValue(); + } + }); + + pieChart.setData(data); + pieChart.invalidate(); + + TextView latestField = (TextView) holder.itemView.findViewById(R.id.field_latest_call); + TextView durationField = (TextView) holder.itemView.findViewById(R.id.field_duration); + TextView directionField = (TextView) holder.itemView.findViewById(R.id.field_direction); + + Date lateDate = new Date(lastTimestamp); + String day = android.text.format.DateFormat.getMediumDateFormat(context).format(lateDate); + String time = android.text.format.DateFormat.getTimeFormat(context).format(lateDate); + + latestField.setText(context.getString(R.string.format_full_timestamp_pdk, day, time)); + durationField.setText(context.getString(R.string.generator_phone_calls_duration_format, ((float) lastDuration) / 60)); + directionField.setText(callType); + + dateLabel.setText(Generator.formatTimestamp(context, lastTimestamp / 1000)); + } else { + cardContent.setVisibility(View.GONE); + cardEmpty.setVisibility(View.VISIBLE); + + dateLabel.setText(R.string.label_never_pdk); + } + } + + @Override + public List fetchPayloads() { + return new ArrayList<>(); } public static View fetchView(ViewGroup parent) @@ -456,8 +525,19 @@ public static View fetchView(ViewGroup parent) return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_phone_calls, parent, false); } - public static void broadcastLatestDataPoint(Context context) { - Generators.getInstance(context).transmitData(PhoneCalls.GENERATOR_IDENTIFIER, new Bundle()); - } + public static long latestPointGenerated(Context context) { + long timestamp = 0; + + PhoneCalls me = PhoneCalls.getInstance(context); + + Cursor c = me.mDatabase.query(PhoneCalls.TABLE_HISTORY, null, null, null, null, null, PhoneCalls.HISTORY_OBSERVED + " DESC"); + if (c.moveToNext()) { + timestamp = c.getLong(c.getColumnIndex(PhoneCalls.HISTORY_OBSERVED)); + } + + c.close(); + + return timestamp; + } } diff --git a/src/com/audacious_software/passive_data_kit/generators/communication/TextMessages.java b/src/com/audacious_software/passive_data_kit/generators/communication/TextMessages.java new file mode 100755 index 0000000..3ad7c05 --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/generators/communication/TextMessages.java @@ -0,0 +1,469 @@ +package com.audacious_software.passive_data_kit.generators.communication; + +import android.Manifest; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.preference.PreferenceManager; +import android.support.v4.content.ContextCompat; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.audacious_software.passive_data_kit.PassiveDataKit; +import com.audacious_software.passive_data_kit.PhoneUtililties; +import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; +import com.audacious_software.passive_data_kit.activities.generators.RequestPermissionActivity; +import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; +import com.audacious_software.passive_data_kit.generators.Generator; +import com.audacious_software.passive_data_kit.generators.Generators; +import com.audacious_software.pdk.passivedatakit.R; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.DigestUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +public class TextMessages extends Generator { + private static final String GENERATOR_IDENTIFIER = "pdk-text-messages"; + + private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.communication.TextMessages.ENABLED"; + private static final boolean ENABLED_DEFAULT = true; + + private static final Uri SMS_INBOX_URI = Uri.parse("content://sms/inbox"); + private static final Uri SMS_SENT_URI = Uri.parse("content://sms/sent"); + + private static final String SMS_DATE = "date"; + private static final String SMS_BODY = "body"; + private static final String SMS_NUMBER_NAME = "person"; + private static final String SMS_NUMBER = "address"; + private static final String SMS_LENGTH = "length"; + private static final String SMS_DIRECTION = "direction"; + + private static int DATABASE_VERSION = 1; + + private static final String TABLE_HISTORY = "history"; + private static final String HISTORY_OBSERVED = "observed"; + private static final String HISTORY_DIRECTION = "direction"; + private static final String HISTORY_LENGTH = "length"; + private static final String HISTORY_BODY = "body"; + private static final String HISTORY_NUMBER_NAME = "number_name"; + private static final String HISTORY_NUMBER = "number"; + private static final String HISTORY_DIRECTION_INCOMING = "incoming"; + private static final String HISTORY_DIRECTION_OUTGOING = "outgoing"; + + private static TextMessages sInstance = null; + + private Handler mHandler = null; + private Context mContext = null; + + private static final String DATABASE_PATH = "pdk-text-messages.sqlite"; + + private SQLiteDatabase mDatabase = null; + private long mSampleInterval = 60000; + + public static TextMessages getInstance(Context context) { + if (TextMessages.sInstance == null) { + TextMessages.sInstance = new TextMessages(context.getApplicationContext()); + } + + return TextMessages.sInstance; + } + + public TextMessages(Context context) { + super(context); + + this.mContext = context.getApplicationContext(); + } + + public static void start(final Context context) { + TextMessages.getInstance(context).startGenerator(); + } + + private void startGenerator() { + final TextMessages me = this; + + if (this.mHandler != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + this.mHandler.getLooper().quitSafely(); + } else { + this.mHandler.getLooper().quit(); + } + + this.mHandler = null; + } + + final Runnable checkLogs = new Runnable() { + @Override + public void run() { + boolean approved = false; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (ContextCompat.checkSelfPermission(me.mContext, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED){ + approved = true; + } + } else { + approved = true; + } + + if (approved) { + long lastObserved = 0; + + Cursor lastCursor = me.mDatabase.query(TextMessages.TABLE_HISTORY, null, null, null, null, null, TextMessages.HISTORY_OBSERVED + " DESC"); + + if (lastCursor.moveToNext()) { + lastObserved = lastCursor.getLong(lastCursor.getColumnIndex(TextMessages.HISTORY_OBSERVED)); + } + + lastCursor.close(); + + ArrayList toTransmit = new ArrayList<>(); + + String where = TextMessages.SMS_DATE + " > ?"; + String[] args = {"" + lastObserved}; + + Cursor c = me.mContext.getContentResolver().query(TextMessages.SMS_INBOX_URI, null, where, args, TextMessages.SMS_DATE); + + while (c.moveToNext()) { + ContentValues values = new ContentValues(); + values.put(TextMessages.HISTORY_OBSERVED, c.getLong(c.getColumnIndex(TextMessages.SMS_DATE))); + + String body = c.getString(c.getColumnIndex(TextMessages.SMS_BODY)); + + values.put(TextMessages.HISTORY_LENGTH, body.length()); + values.put(TextMessages.HISTORY_BODY, body); + + String name = c.getString(c.getColumnIndex(TextMessages.SMS_NUMBER_NAME)); + String number = c.getString(c.getColumnIndex(TextMessages.SMS_NUMBER)); + + if (name == null) { + name = number; + } + + values.put(TextMessages.HISTORY_NUMBER_NAME, name); + values.put(TextMessages.HISTORY_NUMBER, number); + + values.put(TextMessages.HISTORY_DIRECTION, TextMessages.HISTORY_DIRECTION_INCOMING); + + toTransmit.add(values); + } + + c.close(); + + c = me.mContext.getContentResolver().query(TextMessages.SMS_SENT_URI, null, where, args, TextMessages.SMS_DATE); + + while (c.moveToNext()) { + ContentValues values = new ContentValues(); + values.put(TextMessages.HISTORY_OBSERVED, c.getLong(c.getColumnIndex(TextMessages.SMS_DATE))); + + String body = c.getString(c.getColumnIndex(TextMessages.SMS_BODY)); + + values.put(TextMessages.HISTORY_LENGTH, body.length()); + values.put(TextMessages.HISTORY_BODY, body); + + String name = c.getString(c.getColumnIndex(TextMessages.SMS_NUMBER_NAME)); + String number = c.getString(c.getColumnIndex(TextMessages.SMS_NUMBER)); + + if (name == null) { + name = number; + } + + values.put(TextMessages.HISTORY_NUMBER_NAME, name); + values.put(TextMessages.HISTORY_NUMBER, number); + + values.put(TextMessages.HISTORY_DIRECTION, TextMessages.HISTORY_DIRECTION_OUTGOING); + + toTransmit.add(values); + } + + c.close(); + + Collections.sort(toTransmit, new Comparator() { + @Override + public int compare(ContentValues one, ContentValues two) { + Long oneTime = one.getAsLong(TextMessages.HISTORY_OBSERVED); + Long twoTime = two.getAsLong(TextMessages.HISTORY_OBSERVED); + + return oneTime.compareTo(twoTime); + } + }); + + for (ContentValues values : toTransmit) { + String[] sensitiveFields = { + TextMessages.HISTORY_NUMBER_NAME, + TextMessages.HISTORY_NUMBER, + TextMessages.HISTORY_BODY, + }; + + for (String field : sensitiveFields) { + if (values.containsKey(field)) { + String value = values.getAsString(field); + + if (TextMessages.HISTORY_NUMBER.equals(TextMessages.HISTORY_NUMBER)) { + value = PhoneUtililties.normalizedPhoneNumber(value); + } + + values.put(field, new String(Hex.encodeHex(DigestUtils.sha256(value)))); + } + } + + Bundle bundle = new Bundle(); + bundle.putLong(TextMessages.SMS_DATE, values.getAsLong(TextMessages.HISTORY_OBSERVED)); + bundle.putInt(TextMessages.SMS_LENGTH, values.getAsInteger(TextMessages.HISTORY_LENGTH)); + bundle.putString(TextMessages.SMS_NUMBER_NAME, values.getAsString(TextMessages.HISTORY_NUMBER_NAME)); + bundle.putString(TextMessages.SMS_NUMBER, values.getAsString(TextMessages.HISTORY_NUMBER)); + bundle.putString(TextMessages.SMS_DIRECTION, values.getAsString(TextMessages.HISTORY_DIRECTION)); + bundle.putString(TextMessages.SMS_BODY, values.getAsString(TextMessages.HISTORY_BODY)); + + me.mDatabase.insert(TextMessages.TABLE_HISTORY, null, values); + + Generators.getInstance(me.mContext).notifyGeneratorUpdated(TextMessages.GENERATOR_IDENTIFIER, bundle); + } + } + + if (me.mHandler != null) { + me.mHandler.postDelayed(this, me.mSampleInterval); + } + } + }; + + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, TextMessages.DATABASE_PATH); + + this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); + + int version = this.getDatabaseVersion(this.mDatabase); + + switch (version) { + case 0: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_text_messages_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, TextMessages.DATABASE_VERSION); + + Runnable r = new Runnable() { + @Override + public void run() { + Looper.prepare(); + + me.mHandler = new Handler(); + + Looper.loop(); + } + }; + + Thread t = new Thread(r); + t.start(); + + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + me.mHandler.post(checkLogs); + + Generators.getInstance(this.mContext).registerCustomViewClass(TextMessages.GENERATOR_IDENTIFIER, TextMessages.class); + } + + public static boolean isEnabled(Context context) { + SharedPreferences prefs = Generators.getInstance(context).getSharedPreferences(context); + + return prefs.getBoolean(TextMessages.ENABLED, TextMessages.ENABLED_DEFAULT); + } + + public static boolean isRunning(Context context) { + if (TextMessages.sInstance == null) { + return false; + } + + return TextMessages.sInstance.mHandler != null; + } + + public static ArrayList diagnostics(final Context context) { + ArrayList actions = new ArrayList<>(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED){ + final Handler handler = new Handler(Looper.getMainLooper()); + + actions.add(new DiagnosticAction(context.getString(R.string.diagnostic_sms_log_permission_required), new Runnable() { + + @Override + public void run() { + handler.post(new Runnable() { + + @Override + public void run() { + Intent intent = new Intent(context, RequestPermissionActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(RequestPermissionActivity.PERMISSION, Manifest.permission.READ_SMS); + + context.startActivity(intent); + } + }); + } + })); + } + } + + return actions; + } + + public static void bindViewHolder(DataPointViewHolder holder) { + final Context context = holder.itemView.getContext(); + + long lastTimestamp = 0; + int lastLength = 0; + + long totalIncoming = 0; + long totalOutgoing = 0; + long total = 0; + + TextMessages generator = TextMessages.getInstance(holder.itemView.getContext()); + String lastDirection = null; + + Cursor c = generator.mDatabase.query(TextMessages.TABLE_HISTORY, null, null, null, null, null, TextMessages.HISTORY_OBSERVED + " DESC"); + + while (c.moveToNext()) { + if (lastTimestamp == 0) { + lastTimestamp = c.getLong(c.getColumnIndex(TextMessages.HISTORY_OBSERVED)); + lastDirection = c.getString(c.getColumnIndex(TextMessages.HISTORY_DIRECTION)); + lastLength = c.getInt(c.getColumnIndex(TextMessages.HISTORY_LENGTH)); + } + + total += 1; + + String direction = c.getString(c.getColumnIndex(TextMessages.HISTORY_DIRECTION)); + + if (TextMessages.HISTORY_DIRECTION_INCOMING.equals(direction)) { + totalIncoming += 1; + } else if (TextMessages.HISTORY_DIRECTION_OUTGOING.equals(direction)) { + totalOutgoing += 1; + } + } + + c.close(); + + View cardContent = holder.itemView.findViewById(R.id.card_content); + View cardEmpty = holder.itemView.findViewById(R.id.card_empty); + TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date); + + if (total > 0) { + cardContent.setVisibility(View.VISIBLE); + cardEmpty.setVisibility(View.GONE); + + dateLabel.setText(Generator.formatTimestamp(context, lastTimestamp)); + + PieChart pieChart = (PieChart) holder.itemView.findViewById(R.id.chart_text_messages); + pieChart.getLegend().setEnabled(false); + + pieChart.setEntryLabelColor(android.R.color.transparent); + pieChart.getDescription().setEnabled(false); + pieChart.setDrawHoleEnabled(false); + + List entries = new ArrayList<>(); + + if (totalIncoming > 0) { + entries.add(new PieEntry(totalIncoming, context.getString(R.string.generator_text_messages_incoming_label))); + } + + if (totalOutgoing > 0) { + entries.add(new PieEntry(totalOutgoing, context.getString(R.string.generator_text_messages_outgoing_label))); + } + + PieDataSet set = new PieDataSet(entries, " "); + + int[] colors = { + R.color.generator_text_messages_incoming, + R.color.generator_text_messages_outgoing + }; + + set.setColors(colors, context); + + PieData data = new PieData(set); + data.setValueTextSize(14); + data.setValueTypeface(Typeface.DEFAULT_BOLD); + data.setValueTextColor(0xffffffff); + + data.setValueFormatter(new IValueFormatter() { + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return "" + ((Float) value).intValue(); + } + }); + + pieChart.setData(data); + pieChart.invalidate(); + + TextView latestField = (TextView) holder.itemView.findViewById(R.id.field_latest_text_message); + TextView lengthField = (TextView) holder.itemView.findViewById(R.id.field_length); + TextView directionField = (TextView) holder.itemView.findViewById(R.id.field_direction); + + Date lateDate = new Date(lastTimestamp); + String day = android.text.format.DateFormat.getMediumDateFormat(context).format(lateDate); + String time = android.text.format.DateFormat.getTimeFormat(context).format(lateDate); + + latestField.setText(context.getString(R.string.format_full_timestamp_pdk, day, time)); + lengthField.setText(context.getString(R.string.generator_text_messages_length_format, lastLength)); + directionField.setText(lastDirection); + + dateLabel.setText(Generator.formatTimestamp(context, lastTimestamp / 1000)); + } else { + cardContent.setVisibility(View.GONE); + cardEmpty.setVisibility(View.VISIBLE); + + dateLabel.setText(R.string.label_never_pdk); + } + } + + @Override + public List fetchPayloads() { + return new ArrayList<>(); + } + + public static View fetchView(ViewGroup parent) + { + return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_text_messages, parent, false); + } + + public static long latestPointGenerated(Context context) { + long timestamp = 0; + + TextMessages me = TextMessages.getInstance(context); + + Cursor c = me.mDatabase.query(TextMessages.TABLE_HISTORY, null, null, null, null, null, TextMessages.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + timestamp = c.getLong(c.getColumnIndex(TextMessages.HISTORY_OBSERVED)); + } + + c.close(); + + return timestamp; + } +} diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Location.java b/src/com/audacious_software/passive_data_kit/generators/device/Location.java index 5b3abf4..c76a801 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/Location.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/Location.java @@ -1,25 +1,35 @@ package com.audacious_software.passive_data_kit.generators.device; import android.Manifest; +import android.app.Activity; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.content.res.ColorStateList; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.preference.PreferenceManager; -import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; +import android.support.v4.content.res.ResourcesCompat; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v7.widget.SwitchCompat; import android.util.DisplayMetrics; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.CompoundButton; import android.widget.TextView; import com.audacious_software.passive_data_kit.DeviceInformation; +import com.audacious_software.passive_data_kit.PassiveDataKit; import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; import com.audacious_software.passive_data_kit.activities.generators.RequestPermissionActivity; import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; @@ -35,16 +45,20 @@ import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.MapView; import com.google.android.gms.maps.OnMapReadyCallback; -import com.google.android.gms.maps.UiSettings; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; import com.google.android.gms.maps.model.MarkerOptions; +import com.google.maps.android.ui.IconGenerator; +import java.io.File; import java.util.ArrayList; +import java.util.List; @SuppressWarnings("unused") public class Location extends Generator implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { private static final String GENERATOR_IDENTIFIER = "pdk-location"; + private static final String DATABASE_PATH = "pdk-location.sqlite"; private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.Location.ENABLED"; private static final boolean ENABLED_DEFAULT = true; @@ -61,23 +75,29 @@ public class Location extends Generator implements GoogleApiClient.ConnectionCal private static final String BEARING_KEY = "bearing"; private static final String SPEED_KEY = "speed"; private static final String EXTRAS_KEY = "extras"; - - private static final String LAST_KNOWN_LATITUDE = "com.audacious_software.passive_data_kit.generators.device.Location.LAST_KNOWN_LATITUDE"; - private static final float LAST_KNOWN_LATITUDE_DEFAULT = 0; - - private static final String LAST_KNOWN_LONGITUDE = "com.audacious_software.passive_data_kit.generators.device.Location.LAST_KNOWN_LONGITUDE"; - private static final float LAST_KNOWN_LONGITUDE_DEFAULT = 0; - - private static final String LAST_KNOWN_TIMESTAMP = "com.audacious_software.passive_data_kit.generators.device.Location.LAST_KNOWN_TIMESTAMP";; + private static final String SETTING_DISPLAY_HYBRID_MAP = "com.audacious_software.passive_data_kit.generators.device.Location.SETTING_DISPLAY_HYBRID_MAP"; + private static final boolean SETTING_DISPLAY_HYBRID_MAP_DEFAULT = true; private static Location sInstance = null; private GoogleApiClient mGoogleApiClient = null; private android.location.Location mLastLocation = null; private long mUpdateInterval = 60000; - public static Location getInstance(Context context) { - Log.e("BB", "SHARED LOCATION INSTANCE: " + Location.sInstance); + private SQLiteDatabase mDatabase = null; + private static int DATABASE_VERSION = 1; + private static final String TABLE_HISTORY = "history"; + private static final String HISTORY_OBSERVED = "observed"; + private static final String HISTORY_LATITUDE = "latitude"; + private static final String HISTORY_LONGITUDE = "longitude"; + private static final String HISTORY_ALTITUDE = "altitude"; + private static final String HISTORY_BEARING = "bearing"; + private static final String HISTORY_SPEED = "speed"; + private static final String HISTORY_PROVIDER = "provider"; + private static final String HISTORY_LOCATION_TIMESTAMP = "location_timestamp"; + private static final String HISTORY_ACCURACY = "accuracy"; + + public static Location getInstance(Context context) { if (Location.sInstance == null) { Location.sInstance = new Location(context.getApplicationContext()); } @@ -130,6 +150,21 @@ else if (Location.useGoogleLocationServices(me.mContext)) t.start(); Generators.getInstance(this.mContext).registerCustomViewClass(Location.GENERATOR_IDENTIFIER, Location.class); + + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, Location.DATABASE_PATH); + + this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); + + int version = this.getDatabaseVersion(this.mDatabase); + + switch (version) { + case 0: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_location_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, Location.DATABASE_VERSION); } private void stopGenerator() { @@ -137,6 +172,9 @@ private void stopGenerator() { this.mGoogleApiClient.disconnect(); this.mGoogleApiClient = null; } + + this.mDatabase.close(); + this.mDatabase = null; } public static boolean useGoogleLocationServices(Context context) { @@ -220,8 +258,6 @@ public void onConnected(Bundle bundle) { final LocationRequest request = new LocationRequest(); request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); - Log.e("BB", "USING INTERVAL: " + this.mUpdateInterval); - request.setFastestInterval(this.mUpdateInterval); request.setInterval(this.mUpdateInterval); @@ -234,8 +270,6 @@ public void onConnected(Bundle bundle) { @Override public void onConnectionSuspended(int i) { - Log.e("BB", "DISCONNECTING"); - if (this.mGoogleApiClient != null && this.mGoogleApiClient.isConnected()) LocationServices.FusedLocationApi.removeLocationUpdates(this.mGoogleApiClient, this); } @@ -252,80 +286,103 @@ public void onLocationChanged(android.location.Location location) { long now = System.currentTimeMillis(); - Log.e("BB", "LOCATION UPDATE"); + ContentValues values = new ContentValues(); + values.put(Location.HISTORY_OBSERVED, System.currentTimeMillis()); + values.put(Location.HISTORY_LATITUDE, location.getLatitude()); + values.put(Location.HISTORY_LONGITUDE, location.getLongitude()); + values.put(Location.HISTORY_PROVIDER, location.getProvider()); + values.put(Location.HISTORY_LOCATION_TIMESTAMP, location.getTime()); - Bundle bundle = new Bundle(); + Bundle updated = new Bundle(); + updated.putLong(Location.HISTORY_OBSERVED, System.currentTimeMillis()); + updated.putDouble(Location.HISTORY_LATITUDE, location.getLatitude()); + updated.putDouble(Location.HISTORY_LONGITUDE, location.getLongitude()); + updated.putString(Location.HISTORY_PROVIDER, location.getProvider()); + updated.putLong(Location.HISTORY_LOCATION_TIMESTAMP, location.getTime()); - bundle.putDouble(Location.LATITUDE_KEY, location.getLatitude()); - bundle.putDouble(Location.LONGITUDE_KEY, location.getLongitude()); - bundle.putDouble(Location.FIX_TIMESTAMP_KEY, ((double) location.getTime()) / 1000); - bundle.putString(Location.PROVIDER_KEY, location.getProvider()); + Bundle metadata = new Bundle(); + metadata.putDouble(Generator.LATITUDE, location.getLatitude()); + metadata.putDouble(Generator.LONGITUDE, location.getLongitude()); - this.mLastLocation = location; - - if (location.hasAccuracy()) { - bundle.putFloat(Location.ACCURACY_KEY, location.getAccuracy()); - } + updated.putBundle(Generator.PDK_METADATA, metadata); if (location.hasAltitude()) { - bundle.putDouble(Location.ALTITUDE_KEY, location.getAltitude()); + values.put(Location.HISTORY_ALTITUDE, location.getAltitude()); + updated.putDouble(Location.HISTORY_ALTITUDE, location.getAltitude()); } if (location.hasBearing()) { - bundle.putFloat(Location.BEARING_KEY, location.getBearing()); + values.put(Location.HISTORY_BEARING, location.getBearing()); + updated.putDouble(Location.HISTORY_BEARING, location.getBearing()); } if (location.hasSpeed()) { - bundle.putFloat(Location.SPEED_KEY, location.getSpeed()); + values.put(Location.HISTORY_SPEED, location.getBearing()); + updated.putDouble(Location.HISTORY_SPEED, location.getBearing()); } - Bundle extras = location.getExtras(); - - if (extras != null) { - bundle.putBundle(Location.EXTRAS_KEY, extras); + if (location.hasAccuracy()) { + values.put(Location.HISTORY_ACCURACY, location.getAccuracy()); + updated.putDouble(Location.HISTORY_ACCURACY, location.getAccuracy()); } - Generators.getInstance(this.mContext).transmitData(Location.GENERATOR_IDENTIFIER, bundle); + this.mDatabase.insert(Location.TABLE_HISTORY, null, values); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext); - SharedPreferences.Editor e = prefs.edit(); + Generators.getInstance(this.mContext).notifyGeneratorUpdated(Location.GENERATOR_IDENTIFIER, updated); + } - e.putFloat(Location.LAST_KNOWN_LATITUDE, (float) location.getLatitude()); - e.putFloat(Location.LAST_KNOWN_LONGITUDE, (float) location.getLongitude()); - e.putLong(Location.LAST_KNOWN_TIMESTAMP, System.currentTimeMillis()); + public static long latestPointGenerated(Context context) { + long timestamp = 0; - e.apply(); - } + Location me = Location.getInstance(context); - public static void bindViewHolder(DataPointViewHolder holder, final Bundle dataPoint) { - Log.e("PDK", "DRAWING LOCATION: " + dataPoint); + Cursor c = me.mDatabase.query(Location.TABLE_HISTORY, null, null, null, null, null, Location.HISTORY_OBSERVED + " DESC"); + if (c.moveToNext()) { + timestamp = c.getLong(c.getColumnIndex(Location.HISTORY_OBSERVED)); + } + + c.close(); + + return timestamp; + } + + public static void bindViewHolder(DataPointViewHolder holder) { final Context context = holder.itemView.getContext(); - String identifier = dataPoint.getBundle(Generator.PDK_METADATA).getString(Generator.IDENTIFIER); + Location me = Location.getInstance(context); + + double lastLatitude = 0.0; + double lastLongitude = 0.0; + long timestamp = 0; + + final List locations = new ArrayList<>(); + + String where = Location.HISTORY_OBSERVED + " > ?"; + String[] args = { "" + (System.currentTimeMillis() - (1000 * 60 * 60 * 24)) }; - double timestamp = dataPoint.getBundle(Generator.PDK_METADATA).getDouble(Generator.TIMESTAMP); + Cursor c = me.mDatabase.query(Location.TABLE_HISTORY, null, where, args, null, null, Location.HISTORY_OBSERVED); - double latitude = Location.LAST_KNOWN_LATITUDE_DEFAULT; - double longitude = Location.LAST_KNOWN_LONGITUDE_DEFAULT; + while (c.moveToNext()) { + lastLatitude = c.getDouble(c.getColumnIndex(Location.HISTORY_LATITUDE)); + lastLongitude = c.getDouble(c.getColumnIndex(Location.HISTORY_LONGITUDE)); + timestamp = c.getLong(c.getColumnIndex(Location.HISTORY_OBSERVED)); - if (dataPoint.containsKey(Location.LATITUDE_KEY) && dataPoint.containsKey(Location.LONGITUDE_KEY)) { - latitude = dataPoint.getDouble(Location.LATITUDE_KEY); - longitude = dataPoint.getDouble(Location.LONGITUDE_KEY); - } else { // Empty point - retrieve last known location... - Log.e("PDK", "FETCHING LAST KNOWN LOCATION..."); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + LatLng location = new LatLng(lastLatitude, lastLongitude); - latitude = prefs.getFloat(Location.LAST_KNOWN_LATITUDE, Location.LAST_KNOWN_LATITUDE_DEFAULT); - longitude = prefs.getFloat(Location.LAST_KNOWN_LONGITUDE, Location.LAST_KNOWN_LONGITUDE_DEFAULT); - timestamp = ((double) prefs.getLong(Location.LAST_KNOWN_TIMESTAMP, System.currentTimeMillis())) / 1000 ; + locations.add(location); } + c.close(); + TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date); - dateLabel.setText(Generator.formatTimestamp(context, timestamp)); + dateLabel.setText(Generator.formatTimestamp(context, timestamp / 1000)); + + final double finalLatitude = lastLatitude; + final double finalLongitude = lastLongitude; - final double finalLatitude = latitude; - final double finalLongitude = longitude; + final DisplayMetrics metrics = new DisplayMetrics(); + ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics); if (Location.useKindleLocationServices()) { @@ -337,24 +394,93 @@ else if (Location.useGoogleLocationServices(holder.itemView.getContext())) final MapView mapView = (MapView) holder.itemView.findViewById(R.id.map_view); mapView.onCreate(null); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + final boolean useHybrid = prefs.getBoolean(Location.SETTING_DISPLAY_HYBRID_MAP, Location.SETTING_DISPLAY_HYBRID_MAP_DEFAULT); + + SwitchCompat hybridSwitch = (SwitchCompat) holder.itemView.findViewById(R.id.pdk_google_location_map_type_hybrid); + hybridSwitch.setChecked(useHybrid); + + hybridSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, final boolean checked) { + SharedPreferences.Editor e = prefs.edit(); + e.putBoolean(Location.SETTING_DISPLAY_HYBRID_MAP, checked); + e.apply(); + + mapView.getMapAsync(new OnMapReadyCallback() { + public void onMapReady(GoogleMap googleMap) { + if (checked) { + googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); + } else { + googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); + } + } + }); + } + }); + + ColorStateList buttonStates = new ColorStateList( + new int[][]{ + new int[]{android.R.attr.state_checked}, + new int[]{-android.R.attr.state_enabled}, + new int[]{} + }, + new int[]{ + 0xfff1f1f1, + 0x1c000000, + 0xff33691E + } + ); + + DrawableCompat.setTintList(hybridSwitch.getThumbDrawable(), buttonStates); + + IconGenerator iconGen = new IconGenerator(context); + + Drawable shapeDrawable = ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_location_heatmap_marker, null); + iconGen.setBackground(shapeDrawable); + + View view = new View(context); + view.setLayoutParams(new ViewGroup.LayoutParams(8, 8)); + iconGen.setContentView(view); + + final Bitmap bitmap = iconGen.makeIcon(); + mapView.getMapAsync(new OnMapReadyCallback() { public void onMapReady(GoogleMap googleMap) { - googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); - googleMap.getUiSettings().setZoomControlsEnabled(false); + if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED || + ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + googleMap.setMyLocationEnabled(true); + } + + if (useHybrid) { + googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); + } else { + googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); + } + + googleMap.getUiSettings().setZoomControlsEnabled(true); googleMap.getUiSettings().setMyLocationButtonEnabled(false); + googleMap.getUiSettings().setMapToolbarEnabled(false); + googleMap.getUiSettings().setAllGesturesEnabled(false); + + LatLngBounds.Builder builder = new LatLngBounds.Builder(); - googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(finalLatitude, finalLongitude), 14)); + for (LatLng latlng : locations) { + builder.include(latlng); + } - googleMap.addMarker(new MarkerOptions() - .position(new LatLng(finalLatitude, finalLongitude))); -// .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker_none))); + googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), (int) (16 * metrics.density))); DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - googleMap.setPadding(0, 0, 0, (int) (32 * metrics.density)); + for (LatLng latLng : locations) { + googleMap.addMarker(new MarkerOptions() + .position(latLng) + .icon(BitmapDescriptorFactory.fromBitmap(bitmap))); + } - UiSettings settings = googleMap.getUiSettings(); - settings.setMapToolbarEnabled(false); + mapView.onResume(); } }); } @@ -363,9 +489,11 @@ public void onMapReady(GoogleMap googleMap) { // TODO throw new RuntimeException("Throw rocks at developer to implement generic location support."); } + } - TextView description = (TextView) holder.itemView.findViewById(R.id.generator_location_description); - description.setText(context.getResources().getString(R.string.generator_location_value, latitude, longitude)); + @Override + public List fetchPayloads() { + return new ArrayList(); } public static View fetchView(ViewGroup parent) @@ -398,26 +526,16 @@ public android.location.Location getLastKnownLocation() { if (ContextCompat.checkSelfPermission(this.mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this.mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - Log.e("FC", "LOCATION PERMISSIONS GRANTED..."); - last = locations.getLastKnownLocation(LocationManager.GPS_PROVIDER); - Log.e("FC", "GPS: " + last); - if (last == null) { last = locations.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); - - Log.e("FC", "NETWORK: " + last); } } return last; } - public static void broadcastLatestDataPoint(Context context) { - Generators.getInstance(context).transmitData(Location.GENERATOR_IDENTIFIER, new Bundle()); - } - public void setUpdateInterval(long interval) { this.mUpdateInterval = interval; diff --git a/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java b/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java index 4d314aa..2107447 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java @@ -1,13 +1,15 @@ package com.audacious_software.passive_data_kit.generators.device; import android.content.BroadcastReceiver; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; import android.os.Build; import android.os.Bundle; -import android.preference.PreferenceManager; import android.view.Display; import android.view.LayoutInflater; import android.view.View; @@ -16,20 +18,18 @@ import android.widget.LinearLayout; import android.widget.TextView; +import com.audacious_software.passive_data_kit.PassiveDataKit; import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; import com.audacious_software.passive_data_kit.generators.Generator; import com.audacious_software.passive_data_kit.generators.Generators; import com.audacious_software.pdk.passivedatakit.R; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - +import java.io.File; import java.text.DateFormat; import java.util.ArrayList; import java.util.Calendar; -import java.util.Date; +import java.util.List; public class ScreenState extends Generator{ private static final String GENERATOR_IDENTIFIER = "pdk-screen-state"; @@ -37,20 +37,24 @@ public class ScreenState extends Generator{ private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.ScreenState.ENABLED"; private static final boolean ENABLED_DEFAULT = true; - private static final String SCREEN_STATE_KEY = "screen_state"; private static final String STATE_DOZE = "doze"; private static final String STATE_DOZE_SUSPEND = "doze_suspend"; private static final String STATE_ON = "on"; private static final String STATE_OFF = "off"; private static final String STATE_UNKNOWN = "unknown"; - private static final String SCREEN_HISTORY_KEY = "com.audacious_software.passive_data_kit.generators.device.ScreenState.SCREEN_HISTORY_KEY";; - private static final String SCREEN_HISTORY_TIMESTAMP = "ts"; - private static final String SCREEN_HISTORY_STATE = "state"; + + private static final String DATABASE_PATH = "pdk-screen-state.sqlite"; + private static final int DATABASE_VERSION = 2; + private static final String HISTORY_OBSERVED = "observed"; + private static final String HISTORY_STATE = "state"; + private static final String TABLE_HISTORY = "history"; private static ScreenState sInstance = null; private BroadcastReceiver mReceiver = null; + private SQLiteDatabase mDatabase = null; + public static ScreenState getInstance(Context context) { if (ScreenState.sInstance == null) { ScreenState.sInstance = new ScreenState(context.getApplicationContext()); @@ -68,10 +72,18 @@ public static void start(final Context context) { } private void startGenerator() { + final ScreenState me = this; + this.mReceiver = new BroadcastReceiver() { @Override - public void onReceive(Context context, Intent intent) { - Bundle bundle = new Bundle(); + public void onReceive(final Context context, Intent intent) { + long now = System.currentTimeMillis(); + + ContentValues values = new ContentValues(); + values.put(ScreenState.HISTORY_OBSERVED, now); + + Bundle update = new Bundle(); + update.putLong(ScreenState.HISTORY_OBSERVED, now); WindowManager window = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = window.getDefaultDisplay(); @@ -81,62 +93,36 @@ public void onReceive(Context context, Intent intent) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { switch (display.getState()) { case Display.STATE_DOZE: - bundle.putString(ScreenState.SCREEN_STATE_KEY, ScreenState.STATE_DOZE); + values.put(ScreenState.HISTORY_STATE, ScreenState.STATE_DOZE); break; case Display.STATE_DOZE_SUSPEND: - bundle.putString(ScreenState.SCREEN_STATE_KEY, ScreenState.STATE_DOZE_SUSPEND); + values.put(ScreenState.HISTORY_STATE, ScreenState.STATE_DOZE_SUSPEND); break; case Display.STATE_ON: - bundle.putString(ScreenState.SCREEN_STATE_KEY, ScreenState.STATE_ON); + values.put(ScreenState.HISTORY_STATE, ScreenState.STATE_ON); break; case Display.STATE_OFF: - bundle.putString(ScreenState.SCREEN_STATE_KEY, ScreenState.STATE_OFF); + values.put(ScreenState.HISTORY_STATE, ScreenState.STATE_OFF); break; case Display.STATE_UNKNOWN: - bundle.putString(ScreenState.SCREEN_STATE_KEY, ScreenState.STATE_UNKNOWN); + values.put(ScreenState.HISTORY_STATE, ScreenState.STATE_UNKNOWN); break; } } else { if (Intent.ACTION_SCREEN_OFF.equals(action)) { - bundle.putString(ScreenState.SCREEN_STATE_KEY, ScreenState.STATE_OFF); + values.put(ScreenState.HISTORY_STATE, ScreenState.STATE_OFF); } else if (Intent.ACTION_SCREEN_ON.equals(action)) { - bundle.putString(ScreenState.SCREEN_STATE_KEY, ScreenState.STATE_ON); + values.put(ScreenState.HISTORY_STATE, ScreenState.STATE_ON); } else { - bundle.putString(ScreenState.SCREEN_STATE_KEY, ScreenState.STATE_UNKNOWN); + values.put(ScreenState.HISTORY_STATE, ScreenState.STATE_UNKNOWN); } } - Generators.getInstance(context).transmitData(ScreenState.GENERATOR_IDENTIFIER, bundle); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - try { - JSONArray history = new JSONArray(prefs.getString(ScreenState.SCREEN_HISTORY_KEY, "[]")); + me.mDatabase.insert(ScreenState.TABLE_HISTORY, null, values); - JSONObject latest = new JSONObject(); - latest.put(ScreenState.SCREEN_HISTORY_TIMESTAMP, System.currentTimeMillis()); + update.putString(ScreenState.HISTORY_STATE, values.getAsString(ScreenState.HISTORY_STATE)); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { - latest.put(ScreenState.SCREEN_HISTORY_STATE, display.getState()); - } else { - if (Intent.ACTION_SCREEN_OFF.equals(action)) { - latest.put(ScreenState.SCREEN_HISTORY_STATE, 0x01); - } else if (Intent.ACTION_SCREEN_ON.equals(action)) { - latest.put(ScreenState.SCREEN_HISTORY_STATE, 0x02); - } else { - latest.put(ScreenState.SCREEN_HISTORY_STATE, 0x00); - } - } - - history.put(latest); - - SharedPreferences.Editor e = prefs.edit(); - - e.putString(ScreenState.SCREEN_HISTORY_KEY, history.toString()); - e.apply(); - } catch (JSONException e) { - e.printStackTrace(); - } + Generators.getInstance(context).notifyGeneratorUpdated(ScreenState.GENERATOR_IDENTIFIER, update); } }; @@ -147,7 +133,23 @@ public void onReceive(Context context, Intent intent) { Generators.getInstance(this.mContext).registerCustomViewClass(ScreenState.GENERATOR_IDENTIFIER, ScreenState.class); - this.mReceiver.onReceive(this.mContext, null); + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, ScreenState.DATABASE_PATH); + + this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); + + int version = this.getDatabaseVersion(this.mDatabase); + + switch (version) { + case 0: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_screen_state_create_history_table)); + case 1: + this.mDatabase.execSQL("DROP TABLE history"); + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_screen_state_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, ScreenState.DATABASE_VERSION); } public static boolean isEnabled(Context context) { @@ -168,95 +170,111 @@ public static ArrayList diagnostics(Context context) { return new ArrayList<>(); } - public static void bindViewHolder(DataPointViewHolder holder, final Bundle dataPoint) { + public static void bindViewHolder(DataPointViewHolder holder) { final Context context = holder.itemView.getContext(); - try { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - JSONArray history = new JSONArray(prefs.getString(ScreenState.SCREEN_HISTORY_KEY, "[]")); - -// Log.e("PDK", "SCREEN HISTORY: " + history.toString(2)); + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); - Calendar cal = Calendar.getInstance(); - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); + long zeroStart = cal.getTimeInMillis(); + cal.add(Calendar.DATE, -1); - long zeroStart = cal.getTimeInMillis(); - cal.add(Calendar.DATE, -1); + LinearLayout zeroTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_zero_value); - LinearLayout zeroTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_zero_value); + ScreenState.populateTimeline(context, zeroTimeline, zeroStart); - ScreenState.populateTimeline(context, zeroTimeline, zeroStart, history); + long oneStart = cal.getTimeInMillis(); + cal.add(Calendar.DATE, -1); - long oneStart = cal.getTimeInMillis(); - cal.add(Calendar.DATE, -1); + LinearLayout oneTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_one_value); - LinearLayout oneTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_one_value); + ScreenState.populateTimeline(context, oneTimeline, oneStart); - ScreenState.populateTimeline(context, oneTimeline, oneStart, history); + long twoStart = cal.getTimeInMillis(); - long twoStart = cal.getTimeInMillis(); + LinearLayout twoTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_two_value); - LinearLayout twoTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_two_value); + ScreenState.populateTimeline(context, twoTimeline, twoStart); - ScreenState.populateTimeline(context, twoTimeline, twoStart, history); - } catch (JSONException e) { - e.printStackTrace(); - } + ScreenState generator = ScreenState.getInstance(context); - double timestamp = dataPoint.getBundle(Generator.PDK_METADATA).getDouble(Generator.TIMESTAMP); + Cursor c = generator.mDatabase.query(ScreenState.TABLE_HISTORY, null, null, null, null, null, ScreenState.HISTORY_OBSERVED + " DESC"); + View cardContent = holder.itemView.findViewById(R.id.card_content); + View cardEmpty = holder.itemView.findViewById(R.id.card_empty); TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date); - dateLabel.setText(Generator.formatTimestamp(context, timestamp)); + if (c.moveToNext()) { + cardContent.setVisibility(View.VISIBLE); + cardEmpty.setVisibility(View.GONE); - Calendar cal = Calendar.getInstance(); - DateFormat format = android.text.format.DateFormat.getDateFormat(context); + long timestamp = c.getLong(c.getColumnIndex(ScreenState.HISTORY_OBSERVED)) / 1000; - TextView zeroDayLabel = (TextView) holder.itemView.findViewById(R.id.day_zero_label); - zeroDayLabel.setText(format.format(cal.getTime())); + dateLabel.setText(Generator.formatTimestamp(context, timestamp)); - cal.add(Calendar.DATE, -1); + cal = Calendar.getInstance(); + DateFormat format = android.text.format.DateFormat.getDateFormat(context); - TextView oneDayLabel = (TextView) holder.itemView.findViewById(R.id.day_one_label); - oneDayLabel.setText(format.format(cal.getTime())); + TextView zeroDayLabel = (TextView) holder.itemView.findViewById(R.id.day_zero_label); + zeroDayLabel.setText(format.format(cal.getTime())); - cal.add(Calendar.DATE, -1); + cal.add(Calendar.DATE, -1); + + TextView oneDayLabel = (TextView) holder.itemView.findViewById(R.id.day_one_label); + oneDayLabel.setText(format.format(cal.getTime())); + + cal.add(Calendar.DATE, -1); + + TextView twoDayLabel = (TextView) holder.itemView.findViewById(R.id.day_two_label); + twoDayLabel.setText(format.format(cal.getTime())); + } else { + cardContent.setVisibility(View.GONE); + cardEmpty.setVisibility(View.VISIBLE); + + dateLabel.setText(R.string.label_never_pdk); + } - TextView twoDayLabel = (TextView) holder.itemView.findViewById(R.id.day_two_label); - twoDayLabel.setText(format.format(cal.getTime())); + c.close(); } - private static void populateTimeline(Context context, LinearLayout timeline, long start, JSONArray history) { + private static void populateTimeline(Context context, LinearLayout timeline, long start) { timeline.removeAllViews(); + ScreenState generator = ScreenState.getInstance(context); + long end = start + (24 * 60 * 60 * 1000); - long now = System.currentTimeMillis(); + String where = ScreenState.HISTORY_OBSERVED + " >= ? AND " + ScreenState.HISTORY_OBSERVED + " < ?"; + String[] args = { "" + start, "" + end }; - int lastState = -1; + Cursor c = generator.mDatabase.query(ScreenState.TABLE_HISTORY, null, where, args, null, null, ScreenState.HISTORY_OBSERVED); - ArrayList activeStates = new ArrayList<>(); + ArrayList activeStates = new ArrayList<>(); ArrayList activeTimestamps = new ArrayList<>(); - for (int i = 0; i < history.length(); i++) { - try { - JSONObject point = history.getJSONObject(i); + while (c.moveToNext()) { + long timestamp = c.getLong(c.getColumnIndex(ScreenState.HISTORY_OBSERVED)); - long timestamp = point.getLong(ScreenState.SCREEN_HISTORY_TIMESTAMP); - int state = point.getInt(ScreenState.SCREEN_HISTORY_STATE); + activeTimestamps.add(timestamp); - if (timestamp < start) { - lastState = state; - } else if (timestamp < end) { - activeStates.add(state); - activeTimestamps.add(timestamp); - } - } catch (JSONException e) { - e.printStackTrace(); - } + String state = c.getString(c.getColumnIndex(ScreenState.HISTORY_STATE)); + activeStates.add(state); + } + + c.close(); + + String lastState = ScreenState.STATE_UNKNOWN; + + String lastWhere = ScreenState.HISTORY_OBSERVED + " < ?"; + String[] lastArgs = { "" + start }; + + c = generator.mDatabase.query(ScreenState.TABLE_HISTORY, null, lastWhere, lastArgs, null, null, ScreenState.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + lastState = c.getString(c.getColumnIndex(ScreenState.HISTORY_STATE)); } if (activeStates.size() > 0) { @@ -265,25 +283,24 @@ private static void populateTimeline(Context context, LinearLayout timeline, lon View startView = new View(context); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { - if (lastState == -1) { + if (ScreenState.STATE_UNKNOWN.equals(lastState)) { - } else if (firstState == Display.STATE_ON) { + } else if (ScreenState.STATE_ON.equals(lastState)) { startView.setBackgroundColor(0xff4CAF50); - } else if (firstState == Display.STATE_OFF) { + } else if (ScreenState.STATE_OFF.equals(lastState)) { startView.setBackgroundColor(0xff263238); - } else if (firstState == Display.STATE_DOZE) { + } else if (ScreenState.STATE_DOZE.equals(lastState)) { startView.setBackgroundColor(0xff1b5e20); - } else if (firstState == Display.STATE_DOZE_SUSPEND) { + } else if (ScreenState.STATE_DOZE_SUSPEND.equals(lastState)) { startView.setBackgroundColor(0xff1b5e20); } } else { - if (lastState == -1) { + if (ScreenState.STATE_UNKNOWN.equals(lastState)) { - } else if (firstState == 0x02) { + } else if (ScreenState.STATE_ON.equals(lastState)) { startView.setBackgroundColor(0xff4CAF50); - } else if (firstState == 0x01) { + } else if (ScreenState.STATE_OFF.equals(lastState)) { startView.setBackgroundColor(0xff263238); } } @@ -293,28 +310,30 @@ private static void populateTimeline(Context context, LinearLayout timeline, lon timeline.addView(startView); + long now = System.currentTimeMillis(); + if (activeStates.size() == 1) { View v = new View(context); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { - if (firstState == Display.STATE_ON) { + if (ScreenState.STATE_ON.equals(firstState)) { v.setBackgroundColor(0xff4CAF50); - } else if (firstState == Display.STATE_OFF) { + } else if (ScreenState.STATE_OFF.equals(firstState)) { v.setBackgroundColor(0xff263238); - } else if (firstState == Display.STATE_DOZE) { + } else if (ScreenState.STATE_DOZE.equals(firstState)) { v.setBackgroundColor(0xff3f51b5); - } else if (firstState == Display.STATE_DOZE_SUSPEND) { + } else if (ScreenState.STATE_DOZE_SUSPEND.equals(firstState)) { v.setBackgroundColor(0xff3f51b5); } } else { - if (firstState == 0x02) { + if (ScreenState.STATE_ON.equals(firstState)) { v.setBackgroundColor(0xff4CAF50); - } else if (firstState == 0x01) { + } else if (ScreenState.STATE_OFF.equals(firstState)) { v.setBackgroundColor(0xff263238); } } - if (end > now) { + if (end > System.currentTimeMillis()) { params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, now - firstTimestamp); v.setLayoutParams(params); } else { @@ -328,24 +347,24 @@ private static void populateTimeline(Context context, LinearLayout timeline, lon long currentTimestamp = activeTimestamps.get(i); long priorTimestamp = activeTimestamps.get(i - 1); - long priorState = activeStates.get(i - 1); + String priorState = activeStates.get(i - 1); View v = new View(context); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { - if (priorState == Display.STATE_ON) { + if (ScreenState.STATE_ON.equals(priorState)) { v.setBackgroundColor(0xff4CAF50); - } else if (priorState == Display.STATE_OFF) { + } else if (ScreenState.STATE_OFF.equals(priorState)) { v.setBackgroundColor(0xff263238); - } else if (priorState == Display.STATE_DOZE) { + } else if (ScreenState.STATE_DOZE.equals(priorState)) { v.setBackgroundColor(0xff3f51b5); - } else if (priorState == Display.STATE_DOZE_SUSPEND) { + } else if (ScreenState.STATE_DOZE_SUSPEND.equals(priorState)) { v.setBackgroundColor(0xff3f51b5); } } else { - if (priorState == 0x02) { + if (ScreenState.STATE_ON.equals(priorState)) { v.setBackgroundColor(0xff4CAF50); - } else if (priorState == 0x01) { + } else if (ScreenState.STATE_OFF.equals(priorState)) { v.setBackgroundColor(0xff263238); } } @@ -357,17 +376,17 @@ private static void populateTimeline(Context context, LinearLayout timeline, lon } long finalTimestamp = activeTimestamps.get(activeTimestamps.size() - 1); - long finalState = activeStates.get(activeStates.size() - 1); + String finalState = activeStates.get(activeStates.size() - 1); View v = new View(context); - if (finalState == Display.STATE_ON) { + if (ScreenState.STATE_ON.equals(finalState)) { v.setBackgroundColor(0xff4CAF50); - } else if (finalState == Display.STATE_OFF) { + } else if (ScreenState.STATE_OFF.equals(finalState)) { v.setBackgroundColor(0xff263238); - } else if (finalState == Display.STATE_DOZE) { + } else if (ScreenState.STATE_DOZE.equals(finalState)) { v.setBackgroundColor(0xff3f51b5); - } else if (finalState == Display.STATE_DOZE_SUSPEND) { + } else if (ScreenState.STATE_DOZE_SUSPEND.equals(finalState)) { v.setBackgroundColor(0xff3f51b5); } @@ -390,6 +409,8 @@ private static void populateTimeline(Context context, LinearLayout timeline, lon timeline.addView(v); } + } else { + } } @@ -398,7 +419,24 @@ public static View fetchView(ViewGroup parent) return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_screen_state, parent, false); } - public static void broadcastLatestDataPoint(Context context) { - Generators.getInstance(context).transmitData(ScreenState.GENERATOR_IDENTIFIER, new Bundle()); + @Override + public List fetchPayloads() { + return new ArrayList<>(); + } + + public static long latestPointGenerated(Context context) { + long timestamp = 0; + + ScreenState me = ScreenState.getInstance(context); + + Cursor c = me.mDatabase.query(ScreenState.TABLE_HISTORY, null, null, null, null, null, ScreenState.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + timestamp = c.getLong(c.getColumnIndex(ScreenState.HISTORY_OBSERVED)); + } + + c.close(); + + return timestamp; } } diff --git a/src/com/audacious_software/passive_data_kit/generators/services/GoogleAwareness.java b/src/com/audacious_software/passive_data_kit/generators/services/GoogleAwareness.java index 796b1c2..9aea317 100755 --- a/src/com/audacious_software/passive_data_kit/generators/services/GoogleAwareness.java +++ b/src/com/audacious_software/passive_data_kit/generators/services/GoogleAwareness.java @@ -28,6 +28,7 @@ import com.google.android.gms.location.DetectedActivity; import java.util.ArrayList; +import java.util.List; /** * Created by cjkarr on 6/28/2016. @@ -219,4 +220,9 @@ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { Log.e("PDK", "GA onConnectionFailed"); this.mGoogleApiClient = null; } + + @Override + public List fetchPayloads() { + return new ArrayList<>(); + } } diff --git a/src/com/audacious_software/passive_data_kit/generators/wearables/MicrosoftBand.java b/src/com/audacious_software/passive_data_kit/generators/wearables/MicrosoftBand.java index 295aade..b8f4d3b 100755 --- a/src/com/audacious_software/passive_data_kit/generators/wearables/MicrosoftBand.java +++ b/src/com/audacious_software/passive_data_kit/generators/wearables/MicrosoftBand.java @@ -65,6 +65,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.List; @SuppressWarnings("unused") public class MicrosoftBand extends Generator @@ -543,7 +544,7 @@ private void transmitData(BandSensorManager sensors) { this.mHeartRateVariabilityDataPoints.clear(); } - Generators.getInstance(this.mContext).transmitData(MicrosoftBand.GENERATOR_IDENTIFIER, bundle); +// Generators.getInstance(this.mContext).transmitData(MicrosoftBand.GENERATOR_IDENTIFIER, bundle); } public static ArrayList diagnostics(Context context) @@ -1429,4 +1430,9 @@ public static void bindViewHolder(DataPointViewHolder holder, Bundle dataPoint) uvLevel.setText(context.getString(R.string.generator_value_not_applicable)); } } + + @Override + public List fetchPayloads() { + return new ArrayList<>(); + } } diff --git a/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java b/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java index ab65bc8..863d30e 100755 --- a/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java +++ b/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java @@ -46,7 +46,7 @@ import okhttp3.Response; import okio.Buffer; -public class HttpTransmitter extends Transmitter implements Generators.NewDataPointListener { +public class HttpTransmitter extends Transmitter implements Generators.GeneratorUpdatedListener { public static final String UPLOAD_URI = "com.audacious_software.passive_data_kit.transmitters.HttpTransmitter.UPLOAD_URI"; public static final String USER_ID = "com.audacious_software.passive_data_kit.transmitters.HttpTransmitter.USER_ID"; private static final String HASH_ALGORITHM = "com.audacious_software.passive_data_kit.transmitters.HttpTransmitter.HASH_ALGORITHM"; @@ -133,7 +133,7 @@ else if (!options.containsKey(HttpTransmitter.USER_ID)) { this.mContext = context.getApplicationContext(); - Generators.getInstance(this.mContext).addNewDataPointListener(this); + Generators.getInstance(this.mContext).addNewGeneratorUpdatedListener(this); } private boolean shouldAttemptUpload(boolean force) { @@ -424,12 +424,26 @@ public long transmittedSize() { } @Override - public void onNewDataPoint(String identifier, Bundle data) { + public void onGeneratorUpdated(String identifier, Bundle data) { if (data.keySet().size() > 1) { // Only transmit non-empty bundles... + double now = (double) System.currentTimeMillis(); + now = now / 1000; // Convert to seconds... + + Generators generators = Generators.getInstance(this.mContext); + + Bundle metadata = new Bundle(); + if (data.containsKey(Generator.PDK_METADATA)) { - data.getBundle(Generator.PDK_METADATA).putString(Generator.SOURCE, this.mUserId); + metadata = data.getBundle(Generator.PDK_METADATA); } + metadata.putString(Generator.IDENTIFIER, identifier); + metadata.putDouble(Generator.TIMESTAMP, now); + metadata.putString(Generator.GENERATOR, generators.getGeneratorFullName(identifier)); + metadata.putString(Generator.SOURCE, generators.getSource()); + metadata.putString(Generator.SOURCE, this.mUserId); + data.putBundle(Generator.PDK_METADATA, metadata); + if (this.mJsonGenerator == null) { this.mCurrentFile = new File(this.getPendingFolder(), System.currentTimeMillis() + HttpTransmitter.TEMP_EXTENSION); From fb1d0627fdf9f7d3ed4e2a992cfca55b6f747d69 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Wed, 28 Dec 2016 13:55:29 -0600 Subject: [PATCH 05/18] Updating build version --- build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index 308c884..de82287 100755 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.android.tools.build:gradle:2.2.3' } } @@ -17,7 +17,7 @@ repositories { android { compileSdkVersion 25 - buildToolsVersion "25" + buildToolsVersion "25.0.2" useLibrary 'org.apache.http.legacy' @@ -45,9 +45,9 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:25.0.1' - compile 'com.android.support:recyclerview-v7:25.0.1' - compile 'com.android.support:cardview-v7:25.0.1' + compile 'com.android.support:appcompat-v7:25.1.0' + compile 'com.android.support:recyclerview-v7:25.1.0' + compile 'com.android.support:cardview-v7:25.1.0' compile 'com.google.android.gms:play-services-location:10.0.1' compile 'com.google.android.gms:play-services-maps:10.0.1' compile 'com.google.android.gms:play-services-nearby:10.0.1' From 2711f7b6657df4c7010db18a61ffa9b5d19896ab Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Wed, 28 Dec 2016 19:32:39 -0600 Subject: [PATCH 06/18] Additional diagnostics work --- build.gradle | 6 +- res/drawable-hdpi/ic_pdk_diagnostic.png | Bin 0 -> 1397 bytes res/drawable-mdpi/ic_pdk_diagnostic.png | Bin 0 -> 1212 bytes res/drawable-xhdpi/ic_pdk_diagnostic.png | Bin 0 -> 1504 bytes res/drawable-xxhdpi/ic_pdk_diagnostic.png | Bin 0 -> 2018 bytes res/drawable-xxxhdpi/ic_pdk_diagnostic.png | Bin 0 -> 2068 bytes res/layout/card_diagnostic_action.xml | 37 +++++-- res/layout/layout_diagnostics_pdk.xml | 19 +++- res/menu/diagnostic_menu.xml | 6 ++ res/values/diagnostics.xml | 16 ++- res/values/generators.xml | 1 - res/values/strings.xml | 5 + .../activities/DataStreamActivity.java | 16 +-- .../activities/DiagnosticsActivity.java | 92 ++++++++++++++---- .../diagnostics/DiagnosticAction.java | 8 +- .../generators/communication/PhoneCalls.java | 3 +- .../communication/TextMessages.java | 6 +- .../generators/device/Location.java | 5 +- .../generators/services/GoogleAwareness.java | 7 +- .../generators/wearables/MicrosoftBand.java | 6 +- 20 files changed, 168 insertions(+), 65 deletions(-) create mode 100755 res/drawable-hdpi/ic_pdk_diagnostic.png create mode 100755 res/drawable-mdpi/ic_pdk_diagnostic.png create mode 100755 res/drawable-xhdpi/ic_pdk_diagnostic.png create mode 100755 res/drawable-xxhdpi/ic_pdk_diagnostic.png create mode 100755 res/drawable-xxxhdpi/ic_pdk_diagnostic.png create mode 100755 res/menu/diagnostic_menu.xml diff --git a/build.gradle b/build.gradle index c571e90..e6359b3 100755 --- a/build.gradle +++ b/build.gradle @@ -45,9 +45,9 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:25.0.1' - compile 'com.android.support:recyclerview-v7:25.0.1' - compile 'com.android.support:cardview-v7:25.0.1' + compile 'com.android.support:appcompat-v7:25.1.0' + compile 'com.android.support:recyclerview-v7:25.1.0' + compile 'com.android.support:cardview-v7:25.1.0' compile 'com.google.android.gms:play-services-location:10.0.1' compile 'com.google.android.gms:play-services-maps:10.0.1' compile 'com.google.android.gms:play-services-nearby:10.0.1' diff --git a/res/drawable-hdpi/ic_pdk_diagnostic.png b/res/drawable-hdpi/ic_pdk_diagnostic.png new file mode 100755 index 0000000000000000000000000000000000000000..e80247a9482c0d1707daba4b4c829418dd5ae2c0 GIT binary patch literal 1397 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%o>>?5hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s04CfFugAM$)&lec_lEtDG0rmIQ4=OL~a4lW|!2W%(B!Jx1#)91+d4hGI6`b z45xWey(zfeVvbXF!Z)WcGV{#N}ZM^HkEnKZ_dMjh~gZs=EI9{pQG|bIkFt^pNsdK%9$imHA0w+G; zz7cUT!$)UHP>IcCp2AIsj1Hf<`$nyJvi`+4acSplKNnxrN!FSC?1UAgUxV-i77Io{ zhQv3`kp*mV9&QhqIy5%!UlG*n{Ir4norB|_f4tWYBuB|;Z2n(3QH;IfYrgBzr!N|L zFZk_Zw$7=zGU=T|#oP1~u^k(K{oUcSih0kk?+W&hZ0nTPMi(nSdlg-Na2oUCYj$_z zHiUG>E|U*8JGRZRl79=E&j!w}rpb)w_dD2eGPQ-q@2l(%(R;!ZzJQ-kdEVFLq7&At zt-;zxKCC&?eh0cW7bP0l+XkK DN+0+^ literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_pdk_diagnostic.png b/res/drawable-mdpi/ic_pdk_diagnostic.png new file mode 100755 index 0000000000000000000000000000000000000000..539cf8afd7e58849ba1e79786869ffd2442626f0 GIT binary patch literal 1212 zcmaJ>ZAcqu9KSYA)eNJI!E6!s^mHX|=3df7y_=bNx!9!iG^Bxrv9Vq*k9yO4>D^-! zL!nyiX8WOY`w%t?n;Saxvt zyzu+|{%=3-QgYyUqwBDXAc)39Tu9*+*l+zo{I!b34ZIveu?#w;523tdLL!{gvk)Xy zX&9!Ul$$uS1bYd>;Zf2Vlo3zzvaV8+jiCyvf!PGn+gC6oc@!d$g~N&#B+GZcCxMa+ zlJ5jWMl_;uM2Qzoc&a#%mW!h@mm~Y$2E7Fy3#brDprDRv7GDUGJGwmH+t)M+b|7dp zNIp+0BPKyqHz5d6U0#_z)&sgZiVbi*K7S|hF>Dvja5U@pGAz$ zC}s6=6eMw`&r48^Jz34#Z4+)7U62f#r5L-UDo_;vAF8T*XbYv_i+ul6*h)_rkWN8M zA2($@xS=*Xl)*<$C?VZU>-t!=i^&lk>DGvDfN0e2S~u{BlB{UA zLI{#rhfo;{3>=e!`wQ~vA)Z69eYUdKE$)#~Jv_0N`jk|hYj(R*>AM%&fAph$2j|@_;qnZ<=lz0o-H%lbMoTRy*i-1+AQTHLODKKV^@u2f!fJ3rj~@^NzRdU?s^ zJaMk+;e{iiG1vX32aV2D=g-2^zdit^sfX`JU%Gf8yZrjK_RhNOSwB1AS&mP?d--x! z1XsY_n}T&-(8p{p~ga!a#5S!#F|dYN-)w!SK^ zbTvOcefwkQ;MQ9E>~dV)n!2_2Dr)HO4YpCBH2<07w-D>-aJQ(XPxwiS0Ec-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuk*h0bFQqR!T z(!$6@N5ROz&`jUJQs2--*TB%qz|zXVPyq^*fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$DAddqG<*}2 zGxI=#nqXbNzE+-j#U+V($*G<$wn{*A^fEJ3tbl3_94!q@oK4ML4Gmq*oD7{@T`e6g z&0S1gU7QR}V0vBhlS^|`^GaZPQxJMhaq0ynh};68%`T}$nPsUdZbkXI3Sf_0WnyuQ zp_#L(tFxOMPV=C8Q?R%Ntk=cB0H?qQvzPNWS$H}bZrGng!8-%&F;}0BjI_Pb7n^P=I zDI%gHL9rw17lU1+!n8-+5;-h^%2|m^dyfel9TG}1v)=stOwKo%AD(K{_tyNMIX8P< z*y>K5b`eQ_wgU_j49p1(JPnKn3~UpwzSvsx&vF9$E(Y5a-UC)I9QN*KJ=+ksIf(Vs zg0lfTIkeJWN(L~A&a%_iHi+d{_}_oKnak%ov&PEVxuQ=Z6?AvVZ}~aThIhP=faO8?1G`Ew52x>sw|t(*bj_6E(GLGxN*?TY)z31$T=6?4Qi1ah zKhMASiT#2b7^g&k`8oF#lMh2H$MNJ-E&duMaRfwGs+1X#j zH?wKi{3|z@WEq~;#3e;e5Ml7YaPIZ&K0_h)1&{xw2mhR1FlA;&ctrV~ef+-yu76;i zb)iNq=Qi7d=Cxci&XF|R@++{f=g7tzju&cv{C90^DdUR zQYnq;%nhBtLqp#m4%~QAX$FJA%-!YXm}3o0)-t(n_eT1#uz=v-jjpriBV$qR$9$J;op7`hJKU2*>pgIPoH zh1rW4&OI_)ZWa4b`+`Wujg7O{f7i}?#J8&CIMa8pmHhX#&!9%@577jM3p}9-e8+s> Q0E=7(Pgg&ebxsLQ01~Jk9RL6T literal 0 HcmV?d00001 diff --git a/res/drawable-xxhdpi/ic_pdk_diagnostic.png b/res/drawable-xxhdpi/ic_pdk_diagnostic.png new file mode 100755 index 0000000000000000000000000000000000000000..62b411dc02edcd53bbc3b8f18fee93e27217fa77 GIT binary patch literal 2018 zcmaJ?3s4hx8vlpaa47VU`UdI^FV1FaV*7!*?2mdkC#fd2uPHpc?cv@OBIL& zk;=<94j}OWxQ3}Rvhi&3Ke#efO_Mr(XnHk9paFGaalQd(&=4b>{>Ob&-bXGGDXq9O^8NL`r*m+B)m zI_fL~jOb)q6^5%&4di5$=Ap$nk3wYnYYA%X6|F}1vQ0$8=z1weXVMtXl4c#n;{Wfe zR=@Jr;S%JxeE*bKmr;fxbP1wEi?uRhaQRecD2yx6B2paHW}s-%Y!_3ND30osCUx2u{qx+B)5fII?Eq%C~@q;h@eR+Yr-ehhw}k zP$aq{pUK^JD}5}K)1vp7xth|+TfOr6*21y@DT^zNB575DT; zj49kk-#UW5wrJ_hvsoS{(0G9n72K=KW$Rwb;6v~ zsOeqDO;3wtPgM-5R?!{E`Q+UzK52D3vP13etnahC%vSiPkg4Z6APft)?H^pf_S2$f z-Z{1<=7~M0y8ZR@Wj=j^12-*zF7xu*z2=+gGKmjpSp}0pQT2mWHvh>>5plq8?E59w z;Mam9^TAq5_2Gi)j~Wl`@R7i!)dOy_a@f;XRXAvZ!x7J*`L>8J~8pQ zbzKn%DWtOoQsu_5H^IUhJNxmc%Yu`0DAhk0vzzROhr^M6T@T_Xwe0yLXCDT3JaB!- zdwDV!i1^*r+XDMq&IZNb4RMW|Z~8g49JvsDAooz^!4L08%B_<@!veH>{Lw+h-)(zK z+D06UI{Qwgd!Ih@G|2Lq=l+==s5ii&c)TCjYL9fMJKD#mwvCcEZ2Iouf{=lg*B`!p zNB{Zhu^kTkeDjx#UU^_gwEw2S{Ob0Ar^kJ5Ap*+{#}76S*U^ literal 0 HcmV?d00001 diff --git a/res/drawable-xxxhdpi/ic_pdk_diagnostic.png b/res/drawable-xxxhdpi/ic_pdk_diagnostic.png new file mode 100755 index 0000000000000000000000000000000000000000..ed16022e0facabfbd4eba73f338232cd9465e72b GIT binary patch literal 2068 zcmb7Fe^3*57T>HJja1HXazfF{8fbx5@=F3F7|2gdkRS#S?y!YTvXGQ)Hf|P)Qb@3% zoYo=gc_@NK5A>!MTVwz$+T^aCinU5c?igyl$~l$7VGmSn=fV+gW5M1Z$Iac&?0(<- z&ij1c=e_US-Ct*CZ4M5K2m$~gSglfMsnr>{8B3|>C#lE{YI%`V7LYl330Z*I zctw;Q*yga{XCY*nnDwKm0!=n7#|ab`uoL6;yp73ll90_42$T5<8(=<{m&oA?IlP2; zE>FZ2inu&@{$f$t2t$cTt4N>EMR{VDktD4m4yU4`f?ctZjT5CDo=_;{aQPfQKc0$+ zx9zr&h&|q7i&|h%pf){WvXUm;0tXlo9bQg~S(MTrOTetpX)U(-Hc<`Z*bys-$L0n~ zS_sr={(C5fJ&(4LTJ$Hq|B=|1yW5I#w5Sa)C-l_8l|%(nSw(UJMM#{;#c}gO7qg8x ziQA006_(2bT}y&vHHhA132eqaLuoW3wZ%pv7Cov~h*=a5+hj6`c!_d>Ql2i93zC#P zo-$F!lPQ%#sW3?)P%31+gaxbu*Oz0cgH zGA*tpX;Hp;tYL92$%|q+lo(Fnc>nFV7q+Mw3YgD_mvWwuKWd?7o}h+1yH)oc06=@y z3TdwW&cl|QWvis&pY`fPIJ2tv^3?+eeqVpU9`BD^jopqGS3ZL4;rs1pT4Uyx;Ojnp z8(s(f;r#I}TXpB3viAPzMSh*KDM8(}aq`NO%8~Zded@%S)B(ICDKe$2w>Q7_Ep(V3 z4j@1+jk#3vzZ$=}ShBJDyL0B(zwb>O_ARA7_P*1xtf$9(^>O!uxum0ixz#^)Qu+V#5;-%W{~G^lyF4Y}-hpm%{#n(;ga@V@<3>ioJ|^7IIR^*5 zFLiDje3wpNF+NbqZaoGJX1MnR8DEx^9k`jVp&f#b@ABp4cwGL@Snpoo@!-9#yz^s^ z{=Cf-o9XEIY&S5FGpL}&LyTj7KK<^Fkr=4| zwOKSMVLhchUr7swj7sCjO&>}A2Gt*{7KEx=fQgWk?yWWD3|FZyFPqADCC>FKZHKS2 zXLX%<>&4LHn&jD|iH--Z2Ol&@YWkRxYTwG4V5hpGzv0Z>I;l34d!pT)5q~y0Omzr2 zSUu_pU6QMGRt0UCaWh-N?%K0%vv)P*I_ZCJxoa(5%ko^FnvM~Msp_bL9Sks9S(D4~ zW<}4UpY*JEUYXv{lJG-SH9*U!w~G=j%fS8&hYF0A(&WA;XPpKx_psj!S&7Nugf=au z_497{1gKp;5^?WI$_J`u;7nXGbk1?w8`T-=QaPIP{O?4KWV;hau)>D$nn-TVJ5vZz}?6ifxk5e*iDj2IrJa z`rjqa562s$e^?_wG#se8=*Z~HU(j`{JnimpE7Da}Jx?!<74e6zzb+f9ay`j}f-ZgJ z*>wRLOE>y$g<)oK*VF5beN|r%9b|-#eUst5962M87`fUp(c}MFLG>3yDTVpOX-OtX zvfAeSuFVvC*xWHH8uToNJ~DU5AnD+38E} zED?l{1Xnc|;N6*E+8vQ5?b?wP^T%z-&&S^ITsi&uLa?rt8uP?x;2_0i); ls__5k<(~f% + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - + + android:layout_height="wrap_content"> + + + + \ No newline at end of file diff --git a/res/layout/layout_diagnostics_pdk.xml b/res/layout/layout_diagnostics_pdk.xml index e71a783..379c418 100755 --- a/res/layout/layout_diagnostics_pdk.xml +++ b/res/layout/layout_diagnostics_pdk.xml @@ -1,7 +1,16 @@ - + android:layout_height="match_parent"> + + + diff --git a/res/menu/diagnostic_menu.xml b/res/menu/diagnostic_menu.xml new file mode 100755 index 0000000..65f394a --- /dev/null +++ b/res/menu/diagnostic_menu.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/res/values/diagnostics.xml b/res/values/diagnostics.xml index c0adb6a..a656358 100755 --- a/res/values/diagnostics.xml +++ b/res/values/diagnostics.xml @@ -1,7 +1,19 @@ - Unable to connect to Microsoft Band. + Microsoft Band App Not Installed + Unable to connect to Microsoft Band. Please install the app and try again. + + Microsoft Band Permission Required Please grant the app sensor access on the Microsoft Band. - Please grant the app permission to access location services on this device. + + Activity Recognition Permission Required Please grant the app permission to recognize your activity using data from this device. + + Call Log Permission Required Please grant the app permission to access the phone call logs on this device. + + Text Messaging Permission Required + Please all the app permission to access your text messages to gather and report your messaging activity statistics. + + Location Permission Required + Please grant the app permission to access location services on this device. diff --git a/res/values/generators.xml b/res/values/generators.xml index b5e805f..2fbcf3b 100755 --- a/res/values/generators.xml +++ b/res/values/generators.xml @@ -69,7 +69,6 @@ %1$d chars. No text messages have been sent or received on this device. - Please all the app permission to access your text messages to gather and report your messaging activity statistics. Phone Calls diff --git a/res/values/strings.xml b/res/values/strings.xml index 6474efa..a784812 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -15,5 +15,10 @@ Today Never + Diagnostics + Diagnostics (%d) + Diagnostics + + The app is set up correctly.\n\nNo further actions are needed. diff --git a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java index a4a1300..04e416c 100755 --- a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java +++ b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java @@ -66,25 +66,15 @@ protected void onPause() { @Override public void onGeneratorUpdated(String identifier, Bundle data) { - Log.e("PDK", "GOT GENERATOR UPDATE: " + identifier + " -- " + data); - - this.mAdapter.notifyDataSetChanged(); - - final int count = this.mAdapter.getItemCount(); - - Handler mainHandler = new Handler(Looper.getMainLooper()); - final DataStreamActivity me = this; - mainHandler.post(new Runnable() { + this.runOnUiThread(new Runnable() { @Override public void run() { + me.mAdapter.notifyDataSetChanged(); + int count = me.mAdapter.getItemCount(); me.getSupportActionBar().setSubtitle(me.getResources().getQuantityString(R.plurals.activity_data_stream_subtitle, count, count)); } }); - -// RecyclerView listView = (RecyclerView) this.findViewById(R.id.list_view); -// listView.setAdapter(this.mAdapter); -// listView.invalidate(); } } diff --git a/src/com/audacious_software/passive_data_kit/activities/DiagnosticsActivity.java b/src/com/audacious_software/passive_data_kit/activities/DiagnosticsActivity.java index ce8a3d5..851103d 100755 --- a/src/com/audacious_software/passive_data_kit/activities/DiagnosticsActivity.java +++ b/src/com/audacious_software/passive_data_kit/activities/DiagnosticsActivity.java @@ -1,11 +1,14 @@ package com.audacious_software.passive_data_kit.activities; +import android.app.Activity; +import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.util.Log; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; @@ -17,6 +20,38 @@ import java.util.ArrayList; public class DiagnosticsActivity extends AppCompatActivity { + public static void setUpDiagnositicsItem(Activity activity, Menu menu, boolean showAction) { + final ArrayList actions = PassiveDataKit.diagnostics(activity); + + MenuItem item = menu.add(Menu.NONE, R.id.action_diagnostics, 0, activity.getString(R.string.action_diagnostics)); + + if (actions.size() > 0 && showAction) { + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + + item.setIcon(R.drawable.ic_pdk_diagnostic); + item.setTitle("" + actions.size()); + } else { + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + + if (actions.size() > 0) { + item.setTitle(activity.getString(R.string.action_diagnostics_incomplete, actions.size())); + } + } + } + + public static boolean diagnosticItemSelected(Activity activity, MenuItem item) { + int id = item.getItemId(); + + if (id == R.id.action_diagnostics) { + Intent diagnosticsIntent = new Intent(activity, DiagnosticsActivity.class); + activity.startActivity(diagnosticsIntent); + + return true; + } + + return false; + } + private class DiagnosticViewHolder extends RecyclerView.ViewHolder { private View mView = null; @@ -44,7 +79,10 @@ public void bindDiagnosticAction(DiagnosticAction action) { this.mAction = action; - TextView message = (TextView) this.mView.findViewById(R.id.message_action); + TextView title = (TextView) this.mView.findViewById(R.id.action_title); + title.setText(action.getTitle()); + + TextView message = (TextView) this.mView.findViewById(R.id.action_message); message.setText(action.getMessage()); } } @@ -52,34 +90,46 @@ public void bindDiagnosticAction(DiagnosticAction action) protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.layout_diagnostics_pdk); + this.getSupportActionBar().setTitle(R.string.title_pdk_diagnostics); + } - final ArrayList actions = PassiveDataKit.diagnostics(this); + protected void onResume() { + super.onResume(); - Log.e("PDK", "ACTIONS COUNT: " + actions.size()); + final ArrayList actions = PassiveDataKit.diagnostics(this); RecyclerView listView = (RecyclerView) this.findViewById(R.id.list_view); + TextView emptyMessage = (TextView) this.findViewById(R.id.message_no_diagnostics); - listView.setLayoutManager(new LinearLayoutManager(this)); + if (actions.size() > 0) { + listView.setVisibility(View.VISIBLE); + emptyMessage.setVisibility(View.GONE); - listView.setAdapter(new RecyclerView.Adapter() { - @Override - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_diagnostic_action, parent, false); + listView.setLayoutManager(new LinearLayoutManager(this)); - return new DiagnosticViewHolder(v); - } + listView.setAdapter(new RecyclerView.Adapter() { + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_diagnostic_action, parent, false); - @Override - public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { - DiagnosticViewHolder diagHolder = (DiagnosticViewHolder) holder; + return new DiagnosticViewHolder(v); + } - diagHolder.bindDiagnosticAction(actions.get(position)); - } + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + DiagnosticViewHolder diagHolder = (DiagnosticViewHolder) holder; - @Override - public int getItemCount() { - return actions.size(); - } - }); + diagHolder.bindDiagnosticAction(actions.get(position)); + } + + @Override + public int getItemCount() { + return actions.size(); + } + }); + } else { + listView.setVisibility(View.GONE); + emptyMessage.setVisibility(View.VISIBLE); + } } } diff --git a/src/com/audacious_software/passive_data_kit/diagnostics/DiagnosticAction.java b/src/com/audacious_software/passive_data_kit/diagnostics/DiagnosticAction.java index c3869ff..d466198 100755 --- a/src/com/audacious_software/passive_data_kit/diagnostics/DiagnosticAction.java +++ b/src/com/audacious_software/passive_data_kit/diagnostics/DiagnosticAction.java @@ -8,8 +8,10 @@ public class DiagnosticAction { private String mMessage = null; private Runnable mAction = null; + private String mTitle = null; - public DiagnosticAction(String message, Runnable action) { + public DiagnosticAction(String title, String message, Runnable action) { + this.mTitle = title; this.mMessage = message; this.mAction = action; } @@ -25,4 +27,8 @@ public void run() { public String getMessage() { return this.mMessage; } + + public String getTitle() { + return this.mTitle; + } } diff --git a/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java b/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java index 93677c0..f72cbe8 100755 --- a/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java +++ b/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java @@ -142,6 +142,7 @@ private void startGenerator() { final Runnable checkLogs = new Runnable() { @Override public void run() { + Log.e("PDK", "CHECK PHONE LOGS"); boolean approved = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -365,7 +366,7 @@ public static ArrayList diagnostics(final Context context) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED){ final Handler handler = new Handler(Looper.getMainLooper()); - actions.add(new DiagnosticAction(context.getString(R.string.diagnostic_call_log_permission_required), new Runnable() { + actions.add(new DiagnosticAction(context.getString(R.string.diagnostic_call_log_permission_required_title), context.getString(R.string.diagnostic_call_log_permission_required), new Runnable() { @Override public void run() { diff --git a/src/com/audacious_software/passive_data_kit/generators/communication/TextMessages.java b/src/com/audacious_software/passive_data_kit/generators/communication/TextMessages.java index 3ad7c05..f82e641 100755 --- a/src/com/audacious_software/passive_data_kit/generators/communication/TextMessages.java +++ b/src/com/audacious_software/passive_data_kit/generators/communication/TextMessages.java @@ -16,6 +16,7 @@ import android.os.Looper; import android.preference.PreferenceManager; import android.support.v4.content.ContextCompat; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -119,6 +120,9 @@ private void startGenerator() { final Runnable checkLogs = new Runnable() { @Override public void run() { + + Log.e("PDK", "CHECK TEXT LOGS"); + boolean approved = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -311,7 +315,7 @@ public static ArrayList diagnostics(final Context context) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED){ final Handler handler = new Handler(Looper.getMainLooper()); - actions.add(new DiagnosticAction(context.getString(R.string.diagnostic_sms_log_permission_required), new Runnable() { + actions.add(new DiagnosticAction(context.getString(R.string.diagnostic_sms_log_permission_required_title), context.getString(R.string.diagnostic_sms_log_permission_required), new Runnable() { @Override public void run() { diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Location.java b/src/com/audacious_software/passive_data_kit/generators/device/Location.java index c76a801..7363344 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/Location.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/Location.java @@ -22,6 +22,7 @@ import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.widget.SwitchCompat; import android.util.DisplayMetrics; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -230,7 +231,7 @@ private ArrayList runDiagostics() { int permissionCheck = ContextCompat.checkSelfPermission(this.mContext, Manifest.permission.ACCESS_FINE_LOCATION); if (permissionCheck != PackageManager.PERMISSION_GRANTED) { - actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_location_permission), new Runnable() { + actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_location_permission_title), me.mContext.getString(R.string.diagnostic_missing_location_permission), new Runnable() { @Override public void run() { @@ -281,6 +282,8 @@ public void onConnectionFailed(ConnectionResult connectionResult) { @Override public void onLocationChanged(android.location.Location location) { + Log.e("PDK", "LOCATION CHANGED"); + if (location == null) return; diff --git a/src/com/audacious_software/passive_data_kit/generators/services/GoogleAwareness.java b/src/com/audacious_software/passive_data_kit/generators/services/GoogleAwareness.java index 9aea317..0688e7d 100755 --- a/src/com/audacious_software/passive_data_kit/generators/services/GoogleAwareness.java +++ b/src/com/audacious_software/passive_data_kit/generators/services/GoogleAwareness.java @@ -19,13 +19,10 @@ import com.audacious_software.passive_data_kit.generators.Generators; import com.audacious_software.pdk.passivedatakit.R; import com.google.android.gms.awareness.Awareness; -import com.google.android.gms.awareness.snapshot.DetectedActivityResult; import com.google.android.gms.awareness.snapshot.HeadphoneStateResult; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.location.ActivityRecognitionResult; -import com.google.android.gms.location.DetectedActivity; import java.util.ArrayList; import java.util.List; @@ -106,7 +103,7 @@ private ArrayList runDiagostics() { int permissionCheck = ContextCompat.checkSelfPermission(this.mContext, Manifest.permission.ACCESS_FINE_LOCATION); if (permissionCheck != PackageManager.PERMISSION_GRANTED) { - actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_location_permission), new Runnable() { + actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_location_permission_title), me.mContext.getString(R.string.diagnostic_missing_location_permission), new Runnable() { @Override public void run() { handler.post(new Runnable() { @@ -130,7 +127,7 @@ public void run() { if (permissionCheck != PackageManager.PERMISSION_GRANTED) { Log.e("PDK", "3.3"); - actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_activity_recognition_permission), new Runnable() { + actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_activity_recognition_permission_title), me.mContext.getString(R.string.diagnostic_missing_activity_recognition_permission), new Runnable() { @Override public void run() { handler.post(new Runnable() { diff --git a/src/com/audacious_software/passive_data_kit/generators/wearables/MicrosoftBand.java b/src/com/audacious_software/passive_data_kit/generators/wearables/MicrosoftBand.java index b8f4d3b..246e7d3 100755 --- a/src/com/audacious_software/passive_data_kit/generators/wearables/MicrosoftBand.java +++ b/src/com/audacious_software/passive_data_kit/generators/wearables/MicrosoftBand.java @@ -561,7 +561,7 @@ private ArrayList runDiagostics() { final MicrosoftBand me = this; if (MicrosoftBand.sInstance.mBandClient == null) { - actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_msft_band_client), new Runnable() { + actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_msft_band_client_title), me.mContext.getString(R.string.diagnostic_missing_msft_band_client), new Runnable() { @Override public void run() { @@ -575,7 +575,7 @@ public void run() { } })); } else if (!MicrosoftBand.sInstance.mBandClient.isConnected()) { - actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_msft_band_client), new Runnable() { + actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_msft_band_client_title), me.mContext.getString(R.string.diagnostic_missing_msft_band_client), new Runnable() { @Override public void run() { @@ -596,7 +596,7 @@ public void run() { if (this.canAccessSensor(sensors, MicrosoftBand.HeartRateDataPoint.class) || this.canAccessSensor(sensors, MicrosoftBand.HeartRateVariabilityDataPoint.class)) { if (sensors.getCurrentHeartRateConsent() != UserConsent.GRANTED) { - actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_msft_band_auth), new Runnable() { + actions.add(new DiagnosticAction(me.mContext.getString(R.string.diagnostic_missing_msft_band_auth_title), me.mContext.getString(R.string.diagnostic_missing_msft_band_auth), new Runnable() { @Override public void run() { From 5a7bb83ba4379382d44fd6a2d876d4463be16c7e Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Thu, 29 Dec 2016 12:24:57 -0600 Subject: [PATCH 07/18] More graceful behavior when no locations have been collected --- .../passive_data_kit/generators/device/Location.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Location.java b/src/com/audacious_software/passive_data_kit/generators/device/Location.java index 7363344..f00204c 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/Location.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/Location.java @@ -379,7 +379,12 @@ public static void bindViewHolder(DataPointViewHolder holder) { c.close(); TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date); - dateLabel.setText(Generator.formatTimestamp(context, timestamp / 1000)); + + if (timestamp > 0) { + dateLabel.setText(Generator.formatTimestamp(context, timestamp / 1000)); + } else { + dateLabel.setText(R.string.label_never_pdk); + } final double finalLatitude = lastLatitude; final double finalLongitude = lastLongitude; @@ -473,7 +478,9 @@ public void onMapReady(GoogleMap googleMap) { builder.include(latlng); } - googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), (int) (16 * metrics.density))); + if (locations.size() > 0) { + googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), (int) (16 * metrics.density))); + } DisplayMetrics metrics = context.getResources().getDisplayMetrics(); From 3d0e21df925746b4dbe573c3c8f7774f60abe40d Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Sat, 7 Jan 2017 09:04:56 -0600 Subject: [PATCH 08/18] Cleaning up newline. --- AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3b7277a..cff23c6 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -4,7 +4,7 @@ - + From e07ab9b570dbd3c18afa3f4854f568beb05ca3e0 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Sun, 8 Jan 2017 11:21:30 -0600 Subject: [PATCH 09/18] Data disclosure form work --- AndroidManifest.xml | 6 +- .../generator_location_disclosure.html | 26 ++ .../ic_button_disclosure_setting.png | Bin 0 -> 1968 bytes .../ic_button_disclosure_setting.png | Bin 0 -> 1650 bytes .../ic_button_disclosure_setting.png | Bin 0 -> 2330 bytes .../ic_button_disclosure_setting.png | Bin 0 -> 3127 bytes .../ic_button_disclosure_setting.png | Bin 0 -> 3577 bytes res/layout/dialog_location_randomized.xml | 18 + res/layout/dialog_location_user.xml | 18 + .../layout_data_disclosure_detail_pdk.xml | 19 + res/layout/layout_data_disclosure_pdk.xml | 31 ++ res/layout/row_disclosure_action_pdk.xml | 19 + .../row_disclosure_location_accuracy_pdk.xml | 26 ++ .../row_generator_disclosure_generic.xml | 25 ++ res/values/generators.xml | 27 ++ res/values/strings.xml | 7 + .../activities/DataDisclosureActivity.java | 44 +++ .../DataDisclosureDetailActivity.java | 123 ++++++ .../activities/DataStreamActivity.java | 7 - .../generators/GeneratorViewHolder.java | 10 + .../generators/GeneratorsAdapter.java | 129 ++++++ .../generators/Generator.java | 15 + .../generators/Generators.java | 1 + .../generators/device/Location.java | 370 ++++++++++++++++++ 24 files changed, 913 insertions(+), 8 deletions(-) create mode 100755 assets/html/passive_data_kit/generator_location_disclosure.html create mode 100755 res/drawable-hdpi/ic_button_disclosure_setting.png create mode 100755 res/drawable-mdpi/ic_button_disclosure_setting.png create mode 100755 res/drawable-xhdpi/ic_button_disclosure_setting.png create mode 100755 res/drawable-xxhdpi/ic_button_disclosure_setting.png create mode 100755 res/drawable-xxxhdpi/ic_button_disclosure_setting.png create mode 100755 res/layout/dialog_location_randomized.xml create mode 100755 res/layout/dialog_location_user.xml create mode 100755 res/layout/layout_data_disclosure_detail_pdk.xml create mode 100755 res/layout/layout_data_disclosure_pdk.xml create mode 100755 res/layout/row_disclosure_action_pdk.xml create mode 100755 res/layout/row_disclosure_location_accuracy_pdk.xml create mode 100755 res/layout/row_generator_disclosure_generic.xml create mode 100755 src/com/audacious_software/passive_data_kit/activities/DataDisclosureActivity.java create mode 100755 src/com/audacious_software/passive_data_kit/activities/DataDisclosureDetailActivity.java create mode 100755 src/com/audacious_software/passive_data_kit/activities/generators/GeneratorViewHolder.java create mode 100755 src/com/audacious_software/passive_data_kit/activities/generators/GeneratorsAdapter.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index daf0bdd..45147a5 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5,10 +5,14 @@ - + + + diff --git a/assets/html/passive_data_kit/generator_location_disclosure.html b/assets/html/passive_data_kit/generator_location_disclosure.html new file mode 100755 index 0000000..583b4d7 --- /dev/null +++ b/assets/html/passive_data_kit/generator_location_disclosure.html @@ -0,0 +1,26 @@ + + + + + + + +

+ TODO: Write disclosure for use of location data... +

+ +

+ Place completed file in assets/html/passive_data_kit/generator_location_disclosure.html in your project to override this placeholder. +

+ + \ No newline at end of file diff --git a/res/drawable-hdpi/ic_button_disclosure_setting.png b/res/drawable-hdpi/ic_button_disclosure_setting.png new file mode 100755 index 0000000000000000000000000000000000000000..9628f97acd37043be76d45a8eb3ddd6f6d6c3d4e GIT binary patch literal 1968 zcmaJ?dsI?)9LE4P6*om!X~%Bl@rcD!MPQO57nBkXp%v9;B3$97aOrYU)KyN-9=2Ax zxn6UR>|NGYL20_IHm#12v@(|K zy$njDPyrGIg@AY_kD=ql@__^alM^rCbK_7GjA`u*vvtNO0|;4l zC~U!C#0+>CwK<3l6VdQUU#4KPjM18{ugZiMjAhqaSR5wXlhUZ6Lh=8mCexU;6;ng6 zz?6w+b{nu#F!qg7NH5e%^!5ep!Zc&e5FL@Ts9 z*z8f#U!W8Uq1eMH8W`eSW%Q!cf?^gQBf&FM{KX~jS1>Y3K|v$i zyT5m~EigQpHb|22hp#pu53h7tuRr?bN@4Zm_7#u5F1gk{DZu*+a3JkXGA)f%4g^J( zffc^N;@~k!)w=p8P2}#{+RW|Sx5rEuw>~1U+iKe19l9u2y9ejYnWHW$DvDt++Lwr3 zF4vyM#zt|)*+FaZ(;#+pjbfr^AQA@NRMF}5n1qC$rx9R|IwQkIa`{Z*xhdo-{%uf& zq6a(P$h|PRD)29!+eZ9qNk4GK-+8EOzyIy4i^;v^k$0txIjV?gZ)(k&nRBNOETMSI zDmIV<%NEI$!=HkyETuj--m~~j^)bNbh+igqrP(TCebR|fkDEI~J40NAH3PguCoDr} zF!9@(j;6`2`Cd!w|L7`Tn;ot>y{@{cGPQuHF+7;DzPV}Hwq9d$z;l_bs+aXmNo8f( zXX5ESv$CXqtGs8B_mQ>+Xi^kD!wTw!@ce>?TH?CnI@QnI9%kp6NbwzeZddu8L?XpY zw{d#n+$-nX1irOwd8bLWb@?@A#3dH`-DBcPD;nnu;oGK_rd!%%mltMlb@b)shm?w$k%rPqJ1&a5v*}5~uA8KV z-39#=KGL?R}Uxi&E<>BH56OI}OoJR+(jlRqL za~C@|?W!#Nta+sVKCo+A9bv&DXY2e9-U;u^6~{uWv3cL~>VeH!$nqP8P*HNFLpt>Q z@53sZG_WVBKb;CT_X)B-@1?lk5-j7?6%5ii=fzp>eT@@t)jv}o=wsj6y9wFkXWB7u z{tD`Q1Jl<2)ESsFd$1tv!sT;{U}x$F1y7zB`{w#*xNDw;Mt$6}lgFHWR&w{Nmc*R- zoJ)7V?Ok_RQF63OGI#CB^#<61O-zc)@?q%qZ{~kO{&U0ApSM(Rp8RkiYM;YMDd3-U z?TmTociN<)-{+omos8~TvwPc-;q1NkX63yf51ufxBWlU_;vbxQt0gB?NJ%NVhTZH} zU;6dgCWm`Zui?PZpU0Q#Nzr+FB=`ZHZ&Rag-59w+ZrItf@{n!Nh-ZoO-1KC;6KU6MNg8Ngl G%l-l9RRj(I literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_button_disclosure_setting.png b/res/drawable-mdpi/ic_button_disclosure_setting.png new file mode 100755 index 0000000000000000000000000000000000000000..a252c984fe4042ede1b548642abd8ea2b6ef8ed7 GIT binary patch literal 1650 zcmaJ?c~BE~6kb9Vxom5hXi?}It;QqSBqWhU0zx)Hu%Q^zf>e$*yGVrWZa1q5;8YG# zBUoCXii(vgicn5dK~V&!SjCDk)~Sf-pw5UhX1s7Zq_xrw0``y6o!R{z^SBhDDU#Yi$}00;`Pkcd7FqrnZ>Mgy+kJU!da0S%~v z6D!dOG^7$sHLT90u$as!ojx;7FGD#Y!C;U@&JY+e8UZcFblfDjC^%!ha%S(mhB)9@ z2%Vjl&2R41cEDMe4#`ZC=xFRMFOD|638H-m?sd*1v0rn2#!A- zCL4vO$hEL~JQs6Pa8hZSltYl&Z04H-_yn~P63S#UNFahlA|4aLGiBg3V&UPY1rrP~ zX3|pzk~R=H=ww7T5SwWQhf(^b1S2^~i<`#V#54@DAS5K@3!EiQ1Zp(@A8IsCMw@6Y z_EztI5}R}xBnD|Q6S0}nGlNT6;7mozl@x~11f?U0^ocG;rxG+_N+n28sdRR21<2JP zdIRpfTr!5zXyg&NiAHce76B_b3=iL6K;<%tC_tc=h?THdA{45mfdNt#3=1VIL}GQI zSPf5LVM4#zh~e}E7JZAAOo??G!bmciVT>|t!B90t7{Rf^<%X%Xh^FKl$D&hf378TK zF=8O+c>gu-i7UoK&h4amnZu<0F`V%{#hANjWl0(Uum&UGP@SdUzCU4C;o3QudhGF6 z<8PUTO^&5YZ1tscHK;Od;cV^t8M8M<_QN9XddKboGC%ChswG*WwGC?))omT%S{uDR zx+@&dw!KW(5JM0Ax@ES5!#5i4e7}GAiR18a`B27PTT|pQ&JLTzir|Uavj?YHToOA* zdC4#@iO(+J?EBTd=5AA*J&9eF?yKYPQ@!^N$G_-i5}*k#>3a3|g6S?_bVON4gZqxp zjpO>j@@ZaPJX9=I-6PyS=*YS=oZx@IYgW?S zvigzNa|AK>Kh0*}T$jou$Mg17xGtY&4y{TVP&FPh>xHK~dSs~z&(Ch9lG_(IZFgK= zW{5AX9`xz+o7I|kQDJU!{VCTJc0NK9+z@k;8=kPe*Q@c8HX(lI@BM7DOnC{V+0r~! zQ1vi)<*&!?y_<%;dVfghmL)8vk}jM527rCp^;Z**9wAI_+%!7xtBXTz#a*!l(b&Ox zeL!XhaeL>8dx2%K!(NLl&%fUCO;K2y;EqLHNjrrHfyn8Mek#3ezN}~*MmMks_S0*UhG@9{sJcbC}_~u zi^c$0*_i`9;knDoBhLm_9&c(4)*KC&`jQ9yLv)q zp1(bivvc0)+S>fA$`d73>L-;2%5#T*L)!Liu~&Jzl-k~X{?xTRwDruD1I_K7#$gr- zoU5t1-d2Ct?eewkn}foU&huG=OGmwY9yC*uMOmHe4D&4yU*xOFb}Hun)1giCxW5dz zJGdzBeX5|Y#Uli(Z16tYcf^y|+F~W^Uij1&k0j-O)SBbinphfn>`}|YQ?H5&7ai_S zIXZCl6!pAVLiO8KE!(IrkqEzkNgi literal 0 HcmV?d00001 diff --git a/res/drawable-xhdpi/ic_button_disclosure_setting.png b/res/drawable-xhdpi/ic_button_disclosure_setting.png new file mode 100755 index 0000000000000000000000000000000000000000..c006523765298112123dc083b8b542b22dcda1ce GIT binary patch literal 2330 zcma)8c~nzp9(@6#5VitBK%k_7Dp0Z~gai_nL}W1%AdVnwNFET$MiNY75rT-yqNr$u z5k={#Yb%ONa6}A?Vmq}iRHh5XHk0f)Hd18b_i8;fVnVJQf#-!BQ|dA_|M6VkuNC4l(~A zjn-uRY-$2Ca^9A4MMLH)6jCY%qf{!`o=hNB2qa>JNs*f+DNxXmMo-^`AdV%19?cBrs2)B~69}ExxL!eBXo|`ItRnoxI^Ra$Wwzs?#P!Z)t{wI};vJCU@4e>wOy;^VP90p|{2mCb=P&Q=c&XPx&i>2$qBGWmd3t36|JcrrLRn3b4VQ$4U%(_6Yc z`QjV*R=e4NCtYO`?dNs6H^r!MD}8Xo^;?f+^sRYNK}lNH)C{!sM)&ZJ9Xm?<7e9yh zOn12!bj)EN9=X+K5c_MqstQI45ll8D#E`j--r2s11B3?YkHmTY+-SD9+v3d4=CL`0 zZ?}5qd0{tuELYmpj?fDX{sZR>b%xvcvD&ayNSj~$F6->lxDux?0z@x|b%mDnnhmP- zJV@?O!NWWIoI#L>#lmiHk9;gPCee~`82;jt^Jg~CpitUZ@R6$%5!U>=f&wu4D|=ka~~CL z&KY{KeKHu=Hul0MK{I1c3kXmr^quq3mm8)u9qE4@@4I_3{(?pBu1dG8jt(Cy$Ib9e z7gfEL6S?HeEgn!@`O{V0M;Lva-b;@fOj#B+__*g*)xI0gx3ZzNb%;vT;_EMU8cE5u zB4n+b;htgq&dN zXox{M5s@56?jBu>y_4PgjO^SW7>`4Xy|Ra5&uOjP^RvI{o)tbQ`Z^>$dg4s^)6hT_ zP;XPza4n%Luyt{f`;d551*ZLiR@6Yev)iQxgjn`v^)0oRi?F}%yXkO`7-5;RhwhQt zz$BXgxa`6beIWipUuWtgi}e?Um(^!1GY^!-O>ykb{!)JnOasIlL%EH%Qg?;Q*Kbt? z?)nwOgJfre>xAb-tE>Fws?jP7x$H@u=T|g)84U$ir)n))Tnq0Bf1TY~I&*ZE-fWxH z66}-hb!hoN?lZSR?On{#N!WIlk=G+KG(hXM%vg zh27eJv2DV(1;>GCOEI;hh`ktvr7mh~EM%+td$*XgYqZS~$79(|srm-Ls)IGtZKThK zoGN`})>PP_ccIS8KDFP4|GCYTe=^-iKP$NuM*qU>c6kAmU%1HL+L&~LHp4+P`9wEz z^sy?>55NcyF)@#CqbC1cGBLAc??%{cDjh8wYC(QjB(dOE*$#4Syt1=m`u~Q!f`&f?Q@mo4QMmBy^H5uE^ zACKp*ak!hZ=XAGOvBwR#^Bw?AVTOA&!3+@ekE$ytI=)Qz4+9}ZiICaMZy60@UNQ3Y zvM3U4aR1Tiv)*xs3IG0HSX}+{qNbVJ+Fo{mOQ(~jVa9kEvgvtlY1h+lwPTeo1IK;| x!xp8ar1%G(>?s~TO8rwh``Ky8z{WIs0X2oD2bP5pqa9KhTmCpn?GPpD*j1)r+XL>WK3<>Wh(-r^# z8nY<@!T|C%9Gx3uPF3odi(_IHXaK<0UK~rMM=^yk8Z(^Dv4g+9cm)n)Gwk60R%9eO zmcWc)yC(3NJ_+s=dO{Q(%YfT&f!T_23W6A>kO~vWL~{fq1TLQmvog0fqodFmm<`q(Wrf99Sen8tkSJ>e5{p1tnjuj*Bo>E6!M=QO zg*85dh4Uslf3c-l*})@(!dM&vArgtqMQC#_KOBL=VzCIM1;WC@Orc>WkZ^=lu^C6O zZjph=6wvwXSRtFsfhifOG;W;G4zBR@?<2&-ex>CIzJy5;Foc*Ii$Iwpl}B1sB$NMJ zH74e(wm|63{KwyaB^FR5u}p+FQ^1Ym(-jxTTBkG>izD!vR3Vp7;c}xFqv#pI6>gj5clNg~?86+Gr_HUozt5C}+TXEcFmX@x>L zS!2-FPDCQg%ErRd8Dr^8T*MN&^tc!%N4SV({DZapCRUjcF|i8EL?)jd&ty3BxiPTM z2gk9$?F;Igd|$APZ~J2LO)Ns;3_^Lm|8w1oTZ$Y~PQNCvV(~TonH)vV^A*W0OHX_N z0H~Ieh>jHTz(l^Jr6aiyBbP1eP3V**HkUgRHwCVa!#WWTRk0r6Yp+sUsqA# z<>i&E^#>@W=#g59vz9JM?~fWUTY$6^zIW(=5o}rV_`Axz-^Fidooq==c>c-lqeK78 zf|BvqxX?=`RaI5mcz(GI47b!Qma0OC5iq=>0kxpA9)}b~`yyDB0ksxoO2OKH2QjYL z^c=T}Vhp!Ryqh?|ecQlAuW+JQrbb3al1+r_$jDbi7c6M)swQS;nbY*~N{&y%OzR!f z-V87Oor8@r771@6*aP(@kR|CE8KT@%UZu`nyCwj=lYm!(b5%QL*%Xk+se1>vmQM$U zo2(572@4mhUXc>Tmf5a(fY))BkVB?D{$T8GjJl2v)EHY|SG&tW=|1daySYL;^NC+#Kor{=!CxE+O5QjBkE@*V6Mn)~VO!eP67*YVk7QM#=u< z*q=gu2k{j${7^%?d}@_-a7e}Rn&Z_A$m`>Ir@?812@zWOKiz#VolYQI`OSKO@Bh}c zqNHiea+QmjcYbZ4GZ5rL;Jx$Pd_Qo%RdDe_u$7~gQv;!K_>=8qbnuMkAM{VabiyK08;0ew$ z)3(0b3D64G)BYg44WoA?_*qwn`GIO^NrHX*G)K3|I{reW)v2k5o?Dm*kAy(Uf28WSz<)&4yv275WAK`DYfekJ3BxT1`@>X*;yvbUdT*id)eme%Ms) z+fx_D40*f`$4S4=DC6c&_rDtoKRvcKN^+$A9BrvdSzLLuXHT)U{^aA-?5^~N(zJs4 z-PBsj@8NE0c;KHoji|QGtLNLGx8JME7iyk$Yb3wB$vcycw_EY_e9}jPE4}b|U^YK% zd|Vgrq+2B&llV={$yf)X>}nC^(rec@rrqn1$yIYWbIq4?!6TorH})EM?9~6YL=M}N zpI9h7f7sGf{I+^q)BcJ?ha^zp4#y~V(d2M+VYYjU+IxMAsIxW4^9A z)2}d5m|0Y9*KU;Dsh3i-B@&?y(FM4UWL|=$kl)rGP*=N9y)t}uzE!1rudj$PhuH(3 zJ`N`vVBxY2v_JQG+|4a+q!IO$naO+WR!+dxdc|r_ zi$W54n3gq9_5-|| z=G3V7U-%_`l9l)X*~n(G9Az(@B-O8P=Ulm6>CUPciG~A`7@|Y^#wF8INgafv| zd2@10Q4jO&Kei)|(4y;Z{ z-}L~(P;;yn8Yx0?C%x;yHbcy>wwv5R=@+GqX>RrTCq<(O-ln^FAX8yIomvl=ei-ODhf0K5ET9^fv#V{BSvomitVP_i#vX zqWMM}$l9xaRRCzs<*bLr(mKG})j5%GMJM81t}Q8(zM%aa+UVjm6lq@w_1ezkFM090 zB`@vX#ctE7C*t|M0}I>7DL#Q$4rnKTs2wn_=bCOCOm@+}J=a%X>Xw`TSRI7MF+!cH zR_@FTd#xD)ujjkQ+1v$RX^?`h-rTU08sgDwG=g{2{PaOMw5d$<3RXM^$T7 z29-%^a++E^kEa(@G@2<*vh0aWGY3K;y1+)T?epLR^(whSpcK}$RL(9MmHt#A-`$P2 zV=qVe1sVAI`W|#YB{IfmTZ2s~`zKrPoEaH80$(@sjIz$DqQ2R4`*ssOJw0yk>4@>$BK;bA) literal 0 HcmV?d00001 diff --git a/res/drawable-xxxhdpi/ic_button_disclosure_setting.png b/res/drawable-xxxhdpi/ic_button_disclosure_setting.png new file mode 100755 index 0000000000000000000000000000000000000000..b25919efb545a64c2ae4e18934ef85780c348c28 GIT binary patch literal 3577 zcmcgvdpMNq7XOACsgaUIQqzd2=H@b(j7u|aTZ5(%>1NE$#9X!+43QzR%dOHK%j_dMVCzVCY0@3+>w-nHImednAUY!p^+ zUJU?%f}JgaBpqv(-&J7gm-ND0PCBRwtXu^LxeS3n#fJ`9(zqUUs2!Waq?70rTENfu z=%xT5tHUC@3S5c%u~aS_L0Q&82-!R-8UReqg**z?i!OkA(3va_4mNW84h+ho;b1O? zL==&Sr+cz&1AXWR10BfJKrgBZ4Q6fzH5Fo|1Z=v10u{2oIee@T2m8v4mCl#NNEr01 ziogp8`^za;q7xL)^`S!z5g0fXt#1r9GC`mXO^o#n_CWPeXbci%f{NgLw)BJS-CF@9&TB*GF)Dm`JpVi3t*=ht$)9OEuv90FHnn zgmd_s-xvsVKGlcC6R@}(=rSY4gX=56!K9x4wFNfsJ1vL*cbKFBLkcN8BpQKQ-qJTk zBJsbgvf1CY`2rICpZ@+wVm>*5M@N$Ae6Ft#ReEp?&1F+OEZ&Ds5paFTT(0-GC^~s^ z1zf%-mj}h;m!oC`)ge--EY7k}_bZA>#M*KA0t$ypwz@99tHNORst zn%uINj(h@u)l55rC0W=#boo@lHmi+It!LLXJaH}X)M+PWRhsSql|u>jS&1@Xiek%R1myix>LVC zcP4T1bmiVsCeu9g?OfBt5r#k0ulhEV!EAvDuo@uG-Ors?ia-Dg0Km&NuPQBKz#RY} z!2d`%ODnmz$^6u3?@w9yh{(tQ*|PLuvdi_w>_KwDk~k^fFDn%-a(S2U;LvVc>2f)L zc1K4v==bpo$UusvAr&az|9}^}U92W6q*)jm)&=;*1uN75MJYL((_Wim!YX%m#DIQB zli%M$0h9IZ(F3X0zElxLU1S7+c}*|XTFW;{6;_1;vc{C>&0t{+N=ru8*yBd{wbdpg zy=1Y5Y#Dm~ie@zE1v(sr5-}O#t)xOH_#q7hWpLldscDCfrjt5tHsmH3AyU!#*cG>; zz#U1~UBO4%r~MBcE8e_q=_P*ZweS*qWs|d*Icu8x>p+jV)Va9u+R-^Nha8jNLOzz) z{b&Gv98Nhur7I4`%nZAAoEA3)$Qq%$irM$=7V>W=7llTSuJk$cG0zFq_6 zDr?`&K{)Q^TsGO9jJr~mW#4DyM-EO~m*2JJ>g*$x#U{p~YWs47R7$8|=@Bj4^u)mh z%PH+hI?lrPR8aQti~cY7#)xAngPX*MZ}eZ>J(sD8vfw`u+GR=>izFA12nA__D>_s= z?uvVD_|ZQfeHZFEEl0L#1e@)~2)(8!ve(bPexrnchnWO<_s>rjqFST#6Vc%(pWvxi z2b0AEw zZ@0Rb?y!Yi1GJo&v-0asZy(nBt?V*M!95$p%`McX25vc81FTico7H9;m<-gLfxx^* z7xL}LW|ydE)-Wl+iB5s2jg{6_Vi~c37HvZ)_nz>yuLG+g8UZt*jq=-ucdH)O0#kb0 zW(!xgdTW2UnOj}^(<}ayg2V$$MR&sXlhV$jX;DHS+*|t+~mI`X}8*D;%w( z%Hs|#6y|!BOzvPZOf#p}B(+aJGC`#Bf)A<_&s}=$y?*W^*`ID!+%UjDxys%QF%!8J zjusKm9q*oOBn<|N@;u}wYois$^_9$c{S4sODp=y9hVBusvDa= zMZI=BHg8&|-db&$o`ZF+HEI+zJ@+SR*b(8P^8lxuhtSV zu;;_aHw4GVG>wEYriAKd_^iHh$?2RzHu09F%XPC7R1jf*ipQ+jW7a6UXRCOPIQH-9tkSl3m!c;{K4z?&4HIN7HNn!u=Z^A^=|zM6L#q1KS&ms zPw^0Ux}>LvO?_WgVI7(*^{Or{4_l_0Ph5 zN4mVYd+nnaR{=$ibl3DMFzVV`ZVCL3O zL9MPqzlJBoNDBnku^spH_Cj1tMQ0`C0V+6SIwRhFqM*prtxuN{nR2JTQBpJG+ieu) zXv40uieKnCw>0ra#ZJz?R3+!g@v9qEv#K5PhLuZQ@J0SXLuIXdau(qm`)jq23w7(V zk|qWWLaRzBy#r{AKlUWb+}LQe?)jF!s@&s2tb^MflEaiLF(O}yqo!(&oMvrAm&vXD zQ&8||Lv5$-nX*oV=|tM`Uz8NwFH4Y*q}LE%v=|Bs8kdznHMwuIcT~=6rPU-c2rJ=N zWK^W)aM<>_3aT|mUz!{?lOH51ReP)fkG7g#<34CtlB6s}$FD9UuHm>kr`Z*IzxV3h zmn4oq@(?*3`Xr)Er%uv_>29P(eS~ZLNsvEyG1Jqji(MC8r`6mMawi;~VD0rr&K{!s z++Cy~X?xm=h{fRBs&_&ftV0UJkdXmiE+>FR6Q7pYpTAe zcJ~H$-nJrLW+B4!^j+X#1D4yP)Q5<>L%evws-;4Gm_FT(Y1*q3p|bU}d?^dME#U(6 zOX$|#=p{8b zZLgoqk_n=r>ert-PY$MgiX6pKVrnVs^hU zw;OG7q%O-Qp96Un6RDr^@qk!ewrobhLQYk7oeT&(T5o%0pcZ(v!SSaI+8-Ew{>tU9 z;_*FY@ZrYyX3(r1KZdApL(o+Yvx3Vc}HnC=Xt{sBC_ B?{xqG literal 0 HcmV?d00001 diff --git a/res/layout/dialog_location_randomized.xml b/res/layout/dialog_location_randomized.xml new file mode 100755 index 0000000..58965d7 --- /dev/null +++ b/res/layout/dialog_location_randomized.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/res/layout/dialog_location_user.xml b/res/layout/dialog_location_user.xml new file mode 100755 index 0000000..17bbf31 --- /dev/null +++ b/res/layout/dialog_location_user.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/res/layout/layout_data_disclosure_detail_pdk.xml b/res/layout/layout_data_disclosure_detail_pdk.xml new file mode 100755 index 0000000..9cd2e4e --- /dev/null +++ b/res/layout/layout_data_disclosure_detail_pdk.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/res/layout/layout_data_disclosure_pdk.xml b/res/layout/layout_data_disclosure_pdk.xml new file mode 100755 index 0000000..cc763d0 --- /dev/null +++ b/res/layout/layout_data_disclosure_pdk.xml @@ -0,0 +1,31 @@ + + + + + + + + + + diff --git a/res/layout/row_disclosure_action_pdk.xml b/res/layout/row_disclosure_action_pdk.xml new file mode 100755 index 0000000..a5250e3 --- /dev/null +++ b/res/layout/row_disclosure_action_pdk.xml @@ -0,0 +1,19 @@ + + + + diff --git a/res/layout/row_disclosure_location_accuracy_pdk.xml b/res/layout/row_disclosure_location_accuracy_pdk.xml new file mode 100755 index 0000000..78cd4cc --- /dev/null +++ b/res/layout/row_disclosure_location_accuracy_pdk.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/res/layout/row_generator_disclosure_generic.xml b/res/layout/row_generator_disclosure_generic.xml new file mode 100755 index 0000000..200f44e --- /dev/null +++ b/res/layout/row_generator_disclosure_generic.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/res/values/generators.xml b/res/values/generators.xml index 2fbcf3b..cd52c21 100755 --- a/res/values/generators.xml +++ b/res/values/generators.xml @@ -43,6 +43,33 @@ Device Location Coordinates: %1$.4f, %2$.4f + Location Accuracy + Tap here to update the accuracy of your data. + + Best Accuracy + Best Available Data From Location Hardware + + Locally Randomized + Location Combined With Random Noise + + User Provided + Static Location Provided By User + + Disabled + App Does Not Use Location Data + + Best Accuracy + This app will use the location hardware on this device to obtain the most accurate location readings. + + Location Disabled + This app will not use your location, but use an placeholder instead. + + Locally Randomized + Please enter the random distance (miles/kilometers)to use to obfuscate your exact location: + + User Provided + Please enter a postal code or city and province to use as your location: + Unable to find your location. Please enter your location another way and try again. Screen State diff --git a/res/values/strings.xml b/res/values/strings.xml index a784812..73b220f 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4,6 +4,7 @@ 0.0.1 Unknown Version Data Stream + Continue %d generator @@ -20,5 +21,11 @@ Diagnostics The app is set up correctly.\n\nNo further actions are needed. + + Passive Data Disclosure + Select a disclosure item for more information… + + Data Collection Description + Tap here to learn how this app uses your data. diff --git a/src/com/audacious_software/passive_data_kit/activities/DataDisclosureActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataDisclosureActivity.java new file mode 100755 index 0000000..70d41f3 --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/activities/DataDisclosureActivity.java @@ -0,0 +1,44 @@ +package com.audacious_software.passive_data_kit.activities; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.MenuItem; +import android.widget.FrameLayout; + +import com.audacious_software.passive_data_kit.activities.generators.GeneratorsAdapter; +import com.audacious_software.pdk.passivedatakit.R; + +public class DataDisclosureActivity extends AppCompatActivity { + private GeneratorsAdapter mAdapter = null; + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.setContentView(R.layout.layout_data_disclosure_pdk); + this.getSupportActionBar().setTitle(R.string.title_data_disclosure); + this.getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + FrameLayout dataView = (FrameLayout) this.findViewById(R.id.data_view); + + this.mAdapter = new GeneratorsAdapter(); + this.mAdapter.setContext(this.getApplicationContext()); + this.mAdapter.setDataView(dataView); + + RecyclerView listView = (RecyclerView) this.findViewById(R.id.list_view); + + listView.setLayoutManager(new LinearLayoutManager(this)); + + listView.setAdapter(this.mAdapter); + } + + public boolean onOptionsItemSelected(MenuItem item) + { + if (item.getItemId() == android.R.id.home) + { + this.finish(); + } + + return true; + } +} diff --git a/src/com/audacious_software/passive_data_kit/activities/DataDisclosureDetailActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataDisclosureDetailActivity.java new file mode 100755 index 0000000..aaf79bb --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/activities/DataDisclosureDetailActivity.java @@ -0,0 +1,123 @@ +package com.audacious_software.passive_data_kit.activities; + +import android.content.Context; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.FrameLayout; +import android.widget.ListView; +import android.widget.TextView; + +import com.audacious_software.passive_data_kit.Logger; +import com.audacious_software.passive_data_kit.generators.Generator; +import com.audacious_software.pdk.passivedatakit.R; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + +public class DataDisclosureDetailActivity extends AppCompatActivity { + public static class Action { + public String title; + public String subtitle; + + public View view; + } + + public static final String GENERATOR_CLASS_NAME = "com.audacious_software.passive_data_kit.activities.DataDisclosureDetailActivity.GENERATOR_CLASS_NAME"; + + private Class mGeneratorClass = null; + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final DataDisclosureDetailActivity me = this; + + this.setContentView(R.layout.layout_data_disclosure_detail_pdk); + this.getSupportActionBar().setSubtitle(R.string.title_data_disclosure); + this.getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + try { + this.mGeneratorClass = (Class) Class.forName(this.getIntent().getStringExtra(DataDisclosureDetailActivity.GENERATOR_CLASS_NAME)); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + + if (this.mGeneratorClass != null) { + try { + Method getGeneratorTitle = this.mGeneratorClass.getDeclaredMethod("getGeneratorTitle", Context.class); + + String title = (String) getGeneratorTitle.invoke(null, this); + this.getSupportActionBar().setTitle(title); + + Method getDisclosureActions = this.mGeneratorClass.getDeclaredMethod("getDisclosureActions", Context.class); + + final List actions = (List) getDisclosureActions.invoke(null, this); + + ListView actionsList = (ListView) this.findViewById(R.id.disclosure_actions); + ArrayAdapter adapter = new ArrayAdapter(this, R.layout.row_disclosure_action_pdk, actions) { + public View getView (int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(me).inflate(R.layout.row_disclosure_action_pdk, null); + } + + Action action = actions.get(position); + + TextView title = (TextView) convertView.findViewById(R.id.action_title); + title.setText(action.title); + + TextView description = (TextView) convertView.findViewById(R.id.action_description); + description.setText(action.subtitle); + + return convertView; + } + }; + + actionsList.setAdapter(adapter); + + actionsList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long l) { + Log.e("PDK", "TAPPED: " + position); + + Action action = actions.get(position); + + FrameLayout dataView = (FrameLayout) me.findViewById(R.id.data_view); + dataView.removeAllViews(); + + if (action.view != null) { + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + + action.view.setLayoutParams(params); + + dataView.addView(action.view); + } + } + }); + + actionsList.performItemClick(null, 0, 0); + } catch (NoSuchMethodException e1) { + Logger.getInstance(this).logThrowable(e1); + } catch (InvocationTargetException e1) { + Logger.getInstance(this).logThrowable(e1); + } catch (IllegalAccessException e1) { + Logger.getInstance(this).logThrowable(e1); + } + } + } + + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + this.finish(); + } + + return true; + } + +} diff --git a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java index 04e416c..8815505 100755 --- a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java +++ b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java @@ -6,15 +6,8 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import com.audacious_software.passive_data_kit.PassiveDataKit; import com.audacious_software.passive_data_kit.activities.generators.DataPointsAdapter; -import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; import com.audacious_software.passive_data_kit.generators.Generators; import com.audacious_software.pdk.passivedatakit.R; diff --git a/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorViewHolder.java b/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorViewHolder.java new file mode 100755 index 0000000..5726eb2 --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorViewHolder.java @@ -0,0 +1,10 @@ +package com.audacious_software.passive_data_kit.activities.generators; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +public class GeneratorViewHolder extends RecyclerView.ViewHolder { + public GeneratorViewHolder(View itemView) { + super(itemView); + } +} diff --git a/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorsAdapter.java b/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorsAdapter.java new file mode 100755 index 0000000..adf3618 --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/activities/generators/GeneratorsAdapter.java @@ -0,0 +1,129 @@ +package com.audacious_software.passive_data_kit.activities.generators; + +import android.content.Context; +import android.content.Intent; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.Toast; + +import com.audacious_software.passive_data_kit.Logger; +import com.audacious_software.passive_data_kit.activities.DataDisclosureDetailActivity; +import com.audacious_software.passive_data_kit.generators.Generator; +import com.audacious_software.passive_data_kit.generators.Generators; +import com.audacious_software.pdk.passivedatakit.R; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class GeneratorsAdapter extends RecyclerView.Adapter { + private Context mContext = null; + private FrameLayout mDataView = null; + + @Override + public GeneratorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = Generator.fetchDisclosureView(parent); + + return new GeneratorViewHolder(view); + } + + @Override + public void onBindViewHolder(final GeneratorViewHolder holder, int position) { + final GeneratorsAdapter me = this; + + List> activeGenerators = Generators.getInstance(holder.itemView.getContext()).activeGenerators(); + + this.sortGenerators(this.mContext, activeGenerators); + + Class generatorClass = activeGenerators.get(position); + + Log.e("PDK", "GENERATOR CLASS: " + generatorClass); + + try { + Method bindViewHolder = generatorClass.getDeclaredMethod("bindDisclosureViewHolder", GeneratorViewHolder.class); + bindViewHolder.invoke(null, holder); + } catch (Exception e) { +// e.printStackTrace(); + try { + generatorClass = Generator.class; + + Method bindViewHolder = generatorClass.getDeclaredMethod("bindDisclosureViewHolder", GeneratorViewHolder.class); + + bindViewHolder.invoke(null, holder); + } catch (NoSuchMethodException e1) { + Logger.getInstance(holder.itemView.getContext()).logThrowable(e1); + } catch (InvocationTargetException e1) { + Logger.getInstance(holder.itemView.getContext()).logThrowable(e1); + } catch (IllegalAccessException e1) { + Logger.getInstance(holder.itemView.getContext()).logThrowable(e1); + } + } + + final Class finalClass = generatorClass; + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + me.mDataView.removeAllViews(); + + try { + Method bindViewHolder = finalClass.getDeclaredMethod("getDisclosureDataView", GeneratorViewHolder.class); + + View dataView = (View) bindViewHolder.invoke(null, holder); + me.mDataView.addView(dataView); + } catch (NoSuchMethodException e1) { + Logger.getInstance(holder.itemView.getContext()).logThrowable(e1); + } catch (InvocationTargetException e1) { + Logger.getInstance(holder.itemView.getContext()).logThrowable(e1); + } catch (IllegalAccessException e1) { + Logger.getInstance(holder.itemView.getContext()).logThrowable(e1); + } + } + }); + + ImageView settingsButton = (ImageView) holder.itemView.findViewById(R.id.button_disclosure_item); + + settingsButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(holder.itemView.getContext(), DataDisclosureDetailActivity.class); + intent.putExtra(DataDisclosureDetailActivity.GENERATOR_CLASS_NAME, finalClass.getCanonicalName()); + + holder.itemView.getContext().startActivity(intent); + } + }); + + } + + @Override + public int getItemCount() { + return Generators.getInstance(null).activeGenerators().size(); + } + + private void sortGenerators(final Context context, List> generators) { + Collections.sort(generators, new Comparator>() { + @Override + public int compare(Class one, Class two) { + return one.getName().compareTo(two.getName()); + } + }); + } + + public int getItemViewType (int position) { + return 0; + } + + public void setContext(Context context) { + this.mContext = context; + } + + public void setDataView(FrameLayout dataView) { + this.mDataView = dataView; + } +} diff --git a/src/com/audacious_software/passive_data_kit/generators/Generator.java b/src/com/audacious_software/passive_data_kit/generators/Generator.java index abb0938..4adc72e 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generator.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generator.java @@ -11,6 +11,7 @@ import android.widget.TextView; import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; +import com.audacious_software.passive_data_kit.activities.generators.GeneratorViewHolder; import com.audacious_software.pdk.passivedatakit.R; import java.text.DateFormat; @@ -82,6 +83,20 @@ public static void bindViewHolder(DataPointViewHolder holder) { generatorLabel.setText(identifier); } + public static View fetchDisclosureView(ViewGroup parent) { + return LayoutInflater.from(parent.getContext()).inflate(R.layout.row_generator_disclosure_generic, parent, false); + } + + public static void bindDisclosureViewHolder(GeneratorViewHolder holder) { + Class currentClass = new Object() { }.getClass().getEnclosingClass(); + + String identifier = currentClass.getCanonicalName(); + + TextView generatorLabel = (TextView) holder.itemView.findViewById(R.id.label_generator); + + generatorLabel.setText(identifier); + } + public static String formatTimestamp(Context context, double timestamp) { timestamp *= 1000; diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java index 42b4cd9..ddac98b 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generators.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java @@ -24,6 +24,7 @@ public class Generators { private Context mContext = null; private boolean mStarted = false; + private ArrayList mGenerators = new ArrayList<>(); private HashSet mActiveGenerators = new HashSet<>(); private SharedPreferences mSharedPreferences = null; diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Location.java b/src/com/audacious_software/passive_data_kit/generators/device/Location.java index f00204c..64c74b0 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/Location.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/Location.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.content.ContentValues; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -12,6 +13,8 @@ import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.location.Address; +import android.location.Geocoder; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; @@ -20,18 +23,28 @@ import android.support.v4.content.ContextCompat; import android.support.v4.content.res.ResourcesCompat; import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.SwitchCompat; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.webkit.WebView; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ListView; import android.widget.TextView; +import android.widget.Toast; import com.audacious_software.passive_data_kit.DeviceInformation; import com.audacious_software.passive_data_kit.PassiveDataKit; +import com.audacious_software.passive_data_kit.activities.DataDisclosureDetailActivity; import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; +import com.audacious_software.passive_data_kit.activities.generators.GeneratorViewHolder; import com.audacious_software.passive_data_kit.activities.generators.RequestPermissionActivity; import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; import com.audacious_software.passive_data_kit.generators.Generator; @@ -53,6 +66,7 @@ import com.google.maps.android.ui.IconGenerator; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -79,6 +93,22 @@ public class Location extends Generator implements GoogleApiClient.ConnectionCal private static final String SETTING_DISPLAY_HYBRID_MAP = "com.audacious_software.passive_data_kit.generators.device.Location.SETTING_DISPLAY_HYBRID_MAP"; private static final boolean SETTING_DISPLAY_HYBRID_MAP_DEFAULT = true; + private static String ACCURACY_MODE = "com.audacious_software.passive_data_kit.generators.device.Location.ACCURACY_MODE"; + + private static int ACCURACY_BEST = 0; + private static int ACCURACY_RANDOMIZED = 1; + private static int ACCURACY_USER = 2; + private static int ACCURACY_DISABLED = 3; + + private static final String ACCURACY_MODE_RANDOMIZED_RANGE = "com.audacious_software.passive_data_kit.generators.device.Location.ACCURACY_MODE_RANDOMIZED_RANGE"; + private static final long ACCURACY_MODE_RANDOMIZED_RANGE_DEFAULT = 100; + + private static final String ACCURACY_MODE_USER_LOCATION = "com.audacious_software.passive_data_kit.generators.device.Location.ACCURACY_MODE_USER_LOCATION"; + private static final String ACCURACY_MODE_USER_LOCATION_DEFAULT = "Chicago, Illinois"; + + private static final String ACCURACY_MODE_USER_LOCATION_LATITUDE = "com.audacious_software.passive_data_kit.generators.device.Location.ACCURACY_MODE_USER_LOCATION_LATITUDE"; + private static final String ACCURACY_MODE_USER_LOCATION_LONGITUDE = "com.audacious_software.passive_data_kit.generators.device.Location.ACCURACY_MODE_USER_LOCATION_LONGITUDE"; + private static Location sInstance = null; private GoogleApiClient mGoogleApiClient = null; private android.location.Location mLastLocation = null; @@ -524,7 +554,347 @@ else if (Location.useGoogleLocationServices(parent.getContext())) } } + public static String getGeneratorTitle(Context context) { + return context.getString(R.string.generator_location); + } + + public static List getDisclosureActions(final Context context) { + List actions = new ArrayList<>(); + + DataDisclosureDetailActivity.Action disclosure = new DataDisclosureDetailActivity.Action(); + + disclosure.title = context.getString(R.string.label_data_collection_description); + disclosure.subtitle = context.getString(R.string.label_data_collection_description_more); + + WebView disclosureView = new WebView(context); + disclosureView.loadUrl("file:///android_asset/html/passive_data_kit/generator_location_disclosure.html"); + + disclosure.view = disclosureView; + + actions.add(disclosure); + + DataDisclosureDetailActivity.Action accuracy = new DataDisclosureDetailActivity.Action(); + accuracy.title = context.getString(R.string.label_data_collection_location_accuracy); + accuracy.subtitle = context.getString(R.string.label_data_collection_location_accuracy_more); + + final Integer[] options = { Location.ACCURACY_BEST, Location.ACCURACY_RANDOMIZED, Location.ACCURACY_USER, Location.ACCURACY_DISABLED }; + + final ListView listView = new ListView(context); + + final ArrayAdapter accuracyAdapter = new ArrayAdapter(context, R.layout.row_disclosure_location_accuracy_pdk, options) { + public View getView (final int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(context).inflate(R.layout.row_disclosure_location_accuracy_pdk, null); + } + + final Integer option = options[position]; + + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + int selected = prefs.getInt(Location.ACCURACY_MODE, Location.ACCURACY_BEST); + + CheckBox checked = (CheckBox) convertView.findViewById(R.id.action_checked); + + checked.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + + } + }); + + checked.setChecked(selected == option); + + TextView title = (TextView) convertView.findViewById(R.id.action_title); + TextView description = (TextView) convertView.findViewById(R.id.action_description); + + if (option == Location.ACCURACY_BEST) { + title.setText(R.string.label_data_collection_location_accuracy_best); + description.setText(R.string.label_data_collection_location_accuracy_best_more); + } else if (option == Location.ACCURACY_RANDOMIZED) { + title.setText(R.string.label_data_collection_location_accuracy_randomized); + description.setText(R.string.label_data_collection_location_accuracy_randomized_more); + } else if (option == Location.ACCURACY_USER) { + title.setText(R.string.label_data_collection_location_accuracy_user); + description.setText(R.string.label_data_collection_location_accuracy_user_more); + } else if (option == Location.ACCURACY_DISABLED) { + title.setText(R.string.label_data_collection_location_accuracy_disabled); + description.setText(R.string.label_data_collection_location_accuracy_disabled_more); + } + + final ArrayAdapter meAdapter = this; + + checked.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(final CompoundButton compoundButton, final boolean checked) { + Log.e("PDK", "CHECK CHANGE!"); + + final CompoundButton.OnCheckedChangeListener me = this; + + if (option == Location.ACCURACY_BEST) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.title_location_accuracy_best); + builder.setMessage(R.string.message_location_accuracy_best); + + builder.setPositiveButton(R.string.action_continue, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + SharedPreferences.Editor e = prefs.edit(); + e.putInt(Location.ACCURACY_MODE, option); + e.apply(); + + meAdapter.notifyDataSetChanged(); + } + }); + + builder.create().show(); + } else if (option == Location.ACCURACY_RANDOMIZED) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.title_location_accuracy_randomized); + + View body = LayoutInflater.from(context).inflate(R.layout.dialog_location_randomized, null); + builder.setView(body); + + final EditText rangeField = (EditText) body.findViewById(R.id.random_range); + + long existingRange = prefs.getLong(Location.ACCURACY_MODE_RANDOMIZED_RANGE, Location.ACCURACY_MODE_RANDOMIZED_RANGE_DEFAULT); + + rangeField.setText("" + existingRange); + + builder.setPositiveButton(R.string.action_continue, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + SharedPreferences.Editor e = prefs.edit(); + + long randomRange = Long.parseLong(rangeField.getText().toString()); + + e.putLong(Location.ACCURACY_MODE_RANDOMIZED_RANGE, randomRange); + + e.putInt(Location.ACCURACY_MODE, option); + e.apply(); + + meAdapter.notifyDataSetChanged(); + } + }); + + builder.create().show(); + } else if (option == Location.ACCURACY_USER) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.title_location_accuracy_user); + + View body = LayoutInflater.from(context).inflate(R.layout.dialog_location_user, null); + builder.setView(body); + + final EditText locationField = (EditText) body.findViewById(R.id.user_location); + + String existingLocation = prefs.getString(Location.ACCURACY_MODE_USER_LOCATION, Location.ACCURACY_MODE_USER_LOCATION_DEFAULT); + + locationField.setText(existingLocation); + + builder.setPositiveButton(R.string.action_continue, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + SharedPreferences.Editor e = prefs.edit(); + + String location = locationField.getText().toString(); + + e.putString(Location.ACCURACY_MODE_USER_LOCATION, location); + + try { + List
results = (new Geocoder(context)).getFromLocationName(location, 1); + + if (results.size() > 0) { + Address match = results.get(0); + + e.putFloat(Location.ACCURACY_MODE_USER_LOCATION_LATITUDE, (float) match.getLatitude()); + e.putFloat(Location.ACCURACY_MODE_USER_LOCATION_LONGITUDE, (float) match.getLongitude()); + } else { + Toast.makeText(context, R.string.toast_location_lookup_failed, Toast.LENGTH_LONG).show(); + + me.onCheckedChanged(compoundButton, checked); + } + } catch (IOException e1) { + e1.printStackTrace(); + } + + e.putInt(Location.ACCURACY_MODE, option); + e.apply(); + + meAdapter.notifyDataSetChanged(); + } + }); + + builder.create().show(); + } else if (option == Location.ACCURACY_DISABLED) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.title_location_accuracy_disabled); + builder.setMessage(R.string.message_location_accuracy_disabled); + + builder.setPositiveButton(R.string.action_continue, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + SharedPreferences.Editor e = prefs.edit(); + e.putInt(Location.ACCURACY_MODE, option); + e.apply(); + + meAdapter.notifyDataSetChanged(); + } + }); + + builder.create().show(); + } + + } + }); + + return convertView; + } + }; + + listView.setAdapter(accuracyAdapter); + + accuracy.view = listView; + + actions.add(accuracy); + + return actions; + } + + public static View getDisclosureDataView(final GeneratorViewHolder holder) { + final Context context = holder.itemView.getContext(); + + if (Location.useKindleLocationServices()) + { + // TODO + throw new RuntimeException("Throw rocks at developer to implement Kindle support."); + } + else if (Location.useGoogleLocationServices(holder.itemView.getContext())) + { + final MapView mapView = new MapView(context); + + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + mapView.setLayoutParams(params); + + mapView.onCreate(null); + + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + final boolean useHybrid = prefs.getBoolean(Location.SETTING_DISPLAY_HYBRID_MAP, Location.SETTING_DISPLAY_HYBRID_MAP_DEFAULT); + + IconGenerator iconGen = new IconGenerator(context); + + Drawable shapeDrawable = ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_location_heatmap_marker, null); + iconGen.setBackground(shapeDrawable); + + View view = new View(context); + view.setLayoutParams(new ViewGroup.LayoutParams(8, 8)); + iconGen.setContentView(view); + + final Bitmap bitmap = iconGen.makeIcon(); + + mapView.getMapAsync(new OnMapReadyCallback() { + public void onMapReady(GoogleMap googleMap) { + if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED || + ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + googleMap.setMyLocationEnabled(true); + } + + if (useHybrid) { + googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); + } else { + googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); + } + + googleMap.getUiSettings().setZoomControlsEnabled(true); + googleMap.getUiSettings().setMyLocationButtonEnabled(false); + googleMap.getUiSettings().setMapToolbarEnabled(false); + googleMap.getUiSettings().setAllGesturesEnabled(false); + + Location me = Location.getInstance(context); + + double lastLatitude = 0.0; + double lastLongitude = 0.0; + long timestamp = 0; + + final List locations = new ArrayList<>(); + + String where = Location.HISTORY_OBSERVED + " > ?"; + String[] args = { "" + (System.currentTimeMillis() - (1000 * 60 * 60 * 24)) }; + + Cursor c = me.mDatabase.query(Location.TABLE_HISTORY, null, where, args, null, null, Location.HISTORY_OBSERVED); + + while (c.moveToNext()) { + lastLatitude = c.getDouble(c.getColumnIndex(Location.HISTORY_LATITUDE)); + lastLongitude = c.getDouble(c.getColumnIndex(Location.HISTORY_LONGITUDE)); + + LatLng location = new LatLng(lastLatitude, lastLongitude); + + locations.add(location); + } + + c.close(); + + LatLngBounds.Builder builder = new LatLngBounds.Builder(); + + for (LatLng latlng : locations) { + builder.include(latlng); + } + + final DisplayMetrics metrics = new DisplayMetrics(); + ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(metrics); + + if (locations.size() > 0) { + googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), (int) (16 * metrics.density))); + } + + for (LatLng latLng : locations) { + googleMap.addMarker(new MarkerOptions() + .position(latLng) + .icon(BitmapDescriptorFactory.fromBitmap(bitmap))); + } + + mapView.onResume(); + } + }); + + return mapView; + } + else + { + // TODO + throw new RuntimeException("Throw rocks at developer to implement generic location support."); + } + } + + public static void bindDisclosureViewHolder(final GeneratorViewHolder holder) { + Class currentClass = new Object() { }.getClass().getEnclosingClass(); + + String identifier = currentClass.getCanonicalName(); + + TextView generatorLabel = (TextView) holder.itemView.findViewById(R.id.label_generator); + + generatorLabel.setText(Location.getGeneratorTitle(holder.itemView.getContext())); + } + public android.location.Location getLastKnownLocation() { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext); + int selected = prefs.getInt(Location.ACCURACY_MODE, Location.ACCURACY_BEST); + + if (selected == Location.ACCURACY_USER) { + double latitude = prefs.getFloat(Location.ACCURACY_MODE_USER_LOCATION_LATITUDE, 0); + double longitude = prefs.getFloat(Location.ACCURACY_MODE_USER_LOCATION_LONGITUDE, 0); + + android.location.Location location = new android.location.Location(""); + location.setLatitude(latitude); + location.setLongitude(longitude); + + return location; + } else if (selected == Location.ACCURACY_DISABLED) { + android.location.Location location = new android.location.Location(""); + location.setLatitude(41.8781); + location.setLongitude(-87.6298); + + return location; + } + if (this.mLastLocation != null) { return this.mLastLocation; } From 70e5e3eb24c3b0160bdf5093e0407b3b3a10ceaf Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Sun, 15 Jan 2017 21:03:50 -0600 Subject: [PATCH 10/18] Passive data disclosure work --- res/values/generators.xml | 2 +- .../generators/device/Location.java | 63 ++++++++++++++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/res/values/generators.xml b/res/values/generators.xml index cd52c21..a6e8f0e 100755 --- a/res/values/generators.xml +++ b/res/values/generators.xml @@ -65,7 +65,7 @@ This app will not use your location, but use an placeholder instead. Locally Randomized - Please enter the random distance (miles/kilometers)to use to obfuscate your exact location: + Please enter the random distance (kilometers) to use to obfuscate your exact location: User Provided Please enter a postal code or city and province to use as your location: diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Location.java b/src/com/audacious_software/passive_data_kit/generators/device/Location.java index 64c74b0..7428d53 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/Location.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/Location.java @@ -67,8 +67,10 @@ import java.io.File; import java.io.IOException; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; +import java.util.Random; @SuppressWarnings("unused") public class Location extends Generator implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { @@ -312,13 +314,42 @@ public void onConnectionFailed(ConnectionResult connectionResult) { @Override public void onLocationChanged(android.location.Location location) { - Log.e("PDK", "LOCATION CHANGED"); - if (location == null) return; long now = System.currentTimeMillis(); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext); + int selected = prefs.getInt(Location.ACCURACY_MODE, Location.ACCURACY_BEST); + + if (selected == Location.ACCURACY_RANDOMIZED) { + // http://gis.stackexchange.com/a/68275/10230 + + double latitude = location.getLatitude(); + double longitude = location.getLongitude(); + + double radius = prefs.getLong(Location.ACCURACY_MODE_RANDOMIZED_RANGE, Location.ACCURACY_MODE_RANDOMIZED_RANGE_DEFAULT); + + double radiusInDegrees = radius / 111000; + + Random r = new SecureRandom(); + + double u = r.nextDouble(); + double v = r.nextDouble(); + + double w = radiusInDegrees * Math.sqrt(u); + double t = 2 * Math.PI * v; + double x = w * Math.cos(t); + double y = w * Math.sin(t); + + // Adjust the x-coordinate for the shrinking of the east-west distances + longitude = longitude + (x / Math.cos(latitude)); + latitude = y + latitude; + + location.setLongitude(longitude); + location.setLatitude(latitude); + } + ContentValues values = new ContentValues(); values.put(Location.HISTORY_OBSERVED, System.currentTimeMillis()); values.put(Location.HISTORY_LATITUDE, location.getLatitude()); @@ -913,6 +944,34 @@ public android.location.Location getLastKnownLocation() { } } + if (selected == Location.ACCURACY_RANDOMIZED) { + // http://gis.stackexchange.com/a/68275/10230 + + double latitude = last.getLatitude(); + double longitude = last.getLongitude(); + + double radius = prefs.getLong(Location.ACCURACY_MODE_RANDOMIZED_RANGE, Location.ACCURACY_MODE_RANDOMIZED_RANGE_DEFAULT); + + double radiusInDegrees = radius / 111000; + + Random r = new SecureRandom(); + + double u = r.nextDouble(); + double v = r.nextDouble(); + + double w = radiusInDegrees * Math.sqrt(u); + double t = 2 * Math.PI * v; + double x = w * Math.cos(t); + double y = w * Math.sin(t); + + // Adjust the x-coordinate for the shrinking of the east-west distances + longitude = longitude + (x / Math.cos(latitude)); + latitude = y + latitude; + + last.setLongitude(longitude); + last.setLatitude(latitude); + } + return last; } From bdde3fa87d34556b2b8a6e93fae0d72a94bdacbd Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Fri, 3 Mar 2017 16:55:54 -0600 Subject: [PATCH 11/18] Generator work, dependency updates --- build.gradle | 18 +- .../activities/DataStreamActivity.java | 2 +- .../generators/Generator.java | 7 + .../generators/Generators.java | 12 +- .../generators/communication/PhoneCalls.java | 220 +++++++++--------- .../generators/device/Location.java | 25 +- .../generators/device/ScreenState.java | 21 +- .../transmitters/HttpTransmitter.java | 7 +- 8 files changed, 168 insertions(+), 144 deletions(-) diff --git a/build.gradle b/build.gradle index e6359b3..e0deb1f 100755 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:2.3.0' } } @@ -45,14 +45,14 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:25.1.0' - compile 'com.android.support:recyclerview-v7:25.1.0' - compile 'com.android.support:cardview-v7:25.1.0' - compile 'com.google.android.gms:play-services-location:10.0.1' - compile 'com.google.android.gms:play-services-maps:10.0.1' - compile 'com.google.android.gms:play-services-nearby:10.0.1' - compile 'com.google.android.gms:play-services-places:10.0.1' - compile 'com.google.android.gms:play-services-awareness:10.0.1' + compile 'com.android.support:appcompat-v7:25.2.0' + compile 'com.android.support:recyclerview-v7:25.2.0' + compile 'com.android.support:cardview-v7:25.2.0' + compile 'com.google.android.gms:play-services-location:10.2.0' + compile 'com.google.android.gms:play-services-maps:10.2.0' + compile 'com.google.android.gms:play-services-nearby:10.2.0' + compile 'com.google.android.gms:play-services-places:10.2.0' + compile 'com.google.android.gms:play-services-awareness:10.2.0' compile 'com.google.maps.android:android-maps-utils:0.4' compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'commons-io:commons-io:2.4' diff --git a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java index 04e416c..881a6fe 100755 --- a/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java +++ b/src/com/audacious_software/passive_data_kit/activities/DataStreamActivity.java @@ -65,7 +65,7 @@ protected void onPause() { } @Override - public void onGeneratorUpdated(String identifier, Bundle data) { + public void onGeneratorUpdated(String identifier, long timestamp, Bundle data) { final DataStreamActivity me = this; this.runOnUiThread(new Runnable() { diff --git a/src/com/audacious_software/passive_data_kit/generators/Generator.java b/src/com/audacious_software/passive_data_kit/generators/Generator.java index abb0938..1d8835d 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generator.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generator.java @@ -3,6 +3,7 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; +import android.database.MatrixCursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.LayoutInflater; @@ -105,6 +106,12 @@ public static String formatTimestamp(Context context, double timestamp) { public abstract List fetchPayloads(); + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + Cursor c = new MatrixCursor(cols); + + return c; + } + protected int getDatabaseVersion(SQLiteDatabase db) { String where = "type = ? AND name = ?"; String[] args = { "table", Generator.TABLE_METADATA }; diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java index 42b4cd9..fd35553 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generators.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java @@ -237,9 +237,17 @@ public List> activeGenerators() { return active; } + public void notifyGeneratorUpdated(String identifier, long timestamp, Bundle bundle) { + for (GeneratorUpdatedListener listener : this.mGeneratorUpdatedListeners) { + listener.onGeneratorUpdated(identifier, timestamp, bundle); + } + } + public void notifyGeneratorUpdated(String identifier, Bundle bundle) { + long timestamp = System.currentTimeMillis(); + for (GeneratorUpdatedListener listener : this.mGeneratorUpdatedListeners) { - listener.onGeneratorUpdated(identifier, bundle); + listener.onGeneratorUpdated(identifier, timestamp, bundle); } } @@ -269,6 +277,6 @@ public void removeGeneratorUpdatedListener(Generators.GeneratorUpdatedListener l } public interface GeneratorUpdatedListener { - void onGeneratorUpdated(String identifier, Bundle data); + void onGeneratorUpdated(String identifier, long timestamp, Bundle data); } } diff --git a/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java b/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java index f72cbe8..cea19b6 100755 --- a/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java +++ b/src/com/audacious_software/passive_data_kit/generators/communication/PhoneCalls.java @@ -171,130 +171,132 @@ public void run() { Cursor c = me.mContext.getContentResolver().query(CallLog.Calls.CONTENT_URI, null, where, args, CallLog.Calls.DATE); - while (c.moveToNext()) { - ContentValues values = new ContentValues(); - values.put(PhoneCalls.HISTORY_OBSERVED, c.getLong(c.getColumnIndex(CallLog.Calls.DATE))); - values.put(PhoneCalls.HISTORY_DURATION, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); - values.put(PhoneCalls.HISTORY_NUMBER, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); - values.put(PhoneCalls.HISTORY_IS_NEW, (c.getInt(c.getColumnIndex(CallLog.Calls.NEW)) != 0)); - - Bundle bundle = new Bundle(); - bundle.putLong(PhoneCalls.CALL_DATE_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DATE))); - bundle.putLong(PhoneCalls.CALL_DURATION_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); - bundle.putString(PhoneCalls.CALL_NUMBER_KEY, c.getString(c.getColumnIndex(CallLog.Calls.NUMBER))); - bundle.putBoolean(PhoneCalls.CALL_IS_NEW_KEY, (c.getInt(c.getColumnIndex(CallLog.Calls.NEW)) != 0)); - - int features = 0; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - features = c.getInt(c.getColumnIndex(CallLog.Calls.FEATURES)); - } + if (c != null) { + while (c.moveToNext()) { + ContentValues values = new ContentValues(); + values.put(PhoneCalls.HISTORY_OBSERVED, c.getLong(c.getColumnIndex(CallLog.Calls.DATE))); + values.put(PhoneCalls.HISTORY_DURATION, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); + values.put(PhoneCalls.HISTORY_NUMBER, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); + values.put(PhoneCalls.HISTORY_IS_NEW, (c.getInt(c.getColumnIndex(CallLog.Calls.NEW)) != 0)); + + Bundle bundle = new Bundle(); + bundle.putLong(PhoneCalls.CALL_DATE_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DATE))); + bundle.putLong(PhoneCalls.CALL_DURATION_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DURATION))); + bundle.putString(PhoneCalls.CALL_NUMBER_KEY, c.getString(c.getColumnIndex(CallLog.Calls.NUMBER))); + bundle.putBoolean(PhoneCalls.CALL_IS_NEW_KEY, (c.getInt(c.getColumnIndex(CallLog.Calls.NEW)) != 0)); + + int features = 0; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + features = c.getInt(c.getColumnIndex(CallLog.Calls.FEATURES)); + } - int typeInt = c.getInt(c.getColumnIndex(CallLog.Calls.TYPE)); - String type = PhoneCalls.CALL_TYPE_UNKNOWN; + int typeInt = c.getInt(c.getColumnIndex(CallLog.Calls.TYPE)); + String type = PhoneCalls.CALL_TYPE_UNKNOWN; + + switch (Build.VERSION.SDK_INT) { + case 25: + if (typeInt == CallLog.Calls.ANSWERED_EXTERNALLY_TYPE) { + type = PhoneCalls.CALL_TYPE_ANSWERED_EXTERNALLY; + } + + bundle.putBoolean(PhoneCalls.CALL_PULLED_EXTERNALLY_KEY, ((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) == CallLog.Calls.FEATURES_PULLED_EXTERNALLY)); + + values.put(PhoneCalls.HISTORY_PULLED_EXTERNALLY, ((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) == CallLog.Calls.FEATURES_PULLED_EXTERNALLY)); + case 24: + if (typeInt == CallLog.Calls.REJECTED_TYPE) { + type = PhoneCalls.CALL_TYPE_REJECTED; + } else if (typeInt == CallLog.Calls.BLOCKED_TYPE) { + type = PhoneCalls.CALL_TYPE_BLOCKED; + } + + bundle.putString(PhoneCalls.CALL_POST_DIAL_DIGITS_KEY, c.getString(c.getColumnIndex(CallLog.Calls.POST_DIAL_DIGITS))); + bundle.putString(PhoneCalls.CALL_VIA_NUMBER_KEY, c.getString(c.getColumnIndex(CallLog.Calls.VIA_NUMBER))); + + values.put(PhoneCalls.HISTORY_POST_DIAL_DIGITS, c.getString(c.getColumnIndex(CallLog.Calls.POST_DIAL_DIGITS))); + values.put(PhoneCalls.HISTORY_VIA_NUMBER, c.getString(c.getColumnIndex(CallLog.Calls.VIA_NUMBER))); + case 21: + if (typeInt == CallLog.Calls.VOICEMAIL_TYPE) { + type = PhoneCalls.CALL_TYPE_VOICEMAIL; + } + + bundle.putString(PhoneCalls.CALL_COUNTRY_ISO_KEY, c.getString(c.getColumnIndex(CallLog.Calls.COUNTRY_ISO))); + bundle.putLong(PhoneCalls.CALL_DATA_USAGE_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DATA_USAGE))); + bundle.putString(PhoneCalls.CALL_GEOCODED_LOCATION_KEY, c.getString(c.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION))); + bundle.putBoolean(PhoneCalls.CALL_VIDEO_KEY, ((features & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO)); + + values.put(PhoneCalls.HISTORY_COUNTRY_ISO, c.getString(c.getColumnIndex(CallLog.Calls.COUNTRY_ISO))); + values.put(PhoneCalls.HISTORY_DATA_USAGE, c.getLong(c.getColumnIndex(CallLog.Calls.DATA_USAGE))); + values.put(PhoneCalls.HISTORY_GEOCODED_LOCATION, c.getString(c.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION))); + values.put(PhoneCalls.HISTORY_VIDEO, ((features & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO)); - switch(Build.VERSION.SDK_INT) { - case 25: - if (typeInt == CallLog.Calls.ANSWERED_EXTERNALLY_TYPE) { - type = PhoneCalls.CALL_TYPE_ANSWERED_EXTERNALLY; - } - - bundle.putBoolean(PhoneCalls.CALL_PULLED_EXTERNALLY_KEY, ((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) == CallLog.Calls.FEATURES_PULLED_EXTERNALLY)); +// bundle.putString(PhoneCalls.CALL_TRANSCRIPTION_KEY, c.getString(c.getColumnIndex(CallLog.Calls.TRANSCRIPTION))); + case 19: + switch (c.getInt(c.getColumnIndex(CallLog.Calls.NUMBER_PRESENTATION))) { + case CallLog.Calls.PRESENTATION_ALLOWED: + bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_ALLOWED); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_ALLOWED); + break; + case CallLog.Calls.PRESENTATION_RESTRICTED: + bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_RESTRICTED); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_RESTRICTED); + break; + case CallLog.Calls.PRESENTATION_PAYPHONE: + bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_PAYPHONE); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_PAYPHONE); + break; + case CallLog.Calls.PRESENTATION_UNKNOWN: + bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_UNKNOWN); + values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_UNKNOWN); + break; + } + case 14: + bundle.putBoolean(PhoneCalls.CALL_IS_READ_KEY, (c.getInt(c.getColumnIndex(CallLog.Calls.IS_READ)) != 0)); + values.put(PhoneCalls.HISTORY_IS_READ, (c.getInt(c.getColumnIndex(CallLog.Calls.IS_READ)) != 0)); + case 1: + if (typeInt == CallLog.Calls.INCOMING_TYPE) { + type = PhoneCalls.CALL_TYPE_INCOMING; + } else if (typeInt == CallLog.Calls.OUTGOING_TYPE) { + type = PhoneCalls.CALL_TYPE_OUTGOING; + } else if (typeInt == CallLog.Calls.MISSED_TYPE) { + type = PhoneCalls.CALL_TYPE_MISSED; + } + } - values.put(PhoneCalls.HISTORY_PULLED_EXTERNALLY, ((features & CallLog.Calls.FEATURES_PULLED_EXTERNALLY) == CallLog.Calls.FEATURES_PULLED_EXTERNALLY)); - case 24: - if (typeInt == CallLog.Calls.REJECTED_TYPE) { - type = PhoneCalls.CALL_TYPE_REJECTED; - } else if (typeInt == CallLog.Calls.BLOCKED_TYPE) { - type = PhoneCalls.CALL_TYPE_BLOCKED; - } + bundle.putString(PhoneCalls.CALL_TYPE_KEY, type); + values.put(PhoneCalls.HISTORY_CALL_TYPE, type); - bundle.putString(PhoneCalls.CALL_POST_DIAL_DIGITS_KEY, c.getString(c.getColumnIndex(CallLog.Calls.POST_DIAL_DIGITS))); - bundle.putString(PhoneCalls.CALL_VIA_NUMBER_KEY, c.getString(c.getColumnIndex(CallLog.Calls.VIA_NUMBER))); + String[] sensitiveFields = { + PhoneCalls.CALL_NUMBER_KEY, + PhoneCalls.CALL_POST_DIAL_DIGITS_KEY, + PhoneCalls.CALL_VIA_NUMBER_KEY, + }; - values.put(PhoneCalls.HISTORY_POST_DIAL_DIGITS, c.getString(c.getColumnIndex(CallLog.Calls.POST_DIAL_DIGITS))); - values.put(PhoneCalls.HISTORY_VIA_NUMBER, c.getString(c.getColumnIndex(CallLog.Calls.VIA_NUMBER))); - case 21: - if (typeInt == CallLog.Calls.VOICEMAIL_TYPE) { - type = PhoneCalls.CALL_TYPE_VOICEMAIL; + for (String field : sensitiveFields) { + if (bundle.containsKey(field)) { + bundle.putString(field, new String(Hex.encodeHex(DigestUtils.sha256(bundle.getString(field))))); } + } - bundle.putString(PhoneCalls.CALL_COUNTRY_ISO_KEY, c.getString(c.getColumnIndex(CallLog.Calls.COUNTRY_ISO))); - bundle.putLong(PhoneCalls.CALL_DATA_USAGE_KEY, c.getLong(c.getColumnIndex(CallLog.Calls.DATA_USAGE))); - bundle.putString(PhoneCalls.CALL_GEOCODED_LOCATION_KEY, c.getString(c.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION))); - bundle.putBoolean(PhoneCalls.CALL_VIDEO_KEY, ((features & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO)); - - values.put(PhoneCalls.HISTORY_COUNTRY_ISO, c.getString(c.getColumnIndex(CallLog.Calls.COUNTRY_ISO))); - values.put(PhoneCalls.HISTORY_DATA_USAGE, c.getLong(c.getColumnIndex(CallLog.Calls.DATA_USAGE))); - values.put(PhoneCalls.HISTORY_GEOCODED_LOCATION, c.getString(c.getColumnIndex(CallLog.Calls.GEOCODED_LOCATION))); - values.put(PhoneCalls.HISTORY_VIDEO, ((features & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO)); + String[] valueSensitiveFields = { + PhoneCalls.HISTORY_NUMBER, + PhoneCalls.HISTORY_POST_DIAL_DIGITS, + PhoneCalls.HISTORY_VIA_NUMBER, + }; -// bundle.putString(PhoneCalls.CALL_TRANSCRIPTION_KEY, c.getString(c.getColumnIndex(CallLog.Calls.TRANSCRIPTION))); - case 19: - switch (c.getInt(c.getColumnIndex(CallLog.Calls.NUMBER_PRESENTATION))) { - case CallLog.Calls.PRESENTATION_ALLOWED: - bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_ALLOWED); - values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_ALLOWED); - break; - case CallLog.Calls.PRESENTATION_RESTRICTED: - bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_RESTRICTED); - values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_RESTRICTED); - break; - case CallLog.Calls.PRESENTATION_PAYPHONE: - bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_PAYPHONE); - values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_PAYPHONE); - break; - case CallLog.Calls.PRESENTATION_UNKNOWN: - bundle.putString(PhoneCalls.CALL_PRESENTATION_KEY, PhoneCalls.CALL_PRESENTATION_UNKNOWN); - values.put(PhoneCalls.HISTORY_PRESENTATION, PhoneCalls.CALL_PRESENTATION_UNKNOWN); - break; + for (String field : valueSensitiveFields) { + if (values.containsKey(field)) { + values.put(field, new String(Hex.encodeHex(DigestUtils.sha256(values.getAsString(field))))); } - case 14: - bundle.putBoolean(PhoneCalls.CALL_IS_READ_KEY, (c.getInt(c.getColumnIndex(CallLog.Calls.IS_READ)) != 0)); - values.put(PhoneCalls.HISTORY_IS_READ, (c.getInt(c.getColumnIndex(CallLog.Calls.IS_READ)) != 0)); - case 1: - if (typeInt == CallLog.Calls.INCOMING_TYPE) { - type = PhoneCalls.CALL_TYPE_INCOMING; - } else if (typeInt == CallLog.Calls.OUTGOING_TYPE) { - type = PhoneCalls.CALL_TYPE_OUTGOING; - } else if (typeInt == CallLog.Calls.MISSED_TYPE) { - type = PhoneCalls.CALL_TYPE_MISSED; - } - } - - bundle.putString(PhoneCalls.CALL_TYPE_KEY, type); - values.put(PhoneCalls.HISTORY_CALL_TYPE, type); - - String[] sensitiveFields = { - PhoneCalls.CALL_NUMBER_KEY, - PhoneCalls.CALL_POST_DIAL_DIGITS_KEY, - PhoneCalls.CALL_VIA_NUMBER_KEY, - }; - - for (String field : sensitiveFields) { - if (bundle.containsKey(field)) { - bundle.putString(field, new String(Hex.encodeHex(DigestUtils.sha256(bundle.getString(field))))); } - } - String[] valueSensitiveFields = { - PhoneCalls.HISTORY_NUMBER, - PhoneCalls.HISTORY_POST_DIAL_DIGITS, - PhoneCalls.HISTORY_VIA_NUMBER, - }; + me.mDatabase.insert(PhoneCalls.TABLE_HISTORY, null, values); - for (String field : valueSensitiveFields) { - if (values.containsKey(field)) { - values.put(field, new String(Hex.encodeHex(DigestUtils.sha256(values.getAsString(field))))); - } + Generators.getInstance(me.mContext).notifyGeneratorUpdated(PhoneCalls.GENERATOR_IDENTIFIER, bundle); } - me.mDatabase.insert(PhoneCalls.TABLE_HISTORY, null, values); - - Generators.getInstance(me.mContext).notifyGeneratorUpdated(PhoneCalls.GENERATOR_IDENTIFIER, bundle); + c.close(); } - - c.close(); } if (me.mHandler != null) { diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Location.java b/src/com/audacious_software/passive_data_kit/generators/device/Location.java index f00204c..12ab3b8 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/Location.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/Location.java @@ -9,6 +9,7 @@ import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.database.Cursor; +import android.database.MatrixCursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; @@ -88,15 +89,15 @@ public class Location extends Generator implements GoogleApiClient.ConnectionCal private static int DATABASE_VERSION = 1; private static final String TABLE_HISTORY = "history"; - private static final String HISTORY_OBSERVED = "observed"; - private static final String HISTORY_LATITUDE = "latitude"; - private static final String HISTORY_LONGITUDE = "longitude"; - private static final String HISTORY_ALTITUDE = "altitude"; - private static final String HISTORY_BEARING = "bearing"; - private static final String HISTORY_SPEED = "speed"; - private static final String HISTORY_PROVIDER = "provider"; - private static final String HISTORY_LOCATION_TIMESTAMP = "location_timestamp"; - private static final String HISTORY_ACCURACY = "accuracy"; + public static final String HISTORY_OBSERVED = "observed"; + public static final String HISTORY_LATITUDE = "latitude"; + public static final String HISTORY_LONGITUDE = "longitude"; + public static final String HISTORY_ALTITUDE = "altitude"; + public static final String HISTORY_BEARING = "bearing"; + public static final String HISTORY_SPEED = "speed"; + public static final String HISTORY_PROVIDER = "provider"; + public static final String HISTORY_LOCATION_TIMESTAMP = "location_timestamp"; + public static final String HISTORY_ACCURACY = "accuracy"; public static Location getInstance(Context context) { if (Location.sInstance == null) { @@ -282,8 +283,6 @@ public void onConnectionFailed(ConnectionResult connectionResult) { @Override public void onLocationChanged(android.location.Location location) { - Log.e("PDK", "LOCATION CHANGED"); - if (location == null) return; @@ -552,4 +551,8 @@ public void setUpdateInterval(long interval) { this.stopGenerator(); this.startGenerator(); } + + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + return this.mDatabase.query(Location.TABLE_HISTORY, cols, where, args, null, null, orderBy); + } } diff --git a/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java b/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java index 2107447..d8a7432 100755 --- a/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java +++ b/src/com/audacious_software/passive_data_kit/generators/device/ScreenState.java @@ -37,17 +37,18 @@ public class ScreenState extends Generator{ private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.ScreenState.ENABLED"; private static final boolean ENABLED_DEFAULT = true; - private static final String STATE_DOZE = "doze"; - private static final String STATE_DOZE_SUSPEND = "doze_suspend"; - private static final String STATE_ON = "on"; - private static final String STATE_OFF = "off"; - private static final String STATE_UNKNOWN = "unknown"; + public static final String STATE_DOZE = "doze"; + public static final String STATE_DOZE_SUSPEND = "doze_suspend"; + public static final String STATE_ON = "on"; + public static final String STATE_OFF = "off"; + public static final String STATE_UNKNOWN = "unknown"; private static final String DATABASE_PATH = "pdk-screen-state.sqlite"; private static final int DATABASE_VERSION = 2; - private static final String HISTORY_OBSERVED = "observed"; - private static final String HISTORY_STATE = "state"; - private static final String TABLE_HISTORY = "history"; + + public static final String HISTORY_OBSERVED = "observed"; + public static final String HISTORY_STATE = "state"; + public static final String TABLE_HISTORY = "history"; private static ScreenState sInstance = null; @@ -439,4 +440,8 @@ public static long latestPointGenerated(Context context) { return timestamp; } + + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + return this.mDatabase.query(ScreenState.TABLE_HISTORY, cols, where, args, null, null, orderBy); + } } diff --git a/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java b/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java index 863d30e..1d9a2cb 100755 --- a/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java +++ b/src/com/audacious_software/passive_data_kit/transmitters/HttpTransmitter.java @@ -424,10 +424,9 @@ public long transmittedSize() { } @Override - public void onGeneratorUpdated(String identifier, Bundle data) { + public void onGeneratorUpdated(String identifier, long timestamp, Bundle data) { if (data.keySet().size() > 1) { // Only transmit non-empty bundles... - double now = (double) System.currentTimeMillis(); - now = now / 1000; // Convert to seconds... + timestamp = timestamp / 1000; // Convert to seconds... Generators generators = Generators.getInstance(this.mContext); @@ -438,7 +437,7 @@ public void onGeneratorUpdated(String identifier, Bundle data) { } metadata.putString(Generator.IDENTIFIER, identifier); - metadata.putDouble(Generator.TIMESTAMP, now); + metadata.putDouble(Generator.TIMESTAMP, timestamp); metadata.putString(Generator.GENERATOR, generators.getGeneratorFullName(identifier)); metadata.putString(Generator.SOURCE, generators.getSource()); metadata.putString(Generator.SOURCE, this.mUserId); From 2df6c74dc05ed279807d98ac2f27ac55e8f21872 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Sat, 4 Mar 2017 11:37:00 -0600 Subject: [PATCH 12/18] Updating .travis.yml. --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index c65a9c3..6d11e4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,11 @@ language: android jdk: oraclejdk8 before_install: - - wget http://services.gradle.org/distributions/gradle-2.14.1-bin.zip - - unzip gradle-2.14.1-bin.zip - - export GRADLE_HOME=$PWD/gradle-2.14.1 + - wget http://services.gradle.org/distributions/gradle-3.3-bin.zip + - unzip gradle-3.3-bin.zip + - export GRADLE_HOME=$PWD/gradle-3.3 - export PATH=$GRADLE_HOME/bin:$PATH - - ( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk -a --no-ui --filter tool,platform-tool,build-tools-25.0.0 + - ( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk -a --no-ui --filter tool,platform-tool,build-tools-25.0.2 script: - gradle assembleDebug From 9b7c5ca8dc4a14f0172d25ec14450f1bfa5760d5 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Fri, 10 Mar 2017 18:16:00 -0600 Subject: [PATCH 13/18] Implements generic app event logging. --- res/values/databases.xml | 4 + .../passive_data_kit/Logger.java | 11 +- .../generators/Generators.java | 3 + .../generators/diagnostics/AppEvent.java | 436 ++++++++++++++++++ 4 files changed, 452 insertions(+), 2 deletions(-) create mode 100755 src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java diff --git a/res/values/databases.xml b/res/values/databases.xml index a6d7f01..672f1cd 100755 --- a/res/values/databases.xml +++ b/res/values/databases.xml @@ -12,4 +12,8 @@ CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, direction TEXT, length INTEGER, body TEXT, number_name TEXT, number TEXT); + + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, event_name TEXT, event_details TEXT); + \ No newline at end of file diff --git a/src/com/audacious_software/passive_data_kit/Logger.java b/src/com/audacious_software/passive_data_kit/Logger.java index b315574..9ea620e 100755 --- a/src/com/audacious_software/passive_data_kit/Logger.java +++ b/src/com/audacious_software/passive_data_kit/Logger.java @@ -2,7 +2,10 @@ import android.content.Context; +import com.audacious_software.passive_data_kit.generators.diagnostics.AppEvent; + import java.util.HashMap; +import java.util.Map; /** * Created by cjkarr on 4/3/2016. @@ -10,8 +13,12 @@ public class Logger { private Context mContext = null; - public void log(String event, HashMap details) { - // TODO + public void log(String event, Map details) { + if (details == null) { + details = new HashMap<>(); + } + + AppEvent.getInstance(this.mContext).logEvent(event, details); } private static class LoggerHolder { diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java index d33c9f4..14a4d26 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generators.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java @@ -11,6 +11,7 @@ import com.audacious_software.passive_data_kit.Logger; import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; +import com.audacious_software.passive_data_kit.generators.diagnostics.AppEvent; import com.audacious_software.pdk.passivedatakit.R; import java.lang.reflect.InvocationTargetException; @@ -37,6 +38,8 @@ public void start() { { this.mGenerators.clear(); + this.mGenerators.add(AppEvent.class.getCanonicalName()); + for (String className : this.mContext.getResources().getStringArray(R.array.pdk_available_generators)) { this.mGenerators.add(className); diff --git a/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java b/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java new file mode 100755 index 0000000..c8d6001 --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java @@ -0,0 +1,436 @@ +package com.audacious_software.passive_data_kit.generators.diagnostics; + +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.audacious_software.passive_data_kit.PassiveDataKit; +import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; +import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; +import com.audacious_software.passive_data_kit.generators.Generator; +import com.audacious_software.passive_data_kit.generators.Generators; +import com.audacious_software.pdk.passivedatakit.R; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AppEvent extends Generator{ + private static final String GENERATOR_IDENTIFIER = "pdk-app-event"; + + private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.diagnostics.AppEvent.ENABLED"; + private static final boolean ENABLED_DEFAULT = true; + + private static final String DATABASE_PATH = "pdk-app-event.sqlite"; + private static final int DATABASE_VERSION = 1; + + public static final String HISTORY_OBSERVED = "observed"; + public static final String HISTORY_EVENT_NAME = "event_name"; + public static final String HISTORY_EVENT_DETAILS = "event_details"; + public static final String TABLE_HISTORY = "history"; + + private static AppEvent sInstance = null; + + private SQLiteDatabase mDatabase = null; + + public static AppEvent getInstance(Context context) { + if (AppEvent.sInstance == null) { + AppEvent.sInstance = new AppEvent(context.getApplicationContext()); + } + + return AppEvent.sInstance; + } + + public AppEvent(Context context) { + super(context); + } + + public static void start(final Context context) { + AppEvent.getInstance(context).startGenerator(); + } + + private void startGenerator() { + final AppEvent me = this; + + Generators.getInstance(this.mContext).registerCustomViewClass(AppEvent.GENERATOR_IDENTIFIER, AppEvent.class); + + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, AppEvent.DATABASE_PATH); + + this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); + + int version = this.getDatabaseVersion(this.mDatabase); + + switch (version) { + case 0: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_app_events_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, AppEvent.DATABASE_VERSION); + } + + public static boolean isEnabled(Context context) { + SharedPreferences prefs = Generators.getInstance(context).getSharedPreferences(context); + + return prefs.getBoolean(AppEvent.ENABLED, AppEvent.ENABLED_DEFAULT); + } + + public static boolean isRunning(Context context) { + return (AppEvent.sInstance != null); + } + + public static ArrayList diagnostics(Context context) { + return new ArrayList<>(); + } + + public static void bindViewHolder(DataPointViewHolder holder) { +/* final Context context = holder.itemView.getContext(); + + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + + long zeroStart = cal.getTimeInMillis(); + cal.add(Calendar.DATE, -1); + + LinearLayout zeroTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_zero_value); + + AppEvents.populateTimeline(context, zeroTimeline, zeroStart); + + long oneStart = cal.getTimeInMillis(); + cal.add(Calendar.DATE, -1); + + LinearLayout oneTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_one_value); + + AppEvents.populateTimeline(context, oneTimeline, oneStart); + + long twoStart = cal.getTimeInMillis(); + + LinearLayout twoTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_two_value); + + AppEvents.populateTimeline(context, twoTimeline, twoStart); + + AppEvents generator = AppEvents.getInstance(context); + + Cursor c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, null, null, null, null, AppEvents.HISTORY_OBSERVED + " DESC"); + + View cardContent = holder.itemView.findViewById(R.id.card_content); + View cardEmpty = holder.itemView.findViewById(R.id.card_empty); + TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date); + + if (c.moveToNext()) { + cardContent.setVisibility(View.VISIBLE); + cardEmpty.setVisibility(View.GONE); + + long timestamp = c.getLong(c.getColumnIndex(AppEvents.HISTORY_OBSERVED)) / 1000; + + dateLabel.setText(Generator.formatTimestamp(context, timestamp)); + + cal = Calendar.getInstance(); + DateFormat format = android.text.format.DateFormat.getDateFormat(context); + + TextView zeroDayLabel = (TextView) holder.itemView.findViewById(R.id.day_zero_label); + zeroDayLabel.setText(format.format(cal.getTime())); + + cal.add(Calendar.DATE, -1); + + TextView oneDayLabel = (TextView) holder.itemView.findViewById(R.id.day_one_label); + oneDayLabel.setText(format.format(cal.getTime())); + + cal.add(Calendar.DATE, -1); + + TextView twoDayLabel = (TextView) holder.itemView.findViewById(R.id.day_two_label); + twoDayLabel.setText(format.format(cal.getTime())); + } else { + cardContent.setVisibility(View.GONE); + cardEmpty.setVisibility(View.VISIBLE); + + dateLabel.setText(R.string.label_never_pdk); + } + + c.close(); + */ + } + + /* + private static void populateTimeline(Context context, LinearLayout timeline, long start) { + timeline.removeAllViews(); + + AppEvents generator = AppEvents.getInstance(context); + + long end = start + (24 * 60 * 60 * 1000); + + String where = AppEvents.HISTORY_OBSERVED + " >= ? AND " + AppEvents.HISTORY_OBSERVED + " < ?"; + String[] args = { "" + start, "" + end }; + + Cursor c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, where, args, null, null, AppEvents.HISTORY_OBSERVED); + + ArrayList activeStates = new ArrayList<>(); + ArrayList activeTimestamps = new ArrayList<>(); + + while (c.moveToNext()) { + long timestamp = c.getLong(c.getColumnIndex(AppEvents.HISTORY_OBSERVED)); + + activeTimestamps.add(timestamp); + + String state = c.getString(c.getColumnIndex(AppEvents.HISTORY_STATE)); + activeStates.add(state); + } + + c.close(); + + String lastState = AppEvents.STATE_UNKNOWN; + + String lastWhere = AppEvents.HISTORY_OBSERVED + " < ?"; + String[] lastArgs = { "" + start }; + + c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, lastWhere, lastArgs, null, null, AppEvents.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + lastState = c.getString(c.getColumnIndex(AppEvents.HISTORY_STATE)); + } + + if (activeStates.size() > 0) { + long firstTimestamp = activeTimestamps.get(0); + long firstState = activeTimestamps.get(0); + + View startView = new View(context); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (AppEvents.STATE_UNKNOWN.equals(lastState)) { + + } else if (AppEvents.STATE_ON.equals(lastState)) { + startView.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(lastState)) { + startView.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(lastState)) { + startView.setBackgroundColor(0xff1b5e20); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(lastState)) { + startView.setBackgroundColor(0xff1b5e20); + } + } else { + if (AppEvents.STATE_UNKNOWN.equals(lastState)) { + + } else if (AppEvents.STATE_ON.equals(lastState)) { + startView.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(lastState)) { + startView.setBackgroundColor(0xff263238); + } + } + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, firstTimestamp - start); + startView.setLayoutParams(params); + + timeline.addView(startView); + + long now = System.currentTimeMillis(); + + if (activeStates.size() == 1) { + View v = new View(context); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (AppEvents.STATE_ON.equals(firstState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(firstState)) { + v.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(firstState)) { + v.setBackgroundColor(0xff3f51b5); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(firstState)) { + v.setBackgroundColor(0xff3f51b5); + } + } else { + if (AppEvents.STATE_ON.equals(firstState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(firstState)) { + v.setBackgroundColor(0xff263238); + } + } + + if (end > System.currentTimeMillis()) { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, now - firstTimestamp); + v.setLayoutParams(params); + } else { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - firstTimestamp); + v.setLayoutParams(params); + } + + timeline.addView(v); + } else { + for (int i = 1; i < activeStates.size(); i++) { + long currentTimestamp = activeTimestamps.get(i); + + long priorTimestamp = activeTimestamps.get(i - 1); + String priorState = activeStates.get(i - 1); + + View v = new View(context); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + if (AppEvents.STATE_ON.equals(priorState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(priorState)) { + v.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(priorState)) { + v.setBackgroundColor(0xff3f51b5); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(priorState)) { + v.setBackgroundColor(0xff3f51b5); + } + } else { + if (AppEvents.STATE_ON.equals(priorState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(priorState)) { + v.setBackgroundColor(0xff263238); + } + } + + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, currentTimestamp - priorTimestamp); + v.setLayoutParams(params); + + timeline.addView(v); + } + + long finalTimestamp = activeTimestamps.get(activeTimestamps.size() - 1); + String finalState = activeStates.get(activeStates.size() - 1); + + View v = new View(context); + + if (AppEvents.STATE_ON.equals(finalState)) { + v.setBackgroundColor(0xff4CAF50); + } else if (AppEvents.STATE_OFF.equals(finalState)) { + v.setBackgroundColor(0xff263238); + } else if (AppEvents.STATE_DOZE.equals(finalState)) { + v.setBackgroundColor(0xff3f51b5); + } else if (AppEvents.STATE_DOZE_SUSPEND.equals(finalState)) { + v.setBackgroundColor(0xff3f51b5); + } + + if (end > now) { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, now - finalTimestamp); + v.setLayoutParams(params); + } else { + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - finalTimestamp); + v.setLayoutParams(params); + } + + timeline.addView(v); + } + + if (end > now) { + View v = new View(context); + + params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - now); + v.setLayoutParams(params); + + timeline.addView(v); + } + } else { + + } + } + +*/ + public static View fetchView(ViewGroup parent) + { + return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_generic, parent, false); + } + + @Override + public List fetchPayloads() { + return new ArrayList<>(); + } + + public static long latestPointGenerated(Context context) { + long timestamp = 0; + + AppEvent me = AppEvent.getInstance(context); + + Cursor c = me.mDatabase.query(AppEvent.TABLE_HISTORY, null, null, null, null, null, AppEvent.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + timestamp = c.getLong(c.getColumnIndex(AppEvent.HISTORY_OBSERVED)); + } + + c.close(); + + return timestamp; + } + + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + return this.mDatabase.query(AppEvent.TABLE_HISTORY, cols, where, args, null, null, orderBy); + } + + public boolean logEvent(String eventName, Map eventDetails) { + try { + long now = System.currentTimeMillis(); + + ContentValues values = new ContentValues(); + values.put(AppEvent.HISTORY_OBSERVED, now); + values.put(AppEvent.HISTORY_EVENT_NAME, eventName); + + Bundle detailsBundle = new Bundle(); + JSONObject detailsJson = new JSONObject(); + + for (String key : eventDetails.keySet()) { + Object value = eventDetails.get(key); + + if (value instanceof Double) { + Double doubleValue = ((Double) value); + + detailsBundle.putDouble(key, doubleValue); + detailsJson.put(key, doubleValue.doubleValue()); + } else if (value instanceof Float) { + Float floatValue = ((Float) value); + + detailsBundle.putDouble(key, floatValue.doubleValue()); + detailsJson.put(key, floatValue.doubleValue()); + } else if (value instanceof Long) { + Long longValue = ((Long) value); + + detailsBundle.putLong(key, longValue.longValue()); + detailsJson.put(key, longValue.longValue()); + } else if (value instanceof Integer) { + Integer intValue = ((Integer) value); + + detailsBundle.putLong(key, intValue.longValue()); + detailsJson.put(key, intValue.longValue()); + } else if (value instanceof String) { + detailsBundle.putString(key, value.toString()); + detailsJson.put(key, value.toString()); + } else { + detailsBundle.putString(key, "Unknown Class: " + value.getClass().getCanonicalName()); + detailsJson.put(key, "Unknown Class: " + value.getClass().getCanonicalName()); + } + } + + values.put(AppEvent.HISTORY_EVENT_DETAILS, detailsJson.toString(2)); + + this.mDatabase.insert(AppEvent.TABLE_HISTORY, null, values); + + Bundle update = new Bundle(); + update.putLong(AppEvent.HISTORY_OBSERVED, now); + update.putString(AppEvent.HISTORY_EVENT_NAME, values.getAsString(AppEvent.HISTORY_EVENT_NAME)); + update.putBundle(AppEvent.HISTORY_EVENT_DETAILS, detailsBundle); + + Generators.getInstance(this.mContext).notifyGeneratorUpdated(AppEvent.GENERATOR_IDENTIFIER, update); + + return true; + } catch (JSONException e) { + e.printStackTrace(); + } + + return false; + } +} From 834411e0de9c9dc865248a44ab03533ae43c39a2 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Sat, 11 Mar 2017 16:15:55 -0600 Subject: [PATCH 14/18] Updates the MPAndroidPlot depsndency to a newer version. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e0deb1f..461a9f9 100755 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,7 @@ android { compile 'commons-codec:commons-codec:1.10' compile 'org.apache.commons:commons-lang3:3.4' compile 'com.fasterxml.jackson.core:jackson-core:2.7.3' - compile 'com.github.philjay:mpandroidchart:v3.0.0' + compile 'com.github.philjay:mpandroidchart:v3.0.1' } buildTypes { From 35aead21ea58f92be5db64515d818b53ed914909 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Thu, 30 Mar 2017 12:48:53 -0500 Subject: [PATCH 15/18] Added synchronized blocks to prevent thread collisions. * Updated build dependencies. --- build.gradle | 16 ++++++++-------- .../passive_data_kit/generators/Generators.java | 14 ++++++++++---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 461a9f9..09a0adb 100755 --- a/build.gradle +++ b/build.gradle @@ -45,14 +45,14 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) // testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:25.2.0' - compile 'com.android.support:recyclerview-v7:25.2.0' - compile 'com.android.support:cardview-v7:25.2.0' - compile 'com.google.android.gms:play-services-location:10.2.0' - compile 'com.google.android.gms:play-services-maps:10.2.0' - compile 'com.google.android.gms:play-services-nearby:10.2.0' - compile 'com.google.android.gms:play-services-places:10.2.0' - compile 'com.google.android.gms:play-services-awareness:10.2.0' + compile 'com.android.support:appcompat-v7:25.3.1' + compile 'com.android.support:recyclerview-v7:25.3.1' + compile 'com.android.support:cardview-v7:25.3.1' + compile 'com.google.android.gms:play-services-location:10.2.1' + compile 'com.google.android.gms:play-services-maps:10.2.1' + compile 'com.google.android.gms:play-services-nearby:10.2.1' + compile 'com.google.android.gms:play-services-places:10.2.1' + compile 'com.google.android.gms:play-services-awareness:10.2.1' compile 'com.google.maps.android:android-maps-utils:0.4' compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'commons-io:commons-io:2.4' diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java index 14a4d26..384e0f1 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generators.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java @@ -250,8 +250,10 @@ public void notifyGeneratorUpdated(String identifier, long timestamp, Bundle bun public void notifyGeneratorUpdated(String identifier, Bundle bundle) { long timestamp = System.currentTimeMillis(); - for (GeneratorUpdatedListener listener : this.mGeneratorUpdatedListeners) { - listener.onGeneratorUpdated(identifier, timestamp, bundle); + synchronized(this.mGeneratorUpdatedListeners) { + for (GeneratorUpdatedListener listener : this.mGeneratorUpdatedListeners) { + listener.onGeneratorUpdated(identifier, timestamp, bundle); + } } } @@ -273,11 +275,15 @@ private void setContext(Context context) { } public void addNewGeneratorUpdatedListener(Generators.GeneratorUpdatedListener listener) { - this.mGeneratorUpdatedListeners.add(listener); + synchronized(this.mGeneratorUpdatedListeners) { + this.mGeneratorUpdatedListeners.add(listener); + } } public void removeGeneratorUpdatedListener(Generators.GeneratorUpdatedListener listener) { - this.mGeneratorUpdatedListeners.remove(listener); + synchronized(this.mGeneratorUpdatedListeners) { + this.mGeneratorUpdatedListeners.remove(listener); + } } public interface GeneratorUpdatedListener { From faab47514604e3ea39fb468b3892abf22a3e4d97 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Sun, 2 Apr 2017 23:56:12 -0500 Subject: [PATCH 16/18] Adding boolean support for app event reporting --- .../passive_data_kit/generators/diagnostics/AppEvent.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java b/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java index c8d6001..e92d291 100755 --- a/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java +++ b/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java @@ -409,6 +409,9 @@ public boolean logEvent(String eventName, Map eventDet } else if (value instanceof String) { detailsBundle.putString(key, value.toString()); detailsJson.put(key, value.toString()); + } else if (value instanceof Boolean) { + detailsBundle.putBoolean(key, ((Boolean) value).booleanValue()); + detailsJson.put(key, ((Boolean) value).booleanValue()); } else { detailsBundle.putString(key, "Unknown Class: " + value.getClass().getCanonicalName()); detailsJson.put(key, "Unknown Class: " + value.getClass().getCanonicalName()); From 4879ee62bcbb920e618a579b938b87d760fb87e6 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Wed, 5 Apr 2017 09:16:29 -0500 Subject: [PATCH 17/18] Updating build dependencies --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 09a0adb..169f463 100755 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' + classpath 'com.android.tools.build:gradle:2.3.1' } } From 24898e246a5b4861d1c51d86ebd4d4676a5666f0 Mon Sep 17 00:00:00 2001 From: "Chris J. Karr" Date: Mon, 17 Apr 2017 18:11:21 -0500 Subject: [PATCH 18/18] Generator work. * Implemented most of the device battery generator. * Created placeholder files for Accelerometer and Ambient Light. --- res/layout/card_generator_app_event.xml | 63 ++++ res/layout/card_generator_device_battery.xml | 63 ++++ res/values/databases.xml | 3 + res/values/generators.xml | 8 + .../generators/DataPointsAdapter.java | 5 + .../generators/Generators.java | 3 + .../generators/device/Battery.java | 311 ++++++++++++++++++ .../generators/diagnostics/AppEvent.java | 230 +------------ .../generators/sensors/Accelerometer.java | 8 + .../generators/sensors/AmbientLight.java | 8 + 10 files changed, 480 insertions(+), 222 deletions(-) create mode 100755 res/layout/card_generator_app_event.xml create mode 100755 res/layout/card_generator_device_battery.xml create mode 100755 src/com/audacious_software/passive_data_kit/generators/device/Battery.java create mode 100755 src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java create mode 100755 src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java diff --git a/res/layout/card_generator_app_event.xml b/res/layout/card_generator_app_event.xml new file mode 100755 index 0000000..02f65b2 --- /dev/null +++ b/res/layout/card_generator_app_event.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/card_generator_device_battery.xml b/res/layout/card_generator_device_battery.xml new file mode 100755 index 0000000..3244646 --- /dev/null +++ b/res/layout/card_generator_device_battery.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/values/databases.xml b/res/values/databases.xml index 672f1cd..57f13d5 100755 --- a/res/values/databases.xml +++ b/res/values/databases.xml @@ -16,4 +16,7 @@ CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, event_name TEXT, event_details TEXT); + + CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, health TEXT, level INTERGER, plugged TEXT, present INTEGER, scale INTEGER, temperature INTEGER, voltage INTEGER, technology TEXT, status TEXT); + \ No newline at end of file diff --git a/res/values/generators.xml b/res/values/generators.xml index a6e8f0e..03c8ad8 100755 --- a/res/values/generators.xml +++ b/res/values/generators.xml @@ -116,4 +116,12 @@ No phone calls have been made or received on this device. + + Device Battery + No battery levels have been reported yet. + + + App Event History + No app events have been logged yet. + \ No newline at end of file diff --git a/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java b/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java index fd1679d..b60d419 100755 --- a/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java +++ b/src/com/audacious_software/passive_data_kit/activities/generators/DataPointsAdapter.java @@ -2,6 +2,7 @@ import android.content.Context; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -36,6 +37,8 @@ public DataPointViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { Method fetchView = generatorClass.getDeclaredMethod("fetchView", ViewGroup.class); View view = (View) fetchView.invoke(null, parent); + Log.e("PDK", "GENERATOR CLASS: " + generatorClass); + return new DataPointViewHolder(view); } catch (NoSuchMethodException e1) { Logger.getInstance(parent.getContext()).logThrowable(e1); @@ -67,6 +70,8 @@ public void onBindViewHolder(final DataPointViewHolder holder, int position) { Method bindViewHolder = generatorClass.getDeclaredMethod("bindViewHolder", DataPointViewHolder.class); + Log.e("PDK", "GENERATOR CLASS: " + generatorClass); + bindViewHolder.invoke(null, holder); } catch (NoSuchMethodException e1) { Logger.getInstance(holder.itemView.getContext()).logThrowable(e1); diff --git a/src/com/audacious_software/passive_data_kit/generators/Generators.java b/src/com/audacious_software/passive_data_kit/generators/Generators.java index 384e0f1..8924585 100755 --- a/src/com/audacious_software/passive_data_kit/generators/Generators.java +++ b/src/com/audacious_software/passive_data_kit/generators/Generators.java @@ -233,6 +233,9 @@ public List> activeGenerators() { for (String className : this.mActiveGenerators) { try { active.add((Class) Class.forName(className)); + + Log.e("PDK", "ACTIVE GENERATOR CLASS: " + className); + } catch (ClassNotFoundException e) { e.printStackTrace(); } diff --git a/src/com/audacious_software/passive_data_kit/generators/device/Battery.java b/src/com/audacious_software/passive_data_kit/generators/device/Battery.java new file mode 100755 index 0000000..c99b1c1 --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/generators/device/Battery.java @@ -0,0 +1,311 @@ +package com.audacious_software.passive_data_kit.generators.device; + +import android.content.BroadcastReceiver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.os.BatteryManager; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.audacious_software.passive_data_kit.PassiveDataKit; +import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; +import com.audacious_software.passive_data_kit.diagnostics.DiagnosticAction; +import com.audacious_software.passive_data_kit.generators.Generator; +import com.audacious_software.passive_data_kit.generators.Generators; +import com.audacious_software.pdk.passivedatakit.R; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by cjkarr on 4/17/2017. + */ + +public class Battery extends Generator { + + private static final String GENERATOR_IDENTIFIER = "pdk-device-battery"; + + private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.device.Battery.ENABLED"; + private static final boolean ENABLED_DEFAULT = true; + + + private static final String DATABASE_PATH = "pdk-device-battery.sqlite"; + private static final int DATABASE_VERSION = 1; + + public static final String TABLE_HISTORY = "history"; + + public static final String HISTORY_OBSERVED = "observed"; + public static final String HISTORY_HEALTH = "health"; + public static final String HISTORY_LEVEL = "level"; + public static final String HISTORY_PLUGGED = "plugged"; + public static final String HISTORY_PRESENT = "present"; + public static final String HISTORY_SCALE = "scale"; + public static final String HISTORY_TEMPERATURE = "temperature"; + public static final String HISTORY_VOLTAGE = "voltage"; + public static final String HISTORY_TECHNOLOGY = "technology"; + public static final String HISTORY_STATUS = "status"; + + private static final String HEALTH_COLD = "cold"; + private static final String HEALTH_DEAD = "dead"; + private static final String HEALTH_GOOD = "good"; + private static final String HEALTH_OVERHEAT = "overheat"; + private static final String HEALTH_OVER_VOLTAGE = "over-voltage"; + private static final String HEALTH_UNSPECIFIED_FAILURE = "unspecified-failure"; + private static final String HEALTH_UNKNOWN = "unknown"; + + private static final String PLUGGED_AC = "ac"; + private static final String PLUGGED_USB = "usb"; + private static final String PLUGGED_WIRELESS = "wireless"; + private static final String PLUGGED_UNKNOWN = "unknown"; + + private static final String TECHNOLOGY_UNKNOWN = "unknown"; + + private static final String STATUS_CHARGING = "charging"; + private static final String STATUS_DISCHARGING = "discharging"; + private static final String STATUS_FULL = "full"; + private static final String STATUS_NOT_CHARGING = "not-charging"; + private static final String STATUS_UNKNOWN = "unknown"; + + private static Battery sInstance = null; + + private BroadcastReceiver mReceiver = null; + + private SQLiteDatabase mDatabase = null; + + public static Battery getInstance(Context context) { + if (Battery.sInstance == null) { + Battery.sInstance = new Battery(context.getApplicationContext()); + } + + return Battery.sInstance; + } + + public Battery(Context context) { + super(context); + } + + public static void start(final Context context) { + Battery.getInstance(context).startGenerator(); + } + + private void startGenerator() { + final Battery me = this; + + this.mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, Intent intent) { + long now = System.currentTimeMillis(); + + ContentValues values = new ContentValues(); + values.put(Battery.HISTORY_OBSERVED, now); + + Bundle update = new Bundle(); + update.putLong(Battery.HISTORY_OBSERVED, now); + + switch (intent.getIntExtra(BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN)) { + case BatteryManager.BATTERY_HEALTH_COLD: + values.put(Battery.HISTORY_HEALTH, Battery.HEALTH_COLD); + update.putString(Battery.HISTORY_HEALTH, Battery.HEALTH_COLD); + break; + case BatteryManager.BATTERY_HEALTH_DEAD: + values.put(Battery.HISTORY_HEALTH, Battery.HEALTH_DEAD); + update.putString(Battery.HISTORY_HEALTH, Battery.HEALTH_DEAD); + break; + case BatteryManager.BATTERY_HEALTH_GOOD: + values.put(Battery.HISTORY_HEALTH, Battery.HEALTH_GOOD); + update.putString(Battery.HISTORY_HEALTH, Battery.HEALTH_GOOD); + break; + case BatteryManager.BATTERY_HEALTH_OVERHEAT: + values.put(Battery.HISTORY_HEALTH, Battery.HEALTH_OVERHEAT); + update.putString(Battery.HISTORY_HEALTH, Battery.HEALTH_OVERHEAT); + break; + case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE: + values.put(Battery.HISTORY_HEALTH, Battery.HEALTH_OVER_VOLTAGE); + update.putString(Battery.HISTORY_HEALTH, Battery.HEALTH_OVER_VOLTAGE); + break; + case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE: + values.put(Battery.HISTORY_HEALTH, Battery.HEALTH_UNSPECIFIED_FAILURE); + update.putString(Battery.HISTORY_HEALTH, Battery.HEALTH_UNSPECIFIED_FAILURE); + break; + default: + values.put(Battery.HISTORY_HEALTH, Battery.HEALTH_UNKNOWN); + update.putString(Battery.HISTORY_HEALTH, Battery.HEALTH_UNKNOWN); + break; + } + + switch (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)) { + case BatteryManager.BATTERY_PLUGGED_AC: + values.put(Battery.HISTORY_PLUGGED, Battery.PLUGGED_AC); + update.putString(Battery.HISTORY_PLUGGED, Battery.PLUGGED_AC); + break; + case BatteryManager.BATTERY_PLUGGED_USB: + values.put(Battery.HISTORY_PLUGGED, Battery.PLUGGED_USB); + update.putString(Battery.HISTORY_PLUGGED, Battery.PLUGGED_USB); + break; + case BatteryManager.BATTERY_PLUGGED_WIRELESS: + values.put(Battery.HISTORY_PLUGGED, Battery.PLUGGED_WIRELESS); + update.putString(Battery.HISTORY_PLUGGED, Battery.PLUGGED_WIRELESS); + break; + default: + values.put(Battery.HISTORY_PLUGGED, Battery.PLUGGED_UNKNOWN); + update.putString(Battery.HISTORY_PLUGGED, Battery.PLUGGED_UNKNOWN); + break; + } + + switch (intent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN)) { + case BatteryManager.BATTERY_STATUS_CHARGING: + values.put(Battery.HISTORY_STATUS, Battery.STATUS_CHARGING); + update.putString(Battery.HISTORY_STATUS, Battery.STATUS_CHARGING); + break; + case BatteryManager.BATTERY_STATUS_DISCHARGING: + values.put(Battery.HISTORY_STATUS, Battery.STATUS_DISCHARGING); + update.putString(Battery.HISTORY_STATUS, Battery.STATUS_DISCHARGING); + break; + case BatteryManager.BATTERY_STATUS_FULL: + values.put(Battery.HISTORY_STATUS, Battery.STATUS_FULL); + update.putString(Battery.HISTORY_STATUS, Battery.STATUS_FULL); + break; + case BatteryManager.BATTERY_STATUS_NOT_CHARGING: + values.put(Battery.HISTORY_STATUS, Battery.STATUS_NOT_CHARGING); + update.putString(Battery.HISTORY_STATUS, Battery.STATUS_NOT_CHARGING); + break; + default: + values.put(Battery.HISTORY_STATUS, Battery.STATUS_UNKNOWN); + update.putString(Battery.HISTORY_STATUS, Battery.STATUS_UNKNOWN); + break; + } + + values.put(Battery.HISTORY_PRESENT, intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false)); + update.putBoolean(Battery.HISTORY_PRESENT, intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false)); + + values.put(Battery.HISTORY_LEVEL, intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)); + update.putInt(Battery.HISTORY_LEVEL, intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)); + + values.put(Battery.HISTORY_SCALE, intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)); + update.putInt(Battery.HISTORY_SCALE, intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)); + + values.put(Battery.HISTORY_TEMPERATURE, intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1)); + update.putInt(Battery.HISTORY_TEMPERATURE, intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1)); + + values.put(Battery.HISTORY_VOLTAGE, intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1)); + update.putInt(Battery.HISTORY_VOLTAGE, intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1)); + + values.put(Battery.HISTORY_TECHNOLOGY, Battery.TECHNOLOGY_UNKNOWN); + update.putString(Battery.HISTORY_TECHNOLOGY, Battery.TECHNOLOGY_UNKNOWN); + + me.mDatabase.insert(Battery.TABLE_HISTORY, null, values); + + Generators.getInstance(context).notifyGeneratorUpdated(Battery.GENERATOR_IDENTIFIER, update); + } + }; + + IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + + this.mContext.registerReceiver(this.mReceiver, filter); + + Generators.getInstance(this.mContext).registerCustomViewClass(Battery.GENERATOR_IDENTIFIER, Battery.class); + + File path = PassiveDataKit.getGeneratorsStorage(this.mContext); + + path = new File(path, Battery.DATABASE_PATH); + + this.mDatabase = SQLiteDatabase.openOrCreateDatabase(path, null); + + int version = this.getDatabaseVersion(this.mDatabase); + + switch (version) { + case 0: + this.mDatabase.execSQL(this.mContext.getString(R.string.pdk_generator_device_battery_create_history_table)); + } + + this.setDatabaseVersion(this.mDatabase, Battery.DATABASE_VERSION); + } + + public static boolean isEnabled(Context context) { + SharedPreferences prefs = Generators.getInstance(context).getSharedPreferences(context); + + return prefs.getBoolean(Battery.ENABLED, Battery.ENABLED_DEFAULT); + } + + public static boolean isRunning(Context context) { + if (Battery.sInstance == null) { + return false; + } + + return Battery.sInstance.mReceiver != null; + } + + public static ArrayList diagnostics(Context context) { + return new ArrayList<>(); + } + + public static void bindViewHolder(DataPointViewHolder holder) { + final Context context = holder.itemView.getContext(); + + Battery generator = Battery.getInstance(context); + + Cursor c = generator.mDatabase.query(Battery.TABLE_HISTORY, null, null, null, null, null, Battery.HISTORY_OBSERVED + " DESC"); + + View cardContent = holder.itemView.findViewById(R.id.card_content); + View cardEmpty = holder.itemView.findViewById(R.id.card_empty); + TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date); + + if (c.moveToNext()) { + cardContent.setVisibility(View.VISIBLE); + cardEmpty.setVisibility(View.GONE); + + long timestamp = c.getLong(c.getColumnIndex(Battery.HISTORY_OBSERVED)) / 1000; + + dateLabel.setText(Generator.formatTimestamp(context, timestamp)); + + TextView lastLevel = (TextView) holder.itemView.findViewById(R.id.card_last_battery_level); + lastLevel.setText("TODO: LEVEL " + c.getInt(c.getColumnIndex(Battery.HISTORY_LEVEL))); + } else { + cardContent.setVisibility(View.GONE); + cardEmpty.setVisibility(View.VISIBLE); + + dateLabel.setText(R.string.label_never_pdk); + } + + c.close(); + } + + public static View fetchView(ViewGroup parent) + { + return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_device_battery, parent, false); + } + + @Override + public List fetchPayloads() { + return new ArrayList<>(); + } + + public static long latestPointGenerated(Context context) { + long timestamp = 0; + + Battery me = Battery.getInstance(context); + + Cursor c = me.mDatabase.query(Battery.TABLE_HISTORY, null, null, null, null, null, Battery.HISTORY_OBSERVED + " DESC"); + + if (c.moveToNext()) { + timestamp = c.getLong(c.getColumnIndex(Battery.HISTORY_OBSERVED)); + } + + c.close(); + + return timestamp; + } + + public Cursor queryHistory(String[] cols, String where, String[] args, String orderBy) { + return this.mDatabase.query(Battery.TABLE_HISTORY, cols, where, args, null, null, orderBy); + } +} diff --git a/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java b/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java index e92d291..75c420f 100755 --- a/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java +++ b/src/com/audacious_software/passive_data_kit/generators/diagnostics/AppEvent.java @@ -9,6 +9,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import com.audacious_software.passive_data_kit.PassiveDataKit; import com.audacious_software.passive_data_kit.activities.generators.DataPointViewHolder; @@ -96,37 +97,11 @@ public static ArrayList diagnostics(Context context) { } public static void bindViewHolder(DataPointViewHolder holder) { -/* final Context context = holder.itemView.getContext(); + final Context context = holder.itemView.getContext(); - Calendar cal = Calendar.getInstance(); - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); + AppEvent generator = AppEvent.getInstance(context); - long zeroStart = cal.getTimeInMillis(); - cal.add(Calendar.DATE, -1); - - LinearLayout zeroTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_zero_value); - - AppEvents.populateTimeline(context, zeroTimeline, zeroStart); - - long oneStart = cal.getTimeInMillis(); - cal.add(Calendar.DATE, -1); - - LinearLayout oneTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_one_value); - - AppEvents.populateTimeline(context, oneTimeline, oneStart); - - long twoStart = cal.getTimeInMillis(); - - LinearLayout twoTimeline = (LinearLayout) holder.itemView.findViewById(R.id.day_two_value); - - AppEvents.populateTimeline(context, twoTimeline, twoStart); - - AppEvents generator = AppEvents.getInstance(context); - - Cursor c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, null, null, null, null, AppEvents.HISTORY_OBSERVED + " DESC"); + Cursor c = generator.mDatabase.query(AppEvent.TABLE_HISTORY, null, null, null, null, null, AppEvent.HISTORY_OBSERVED + " DESC"); View cardContent = holder.itemView.findViewById(R.id.card_content); View cardEmpty = holder.itemView.findViewById(R.id.card_empty); @@ -136,25 +111,13 @@ public static void bindViewHolder(DataPointViewHolder holder) { cardContent.setVisibility(View.VISIBLE); cardEmpty.setVisibility(View.GONE); - long timestamp = c.getLong(c.getColumnIndex(AppEvents.HISTORY_OBSERVED)) / 1000; + long timestamp = c.getLong(c.getColumnIndex(AppEvent.HISTORY_OBSERVED)) / 1000; dateLabel.setText(Generator.formatTimestamp(context, timestamp)); - cal = Calendar.getInstance(); - DateFormat format = android.text.format.DateFormat.getDateFormat(context); - - TextView zeroDayLabel = (TextView) holder.itemView.findViewById(R.id.day_zero_label); - zeroDayLabel.setText(format.format(cal.getTime())); - - cal.add(Calendar.DATE, -1); - - TextView oneDayLabel = (TextView) holder.itemView.findViewById(R.id.day_one_label); - oneDayLabel.setText(format.format(cal.getTime())); - - cal.add(Calendar.DATE, -1); + TextView eventCount = (TextView) holder.itemView.findViewById(R.id.card_app_event_count); - TextView twoDayLabel = (TextView) holder.itemView.findViewById(R.id.day_two_label); - twoDayLabel.setText(format.format(cal.getTime())); + eventCount.setText("TODO: EVENT VIEW - " + c.getCount()); } else { cardContent.setVisibility(View.GONE); cardEmpty.setVisibility(View.VISIBLE); @@ -163,188 +126,11 @@ public static void bindViewHolder(DataPointViewHolder holder) { } c.close(); - */ - } - - /* - private static void populateTimeline(Context context, LinearLayout timeline, long start) { - timeline.removeAllViews(); - - AppEvents generator = AppEvents.getInstance(context); - - long end = start + (24 * 60 * 60 * 1000); - - String where = AppEvents.HISTORY_OBSERVED + " >= ? AND " + AppEvents.HISTORY_OBSERVED + " < ?"; - String[] args = { "" + start, "" + end }; - - Cursor c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, where, args, null, null, AppEvents.HISTORY_OBSERVED); - - ArrayList activeStates = new ArrayList<>(); - ArrayList activeTimestamps = new ArrayList<>(); - - while (c.moveToNext()) { - long timestamp = c.getLong(c.getColumnIndex(AppEvents.HISTORY_OBSERVED)); - - activeTimestamps.add(timestamp); - - String state = c.getString(c.getColumnIndex(AppEvents.HISTORY_STATE)); - activeStates.add(state); - } - - c.close(); - - String lastState = AppEvents.STATE_UNKNOWN; - - String lastWhere = AppEvents.HISTORY_OBSERVED + " < ?"; - String[] lastArgs = { "" + start }; - - c = generator.mDatabase.query(AppEvents.TABLE_HISTORY, null, lastWhere, lastArgs, null, null, AppEvents.HISTORY_OBSERVED + " DESC"); - - if (c.moveToNext()) { - lastState = c.getString(c.getColumnIndex(AppEvents.HISTORY_STATE)); - } - - if (activeStates.size() > 0) { - long firstTimestamp = activeTimestamps.get(0); - long firstState = activeTimestamps.get(0); - - View startView = new View(context); - - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { - if (AppEvents.STATE_UNKNOWN.equals(lastState)) { - - } else if (AppEvents.STATE_ON.equals(lastState)) { - startView.setBackgroundColor(0xff4CAF50); - } else if (AppEvents.STATE_OFF.equals(lastState)) { - startView.setBackgroundColor(0xff263238); - } else if (AppEvents.STATE_DOZE.equals(lastState)) { - startView.setBackgroundColor(0xff1b5e20); - } else if (AppEvents.STATE_DOZE_SUSPEND.equals(lastState)) { - startView.setBackgroundColor(0xff1b5e20); - } - } else { - if (AppEvents.STATE_UNKNOWN.equals(lastState)) { - - } else if (AppEvents.STATE_ON.equals(lastState)) { - startView.setBackgroundColor(0xff4CAF50); - } else if (AppEvents.STATE_OFF.equals(lastState)) { - startView.setBackgroundColor(0xff263238); - } - } - - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, firstTimestamp - start); - startView.setLayoutParams(params); - - timeline.addView(startView); - - long now = System.currentTimeMillis(); - - if (activeStates.size() == 1) { - View v = new View(context); - - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { - if (AppEvents.STATE_ON.equals(firstState)) { - v.setBackgroundColor(0xff4CAF50); - } else if (AppEvents.STATE_OFF.equals(firstState)) { - v.setBackgroundColor(0xff263238); - } else if (AppEvents.STATE_DOZE.equals(firstState)) { - v.setBackgroundColor(0xff3f51b5); - } else if (AppEvents.STATE_DOZE_SUSPEND.equals(firstState)) { - v.setBackgroundColor(0xff3f51b5); - } - } else { - if (AppEvents.STATE_ON.equals(firstState)) { - v.setBackgroundColor(0xff4CAF50); - } else if (AppEvents.STATE_OFF.equals(firstState)) { - v.setBackgroundColor(0xff263238); - } - } - - if (end > System.currentTimeMillis()) { - params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, now - firstTimestamp); - v.setLayoutParams(params); - } else { - params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - firstTimestamp); - v.setLayoutParams(params); - } - - timeline.addView(v); - } else { - for (int i = 1; i < activeStates.size(); i++) { - long currentTimestamp = activeTimestamps.get(i); - - long priorTimestamp = activeTimestamps.get(i - 1); - String priorState = activeStates.get(i - 1); - - View v = new View(context); - - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { - if (AppEvents.STATE_ON.equals(priorState)) { - v.setBackgroundColor(0xff4CAF50); - } else if (AppEvents.STATE_OFF.equals(priorState)) { - v.setBackgroundColor(0xff263238); - } else if (AppEvents.STATE_DOZE.equals(priorState)) { - v.setBackgroundColor(0xff3f51b5); - } else if (AppEvents.STATE_DOZE_SUSPEND.equals(priorState)) { - v.setBackgroundColor(0xff3f51b5); - } - } else { - if (AppEvents.STATE_ON.equals(priorState)) { - v.setBackgroundColor(0xff4CAF50); - } else if (AppEvents.STATE_OFF.equals(priorState)) { - v.setBackgroundColor(0xff263238); - } - } - - params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, currentTimestamp - priorTimestamp); - v.setLayoutParams(params); - - timeline.addView(v); - } - - long finalTimestamp = activeTimestamps.get(activeTimestamps.size() - 1); - String finalState = activeStates.get(activeStates.size() - 1); - - View v = new View(context); - - if (AppEvents.STATE_ON.equals(finalState)) { - v.setBackgroundColor(0xff4CAF50); - } else if (AppEvents.STATE_OFF.equals(finalState)) { - v.setBackgroundColor(0xff263238); - } else if (AppEvents.STATE_DOZE.equals(finalState)) { - v.setBackgroundColor(0xff3f51b5); - } else if (AppEvents.STATE_DOZE_SUSPEND.equals(finalState)) { - v.setBackgroundColor(0xff3f51b5); - } - - if (end > now) { - params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, now - finalTimestamp); - v.setLayoutParams(params); - } else { - params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - finalTimestamp); - v.setLayoutParams(params); - } - - timeline.addView(v); - } - - if (end > now) { - View v = new View(context); - - params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, end - now); - v.setLayoutParams(params); - - timeline.addView(v); - } - } else { - - } } -*/ public static View fetchView(ViewGroup parent) { - return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_generic, parent, false); + return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_app_event, parent, false); } @Override diff --git a/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java b/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java new file mode 100755 index 0000000..3828e56 --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/generators/sensors/Accelerometer.java @@ -0,0 +1,8 @@ +package com.audacious_software.passive_data_kit.generators.sensors; + +/** + * Created by cjkarr on 4/17/2017. + */ + +public class Accelerometer { +} diff --git a/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java b/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java new file mode 100755 index 0000000..09c7f3c --- /dev/null +++ b/src/com/audacious_software/passive_data_kit/generators/sensors/AmbientLight.java @@ -0,0 +1,8 @@ +package com.audacious_software.passive_data_kit.generators.sensors; + +/** + * Created by cjkarr on 4/17/2017. + */ + +public class AmbientLight { +}