/*
* Prefs.java - Copyright(c) 2013 Joe Pasqua
* Provided under the MIT License. See the LICENSE file for details.
* Created: Nov 17, 2013
*/
package org.noroomattheinn.visibletesla;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Range;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.prefs.Preferences;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import org.noroomattheinn.utils.CalTime;
import org.noroomattheinn.utils.Utils;
import org.noroomattheinn.visibletesla.data.VTData;
import org.noroomattheinn.visibletesla.vehicle.VTVehicle;
/**
* Prefs - Stores and Manages Preferences data the app.
*
* @author Joe Pasqua <joe at NoRoomAtTheInn dot org>
*/
public class Prefs {
/*------------------------------------------------------------------------------
*
* Constants and Enums
*
*----------------------------------------------------------------------------*/
public enum LoadPeriod {Last7, Last14, Last30, ThisWeek, ThisMonth, All, None};
public static final BiMap<String,LoadPeriod> nameToLoadPeriod = HashBiMap.create();
static {
nameToLoadPeriod.put("Last 7 days", LoadPeriod.Last7);
nameToLoadPeriod.put("Last 14 days", LoadPeriod.Last14);
nameToLoadPeriod.put("Last 30 days", LoadPeriod.Last30);
nameToLoadPeriod.put("This week", LoadPeriod.ThisWeek);
nameToLoadPeriod.put("This month", LoadPeriod.ThisMonth);
nameToLoadPeriod.put("All", LoadPeriod.All);
nameToLoadPeriod.put("None", LoadPeriod.None);
}
public static final String GoogleMapsAPIKey = "AIzaSyAZDh-9z3wgvLFnhTu72O5h2Qn9_4Omyj4";
public static final String MailGunKey = "key-2x6kwt4t-f4qcy9nb9wmo4yed681ogr6";
/*------------------------------------------------------------------------------
*
* Internal State
*
*----------------------------------------------------------------------------*/
private final Preferences persistentState;
/*==============================================================================
* ------- -------
* ------- Public Interface To This Class -------
* ------- -------
*============================================================================*/
public Preferences storage() { return persistentState; }
public void persist(String key, String value) {
persistentState.put(key, value);
}
public Prefs(Preferences underlyingStore) {
this.persistentState = underlyingStore;
loadGeneralPrefs();
loadGraphPrefs();
loadSchedulerPrefs();
loadLocationPrefs();
}
/*------------------------------------------------------------------------------
*
* General Application Preferences
*
*----------------------------------------------------------------------------*/
// Basic Preferences
public IntegerProperty idleThresholdInMinutes = new SimpleIntegerProperty();
public BooleanProperty wakeOnTabChange = new SimpleBooleanProperty();
public StringProperty loadPeriod = new SimpleStringProperty();
public StringProperty overviewRange = new SimpleStringProperty();
public StringProperty chargeTimeType = new SimpleStringProperty();
public StringProperty notificationAddress = new SimpleStringProperty();
private static final String WakeOnTCKey = "APP_WAKE_ON_TC";
private static final String IdleThresholdKey = "APP_IDLE_THRESHOLD";
private static final String LoadPeriodKey = "GRAPH_PERIOD";
private static final String OverviewRangeKey = "OVERVIEW_RANGE";
private static final String ChargeTimeKey = "CHARGE_TIME_TYPE";
private static final String NotifyAddressKey = "NOTIFICATION_ADDR";
public VTData.Options dataOptions = new VTData.Options();
public BooleanProperty submitAnonFailure = new SimpleBooleanProperty();
private static final String SubmitAnonRest = "APP_SUBMIT_ANON_REST";
private static final String SubmitAnonCharge = "APP_SUBMIT_ANON_CHARGE";
private static final String SubmitAnonFailure = "APP_SUBMIT_ANON_FAILURE";
private static final String IncludeLocData = "APP_INCLUDE_LOC";
private static final String DitherAmtKey = "APP_DITHER_AMT";
// Advanced
public BooleanProperty offerExperimental = new SimpleBooleanProperty();
public BooleanProperty enableProxy = new SimpleBooleanProperty();
public StringProperty proxyHost = new SimpleStringProperty();
public IntegerProperty proxyPort = new SimpleIntegerProperty();
public BooleanProperty useCustomGoogleAPIKey = new SimpleBooleanProperty();
public StringProperty googleAPIKey = new SimpleStringProperty();
public BooleanProperty useCustomMailGunKey = new SimpleBooleanProperty();
public StringProperty mailGunKey = new SimpleStringProperty();
public StringProperty numerousKey = new SimpleStringProperty();
public IntegerProperty fontScale = new SimpleIntegerProperty();
public BooleanProperty enableRest = new SimpleBooleanProperty();
public IntegerProperty restPort = new SimpleIntegerProperty();
public StringProperty authCode = new SimpleStringProperty();
public StringProperty customURLSource = new SimpleStringProperty();
public StringProperty logLevel = new SimpleStringProperty();
private static final String OfferExpKey = "APP_OFFER_EXP";
private static final String EnableProxyKey = "APP_ENABLE_PROXY";
private static final String ProxyHostKey = "APP_PROXY_HOST";
private static final String ProxyPortKey = "APP_PROXY_PORT";
private static final String UseCustomGoogleKey = "APP_USE_CUSTOM_GKEY";
private static final String CustomGoogleKey = "APP_CUSTOM_GKEY";
private static final String UseCustomMailGunKey = "APP_USE_CUSTOM_MGKEY";
private static final String CustomMailGunKey = "APP_CUSTOM_MGKEY";
private static final String CustomNumerousKey = "APP_CUSTOM_NUMKEY";
private static final String FontScaleKey = "APP_FONT_SCALE";
private static final String RestPortKey = "APP_REST_PORT";
private static final String EnableRestKey = "APP_ENABLE_REST";
private static final String AuthCodeKey = "APP_AUTH_CODE";
private static final String CustomURLKey = "APP_CUSTOM_URL";
private static final String LogLevelKey = "APP_LOG_LEVEL";
private static final HashMap<String,Level> levelMap = Utils.newHashMap(
"Severe", Level.SEVERE, "Warning", Level.WARNING,
"Info", Level.INFO, "Config", Level.CONFIG,
"Fine", Level.FINE, "Finer", Level.FINER,
"Finest", Level.FINEST
);
// Overrides
public VTVehicle.Overrides overrides = new VTVehicle.Overrides();
private static final String ORWheelToKey = "APP_OWT";
private static final String ORWheelActiveKey = "APP_OWA";
private static final String ORColorToKey = "APP_OCT";
private static final String ORColorActiveKey = "APP_OCA";
private static final String ORUnitsToKey = "APP_OUT";
private static final String ORUnitsActiveKey = "APP_OUA";
private static final String ORModelToKey = "APP_OMT";
private static final String ORModelActiveKey = "APP_OMA";
private static final String ORRoofToKey = "APP_ORT";
private static final String ORRoofActiveKey = "APP_ORA";
public Level getLogLevel() { return levelMap.get(logLevel.get()); }
private void loadGeneralPrefs() {
// ----- Basic Preferences
integerPref(IdleThresholdKey, idleThresholdInMinutes, 15);
booleanPref(WakeOnTCKey, wakeOnTabChange, true);
stringPref(NotifyAddressKey, notificationAddress, "");
stringPref(OverviewRangeKey, overviewRange, "Rated");
stringPref(ChargeTimeKey, chargeTimeType, "Remaining");
booleanPref(SubmitAnonRest, dataOptions.submitAnonRest, false);
booleanPref(SubmitAnonCharge, dataOptions.submitAnonCharge, false);
booleanPref(SubmitAnonFailure, submitAnonFailure, false);
booleanPref(IncludeLocData, dataOptions.includeLocData, false);
doublePref(DitherAmtKey, dataOptions.ditherLocAmt, 1.5);
loadPeriod.addListener(new ChangeListener<String>() {
@Override public void changed(ObservableValue<? extends String> ov, String t, String t1) {
Range<Long>period = getLoadPeriod();
dataOptions.loadPeriod.set(period);
}
});
stringPref(LoadPeriodKey, loadPeriod, LoadPeriod.All.name());
// ----- Advanced Preferences
booleanPref(OfferExpKey, offerExperimental, false);
booleanPref(EnableProxyKey, enableProxy, false);
stringPref(ProxyHostKey, proxyHost, "");
integerPref(ProxyPortKey, proxyPort, 8080);
booleanPref(UseCustomGoogleKey, useCustomGoogleAPIKey, false);
stringPref(CustomGoogleKey, googleAPIKey, GoogleMapsAPIKey);
booleanPref(UseCustomMailGunKey, useCustomMailGunKey, false);
stringPref(CustomMailGunKey, mailGunKey, MailGunKey);
stringPref(CustomNumerousKey, numerousKey, "");
integerPref(FontScaleKey, fontScale, 100);
booleanPref(EnableRestKey, enableRest, false);
integerPref(RestPortKey, restPort, 9090);
stringPref(CustomURLKey, customURLSource, "");
stringPref(LogLevelKey, logLevel, "Info");
stringPref(AuthCodeKey, authCode, "");
// ----- Overrides
stringPref(ORWheelToKey, overrides.wheels, "From Car");
booleanPref(ORWheelActiveKey, overrides.doWheels, false);
stringPref(ORColorToKey, overrides.color, "From Car");
booleanPref(ORColorActiveKey, overrides.doColor, false);
stringPref(ORUnitsToKey, overrides.units, "From Car");
booleanPref(ORUnitsActiveKey, overrides.doUnits, false);
stringPref(ORModelToKey, overrides.model, "From Car");
booleanPref(ORModelActiveKey, overrides.doModel, false);
stringPref(ORRoofToKey, overrides.roof, "From Car");
booleanPref(ORRoofActiveKey, overrides.doRoof, false);
}
private Range<Long> getLoadPeriod() {
Range<Long> range = Range.closed(Long.MIN_VALUE, Long.MAX_VALUE);
long now = System.currentTimeMillis();
LoadPeriod period = nameToLoadPeriod.get(loadPeriod.get());
if (period == null) {
period = LoadPeriod.All;
loadPeriod.set(nameToLoadPeriod.inverse().get(period));
}
switch (period) {
case None:
range = Range.closed(now + 1000, now + 1000L); // Empty Range
break;
case Last7:
range = Range.closed(now - (7 * 24 * 60 * 60 * 1000L), now);
break;
case Last14:
range = Range.closed(now - (14 * 24 * 60 * 60 * 1000L), now);
break;
case Last30:
range = Range.closed(now - (30 * 24 * 60 * 60 * 1000L), now);
break;
case ThisWeek:
Range<Date> thisWeek = getThisWeek();
range = Range.closed(
thisWeek.lowerEndpoint().getTime(),
thisWeek.upperEndpoint().getTime());
break;
case ThisMonth:
Range<Date> thisMonth = getThisMonth();
range = Range.closed(
thisMonth.lowerEndpoint().getTime(),
thisMonth.upperEndpoint().getTime());
break;
case All:
default:
break;
}
return range;
}
/*------------------------------------------------------------------------------
*
* Preferences related to the Graphs Tab
*
*----------------------------------------------------------------------------*/
public BooleanProperty ignoreGraphGaps = new SimpleBooleanProperty();
public IntegerProperty graphGapTime = new SimpleIntegerProperty();
private static final String GraphIgnoreGapsKey = "GRAPH_GAP_IGNORE";
private static final String GraphGapTimeKey = "GRAPH_GAP_TIME";
private static final String VSLimitEnabledKey = "VS_LIMIT_ENABLED";
private static final String VSFromKey = "VS_FROM";
private static final String VSToKey = "VS_TO";
private void loadGraphPrefs() {
booleanPref(GraphIgnoreGapsKey, ignoreGraphGaps, false);
integerPref(GraphGapTimeKey, graphGapTime, 15); // 15 minutes
booleanPref(VSLimitEnabledKey, dataOptions.restLimitEnabled, false);
calTimePref(VSFromKey, dataOptions.restLimitFrom, new CalTime("10^00^PM"));
calTimePref(VSToKey, dataOptions.restLimitTo, new CalTime("06^00^AM"));
}
/*------------------------------------------------------------------------------
*
* Preferences related to the Scheduler Tab
*
*----------------------------------------------------------------------------*/
public BooleanProperty safeIncludesMinCharge = new SimpleBooleanProperty();
public BooleanProperty safeIncludesPluggedIn = new SimpleBooleanProperty();
private static final String SchedSafeInclBat = "SCHED_SAFE_BATTERY";
private static final String SchedSafeInclPlug = "SCHED_SAFE_PLUGGED_IN";
private void loadSchedulerPrefs() {
booleanPref(SchedSafeInclBat, safeIncludesMinCharge, true);
booleanPref(SchedSafeInclPlug, safeIncludesPluggedIn, false);
}
/*------------------------------------------------------------------------------
*
* Preferences related to the Location Tab
*
*----------------------------------------------------------------------------*/
public BooleanProperty collectLocationData = new SimpleBooleanProperty();
private static final String LocCollectData = "LOC_COLLECT_DATA";
private static final String LocStreamMore = "LOC_STREAM_MORE";
private static final String LocMinTime = "LOC_MIN_TIME";
private static final String LocMinDist = "LOC_MIN_DIST";
private void loadLocationPrefs() {
booleanPref(LocCollectData, collectLocationData, true);
booleanPref(LocStreamMore, dataOptions.streamWhenPossible, true);
integerPref(LocMinTime, dataOptions.locMinTime, 5); // 5 Seconds
integerPref(LocMinDist, dataOptions.locMinDist, 5); // 5 Meters
}
/*------------------------------------------------------------------------------
*
* PRIVATE - Convenience Methods for handling preferences
*
*----------------------------------------------------------------------------*/
private void integerPref(final String key, IntegerProperty property, int defaultValue) {
property.set(persistentState.getInt(key, defaultValue));
property.addListener(new ChangeListener<Number>() {
@Override public void changed(
ObservableValue<? extends Number> ov, Number old, Number cur) {
persistentState.putInt(key, cur.intValue());
}
});
}
private void doublePref(final String key, DoubleProperty property, double defaultValue) {
property.set(persistentState.getDouble(key, defaultValue));
property.addListener(new ChangeListener<Number>() {
@Override public void changed(
ObservableValue<? extends Number> ov, Number old, Number cur) {
persistentState.putDouble(key, cur.doubleValue());
}
});
}
private void booleanPref(final String key, BooleanProperty property, boolean defaultValue) {
property.set(persistentState.getBoolean(key, defaultValue));
property.addListener(new ChangeListener<Boolean>() {
@Override public void changed(
ObservableValue<? extends Boolean> ov, Boolean old, Boolean cur) {
persistentState.putBoolean(key, cur);
}
});
}
private void stringPref(final String key, StringProperty property, String defaultValue) {
property.set(persistentState.get(key, defaultValue));
property.addListener(new ChangeListener<String>() {
@Override public void changed(
ObservableValue<? extends String> ov, String old, String cur) {
persistentState.put(key, cur);
}
});
}
private void calTimePref(final String key, ObjectProperty<CalTime> property, CalTime defaultValue) {
String initial = persistentState.get(key, defaultValue.toString());
property.set(new CalTime(initial));
property.addListener(new ChangeListener<CalTime>() {
@Override public void changed(
ObservableValue<? extends CalTime> ov, CalTime old, CalTime cur) {
persistentState.put(key, cur.toString());
}
});
}
/*------------------------------------------------------------------------------
*
* PRIVATE - Utility Methods
*
*----------------------------------------------------------------------------*/
private Range<Date> getThisWeek() {
return getDateRange(Calendar.DAY_OF_WEEK);
}
private Range<Date> getThisMonth() {
return getDateRange(Calendar.DATE);
}
private Range<Date> getDateRange(int dateField) {
Calendar cal = Calendar.getInstance();
cal.set(dateField, 1);
Date start = cal.getTime();
cal.set(dateField, cal.getActualMaximum(dateField));
Date end = cal.getTime();
return Range.closed(start, end);
}
}