diff --git a/build.gradle b/build.gradle
index 209cff5..173bd45 100755
--- a/build.gradle
+++ b/build.gradle
@@ -71,6 +71,7 @@ android {
compile 'com.fasterxml.jackson.core:jackson-core:2.8.8'
compile 'com.github.philjay:mpandroidchart:v3.0.1'
compile 'com.rvalerio:fgchecker:1.0.1'
+ compile 'com.luckycatlabs:SunriseSunsetCalculator:1.2'
}
buildTypes {
diff --git a/res/drawable-hdpi/ic_afternoon.png b/res/drawable-hdpi/ic_afternoon.png
new file mode 100755
index 0000000..b50a265
Binary files /dev/null and b/res/drawable-hdpi/ic_afternoon.png differ
diff --git a/res/drawable-hdpi/ic_evening.png b/res/drawable-hdpi/ic_evening.png
new file mode 100755
index 0000000..4ac0b4c
Binary files /dev/null and b/res/drawable-hdpi/ic_evening.png differ
diff --git a/res/drawable-hdpi/ic_morning.png b/res/drawable-hdpi/ic_morning.png
new file mode 100755
index 0000000..d8f53a7
Binary files /dev/null and b/res/drawable-hdpi/ic_morning.png differ
diff --git a/res/drawable-hdpi/ic_night_morning.png b/res/drawable-hdpi/ic_night_morning.png
new file mode 100755
index 0000000..d2dbbf0
Binary files /dev/null and b/res/drawable-hdpi/ic_night_morning.png differ
diff --git a/res/drawable-hdpi/ic_night_night.png b/res/drawable-hdpi/ic_night_night.png
new file mode 100755
index 0000000..4e553cc
Binary files /dev/null and b/res/drawable-hdpi/ic_night_night.png differ
diff --git a/res/drawable-hdpi/ic_time_of_day.png b/res/drawable-hdpi/ic_time_of_day.png
new file mode 100755
index 0000000..aeeaf4a
Binary files /dev/null and b/res/drawable-hdpi/ic_time_of_day.png differ
diff --git a/res/drawable-mdpi/ic_afternoon.png b/res/drawable-mdpi/ic_afternoon.png
new file mode 100755
index 0000000..bf4f049
Binary files /dev/null and b/res/drawable-mdpi/ic_afternoon.png differ
diff --git a/res/drawable-mdpi/ic_evening.png b/res/drawable-mdpi/ic_evening.png
new file mode 100755
index 0000000..5356d15
Binary files /dev/null and b/res/drawable-mdpi/ic_evening.png differ
diff --git a/res/drawable-mdpi/ic_morning.png b/res/drawable-mdpi/ic_morning.png
new file mode 100755
index 0000000..08a9aaa
Binary files /dev/null and b/res/drawable-mdpi/ic_morning.png differ
diff --git a/res/drawable-mdpi/ic_night_morning.png b/res/drawable-mdpi/ic_night_morning.png
new file mode 100755
index 0000000..5226ff4
Binary files /dev/null and b/res/drawable-mdpi/ic_night_morning.png differ
diff --git a/res/drawable-mdpi/ic_night_night.png b/res/drawable-mdpi/ic_night_night.png
new file mode 100755
index 0000000..34d23af
Binary files /dev/null and b/res/drawable-mdpi/ic_night_night.png differ
diff --git a/res/drawable-mdpi/ic_time_of_day.png b/res/drawable-mdpi/ic_time_of_day.png
new file mode 100755
index 0000000..29b7390
Binary files /dev/null and b/res/drawable-mdpi/ic_time_of_day.png differ
diff --git a/res/drawable-xhdpi/ic_afternoon.png b/res/drawable-xhdpi/ic_afternoon.png
new file mode 100755
index 0000000..f6f4b49
Binary files /dev/null and b/res/drawable-xhdpi/ic_afternoon.png differ
diff --git a/res/drawable-xhdpi/ic_evening.png b/res/drawable-xhdpi/ic_evening.png
new file mode 100755
index 0000000..9293a30
Binary files /dev/null and b/res/drawable-xhdpi/ic_evening.png differ
diff --git a/res/drawable-xhdpi/ic_morning.png b/res/drawable-xhdpi/ic_morning.png
new file mode 100755
index 0000000..0d7d9eb
Binary files /dev/null and b/res/drawable-xhdpi/ic_morning.png differ
diff --git a/res/drawable-xhdpi/ic_night_morning.png b/res/drawable-xhdpi/ic_night_morning.png
new file mode 100755
index 0000000..8f528d9
Binary files /dev/null and b/res/drawable-xhdpi/ic_night_morning.png differ
diff --git a/res/drawable-xhdpi/ic_night_night.png b/res/drawable-xhdpi/ic_night_night.png
new file mode 100755
index 0000000..1c1af33
Binary files /dev/null and b/res/drawable-xhdpi/ic_night_night.png differ
diff --git a/res/drawable-xhdpi/ic_time_of_day.png b/res/drawable-xhdpi/ic_time_of_day.png
new file mode 100755
index 0000000..e68c611
Binary files /dev/null and b/res/drawable-xhdpi/ic_time_of_day.png differ
diff --git a/res/drawable-xxhdpi/ic_afternoon.png b/res/drawable-xxhdpi/ic_afternoon.png
new file mode 100755
index 0000000..e82afc0
Binary files /dev/null and b/res/drawable-xxhdpi/ic_afternoon.png differ
diff --git a/res/drawable-xxhdpi/ic_evening.png b/res/drawable-xxhdpi/ic_evening.png
new file mode 100755
index 0000000..f141bca
Binary files /dev/null and b/res/drawable-xxhdpi/ic_evening.png differ
diff --git a/res/drawable-xxhdpi/ic_morning.png b/res/drawable-xxhdpi/ic_morning.png
new file mode 100755
index 0000000..d3c90ec
Binary files /dev/null and b/res/drawable-xxhdpi/ic_morning.png differ
diff --git a/res/drawable-xxhdpi/ic_night_morning.png b/res/drawable-xxhdpi/ic_night_morning.png
new file mode 100755
index 0000000..f144281
Binary files /dev/null and b/res/drawable-xxhdpi/ic_night_morning.png differ
diff --git a/res/drawable-xxhdpi/ic_night_night.png b/res/drawable-xxhdpi/ic_night_night.png
new file mode 100755
index 0000000..1a30c94
Binary files /dev/null and b/res/drawable-xxhdpi/ic_night_night.png differ
diff --git a/res/drawable-xxhdpi/ic_time_of_day.png b/res/drawable-xxhdpi/ic_time_of_day.png
new file mode 100755
index 0000000..6e61ef3
Binary files /dev/null and b/res/drawable-xxhdpi/ic_time_of_day.png differ
diff --git a/res/drawable-xxxhdpi/ic_afternoon.png b/res/drawable-xxxhdpi/ic_afternoon.png
new file mode 100755
index 0000000..f7a2b6d
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_afternoon.png differ
diff --git a/res/drawable-xxxhdpi/ic_evening.png b/res/drawable-xxxhdpi/ic_evening.png
new file mode 100755
index 0000000..79b4d55
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_evening.png differ
diff --git a/res/drawable-xxxhdpi/ic_morning.png b/res/drawable-xxxhdpi/ic_morning.png
new file mode 100755
index 0000000..d4f7245
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_morning.png differ
diff --git a/res/drawable-xxxhdpi/ic_night_morning.png b/res/drawable-xxxhdpi/ic_night_morning.png
new file mode 100755
index 0000000..e161685
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_night_morning.png differ
diff --git a/res/drawable-xxxhdpi/ic_night_night.png b/res/drawable-xxxhdpi/ic_night_night.png
new file mode 100755
index 0000000..adb391a
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_night_night.png differ
diff --git a/res/drawable-xxxhdpi/ic_time_of_day.png b/res/drawable-xxxhdpi/ic_time_of_day.png
new file mode 100755
index 0000000..b63eb55
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_time_of_day.png differ
diff --git a/res/layout/card_generator_time_of_day.xml b/res/layout/card_generator_time_of_day.xml
new file mode 100755
index 0000000..33b4658
--- /dev/null
+++ b/res/layout/card_generator_time_of_day.xml
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/databases.xml b/res/values/databases.xml
index a09e886..6682e9e 100755
--- a/res/values/databases.xml
+++ b/res/values/databases.xml
@@ -40,4 +40,7 @@
CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, runtime INTEGER, storage_app INTEGER, storage_other INTEGER, storage_available INTEGER, storage_total INTEGER, storage_path TEXT);
+
+
+ CREATE TABLE history(_id INTEGER PRIMARY KEY AUTOINCREMENT, fetched INTEGER, transmitted INTEGER, observed INTEGER, latitude REAL, longitude REAL, timezone TEXT, sunrise INTEGER, sunset INTEGER);
diff --git a/res/values/generators.xml b/res/values/generators.xml
index d68b3a5..84ed83d 100755
--- a/res/values/generators.xml
+++ b/res/values/generators.xml
@@ -14,6 +14,7 @@
- com.audacious_software.passive_data_kit.generators.device.ForegroundApplication
- com.audacious_software.passive_data_kit.generators.wearables.WithingsDevice
- com.audacious_software.passive_data_kit.generators.diagnostics.SystemStatus
+ - com.audacious_software.passive_data_kit.generators.environment.TimeOfDay
@@ -210,5 +211,15 @@
Continuous Runtime: %1$s
%1$dd %2$s:%3$s:%4$s.%5$s
+
+
+ Time Of Day
+ Night
+ Morning
+ Afternoon
+ Evening
+
+ Sunrise:
+ Sunset:
diff --git a/src/com/audacious_software/passive_data_kit/generators/environment/TimeOfDay.java b/src/com/audacious_software/passive_data_kit/generators/environment/TimeOfDay.java
new file mode 100755
index 0000000..9d1d081
--- /dev/null
+++ b/src/com/audacious_software/passive_data_kit/generators/environment/TimeOfDay.java
@@ -0,0 +1,512 @@
+package com.audacious_software.passive_data_kit.generators.environment;
+
+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.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+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.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;
+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.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.location.LocationListener;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationServices;
+import com.luckycatlabs.sunrisesunset.SunriseSunsetCalculator;
+
+import java.io.File;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+import java.util.TimeZone;
+
+public class TimeOfDay extends Generator implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
+ private static final String GENERATOR_IDENTIFIER = "pdk-time-of-day";
+ private static final String DATABASE_PATH = "pdk-time-of-day.sqlite";
+
+ private static final String ENABLED = "com.audacious_software.passive_data_kit.generators.environment.TimeOfDay.ENABLED";
+ private static final boolean ENABLED_DEFAULT = true;
+
+ private static final String DATA_RETENTION_PERIOD = "com.audacious_software.passive_data_kit.generators.environment.TimeOfDay.DATA_RETENTION_PERIOD";
+ private static final long DATA_RETENTION_PERIOD_DEFAULT = (60 * 24 * 60 * 60 * 1000);
+
+ private static final String USE_GOOGLE_SERVICES = "com.audacious_software.passive_data_kit.generators.environment.TimeOfDay.USE_GOOGLE_SERVICES";
+ private static final boolean USE_GOOGLE_SERVICES_DEFAULT = true;
+
+ private static final String INCLUDE_LOCATION = "com.audacious_software.passive_data_kit.generators.environment.TimeOfDay.INCLUDE_LOCATION";
+ private static final boolean INCLUDE_LOCATION_DEFAULT = false;
+
+ public static final int TIME_OF_DAY_MORNING = 0;
+ public static final int TIME_OF_DAY_AFTERNOON = 1;
+ public static final int TIME_OF_DAY_EVENING = 2;
+ public static final int TIME_OF_DAY_NIGHT = 3;
+ public static final int TIME_OF_DAY_UNKNOWN = -1;
+
+ private static TimeOfDay sInstance = null;
+ private GoogleApiClient mGoogleApiClient = null;
+ private long mUpdateInterval = 300000;
+
+ private boolean mIncludeLocation = TimeOfDay.INCLUDE_LOCATION_DEFAULT;
+
+ private SQLiteDatabase mDatabase = null;
+ private static final int DATABASE_VERSION = 1;
+
+ private static final String TABLE_HISTORY = "history";
+ 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_TIMEZONE = "timezone";
+ public static final String HISTORY_SUNRISE = "sunrise";
+ public static final String HISTORY_SUNSET = "sunset";
+
+ private long mSunrise = 0;
+ private long mSunset = 0;
+
+ public static String generatorIdentifier() {
+ return TimeOfDay.GENERATOR_IDENTIFIER;
+ }
+
+ public static TimeOfDay getInstance(Context context) {
+ if (TimeOfDay.sInstance == null) {
+ TimeOfDay.sInstance = new TimeOfDay(context.getApplicationContext());
+ }
+
+ return TimeOfDay.sInstance;
+ }
+
+ private TimeOfDay(Context context) {
+ super(context);
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ this.mIncludeLocation = prefs.getBoolean(TimeOfDay.INCLUDE_LOCATION, TimeOfDay.INCLUDE_LOCATION_DEFAULT);
+ }
+
+ public static void start(final Context context) {
+ TimeOfDay.getInstance(context).startGenerator();
+ }
+
+ @Override
+ public List fetchPayloads() {
+ return null;
+ }
+
+ private void startGenerator() {
+ final TimeOfDay me = this;
+
+ Runnable r = new Runnable()
+ {
+
+ @Override
+ public void run() {
+ if (TimeOfDay.useKindleLocationServices())
+ {
+ // TODO
+ throw new RuntimeException("Throw rocks at developer to implement Kindle support.");
+ }
+ else if (TimeOfDay.useGoogleLocationServices(me.mContext))
+ {
+ if (me.mGoogleApiClient == null) {
+ GoogleApiClient.Builder builder = new GoogleApiClient.Builder(me.mContext);
+ builder.addConnectionCallbacks(me);
+ builder.addOnConnectionFailedListener(me);
+ builder.addApi(LocationServices.API);
+
+ me.mGoogleApiClient = builder.build();
+ me.mGoogleApiClient.connect();
+ }
+ }
+ else
+ {
+ // TODO
+ throw new RuntimeException("Throw rocks at developer to implement generic location support.");
+ }
+ }
+ };
+
+ Thread t = new Thread(r);
+ t.start();
+
+ Generators.getInstance(this.mContext).registerCustomViewClass(TimeOfDay.GENERATOR_IDENTIFIER, TimeOfDay.class);
+
+ File path = PassiveDataKit.getGeneratorsStorage(this.mContext);
+
+ path = new File(path, TimeOfDay.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_time_of_day_create_history_table));
+ }
+
+ this.setDatabaseVersion(this.mDatabase, TimeOfDay.DATABASE_VERSION);
+ }
+
+ private void stopGenerator() {
+ if (this.mGoogleApiClient != null) {
+ this.mGoogleApiClient.disconnect();
+ this.mGoogleApiClient = null;
+ }
+
+ this.mDatabase.close();
+ this.mDatabase = null;
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public static boolean useGoogleLocationServices(Context context) {
+ SharedPreferences prefs = Generators.getInstance(context).getSharedPreferences(context);
+
+ return prefs.getBoolean(TimeOfDay.USE_GOOGLE_SERVICES, TimeOfDay.USE_GOOGLE_SERVICES_DEFAULT);
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public static boolean useKindleLocationServices() {
+ return DeviceInformation.isKindleFire();
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public static boolean isEnabled(Context context) {
+ SharedPreferences prefs = Generators.getInstance(context).getSharedPreferences(context);
+
+ return prefs.getBoolean(TimeOfDay.ENABLED, TimeOfDay.ENABLED_DEFAULT);
+ }
+
+ @SuppressWarnings({"Contract", "WeakerAccess"})
+ public static boolean isRunning(Context context) {
+ if (TimeOfDay.sInstance == null) {
+ return false;
+ }
+
+ if (TimeOfDay.useKindleLocationServices()) {
+ // TODO
+ throw new RuntimeException("Throw rocks at developer to implement Kindle support.");
+ } else if (TimeOfDay.useGoogleLocationServices(context)) {
+ return (TimeOfDay.sInstance.mGoogleApiClient != null);
+ } else {
+ // TODO
+ throw new RuntimeException("Throw rocks at developer to implement generic location support.");
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public static ArrayList diagnostics(Context context)
+ {
+ return TimeOfDay.getInstance(context).runDiagostics();
+ }
+
+ private ArrayList runDiagostics() {
+ ArrayList actions = new ArrayList<>();
+
+ final TimeOfDay me = this;
+
+ final Handler handler = new Handler(Looper.getMainLooper());
+
+ if (TimeOfDay.isEnabled(this.mContext)) {
+ 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_title), me.mContext.getString(R.string.diagnostic_missing_location_permission), new Runnable() {
+
+ @Override
+ public void run() {
+ handler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ Intent intent = new Intent(me.mContext, RequestPermissionActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(RequestPermissionActivity.PERMISSION, Manifest.permission.ACCESS_FINE_LOCATION);
+
+ me.mContext.startActivity(intent);
+ }
+ });
+ }
+ }));
+ }
+ }
+
+ return actions;
+ }
+
+ @Override
+ public void onConnected(Bundle bundle) {
+ final LocationRequest request = new LocationRequest();
+ request.setPriority(LocationRequest.PRIORITY_LOW_POWER);
+
+ 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) {
+ LocationServices.FusedLocationApi.requestLocationUpdates(this.mGoogleApiClient, request, this, this.mContext.getMainLooper());
+ }
+ }
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+ if (this.mGoogleApiClient != null && this.mGoogleApiClient.isConnected())
+ LocationServices.FusedLocationApi.removeLocationUpdates(this.mGoogleApiClient, this);
+ }
+
+ @Override
+ public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
+ this.mGoogleApiClient = null;
+ }
+
+ @Override
+ public void onLocationChanged(android.location.Location location) {
+ if (location == null)
+ return;
+
+ com.luckycatlabs.sunrisesunset.dto.Location calcLocation = new com.luckycatlabs.sunrisesunset.dto.Location("" + location.getLatitude(), "" + location.getLongitude());
+ TimeZone timezone = TimeZone.getDefault();
+
+ SunriseSunsetCalculator calculator = new SunriseSunsetCalculator(calcLocation, timezone.getDisplayName());
+
+ Calendar now = Calendar.getInstance();
+
+ Calendar sunrise = calculator.getOfficialSunriseCalendarForDate(now);
+ Calendar sunset = calculator.getOfficialSunsetCalendarForDate(now);
+
+ ContentValues values = new ContentValues();
+ values.put(TimeOfDay.HISTORY_OBSERVED, System.currentTimeMillis());
+ values.put(TimeOfDay.HISTORY_LATITUDE, location.getLatitude());
+ values.put(TimeOfDay.HISTORY_LONGITUDE, location.getLongitude());
+ values.put(TimeOfDay.HISTORY_TIMEZONE, timezone.getDisplayName());
+ values.put(TimeOfDay.HISTORY_SUNRISE, sunrise.getTimeInMillis());
+ values.put(TimeOfDay.HISTORY_SUNSET, sunset.getTimeInMillis());
+
+ Bundle updated = new Bundle();
+ updated.putLong(TimeOfDay.HISTORY_OBSERVED, System.currentTimeMillis());
+
+ if (this.mIncludeLocation) {
+ updated.putDouble(TimeOfDay.HISTORY_LATITUDE, location.getLatitude());
+ updated.putDouble(TimeOfDay.HISTORY_LONGITUDE, location.getLongitude());
+
+ Bundle metadata = new Bundle();
+ metadata.putDouble(Generator.LATITUDE, location.getLatitude());
+ metadata.putDouble(Generator.LONGITUDE, location.getLongitude());
+
+ updated.putBundle(Generator.PDK_METADATA, metadata);
+ }
+
+ updated.putString(TimeOfDay.HISTORY_TIMEZONE, timezone.getDisplayName());
+ updated.putLong(TimeOfDay.HISTORY_SUNRISE, sunrise.getTimeInMillis());
+ updated.putLong(TimeOfDay.HISTORY_SUNSET, sunset.getTimeInMillis());
+
+ this.mDatabase.insert(TimeOfDay.TABLE_HISTORY, null, values);
+
+ Log.e("PDK", "TIME OF DAY BUNDLE: " + updated);
+
+ Generators.getInstance(this.mContext).notifyGeneratorUpdated(TimeOfDay.GENERATOR_IDENTIFIER, updated);
+
+ this.mSunrise = sunrise.getTimeInMillis();
+ this.mSunset = sunset.getTimeInMillis();
+
+ this.flushCachedData();
+ }
+
+ @SuppressWarnings("unused")
+ public static long latestPointGenerated(Context context) {
+ long timestamp = 0;
+
+ TimeOfDay me = TimeOfDay.getInstance(context);
+
+ Cursor c = me.mDatabase.query(TimeOfDay.TABLE_HISTORY, null, null, null, null, null, TimeOfDay.HISTORY_OBSERVED + " DESC");
+
+ if (c.moveToNext()) {
+ timestamp = c.getLong(c.getColumnIndex(TimeOfDay.HISTORY_OBSERVED));
+ }
+
+ c.close();
+
+ return timestamp;
+ }
+
+ public void setIncludeLocation(boolean include) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext);
+ SharedPreferences.Editor e = prefs.edit();
+ e.putBoolean(TimeOfDay.INCLUDE_LOCATION, include);
+ e.apply();
+
+ this.mIncludeLocation = true;
+ }
+
+ public long getSunrise() {
+ return this.mSunrise;
+ }
+
+ public long getSunset() {
+ return this.mSunset;
+ }
+
+ public int getTimeOfDay() {
+ if (this.mSunrise == 0 || this.mSunset == 0) {
+ return TIME_OF_DAY_UNKNOWN;
+ }
+
+ long now = System.currentTimeMillis();
+
+ Calendar noon = Calendar.getInstance();
+ noon.setTimeInMillis(now);
+ noon.set(Calendar.HOUR_OF_DAY, 12);
+ noon.set(Calendar.MINUTE, 0);
+ noon.set(Calendar.SECOND, 0);
+ noon.set(Calendar.MILLISECOND, 0);
+
+ if (now < this.mSunrise) {
+ return TimeOfDay.TIME_OF_DAY_NIGHT;
+ } else if (now < noon.getTimeInMillis()) {
+ return TimeOfDay.TIME_OF_DAY_MORNING;
+ } else if (now < this.mSunrise - (60 * 60 * 1000)) {
+ return TimeOfDay.TIME_OF_DAY_AFTERNOON;
+ } else if (now < this.mSunrise + (60 * 60 * 1000)) {
+ return TimeOfDay.TIME_OF_DAY_EVENING;
+ }
+
+ return TimeOfDay.TIME_OF_DAY_NIGHT;
+ }
+
+ @Override
+ protected void flushCachedData() {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext);
+
+ long retentionPeriod = prefs.getLong(TimeOfDay.DATA_RETENTION_PERIOD, TimeOfDay.DATA_RETENTION_PERIOD_DEFAULT);
+
+ long start = System.currentTimeMillis() - retentionPeriod;
+
+ String where = TimeOfDay.HISTORY_OBSERVED + " < ?";
+ String[] args = { "" + start };
+
+ this.mDatabase.delete(TimeOfDay.TABLE_HISTORY, where, args);
+ }
+
+ @Override
+ public void setCachedDataRetentionPeriod(long period) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.mContext);
+ SharedPreferences.Editor e = prefs.edit();
+
+ e.putLong(TimeOfDay.DATA_RETENTION_PERIOD, period);
+
+ e.apply();
+ }
+
+ @SuppressWarnings("unused")
+ public static View fetchView(ViewGroup parent) {
+ if (TimeOfDay.useKindleLocationServices()) {
+ // TODO
+ throw new RuntimeException("Throw rocks at developer to implement Kindle support.");
+ } else if (TimeOfDay.useGoogleLocationServices(parent.getContext())) {
+ return LayoutInflater.from(parent.getContext()).inflate(R.layout.card_generator_time_of_day, parent, false);
+ } else {
+ // TODO
+ throw new RuntimeException("Throw rocks at developer to implement generic location support.");
+ }
+ }
+
+ @SuppressWarnings({"UnusedAssignment", "unused"})
+ public static void bindViewHolder(DataPointViewHolder holder) {
+ final Context context = holder.itemView.getContext();
+
+ TimeOfDay me = TimeOfDay.getInstance(context);
+
+ long timestamp = 0;
+
+ Cursor c = me.mDatabase.query(TimeOfDay.TABLE_HISTORY, null, null, null, null, null, TimeOfDay.HISTORY_OBSERVED + " DESC");
+
+ if (c.moveToNext()) {
+ timestamp = c.getLong(c.getColumnIndex(TimeOfDay.HISTORY_OBSERVED));
+ }
+
+ View cardEmpty = holder.itemView.findViewById(R.id.card_empty);
+ View cardContent = holder.itemView.findViewById(R.id.card_content);
+
+ TextView dateLabel = (TextView) holder.itemView.findViewById(R.id.generator_data_point_date);
+
+ if (timestamp > 0) {
+ dateLabel.setText(Generator.formatTimestamp(context, timestamp / 1000));
+ cardEmpty.setVisibility(View.GONE);
+ cardContent.setVisibility(View.VISIBLE);
+
+ View nightMorning = cardContent.findViewById(R.id.cell_night_morning);
+ View morning = cardContent.findViewById(R.id.cell_morning);
+ View afternoon = cardContent.findViewById(R.id.cell_afternoon);
+ View evening = cardContent.findViewById(R.id.cell_evening);
+ View nightNight = cardContent.findViewById(R.id.cell_night_night);
+
+ nightMorning.setAlpha(0.5f);
+ morning.setAlpha(0.5f);
+ afternoon.setAlpha(0.5f);
+ evening.setAlpha(0.5f);
+ nightNight.setAlpha(0.5f);
+
+ switch (me.getTimeOfDay()) {
+ case TIME_OF_DAY_NIGHT:
+ Calendar now = Calendar.getInstance();
+
+ if (now.get(Calendar.HOUR_OF_DAY) < 12) {
+ nightMorning.setAlpha(1.0f);
+ } else {
+ nightNight.setAlpha(1.0f);
+ }
+
+ break;
+ case TIME_OF_DAY_MORNING:
+ morning.setAlpha(1.0f);
+ break;
+ case TIME_OF_DAY_AFTERNOON:
+ afternoon.setAlpha(1.0f);
+ break;
+ case TIME_OF_DAY_EVENING:
+ evening.setAlpha(1.0f);
+ break;
+ }
+
+ DateFormat format = android.text.format.DateFormat.getTimeFormat(context);
+
+ TextView sunrise = (TextView) cardContent.findViewById(R.id.label_sunrise);
+
+ Calendar sunriseCalendar = Calendar.getInstance();
+ sunriseCalendar.setTimeInMillis(me.mSunrise);
+
+ sunrise.setText(format.format(sunriseCalendar.getTime()));
+
+ TextView sunset = (TextView) cardContent.findViewById(R.id.label_sunset);
+
+ Calendar sunsetCalendar = Calendar.getInstance();
+ sunsetCalendar.setTimeInMillis(me.mSunset);
+
+ sunset.setText(format.format(sunsetCalendar.getTime()));
+ } else {
+ dateLabel.setText(R.string.label_never_pdk);
+ cardEmpty.setVisibility(View.VISIBLE);
+ cardContent.setVisibility(View.GONE);
+ }
+ }
+}