View messy_prefs.kt
@Singleton | |
class Preferences(context: Context) { | |
private val prefs: SharedPreferences = context.getSharedPreferences("Prefs", Context.MODE_PRIVATE) | |
companion object { | |
const val PREF_CURRENT_STATE = "CurrentState" | |
const val PREF_LAST_KNOWN_ERROR = "LastKnownError" | |
const val PREF_LAST_ACTION_PERFORMED = "LastActionPerformed" | |
} |
View state_machine.kt
/** | |
* Keeps track of current and prior application state. | |
* | |
* @param dataStore storage | |
*/ | |
@Singleton | |
class StateMachine(private val dataStore: DataStore) { | |
/** | |
* Storage implementation for our state machine. | |
* |
View gson_utils.kt
/** | |
* A factory that can serialize/deserialize json. | |
*/ | |
object GsonUtils { | |
/** | |
* Handles deserializing the json string into a [T] instance. | |
* | |
* @param json the json to deserialize into an instance of [T] | |
* @throws JsonSyntaxException if there is a failure deserializing then this exception is thrown | |
*/ |
View datastore.kt
/** | |
* A storage implementation that enforces whole class serialization/deserialization. | |
* | |
* This enforces organization of data that needs to be persisted rather than having generic classes | |
* with lots of unrelated data. | |
* | |
* @param context the caller's context | |
*/ | |
@Singleton | |
class DataStore(context: Context) { |
View encrypted_room_db.kt
abstract class EncryptedDatabase : RoomDatabase() { | |
companion object { | |
fun getInstance(passcode: CharArray, context: Context): | |
EncryptedDatabase = buildDatabase(passcode, context) | |
private fun buildDatabase( | |
passcode: CharArray, | |
context: Context | |
): EncryptedDatabase { | |
// DatabaseKeyMgr is a singleton that all of the above code is wrapped into. |
View get_char_key.kt
/** | |
* Returns the database key suitable for using with Room. | |
* | |
* @param passcode the user's passcode | |
* @param context the caller's context | |
*/ | |
fun getCharKey(passcode: CharArray, context: Context): CharArray { | |
if (dbCharKey == null) { | |
initKey(passcode, context) | |
} |
View get_raw_byte_key.kt
/** | |
* Decrypts the [Storable] instance using the [passcode]. | |
* | |
* @pararm passcode the user's passcode | |
* @param storable the storable instance previously saved with [saveToPrefs] | |
* @return the raw byte key previously generated with [generateRandomKey] | |
*/ | |
fun getRawByteKey(passcode: CharArray, storable: Storable): ByteArray { | |
val aesWrappedKey = Base64.decode(storable.key, Base64.DEFAULT) | |
val iv = Base64.decode(storable.iv, Base64.DEFAULT) |
View get_storable.kt
/** | |
* Retrieves the [Storable] instance from prefs. | |
* | |
* @param context the caller's context | |
* @return the storable instance | |
*/ | |
fun getStorable(context: Context): Storable? { | |
val prefs = context.getSharedPreferences("database", | |
Context.MODE_PRIVATE) | |
val serialized = prefs.getString("key", null) |
View save_to_prefs.kt
/** | |
* Save the storable instance to preferences. | |
* | |
* @param storable a storable instance | |
*/ | |
fun saveToPrefs(context: Context, storable: Storable) { | |
val serialized = Gson().toJson(storable) | |
val prefs = context.getSharedPreferences("database", | |
Context.MODE_PRIVATE) | |
prefs.edit().putString("key", serialized).apply() |
View encrypt_and_store_key.kt
fun persistRawKey(userPasscode: CharArray) { | |
val storable = toStorable(rawByteKey, userPasscode) | |
// Implementation explained in next step | |
saveToPrefs(storable) | |
} | |
/** | |
* Returns a [Storable] instance with the db key encrypted using PBE. | |
* | |
* @param rawDbKey the raw database key |
NewerOlder