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); + } + } +}