package com.radicaldynamic.groupinform.logic; import java.io.File; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.UUID; import org.apache.http.client.CookieStore; import org.odk.collect.android.utilities.FileUtils; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.telephony.TelephonyManager; import android.util.Log; import com.couchbase.libcouch.CouchInstaller; import com.couchbase.libcouch.CouchService; import com.radicaldynamic.groupinform.R; import com.radicaldynamic.groupinform.application.Collect; import com.radicaldynamic.groupinform.services.DatabaseService; import com.radicaldynamic.groupinform.services.InformOnlineService; import com.radicaldynamic.groupinform.utilities.FileUtilsExtended; /** * Stores the state of this device as registered with Inform Online * (some of this information is dynamically populated) */ public class InformOnlineState { private static final String t = "InformOnlineState: "; // Constants for strings commonly encountered when interacting with the Inform Online service public static final String OK = "ok"; public static final String ERROR = "error"; public static final String EXPIRED = "expired"; public static final String FAILURE = "failure"; public static final String RESULT = "result"; public static final String REASON = "reason"; // Constants for account information stored in preferences public static final String ACCOUNT_KEY = "informonline_accountkey"; // Accessible public static final String ACCOUNT_NUM = "informonline_accountnum"; // Accessible private static final String ACCOUNT_OWNER = "informonline_accountown"; // Invisible // private static final String ACCOUNT_PLAN = "informonline_accountplan"; // Accessible // private static final String ACCOUNT_LICENCED_SEATS = "informonline_accountlicencedseats"; // Accessible // private static final String ACCOUNT_ASSIGNED_SEATS = "informonline_accountassignedseats"; // Accessible // Constants for device information stored in preferences public static final String DEVICE_ID = "informonline_deviceid"; // Invisible public static final String DEVICE_KEY = "informonline_devicekey"; // Invisible public static final String DEVICE_PIN = "informonline_devicepin"; // Accessible public static final String DEVICE_ROLE = "informonline_devicerole"; // Accessible public static final String DEFAULT_DATABASE = "informonline_defaultdb"; // Invisible // The order associated to this device is expired - the account owner needs to fix it // (either by renewing the order or associating the device profile with a new, unexpired order) public static final String EXPIRED_ORDER = "informonline_expired"; // Dictates whether or not the app was put into offline mode manually public static final String OFFLINE_MODE = "informonline_offlinemode"; // Invisible // Constants for session information stored in preferences public static final String SESSION = "informonline_session"; // Invisible // Lookup map for account devices, indexed by device ID private Map<String, AccountDevice> accountDevicesMap = new HashMap<String, AccountDevice>(); private Map<String, AccountDevice> accountDevicesSyncMap = Collections.synchronizedMap(accountDevicesMap); // Lookup map for account folders, indexed by folder ID private Map<String, AccountFolder> accountFoldersMap = new HashMap<String, AccountFolder>(); private Map<String, AccountFolder> accountFoldersSyncMap = Collections.synchronizedMap(accountFoldersMap); private String accountNumber; // The licence number private String accountKey; // The licence key private boolean accountOwner; // Is the user also the account owner? // private String accountPlan; // Plan type // private int accountLicencedSeats; // The number of licenced seats for the account // private int accountAssignedSeats; // The number of seats allocated and assigned (not necessarily active) private String deviceId; private String deviceKey; private String devicePin; private String deviceFingerprint; private String deviceRole; private String defaultDatabase; private String selectedDatabase; // The database the user has selected to work with at any given time private String serverUrl; private CookieStore session = null; private boolean offlineModeEnabled = false; private boolean expired = false; private Context mContext = null; private SharedPreferences mPrefs = null; // Used by Collect public InformOnlineState() { } public InformOnlineState(Context context) { mContext = context; mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); loadPreferences(); // Initialize server URL setServerUrl("https://" + mContext.getText(R.string.tf_default_ionline_server) + ":" + mContext.getText(R.string.tf_default_ionline_port)); // Set the device finger print setDeviceFingerprint(mContext); } public void setAccountDevices(Map<String, AccountDevice> accountDevices) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setAccountDevices()"); this.accountDevicesSyncMap = accountDevices; } public Map<String, AccountDevice> getAccountDevices() { return accountDevicesSyncMap; } public void setAccountFolders(Map<String, AccountFolder> accountFolders) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setAccountFolders()"); this.accountFoldersSyncMap = accountFolders; } public Map<String, AccountFolder> getAccountFolders() { return accountFoldersSyncMap; } public void setAccountKey(String accountKey) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setAccountKey() HIDDEN"); SharedPreferences.Editor editor = mPrefs.edit(); editor.putString(ACCOUNT_KEY, accountKey); editor.commit(); this.accountKey = accountKey; } public String getAccountKey() { return accountKey; } // public void setAccountAssignedSeats(int accountAssignedSeats) // { // Log.d(Collect.LOGTAG, t + "setAccountAssignedSeats() " + accountAssignedSeats); // // SharedPreferences.Editor editor = mPrefs.edit(); // editor.putInt(ACCOUNT_ASSIGNED_SEATS, accountAssignedSeats); // editor.commit(); // // this.accountAssignedSeats = accountAssignedSeats; // } // // public int getAccountAssignedSeats() // { // return accountAssignedSeats; // } // // public void setAccountLicencedSeats(int accountLicencedSeats) // { // Log.d(Collect.LOGTAG, t + "setAccountLicencedSeats() " + accountLicencedSeats); // // SharedPreferences.Editor editor = mPrefs.edit(); // editor.putInt(ACCOUNT_LICENCED_SEATS, accountLicencedSeats); // editor.commit(); // // this.accountLicencedSeats = accountLicencedSeats; // } // // public int getAccountLicencedSeats() // { // return accountLicencedSeats; // } public void setAccountNumber(String accountNumber) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setAccountNumber() " + accountNumber); SharedPreferences.Editor editor = mPrefs.edit(); editor.putString(ACCOUNT_NUM, accountNumber); editor.commit(); this.accountNumber = accountNumber; } public String getAccountNumber() { return accountNumber; } public void setAccountOwner(boolean accountOwner) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setAccountOwner() " + accountOwner); SharedPreferences.Editor editor = mPrefs.edit(); editor.putBoolean(ACCOUNT_OWNER, accountOwner); editor.commit(); this.accountOwner = accountOwner; } // This is a convenience method for making UI decisions ONLY. NOTHING secure should be based upon this. public boolean isAccountOwner() { return accountOwner; } // public void setAccountPlan(String accountPlan) // { // Log.d(Collect.LOGTAG, t + "setAccountPlan() " + accountPlan); // // SharedPreferences.Editor editor = mPrefs.edit(); // editor.putString(ACCOUNT_PLAN, accountPlan); // editor.commit(); // // this.accountPlan = accountPlan; // } // // public String getAccountPlan() // { // return accountPlan; // } public void setDeviceId(String deviceId) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setDeviceId() " + deviceId); SharedPreferences.Editor editor = mPrefs.edit(); editor.putString(DEVICE_ID, deviceId); editor.commit(); this.deviceId = deviceId; } public String getDeviceId() { return deviceId; } public void setDeviceKey(String deviceKey) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setDeviceKey() HIDDEN"); SharedPreferences.Editor editor = mPrefs.edit(); editor.putString(DEVICE_KEY, deviceKey); editor.commit(); this.deviceKey = deviceKey; } public String getDeviceKey() { return deviceKey; } public void setDevicePin(String devicePin) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setDevicePin() HIDDEN"); SharedPreferences.Editor editor = mPrefs.edit(); editor.putString(DEVICE_PIN, devicePin); editor.commit(); this.devicePin = devicePin; } public String getDevicePin() { return devicePin; } public void setDeviceRole(String deviceRole) { SharedPreferences.Editor editor = mPrefs.edit(); editor.putString(DEVICE_ROLE, deviceRole); editor.commit(); this.deviceRole = deviceRole; } public String getDeviceRole() { return deviceRole; } public void setExpired(boolean expired) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setExpired() " + expired); SharedPreferences.Editor editor = mPrefs.edit(); editor.putBoolean(EXPIRED_ORDER, expired); editor.commit(); this.expired = expired; } public boolean isExpired() { return expired; } public void setServerUrl(String serverUrl) { this.serverUrl = serverUrl; } public String getServerUrl() { return serverUrl; } public void setSession(CookieStore session) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setSession() called"); this.session = session; } public CookieStore getSession() { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "getSession() called"); return session; } public void setDefaultDatabase(String defaultDatabase) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setDefaultDatabase() " + defaultDatabase); SharedPreferences.Editor editor = mPrefs.edit(); editor.putString(DEFAULT_DATABASE, defaultDatabase); editor.commit(); this.defaultDatabase = defaultDatabase; } public String getDefaultDatabase() { return defaultDatabase; } public void setSelectedDatabase(String selectedDatabase) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setSelectedDatabase() " + selectedDatabase); this.selectedDatabase = selectedDatabase; } public String getSelectedDatabase() { return selectedDatabase; } /* * See http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id */ private void setDeviceFingerprint(Context context) { final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); final String tmDevice, tmSerial, androidId; tmDevice = "" + tm.getDeviceId(); tmSerial = "" + tm.getSimSerialNumber(); androidId = "" + android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); UUID deviceUuid = new UUID(androidId.hashCode(), ((long)tmDevice.hashCode() << 32) | tmSerial.hashCode()); if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setDeviceFingerprint() " + deviceUuid.toString()); this.deviceFingerprint = deviceUuid.toString(); } public String getDeviceFingerprint() { return deviceFingerprint; } public void setOfflineModeEnabled(boolean offlineMode) { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "setOfflineModeEnabled() " + offlineMode); SharedPreferences.Editor editor = mPrefs.edit(); editor.putBoolean(OFFLINE_MODE, offlineMode); editor.commit(); this.offlineModeEnabled = offlineMode; } public boolean isOfflineModeEnabled() { return offlineModeEnabled; } // Whether this device appears to be registered public boolean hasRegistration() { if (deviceId == null) return false; else { return true; } } // Whether this device has folders that are marked for offline use (aka synchronized or replicated) public boolean hasReplicatedFolders() { // Does the user have at least one database selected for offline use? Iterator<String> folderIds = accountFoldersSyncMap.keySet().iterator(); int replicatedFolders = 0; while (folderIds.hasNext()) { String id = folderIds.next(); if (Collect.getInstance().getInformOnlineState().getAccountFolders().get(id).isReplicated()) replicatedFolders++; } if (replicatedFolders == 0) return false; else return true; } public void resetDevice() { setAccountKey(null); // setAccountAssignedSeats(0); // setAccountLicencedSeats(0); setAccountNumber(null); setAccountOwner(false); // setAccountPlan(null); setDeviceId(null); setDeviceKey(null); setDevicePin(null); setDeviceRole(AccountDevice.ROLE_UNASSIGNED); setDefaultDatabase(null); setExpired(false); setOfflineModeEnabled(false); setSession(null); // Remove cache files new File(mContext.getCacheDir(), FileUtilsExtended.DEVICE_CACHE_FILE).delete(); new File(mContext.getCacheDir(), FileUtilsExtended.FOLDER_CACHE_FILE).delete(); new File(mContext.getCacheDir(), FileUtilsExtended.SESSION_CACHE_FILE).delete(); // Remove external storage cache & files CouchInstaller.deleteDirectory(new File(FileUtilsExtended.EXTERNAL_CACHE)); CouchInstaller.deleteDirectory(new File(FileUtilsExtended.EXTERNAL_FILES)); // Shutdown CouchDB and remove databases & log files if (Collect.getInstance().stopService(new Intent(Collect.getInstance().getApplicationContext(), CouchService.class))) if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "CouchService stopped"); // Shutdown other services to ensure a full reset off all stateful information if (Collect.getInstance().stopService(new Intent(Collect.getInstance().getApplicationContext(), DatabaseService.class))) if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "DatabaseService stopped"); if (Collect.getInstance().stopService(new Intent(Collect.getInstance().getApplicationContext(), InformOnlineService.class))) if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "InformOnlineService stopped"); // Remove DB files & log files if (CouchInstaller.deleteDirectory(new File(FileUtilsExtended.EXTERNAL_DB))) FileUtils.createFolder(FileUtilsExtended.EXTERNAL_DB); } private void loadPreferences() { setAccountKey(mPrefs.getString(ACCOUNT_KEY, null)); // setAccountAssignedSeats(mPrefs.getInt(ACCOUNT_ASSIGNED_SEATS, 0)); // setAccountLicencedSeats(mPrefs.getInt(ACCOUNT_LICENCED_SEATS, 0)); setAccountNumber(mPrefs.getString(ACCOUNT_NUM, null)); setAccountOwner(mPrefs.getBoolean(ACCOUNT_OWNER, false)); // setAccountPlan(mPrefs.getString(ACCOUNT_PLAN, null)); setDeviceId(mPrefs.getString(DEVICE_ID, null)); setDeviceKey(mPrefs.getString(DEVICE_KEY, null)); setDevicePin(mPrefs.getString(DEVICE_PIN, null)); setDeviceRole(mPrefs.getString(DEVICE_ROLE, AccountDevice.ROLE_UNASSIGNED)); setDefaultDatabase(mPrefs.getString(DEFAULT_DATABASE, null)); setExpired(mPrefs.getBoolean(EXPIRED_ORDER, false)); setSelectedDatabase(getDefaultDatabase()); setOfflineModeEnabled(mPrefs.getBoolean(OFFLINE_MODE, false)); // Further post-cleanup if the user "cleared data" via the Application Info screen if (getDeviceId() == null) resetDevice(); } }