Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
cioccarellia committed Aug 10, 2018
2 parents cc04048 + ab949a6 commit 17ca8a1
Showing 1 changed file with 34 additions and 26 deletions.
60 changes: 34 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CryptoPrefs
[![Latest commit](https://jitpack.io/v/AndreaCioccarelli/CryptoPrefs.svg)](https://jitpack.io/#AndreaCioccarelli/CryptoPrefs)
[![Codacy badge](https://api.codacy.com/project/badge/Grade/b294eaf4988842c090584b1315a5f348)](https://www.codacy.com/app/cioccarelliandrea01/CryptoPrefs)
[![Latest Release](https://jitpack.io/v/AndreaCioccarelli/CryptoPrefs.svg)](https://jitpack.io/#AndreaCioccarelli/CryptoPrefs)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/b294eaf4988842c090584b1315a5f348)](https://www.codacy.com/app/cioccarelliandrea01/CryptoPrefs)
[![Android Arsenal]( https://img.shields.io/badge/Android%20Arsenal-CryptoPrefs-green.svg?style=flat )]( https://android-arsenal.com/details/1/7009 )
[![License](https://img.shields.io/hexpm/l/plug.svg)](https://github.com/AndreaCioccarelli/CryptoPrefs/blob/master/LICENSE)

Expand All @@ -27,11 +27,11 @@ dependencies {

## Usage
```kotlin
val prefs = CryptoPrefs(applicationContext, "CryptoFileName", "c29maWE=")
val prefs = CryptoPrefs(this, "CryptoFileName", "c29maWE=")
```
You need to pass 3 parameters in order to create an instance of the class CryptoPrefs:
- The context of your Activity/Fragment
- The file preferences name
- The file name, internally used to store the preferences
- Your secret key

**Warning #1:** this library supports (indirectly) multi-files and multi-keys operations; However remember that saving all the preferences to one single file is much easier and has got a better performance rate. View the [multi files and multi keys details](#multi)<br>
Expand All @@ -40,13 +40,13 @@ You need to pass 3 parameters in order to create an instance of the class Crypto

#### Set/Update values
```kotlin
prefs.put("crypto_age", 16)
prefs.put("crypto_age", 17)
```
This functions accepts 2 parameters, key and value, that are used to store the preference.
If an item with the matching key is found, its value will be overwritten. Else, a preference is created.

The `value` parameter is `Any` type (For Java programmers, Object), it means that it can be everything; however when you get back the value you will have to choose whether to parse it to String, Boolean, Int, Float, and so on and so forth.
If you need to store another type of variable you can consider the idea of converting it to String before storing in the preferences.
The `value` parameter is of `Any` type (For java programmers, Object), it means that it can be everything; however when you get back the value you will have to choose to parse it to String, Boolean, Int, Float, and so forth.
If you need to store another type of variable you can consider the idea of converting it to String before storing in the preferences. Even, you can create an extension function to make a custom parser (e.g. get and put JSON objects using gson).


#### Read values
Expand All @@ -58,8 +58,8 @@ val isMajor = prefs.getBoolean("crypto_is_major", false)
val roomNumber = prefs.getFloat("crypto_room_number", 107.0F)
val infinite = prefs.getLong("crypto_∞", 999999999999)
```
These functions accepts 2 parameters, key and default.
Key is used to search the preference into the file, and default is put in the matching key position and then returned if no item is matching with the given key.
These functions accepts 2 parameters, `key` and `default`.
Key is used to search the preference into the file, and default is put in the matching key position and then returned if no item is found with the given key.
This means that if you need to use and create an item you can do it in just one line.
```kotlin
val startCounter = prefs.getInt("start_count", 0) // Creates the field start_count and set it at 0
Expand All @@ -68,16 +68,16 @@ val startCounter = prefs.getInt("start_count", 0) // Creates the field start_cou
#### Batch operations
```kotlin
for (i in 1..10000) {
prefs.queue("$i", (i*i).toFloat())
prefs.queue("$i", (i*i).toLong())
}
prefs.apply()
```
Sometimes SharedPreferences are used to store a huge number of items and in these cases I/O operations can be cpu intensive and slow down your app, especially if done on the main thread.
Because of that, you can enqueue your modifications on the go just like normally using `put()`, but to actually apply them to the file you will have to call `apply()` once.
Remember that doing these I/O operations in a background/asynchronous thread is not a brilliant idea because them won't be available for immediate reading on the very next lines, but if you already are in a thread and you can't do other way it's ok
Sometimes SharedPreferences are used to store a huge number of values and in these cases I/O operations can be cpu intensive and slow down your app, since them are executed on the main thread.
Because of that, you should enqueue your modifications just like using `put()`, but actually apply them to the file with `apply()`.
Remember that doing these I/O operations in a background/asynchronous thread is not advisable, but if you already are are in a thread and you can't do other way it's ok.

**Warning #1:** calling `put()` automatically applies all queued modifications.<br>
**Warning #2:** `get` fetches the key on the file and not on the queue.
**Warning #1:** calling `put()` automatically applies all the queued modifications.<br>
**Warning #2:** `get` fetches the values on the file, and not on the modification queue.


#### All preferences lists
Expand All @@ -86,27 +86,27 @@ val bundle: Bundle = prefs.allPrefsBundle
val map: Map<String, String> = prefs.allPrefsMap
val list: ArrayList<Pair<String, String>> = prefs.allPrefsList
```
You can get all your SharedPreferences data and perform reading operations on them.
The default type provided by the android API is a Map, but here you have a little bit more of choice, so between a bundle and a list.
You can get all your SharedPreferences list and perform reading operations on them.
The default type provided by the android API is a Map, but here you have a little bit more of choice, so also between a bundle and a list of `Pair`s .


#### Remove
```kotlin
prefs.remove("pizza_with_pineapple")
```
You can remove at every time a value just passing its key to `remove()`.
You can remove a record from a file just passing its key to `remove()`. If no item with the matching key is found nothing happens.


#### Erase
```kotlin
prefs.erase()
```
This a simple wrap of the `clear()` function of the android standard library, so what it does is deleting the whole file content. Manage with caution.
This a simple wrap of the `clear()` function of the android standard library, so what it does is deleting the whole file content. Use with caution.


## Smart cast
A clean and fast approach is what this library aims to provide. I always found myself in java working with `String.valueOf()`, `Integer.parseInt()`, `Boolean.parseBoolean()` while reading SharedPreferences, and then I decided I didn't want to see that happen again.
Every argument you pass as value or default is `Any` type, so it can be everything. CryptoPrefs will convert it back to string for the encryption and eventually you will do the conversion from string to your target type.
A clean and fast approach is what this library aims to provide. I always found myself in java working with `String.valueOf()`, `Integer.parseInt()`, `Boolean.parseBoolean()` while reading SharedPreferences, and then I decided I didn't want to see that happen again with kotlin.
Every argument you pass as value or default is of `Any` type, so that it can be everything. CryptoPrefs will convert it back to string for the encryption and eventually you will do the conversion from string to your target type.

This is an example for a situation where you have a JSON response and you want to parse it later. You will find it also in the sample project.
```json
Expand All @@ -129,9 +129,18 @@ prefs.put("json_response", jsonErrorString)
val jsonFromPrefs = JSONObject(prefs.getString("json_response", ""))
```

Also, as said before, you can create extension functions using kotlin. It would simplify the way you interact with your preferences, because doing so you can store and fetch custom types for your specific app architecture. Let's suppose that you are using a class called `Pizza` to parse a json response with gson. To save it for offline use, you just have to write 1 extension function used to parse it.

```kotlin
fun CryptoPrefs.getPizza(key: String, default: String): Pizza {
val json = preferences.get(key, default).toString()
return Gson().fromJson(json, Pizza::class.java)
}
```


## <a name="multi"></a> Multi-files and multi-keys
I decided to not provide built-in support for multiple files because it would have impacted performances.
I decided to not provide built-in support for multiple files because it would have slightly impacted performances.
Instead, if you wish, you can have 2 instances and different filenames/keys for every file, that actually is the best solution for code style, logical division and performances.
Please keep in mind that:
- Saving a preference to one file won't make it available also on the other one
Expand All @@ -142,14 +151,14 @@ Please keep in mind that:
Even though this library is all about encryption, you still can operate with standard unencrypted preferences. Why?
- For the purpose of testing, for example if in your app you need to debug SharedPreferences and you want to see the effective data
- To provide compatibility with files that have been stored in the past without decryption
- To provide compatibility with files that have been created using android settings
- To provide compatibility with files that have been created using android settings, that does not use encryption

To do so, you have to initialize the instance like this
```kotlin
val prefs = CryptoPrefs(applicationContext, "CryptoFileName", "c29maWE=", false)
```

**Warning:** Remember than encrypted files cannot be read without a key and that a plain text file read with a key will throw an exception with a clear message: use that if you know what you're doing
**Warning:** Remember than encrypted files cannot be read without a key and that a plain text file read with a key will throw an exception with a clear message: use that if you know what you're doing is right


## SharedPreferences plain XML vs CryptoPrefs encrypted XML
Expand All @@ -171,8 +180,7 @@ If you wish a complete and detailed proof of concept with code examples you can

## Concept
Android default SharedPreferences APIs allows you to dynamically store some configuration data on your application internal (and private) storage.
With the time, android had become more popular and so many softwares were developed. The result is that secure informations, critical/sensitive
data and billing details are often stored there [without even a basic protection](https://medium.com/@andreacioccarelli/android-sharedpreferences-data-weakness-66a44f070e76).
With the time, android had become more popular and so many softwares were developed. The result is that secure informations, critical/sensitive data (and billing details) are often stored there [without even a basic protection](https://medium.com/@andreacioccarelli/android-sharedpreferences-data-weakness-66a44f070e76).
This library aims to terminate easy application hacking and security mechanisms bypass.

## License
Expand Down

0 comments on commit 17ca8a1

Please sign in to comment.