Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.3.31'
ext.kotlin_version = '1.3.41'
repositories {
google()
jcenter()

}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
classpath 'com.android.tools.build:gradle:3.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.novoda:bintray-release:0.9.1'
}
}

version = '0.9.0'

allprojects {
repositories {
google()
Expand Down
1 change: 1 addition & 0 deletions library-android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
57 changes: 57 additions & 0 deletions library-android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'com.novoda.bintray-release'

android {
compileSdkVersion 28

defaultConfig {
versionCode 1
versionName rootProject.version
minSdkVersion 14
targetSdkVersion 28
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}

dependencies {
implementation project(':library')
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "io.reactivex.rxjava2:rxjava:2.2.10"
implementation 'com.facebook.conceal:conceal:2.0.2'

implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
}

dependencies {
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.test:runner:1.2.0"
androidTestImplementation "androidx.test:rules:1.2.0"
androidTestImplementation "androidx.test.ext:junit-ktx:1.1.1"
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

def properties = project.rootProject.file('local.properties').withDataInputStream { stream ->
def props = new Properties()
props.load(stream)
props
}

publish {
userOrg 'elementsinteractive'
groupId 'nl.elements.objectstore.android'
artifactId 'objectstore-android'
publishVersion rootProject.version
uploadName 'ObjectStore Android'
desc 'Android specific implementations for ObjectStore'
website 'https://github.com/elementsinteractive/ObjectStore'

bintrayUser properties['BINTRAY_USER']
bintrayKey properties['BINTRAY_KEY']

dryRun false
override true
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package nl.elements.objectstore
package nl.elements.objectstore.android

import android.content.Context
import androidx.test.platform.app.InstrumentationRegistry
import nl.elements.objectstore.stores.PreferencesStore
import nl.elements.objectstore.ObjectStore
import nl.elements.objectstore.android.stores.PreferencesStore
import org.junit.After
import org.junit.Before
import org.junit.Test
Expand Down
1 change: 1 addition & 0 deletions library-android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest package="nl.elements.objectstore.android" />
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package nl.elements.objectstore.stores
package nl.elements.objectstore.android.stores

import android.content.ContentValues
import android.database.Cursor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package nl.elements.objectstore.stores
package nl.elements.objectstore.android.stores

import android.content.SharedPreferences
import android.util.Base64
Expand Down Expand Up @@ -117,10 +117,11 @@ private class StorePreferencesEditor(

override fun putFloat(key: String?, value: Float): SharedPreferences.Editor = put(key, value)

override fun putString(key: String?, value: String?): SharedPreferences.Editor = put(key, value)
override fun putString(key: String?, value: String?): SharedPreferences.Editor = put(key, value!!)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't value be non null then? Otherwise don't force unwrap?

Copy link
Contributor Author

@Lamartio Lamartio Sep 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment the lib anticipates a value to be non-null whenever it got requested. When requesting you always should check wether there is a value first.

I got some feedback on the and I would like to refactor to 'Maybe'. But that would be a new PR.


override fun putStringSet(key: String?, values: MutableSet<String>?): SharedPreferences.Editor = put(key, values)
override fun putStringSet(key: String?, values: MutableSet<String>?): SharedPreferences.Editor =
put(key, values ?: emptySet())

private fun <T> put(key: String?, value: T) = apply { key?.let { store[key] = value!! } }
private fun <T : Any> put(key: String?, value: T) = apply { key?.let { store[it] = value } }

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package nl.elements.objectstore.transformers
package nl.elements.objectstore.android.transformers

import com.facebook.crypto.Crypto
import com.facebook.crypto.Entity
Expand All @@ -18,4 +18,4 @@ class ConcealTransformer(private val crypto: Crypto) : Transformer {
.encrypt(input.readBytes(), Entity.create(key))
.let(output::write)

}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package nl.elements.objectstore
package nl.elements.objectstore.android

import android.content.Context
import android.content.SharedPreferences
import android.graphics.Bitmap
import com.facebook.android.crypto.keychain.AndroidConceal
import com.facebook.android.crypto.keychain.SharedPrefsBackedKeyChain
import com.facebook.crypto.CryptoConfig
import nl.elements.objectstore.*
import nl.elements.objectstore.stores.DirectoryStore
import nl.elements.objectstore.stores.PreferencesStore
import nl.elements.objectstore.transformers.ConcealTransformer
import nl.elements.objectstore.android.stores.PreferencesStore
import nl.elements.objectstore.android.transformers.ConcealTransformer
import java.io.File
import java.util.prefs.Preferences

fun example(store: ObjectStore) {
if ("id" !in store)
Expand Down
43 changes: 10 additions & 33 deletions library/build.gradle
Original file line number Diff line number Diff line change
@@ -1,42 +1,15 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'java-library'
apply plugin: 'kotlin'
apply plugin: 'com.novoda.bintray-release'

android {
compileSdkVersion 28

defaultConfig {
versionCode 1
versionName '0.9.0'
minSdkVersion 14
targetSdkVersion 28
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}

dependencies {
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "io.reactivex.rxjava2:rxjava:2.2.9"
implementation 'com.facebook.conceal:conceal:2.0.2'

implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.core:core-ktx:1.0.2'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "io.reactivex.rxjava2:rxjava:2.2.10"
}

dependencies {
testImplementation 'junit:junit:4.12'
testImplementation "androidx.test:core:1.2.0"
testImplementation "org.jetbrains.kotlin:kotlin-test:1.3.31"
testImplementation "androidx.test.ext:junit-ktx:1.1.1"

androidTestImplementation "org.jetbrains.kotlin:kotlin-test:1.3.31"

androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.test:runner:1.2.0"
androidTestImplementation "androidx.test:rules:1.2.0"
androidTestImplementation "androidx.test.ext:junit-ktx:1.1.1"
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
}

def properties = project.rootProject.file('local.properties').withDataInputStream { stream ->
Expand All @@ -49,7 +22,7 @@ publish {
userOrg 'elementsinteractive'
groupId 'nl.elements.objectstore'
artifactId 'objectstore'
publishVersion android.defaultConfig.versionName
publishVersion rootProject.version
uploadName 'ObjectStore'
desc 'Convenient interface for persisting objects.'
website 'https://github.com/elementsinteractive/ObjectStore'
Expand All @@ -60,3 +33,7 @@ publish {
dryRun false
override true
}


sourceCompatibility = "7"
targetCompatibility = "7"
4 changes: 0 additions & 4 deletions library/src/main/AndroidManifest.xml

This file was deleted.

44 changes: 44 additions & 0 deletions library/src/main/java/nl/elements/objectstore/ObjectStore+utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package nl.elements.objectstore

import nl.elements.objectstore.utils.unknownNamespaceObjectStore
import nl.elements.objectstore.utils.withNamespace

private const val DEFAULT_DELIMITER = ":"

/**
* Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
*
* Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
*/

fun Array<Pair<String, ObjectStore>>.reduceWithNamespace(delimiter: String = DEFAULT_DELIMITER) =
asSequence().reduceWithNamespace(delimiter)

/**
* Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
*
* Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
*/

fun Map<String, ObjectStore>.reduceWithNamespace(delimiter: String = DEFAULT_DELIMITER): ObjectStore =
toList().asSequence().reduceWithNamespace(delimiter)

/**
* Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
*
* Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
*/

fun Iterable<Pair<String, ObjectStore>>.reduceWithNamespace(delimiter: String = DEFAULT_DELIMITER) =
asSequence().reduceWithNamespace(delimiter)

/**
* Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
*
* Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
*/

fun Sequence<Pair<String, ObjectStore>>.reduceWithNamespace(delimiter: String = DEFAULT_DELIMITER) =
fold(unknownNamespaceObjectStore) { next, (prefix, store) ->
store.withNamespace("$prefix$delimiter", next)
}
104 changes: 3 additions & 101 deletions library/src/main/java/nl/elements/objectstore/ObjectStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import io.reactivex.Observable
import io.reactivex.ObservableSource
import io.reactivex.Observer
import io.reactivex.subjects.PublishSubject
import nl.elements.objectstore.utils.UnknownNamespaceObjectStore
import nl.elements.objectstore.utils.unknownNamespaceObjectStore
import nl.elements.objectstore.utils.withNamespace
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.io.OutputStream
Expand Down Expand Up @@ -61,104 +64,3 @@ fun ObjectStore.writeToBytes(key: String, value: Any): ByteArray =
ByteArrayOutputStream()
.also { it.write(key, value) }
.toByteArray()

/// Utils for reducing `ObjectStore` which prefixes each key with the given `String` and delimiter

private const val DEFAULT_DELIMITER = ":"

/**
* Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
*
* Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
*/

fun Array<Pair<String, ObjectStore>>.reduceWithNamespace(delimiter: String = DEFAULT_DELIMITER) =
asSequence().reduceWithNamespace(delimiter)

/**
* Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
*
* Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
*/

fun Map<String, ObjectStore>.reduceWithNamespace(delimiter: String = DEFAULT_DELIMITER): ObjectStore =
toList().asSequence().reduceWithNamespace(delimiter)

/**
* Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
*
* Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
*/

fun Iterable<Pair<String, ObjectStore>>.reduceWithNamespace(delimiter: String = DEFAULT_DELIMITER) =
asSequence().reduceWithNamespace(delimiter)

/**
* Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
*
* Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
*/

fun Sequence<Pair<String, ObjectStore>>.reduceWithNamespace(delimiter: String = DEFAULT_DELIMITER) =
fold(unknownNamespaceObjectStore()) { next, (prefix, store) ->
store.withNamespace("$prefix$delimiter", next)
}

private fun ObjectStore.withNamespace(namespace: String, next: ObjectStore): ObjectStore =
object : ObjectStore {

val store = this@withNamespace

override val converter: Converter = store.converter
override val transformer: Transformer = store.transformer
override val keys: Set<String>
get() = store.keys.map { namespace + it }.toMutableSet().apply { addAll(next.keys) }

override fun <T : Any> get(key: String): T =
key.removeNamespace()?.let(store::get) ?: next[key]

override fun contains(key: String): Boolean =
key.removeNamespace()?.let(store::contains) ?: next.contains(key)

override fun set(key: String, value: Any) =
key.removeNamespace()?.let { store[it] = value } ?: next.set(key, value)

override fun remove(key: String) = key.removeNamespace()?.let(store::remove) ?: next.remove(key)

override fun subscribe(observer: Observer<in ObjectStore.Event>) =
store
.toObservable()
.map(::prependNamespace)
.let { Observable.merge(it, next) }
.subscribe(observer)

private fun String.removeNamespace(): String? = removePrefix(namespace).takeIf { it != this@removeNamespace }

private fun prependNamespace(event: ObjectStore.Event): ObjectStore.Event =
when (event) {
is ObjectStore.Event.Updated -> ObjectStore.Event.Updated(namespace + event.key)
is ObjectStore.Event.Removed -> ObjectStore.Event.Removed(namespace + event.key)
}

}

private fun unknownNamespaceObjectStore(): ObjectStore = object : ObjectStore {

override val converter: Converter = Converter.DEFAULT
override val transformer: Transformer = Transformer.DEFAULT
override val keys: Set<String> = emptySet()

override fun set(key: String, value: Any) = throw UnknownNamespaceException(key)

override fun remove(key: String) = throw UnknownNamespaceException(key)

override fun <T : Any> get(key: String): T = throw UnknownNamespaceException(key)

override fun contains(key: String): Boolean = throw UnknownNamespaceException(key)

override fun subscribe(observer: Observer<in ObjectStore.Event>) {
}

}

class UnknownNamespaceException internal constructor(key: String) : Exception(key)
Loading