diff --git a/README.MD b/README.MD
index cfa90ee..7325503 100644
--- a/README.MD
+++ b/README.MD
@@ -2,12 +2,13 @@
-
+
# Transport ETA
-[![License Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=true)](http://www.apache.org/licenses/LICENSE-2.0)
+
+![production version](https://img.shields.io/badge/playstore-unreleased-lightgrey.svg?style=true)
![minSdkVersion 19](https://img.shields.io/badge/minSdkVersion-19-yellow.svg?style=true)
-![compileSdkVersion 27](https://img.shields.io/badge/compileSdkVersion-28-green.svg?style=true)
+![compileSdkVersion 28](https://img.shields.io/badge/compileSdkVersion-28-green.svg?style=true)
[![Build Status](https://app.bitrise.io/app/f75916759d698e6e/status.svg?token=nCaNQBZcMNPMckWWwn8Gxg&branch=develop)](https://app.bitrise.io/app/f75916759d698e6e)
[![codecov](https://codecov.io/gh/JoaquimLey/transport-eta/branch/develop/graph/badge.svg)](https://codecov.io/gh/JoaquimLey/transport-eta)
@@ -28,8 +29,8 @@ An utility app using an SMS based service (or the web) to request a more precise
## Why 🤔
Since I'm always working on some side-projects, I decided to document the progress live on a coding stream, this way I'll force myself into completing, while giving something back to a community that already thought me so much.
-## Stream log
-##### Come and say Hi 👋, join me at [twitch.tv/joaquimley](http:twitch.tv/joaquimley): watch, help, and learn as I develop and make mistakes
+## Stream PR log
+##### Come and say Hi 👋 and be part of the develpoment live at [twitch.tv/joaquimley](http:twitch.tv/joaquimley)
- Ep.1: https://github.com/JoaquimLey/transport-eta/tree/4642c5fd6af9de3b258b179d0a7a8c69195fa293
@@ -61,7 +62,9 @@ Since I'm always working on some side-projects, I decided to document the progre
- Ep.15: https://github.com/JoaquimLey/transport-eta/pull/70
-- Ep.15: https://github.com/JoaquimLey/transport-eta/pull/74
+- Ep.16: https://github.com/JoaquimLey/transport-eta/pull/74
+
+- Ep.17: https://github.com/JoaquimLey/transport-eta/pull/81
## About the author
@@ -92,7 +95,7 @@ Personal website:
#### Important references
-It would take substantially more time to setup this project without this reference projects
+It would take substantially more time to setup this project without this reference projects, so a special thanks to:
- https://github.com/bufferapp/clean-architecture-components-boilerplate
diff --git a/transport-eta-android/build.gradle b/transport-eta-android/build.gradle
index 0a5d97a..b735135 100644
--- a/transport-eta-android/build.gradle
+++ b/transport-eta-android/build.gradle
@@ -4,12 +4,10 @@ buildscript {
addRepos(repositories)
dependencies {
classpath deps.kotlin.plugin
+ classpath deps.kotlin.serialization_plugin
classpath deps.navigation.safe_args
classpath deps.android_gradle_plugin
}
- repositories {
- google()
- }
}
allprojects {
diff --git a/transport-eta-android/data-sharedpreferences/build.gradle b/transport-eta-android/data-sharedpreferences/build.gradle
index 40a8682..1314aba 100644
--- a/transport-eta-android/data-sharedpreferences/build.gradle
+++ b/transport-eta-android/data-sharedpreferences/build.gradle
@@ -1,5 +1,6 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlinx-serialization'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
@@ -11,6 +12,12 @@ android {
testInstrumentationRunner "com.joaquimley.transporteta.ui.test.TestRunner"
}
+ testOptions {
+ unitTests {
+ includeAndroidResources = true
+ }
+ }
+
packagingOptions {
exclude 'LICENSE.txt'
exclude 'META-INF/DEPENDENCIES'
@@ -90,6 +97,7 @@ dependencies {
implementation deps.rx.android
// Kotlin
implementation deps.kotlin.rx
+ implementation deps.kotlin.serialization
implementation deps.kotlin.stdlib
/***********
@@ -102,6 +110,8 @@ dependencies {
testImplementation deps.mockito.kotlin
testImplementation deps.mockito.inline
testImplementation deps.lifecycle.testing
+ testImplementation deps.robolectric
+
// Resolve conflicts between main and local unit tests
testImplementation deps.androidx.annotation
}
\ No newline at end of file
diff --git a/transport-eta-android/data-sharedpreferences/proguard-rules.pro b/transport-eta-android/data-sharedpreferences/proguard-rules.pro
index f1b4245..dcbf669 100644
--- a/transport-eta-android/data-sharedpreferences/proguard-rules.pro
+++ b/transport-eta-android/data-sharedpreferences/proguard-rules.pro
@@ -5,13 +5,6 @@
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
@@ -19,3 +12,13 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
+
+-keepattributes *Annotation*, InnerClasses
+-dontnote kotlinx.serialization.SerializationKt
+-keep,includedescriptorclasses class com.yourcompany.yourpackage.**$$serializer { *; } # <-- change package name to your app's
+-keepclassmembers class com.yourcompany.yourpackage.** { # <-- change package name to your app's
+ *** Companion;
+}
+-keepclasseswithmembers class com.yourcompany.yourpackage.** { # <-- change package name to your app's
+ kotlinx.serialization.KSerializer serializer(...);
+}
diff --git a/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/FrameworkLocalStorageImpl.kt b/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/FrameworkLocalStorageImpl.kt
index 6888c68..81eae37 100644
--- a/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/FrameworkLocalStorageImpl.kt
+++ b/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/FrameworkLocalStorageImpl.kt
@@ -1,13 +1,31 @@
package com.joaquimley.transporteta.sharedpreferences
+import android.content.SharedPreferences
import com.joaquimley.transporteta.data.model.TransportEntity
import com.joaquimley.transporteta.data.source.FrameworkLocalStorage
+import com.joaquimley.transporteta.sharedpreferences.mapper.SharedPrefTransportMapper
+import com.joaquimley.transporteta.sharedpreferences.model.SharedPrefTransport
import io.reactivex.Completable
import io.reactivex.Single
+import io.reactivex.subjects.PublishSubject
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class FrameworkLocalStorageImpl @Inject constructor(private val sharedPreferences: SharedPreferences,
+ private val mapper: SharedPrefTransportMapper) : FrameworkLocalStorage {
+
+ private val sharedPreferencesObservable: PublishSubject> = PublishSubject.create()
+
+ init {
+ loadAll()
+ observeSharedPreferencesChanges()
+ }
-class FrameworkLocalStorageImpl: FrameworkLocalStorage {
override fun saveTransport(transportEntity: TransportEntity): Completable {
- return Completable.complete()
+ return Completable.fromAction {
+ saveToSharedPrefs(mapper.toSharedPref(transportEntity))
+ }
}
override fun deleteTransport(transportEntityId: String): Completable {
@@ -15,17 +33,59 @@ class FrameworkLocalStorageImpl: FrameworkLocalStorage {
}
override fun getTransport(transportEntityId: String): Single {
- return Single.just(TransportEntity("hi", "mock",2, "el", true,"bus"))
+ return Single.just(TransportEntity("hi", "mock", 2, "el", true, "bus"))
}
override fun getAll(): Single> {
val list = mutableListOf()
- list.add(TransportEntity("hi", "mock",2, "latestEta 12324", true,"bus"))
- list.add(TransportEntity("there", "mock",23, "latestEta 123", true,"bus"))
+ list.add(TransportEntity("hi", "mock", 2, "latestEta 12324", true, "bus"))
+ list.add(TransportEntity("there", "mock", 23, "latestEta 123", true, "bus"))
+ list.add(TransportEntity("world", "mock", 25, "latestEta 12454", true, "bus"))
+ list.add(TransportEntity("sup", "mock", 29, "latestEta 675", true, "bus"))
return Single.just(list)
}
override fun clearAll(): Completable {
- return Completable.complete()
+ return Completable.fromAction {
+
+ }
+ }
+
+ private fun observeSharedPreferencesChanges() {
+ sharedPreferences.registerOnSharedPreferenceChangeListener { _, key ->
+ if (key != SHARED_PREFERENCES_LAST_UPDATED) {
+ loadAll()
+ }
+ }
+ }
+
+ private fun loadAll() {
+ val data = mutableListOf()
+ getFromSharedPrefs(Slot.ONE)?.let { data.add(mapper.toEntity(it)) }
+ getFromSharedPrefs(Slot.TWO)?.let { data.add(mapper.toEntity(it)) }
+ getFromSharedPrefs(Slot.THREE)?.let { data.add(mapper.toEntity(it)) }
+ sharedPreferencesObservable.onNext(data)
+ }
+
+ private fun saveToSharedPrefs(sharedPrefTransport: SharedPrefTransport) {
+ sharedPreferences.edit()
+ .putString(Slot.ONE.name, mapper.toCacheString(sharedPrefTransport))
+ .apply()
+ }
+
+ private fun getFromSharedPrefs(slot: Slot): SharedPrefTransport? {
+ sharedPreferences.getString(slot.name, null)?.let {
+ return mapper.fromCacheString(it)
+ } ?: return null
+ }
+
+ companion object {
+ private const val SHARED_PREFERENCES_LAST_UPDATED = "sharedpreferences.last_updated"
+ }
+
+ enum class Slot(name: String) {
+ ONE("transport_eta_fav_1"),
+ TWO("transport_eta_fav_2"),
+ THREE("transport_eta_fav_3"),
}
-}
\ No newline at end of file
+}
diff --git a/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/mapper/SharedPrefMapper.kt b/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/mapper/SharedPrefMapper.kt
deleted file mode 100644
index cb2d8f8..0000000
--- a/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/mapper/SharedPrefMapper.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.joaquimley.transporteta.sharedpreferences.mapper
-
-class SharedPrefMapper {
-
- // TODO
-}
\ No newline at end of file
diff --git a/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/mapper/SharedPrefTransportMapper.kt b/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/mapper/SharedPrefTransportMapper.kt
new file mode 100644
index 0000000..7214ba8
--- /dev/null
+++ b/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/mapper/SharedPrefTransportMapper.kt
@@ -0,0 +1,32 @@
+package com.joaquimley.transporteta.sharedpreferences.mapper
+
+import com.joaquimley.transporteta.data.model.TransportEntity
+import com.joaquimley.transporteta.sharedpreferences.model.SharedPrefTransport
+import kotlinx.serialization.json.JSON
+
+class SharedPrefTransportMapper {
+
+ fun toCacheString(from: SharedPrefTransport): String {
+ return JSON.stringify(from)
+ }
+
+ fun fromCacheString(from: String): SharedPrefTransport {
+ return JSON.parse(from)
+ }
+
+ fun toSharedPref(from: List): List {
+ return from.map { toSharedPref(it) }
+ }
+
+ fun toSharedPref(from: TransportEntity): SharedPrefTransport {
+ return SharedPrefTransport(from.id, from.name, from.code, from.latestEta, from.isFavorite, from.type, 1312, "")
+ }
+
+ fun toEntity(from: List): List {
+ return from.map { toEntity(it) }
+ }
+
+ fun toEntity(from: SharedPrefTransport): TransportEntity {
+ return TransportEntity(from.id, from.name, from.code, from.latestEta, from.isFavorite, from.type)
+ }
+}
\ No newline at end of file
diff --git a/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/model/SharedPrefTransport.kt b/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/model/SharedPrefTransport.kt
index 5630f72..7fd7241 100644
--- a/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/model/SharedPrefTransport.kt
+++ b/transport-eta-android/data-sharedpreferences/src/main/java/com/joaquimley/transporteta/sharedpreferences/model/SharedPrefTransport.kt
@@ -1,6 +1,6 @@
package com.joaquimley.transporteta.sharedpreferences.model
-class SharedPrefTransport {
-
- // TODO
-}
\ No newline at end of file
+@kotlinx.serialization.Serializable
+data class SharedPrefTransport(val id: String, val name: String, val code: Int, val latestEta: String,
+ val isFavorite: Boolean = false, val type: String, val lastUpdated: Long,
+ val slot: String)
\ No newline at end of file
diff --git a/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/ExampleUnitTest.java b/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/ExampleUnitTest.java
deleted file mode 100644
index 031e2fa..0000000
--- a/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.joaquimley.transporteta.sharedpreferences;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/FrameworkLocalStorageTest.kt b/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/FrameworkLocalStorageTest.kt
new file mode 100644
index 0000000..7839ac2
--- /dev/null
+++ b/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/FrameworkLocalStorageTest.kt
@@ -0,0 +1,148 @@
+package com.joaquimley.transporteta.sharedpreferences
+
+import android.content.SharedPreferences
+import com.joaquimley.transporteta.data.model.TransportEntity
+import com.joaquimley.transporteta.data.source.FrameworkLocalStorage
+import com.joaquimley.transporteta.sharedpreferences.factory.SharedPrefDataFactory
+import com.joaquimley.transporteta.sharedpreferences.factory.SharedPrefTransportFactory
+import com.joaquimley.transporteta.sharedpreferences.mapper.SharedPrefTransportMapper
+import com.joaquimley.transporteta.sharedpreferences.model.SharedPrefTransport
+import com.nhaarman.mockitokotlin2.*
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+
+
+//@RunWith(RobolectricTestRunner::class)
+class FrameworkLocalStorageTest {
+
+
+ private val SHARED_PREFERENCES_NAME = "com.joaquimley.transporteta.sharedpreferences"
+
+ private val robot = Robot()
+
+// private val robolectricSharedPreferences = RuntimeEnvironment.application.applicationContext
+// .getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
+
+ private val mockSharedPreferences = mock()
+ private val mockMapper = mock()
+
+ private lateinit var frameworkLocalStorage: FrameworkLocalStorage
+
+
+ @Before
+ fun setup() {
+ frameworkLocalStorage = FrameworkLocalStorageImpl(mockSharedPreferences, mockMapper)
+// frameworkLocalStorage = FrameworkLocalStorageImpl(robolectricSharedPreferences, mockMapper)
+ }
+
+ @After
+ fun tearDown() {
+
+ }
+
+ @Test
+ fun allDataIsFetchedAtStartup() {
+ // Assemble
+ val modelStringOne = robot.stubSharedPrefGetFromSlotSuccess(slot = FrameworkLocalStorageImpl.Slot.ONE)
+ val modelStringTwo = robot.stubSharedPrefGetFromSlotSuccess(slot = FrameworkLocalStorageImpl.Slot.TWO)
+ val modelStringThree = robot.stubSharedPrefGetFromSlotSuccess(slot = FrameworkLocalStorageImpl.Slot.THREE)
+
+ robot.stubMapperFromStringToModel(modelStringOne)
+ robot.stubMapperFromStringToModel(modelStringTwo)
+ robot.stubMapperFromStringToModel(modelStringThree)
+
+ // Act
+ // Nothing <->
+ // Assert
+ verify(mockSharedPreferences, times(1)).getString(FrameworkLocalStorageImpl.Slot.ONE.name, null)
+ verify(mockSharedPreferences, times(1)).getString(FrameworkLocalStorageImpl.Slot.TWO.name, null)
+ verify(mockSharedPreferences, times(1)).getString(FrameworkLocalStorageImpl.Slot.THREE.name, null)
+
+
+// val modelStringOne = SharedPrefTransportFactory.makeSharedPrefTransportString()
+// val modelStringTwo = SharedPrefTransportFactory.makeSharedPrefTransportString()
+// val modelStringThree = SharedPrefTransportFactory.makeSharedPrefTransportString()
+
+// robot.stubMapperFromStringToModel(modelStringOne)
+// robot.stubMapperFromStringToModel(modelStringTwo)
+// robot.stubMapperFromStringToModel(modelStringThree)
+ // Act
+ // Nothing <->
+ // Assert
+// verify(robolectricSharedPreferences, times(1)).getString(FrameworkLocalStorageImpl.Slot.ONE.name, null)
+// verify(robolectricSharedPreferences, times(1)).getString(FrameworkLocalStorageImpl.Slot.TWO.name, null)
+// verify(robolectricSharedPreferences, times(1)).getString(FrameworkLocalStorageImpl.Slot.THREE.name, null)
+ }
+
+ @Test
+ fun sharedPreferencesChangesAreObservedAtStartup() {
+ // Assemble
+ // Nothing <->
+
+ // Act
+ // Nothing <->
+
+ // Assert
+// verify(robolectricSharedPreferences, times(1)).registerOnSharedPreferenceChangeListener(any())
+ verify(mockSharedPreferences, times(1)).registerOnSharedPreferenceChangeListener(any())
+ }
+
+ @Test
+ @Ignore("Lacking SharedPreferences mocking/roboeletric")
+ fun saveTransportCompletes() {
+ // Assemble
+ val stubEntity = SharedPrefTransportFactory.makeTransportEntity()
+ // Act
+ val testObserver = frameworkLocalStorage.saveTransport(stubEntity).test()
+ // Assert
+ testObserver.assertComplete()
+ }
+
+ @Test
+ @Ignore("Lacking SharedPreferences mocking/roboeletric")
+ fun saveTransportCallsCorrectMethodOnSharedPrefs() {
+ // Assemble
+ val stubEntity = SharedPrefTransportFactory.makeTransportEntity()
+ val stubModel = robot.stubMapperFromEntityToModel(stubEntity)
+ val stubbedString = robot.stubMapperFromModelToString(stubModel)
+ robot.stubSharedPrefGetFromSlotSuccess(stubbedString, FrameworkLocalStorageImpl.Slot.ONE)
+ // Act
+ frameworkLocalStorage.saveTransport(stubEntity)
+ // Assert
+// verify(robolectricSharedPreferences, times(1)).edit().putString(any(), stubbedString).apply()
+ verify(mockSharedPreferences, times(1)).edit().putString(any(), stubbedString).apply()
+ }
+
+
+ inner class Robot {
+
+
+ fun stubSharedPrefSaveToSlotSuccess(sharedPrefTransportString: String = SharedPrefTransportFactory.makeSharedPrefTransportString(), slot: FrameworkLocalStorageImpl.Slot): String {
+ whenever(mockSharedPreferences.edit().putString(slot.name, sharedPrefTransportString)).then { sharedPrefTransportString }
+ return sharedPrefTransportString
+ }
+
+ fun stubSharedPrefGetFromSlotSuccess(sharedPrefTransportString: String = SharedPrefTransportFactory.makeSharedPrefTransportString(), slot: FrameworkLocalStorageImpl.Slot): String {
+ whenever(mockSharedPreferences.getString(slot.name, null)).then { sharedPrefTransportString }
+ return sharedPrefTransportString
+ }
+
+ fun stubMapperFromModelToString(sharedPrefTransport: SharedPrefTransport = SharedPrefTransportFactory.makeSharedPrefTransport(), sharedPrefTransportString: String = SharedPrefDataFactory.randomUuid()): String {
+ whenever(mockMapper.toCacheString(sharedPrefTransport)).then { sharedPrefTransportString }
+ return sharedPrefTransportString
+ }
+
+ fun stubMapperFromStringToModel(sharedPrefTransportString: String, sharedPrefTransport: SharedPrefTransport = SharedPrefTransportFactory.makeSharedPrefTransport()): SharedPrefTransport {
+ whenever(mockMapper.fromCacheString(sharedPrefTransportString)).then { sharedPrefTransport }
+ return sharedPrefTransport
+ }
+
+ fun stubMapperFromEntityToModel(transportEntity: TransportEntity = SharedPrefTransportFactory.makeTransportEntity(), sharedPrefTransport: SharedPrefTransport = SharedPrefTransportFactory.makeSharedPrefTransport()): SharedPrefTransport {
+ whenever(mockMapper.toSharedPref(transportEntity)).then { sharedPrefTransport }
+ return sharedPrefTransport
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/factory/SharedPrefDataFactory.kt b/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/factory/SharedPrefDataFactory.kt
new file mode 100644
index 0000000..56ff633
--- /dev/null
+++ b/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/factory/SharedPrefDataFactory.kt
@@ -0,0 +1,26 @@
+package com.joaquimley.transporteta.sharedpreferences.factory
+
+import java.util.concurrent.ThreadLocalRandom
+
+/**
+ * Factory class for data instances
+ */
+class SharedPrefDataFactory {
+
+ companion object Factory {
+
+ fun randomInt(): Int {
+ return ThreadLocalRandom.current().nextInt(0, 1000 + 1)
+ }
+
+ fun randomLong(): Long {
+ return randomInt().toLong()
+ }
+
+ fun randomUuid(): String {
+ return java.util.UUID.randomUUID().toString()
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/factory/SharedPrefTransportFactory.kt b/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/factory/SharedPrefTransportFactory.kt
new file mode 100644
index 0000000..9a26bf1
--- /dev/null
+++ b/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/factory/SharedPrefTransportFactory.kt
@@ -0,0 +1,47 @@
+package com.joaquimley.transporteta.sharedpreferences.factory
+
+import com.joaquimley.transporteta.sharedpreferences.factory.SharedPrefDataFactory.Factory.randomInt
+import com.joaquimley.transporteta.sharedpreferences.factory.SharedPrefDataFactory.Factory.randomLong
+import com.joaquimley.transporteta.sharedpreferences.factory.SharedPrefDataFactory.Factory.randomUuid
+import com.joaquimley.transporteta.data.model.TransportEntity
+import com.joaquimley.transporteta.sharedpreferences.FrameworkLocalStorageImpl
+import com.joaquimley.transporteta.sharedpreferences.model.SharedPrefTransport
+
+/**
+ * Factory class for [SharedPrefTransport] related instances
+ */
+class SharedPrefTransportFactory {
+
+ companion object Factory {
+
+ fun makeSharedPrefTransportList(count: Int, isFavorite: Boolean = false, type: String = randomUuid()): List {
+ val transports = mutableListOf()
+ repeat(count) {
+ transports.add(makeSharedPrefTransport(isFavorite, type))
+ }
+ return transports
+ }
+
+ fun makeSharedPrefTransport(isFavorite: Boolean = false, type: String = "bus", code: Int = randomInt(), id: String = randomUuid(), slot: FrameworkLocalStorageImpl.Slot = FrameworkLocalStorageImpl.Slot.ONE): SharedPrefTransport {
+ return SharedPrefTransport(id, randomUuid(), code, randomUuid(), isFavorite, type, randomLong(), slot.name)
+ }
+
+ fun makeSharedPrefTransportString(id: String = randomUuid(), transportName: String = randomUuid(), code: Int = randomInt(), latestEta: String = randomUuid(), isFavorite: Boolean = true, type: String = "bus", lastUpdated: Long = randomLong(), slot: String = "ONE"): String {
+ return "{\"id\":\"$id\",\"name\":\"$transportName\",\"code\":$code,\"latestEta\":\"$latestEta\",\"isFavorite\":$isFavorite,\"type\":\"$type\",\"lastUpdated\":$lastUpdated,\"slot\":\"$slot\"}"
+ }
+
+ fun makeTransportEntityList(count: Int, isFavorite: Boolean = false, type: String = randomUuid()): List {
+ val transports = mutableListOf()
+ repeat(count) {
+ transports.add(makeTransportEntity(isFavorite, type))
+ }
+ return transports
+ }
+
+ fun makeTransportEntity(isFavorite: Boolean = false, type: String = randomUuid(), code: Int = randomInt(), id: String = randomUuid()): TransportEntity {
+ return TransportEntity(id, randomUuid(), code, randomUuid(), isFavorite, type)
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/mapper/SharedPrefTransportMapperTest.kt b/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/mapper/SharedPrefTransportMapperTest.kt
new file mode 100644
index 0000000..e7c403e
--- /dev/null
+++ b/transport-eta-android/data-sharedpreferences/src/test/java/com/joaquimley/transporteta/sharedpreferences/mapper/SharedPrefTransportMapperTest.kt
@@ -0,0 +1,115 @@
+package com.joaquimley.transporteta.sharedpreferences.mapper
+
+import com.joaquimley.transporteta.data.model.TransportEntity
+import com.joaquimley.transporteta.sharedpreferences.factory.SharedPrefTransportFactory
+import com.joaquimley.transporteta.sharedpreferences.model.SharedPrefTransport
+import org.junit.Before
+import org.junit.Test
+
+
+class SharedPrefTransportMapperTest {
+
+ private val robot = Robot()
+ private lateinit var mapper: SharedPrefTransportMapper
+
+ @Before
+ fun setup() {
+ mapper = SharedPrefTransportMapper()
+ }
+
+ @Test
+ fun fromCacheStringToModel() {
+ // Assemble
+ val stubbedSharedPrefTransport = SharedPrefTransportFactory.makeSharedPrefTransport()
+ val stringCounterPart = SharedPrefTransportFactory.makeSharedPrefTransportString(stubbedSharedPrefTransport.id, stubbedSharedPrefTransport.name,
+ stubbedSharedPrefTransport.code, stubbedSharedPrefTransport.latestEta, stubbedSharedPrefTransport.isFavorite,
+ stubbedSharedPrefTransport.type, stubbedSharedPrefTransport.lastUpdated, stubbedSharedPrefTransport.slot)
+ // Act
+ val modelMappedFromString = mapper.fromCacheString(stringCounterPart)
+
+ // Assert
+ assert(robot.areItemsTheSame(modelMappedFromString, stubbedSharedPrefTransport))
+ }
+
+ @Test
+ fun fromModelToCacheString() {
+ // Assemble
+ val stubbedSharedPrefTransport = SharedPrefTransportFactory.makeSharedPrefTransport()
+ val stringCounterPart = SharedPrefTransportFactory.makeSharedPrefTransportString(stubbedSharedPrefTransport.id, stubbedSharedPrefTransport.name,
+ stubbedSharedPrefTransport.code, stubbedSharedPrefTransport.latestEta, stubbedSharedPrefTransport.isFavorite,
+ stubbedSharedPrefTransport.type, stubbedSharedPrefTransport.lastUpdated, stubbedSharedPrefTransport.slot)
+ // Act
+ val mappedString = mapper.toCacheString(stubbedSharedPrefTransport)
+
+ // Assert
+ assert(robot.areItemsTheSame(mappedString, stringCounterPart))
+ }
+
+ @Test
+ fun fromEntityToModel() {
+ // Assemble
+ val stubbed = SharedPrefTransportFactory.makeTransportEntity()
+ // Act
+ val mapped = mapper.toSharedPref(stubbed)
+ // Assert
+ assert(robot.areItemsTheSame(mapped, stubbed))
+ }
+
+ @Test
+ fun fromEntityListToModelList() {
+ // Assemble
+ val stubbed = SharedPrefTransportFactory.makeTransportEntityList(5)
+ // Act
+ val mapped = mapper.toSharedPref(stubbed)
+ // Assert
+ assert(robot.areItemsTheSame(mapped, stubbed))
+ }
+
+ @Test
+ fun fromModelToEntity() {
+ // Assemble
+ val stubbed = SharedPrefTransportFactory.makeSharedPrefTransport()
+ // Act
+ val mapped = mapper.toEntity(stubbed)
+ // Assert
+ assert(robot.areItemsTheSame(stubbed, mapped))
+ }
+
+ @Test
+ fun fromModelListToEntityList() {
+ // Assemble
+ val stubbed = SharedPrefTransportFactory.makeSharedPrefTransportList(5)
+ // Act
+ val mapped = mapper.toEntity(stubbed)
+ // Assert
+ assert(robot.areItemsTheSame(stubbed, mapped))
+ }
+
+ inner class Robot {
+
+ fun areItemsTheSame(sharedPrefTransportStringLeft: String, sharedPrefTransportStringRight: String): Boolean {
+ return sharedPrefTransportStringLeft == sharedPrefTransportStringRight
+ }
+
+ fun areItemsTheSame(sharedPrefTransportLeft: SharedPrefTransport, sharedPrefTransportRight: SharedPrefTransport): Boolean {
+ return sharedPrefTransportLeft == sharedPrefTransportRight
+ }
+
+ fun areItemsTheSame(sharedPrefTransport: SharedPrefTransport, transportEntity: TransportEntity): Boolean {
+ return sharedPrefTransport.id == transportEntity.id &&
+ sharedPrefTransport.code == transportEntity.code &&
+ sharedPrefTransport.latestEta == transportEntity.latestEta &&
+ transportEntity.isFavorite == transportEntity.isFavorite &&
+ sharedPrefTransport.type == transportEntity.type
+ }
+
+ fun areItemsTheSame(sharedPrefTransportList: List, transportEntity: List): Boolean {
+ for (transport in sharedPrefTransportList.withIndex()) {
+ if (!areItemsTheSame(sharedPrefTransportList[transport.index], transportEntity[transport.index])) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+}
diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/FavoritesRepositoryImpl.kt b/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/FavoritesRepositoryImpl.kt
index b64e23c..2bafc06 100644
--- a/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/FavoritesRepositoryImpl.kt
+++ b/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/FavoritesRepositoryImpl.kt
@@ -6,9 +6,12 @@ import com.joaquimley.transporteta.domain.model.Transport
import com.joaquimley.transporteta.domain.repository.FavoritesRepository
import io.reactivex.Completable
import io.reactivex.Flowable
+import javax.inject.Inject
+import javax.inject.Singleton
-class FavoritesRepositoryImpl(private val transportDataStore: TransportDataStore,
- private val mapper: DataTransportMapper) : FavoritesRepository {
+@Singleton
+class FavoritesRepositoryImpl @Inject constructor(private val transportDataStore: TransportDataStore,
+ private val mapper: DataTransportMapper) : FavoritesRepository {
override fun markAsFavorite(transport: Transport): Completable {
return transportDataStore.markAsFavorite(mapper.toEntity(transport))
diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/TransportRepositoryImpl.kt b/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/TransportRepositoryImpl.kt
index 77bbbbd..63dc7c8 100644
--- a/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/TransportRepositoryImpl.kt
+++ b/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/TransportRepositoryImpl.kt
@@ -7,12 +7,15 @@ import com.joaquimley.transporteta.domain.repository.TransportRepository
import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.Observable
+import javax.inject.Inject
+import javax.inject.Singleton
-class TransportRepositoryImpl(private val transportDataStore: TransportDataStore,
- private val mapper: DataTransportMapper): TransportRepository {
+@Singleton
+class TransportRepositoryImpl @Inject constructor(private val transportDataStore: TransportDataStore,
+ private val mapper: DataTransportMapper) : TransportRepository {
override fun requestTransportEta(transportCode: Int): Observable {
- return transportDataStore.requestTransportEta(transportCode).map{ mapper.toModel(it)}
+ return transportDataStore.requestTransportEta(transportCode).map { mapper.toModel(it) }
}
override fun cancelTransportEtaRequest(transportCode: Int?): Completable {
diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/store/TransportDataStoreImpl.kt b/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/store/TransportDataStoreImpl.kt
index 69a1abd..8fcd973 100644
--- a/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/store/TransportDataStoreImpl.kt
+++ b/transport-eta-android/data/src/main/java/com/joaquimley/transporteta/data/store/TransportDataStoreImpl.kt
@@ -5,8 +5,12 @@ import com.joaquimley.transporteta.data.source.FrameworkLocalStorage
import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.Observable
+import javax.inject.Inject
+import javax.inject.Singleton
-class TransportDataStoreImpl(private val frameworkLocalStorage: FrameworkLocalStorage): TransportDataStore {
+@Singleton
+class TransportDataStoreImpl @Inject constructor(private val frameworkLocalStorage: FrameworkLocalStorage)
+ : TransportDataStore {
override fun markAsFavorite(transportEntity: TransportEntity): Completable {
return Completable.error(NotImplementedError("Won't be ready for v1.0"))
@@ -33,7 +37,7 @@ class TransportDataStoreImpl(private val frameworkLocalStorage: FrameworkLocalSt
}
override fun getTransport(transportEntityId: String): Observable {
- return frameworkLocalStorage.getTransport(transportEntityId).toObservable()
+ return frameworkLocalStorage.getTransport(transportEntityId).toObservable()
}
override fun getAll(): Flowable> {
diff --git a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/AppModule.kt b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/AppModule.kt
index 44bfda9..6d11b91 100644
--- a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/AppModule.kt
+++ b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/AppModule.kt
@@ -10,6 +10,7 @@ import com.joaquimley.transporteta.ui.di.component.ControllerSubComponent
import com.joaquimley.transporteta.ui.di.component.DataSubComponent
import com.joaquimley.transporteta.ui.di.component.RepositorySubComponent
import com.joaquimley.transporteta.ui.di.component.ViewModelSubComponent
+import com.joaquimley.transporteta.ui.di.qualifier.AndroidContext
import com.joaquimley.transporteta.ui.di.scope.PerApplication
import dagger.Module
import dagger.Provides
@@ -39,6 +40,7 @@ class AppModule {
@Provides
@PerApplication
+ @AndroidContext.ApplicationContext
fun provideContext(app: Application): Context {
return app
}
diff --git a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/DataSourceModule.kt b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/DataSourceModule.kt
index 7d3e381..8115d56 100644
--- a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/DataSourceModule.kt
+++ b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/DataSourceModule.kt
@@ -1,5 +1,9 @@
package com.joaquimley.transporteta.ui.di.module
+import android.content.Context
+import android.content.SharedPreferences
+import com.joaquimley.transporteta.sharedpreferences.mapper.SharedPrefTransportMapper
+import com.joaquimley.transporteta.ui.di.qualifier.AndroidContext
import com.joaquimley.transporteta.data.source.FrameworkLocalStorage
import com.joaquimley.transporteta.sharedpreferences.FrameworkLocalStorageImpl
import com.joaquimley.transporteta.ui.di.scope.PerApplication
@@ -9,10 +13,21 @@ import dagger.Provides
@Module
class DataSourceModule {
+ companion object {
+ private const val SHARED_PREFERENCES_NAME = "com.joaquimley.transporteta.sharedpreferences"
+ }
+
+ @Provides
+ @PerApplication
+ fun provideSharedPreferences(@AndroidContext.ApplicationContext applicationContext: Context): SharedPreferences {
+ return applicationContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
+ }
+
@Provides
@PerApplication
- fun provideSharedPreferencesDataSource(): FrameworkLocalStorage {
- return FrameworkLocalStorageImpl()
+ fun provideSharedPreferencesDataSource(sharedPreferences: SharedPreferences,
+ sharedPrefTransportMapper: SharedPrefTransportMapper): FrameworkLocalStorage {
+ return FrameworkLocalStorageImpl(sharedPreferences, sharedPrefTransportMapper)
}
// @Provides
diff --git a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/MapperModule.kt b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/MapperModule.kt
index 4092b83..1d2053b 100644
--- a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/MapperModule.kt
+++ b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/MapperModule.kt
@@ -2,12 +2,18 @@ package com.joaquimley.transporteta.ui.di.module
import com.joaquimley.transporteta.data.mapper.DataTransportMapper
import com.joaquimley.transporteta.presentation.mapper.PresentationTransportMapper
+import com.joaquimley.transporteta.sharedpreferences.mapper.SharedPrefTransportMapper
import dagger.Module
import dagger.Provides
@Module
class MapperModule {
+ @Provides
+ fun sharedPrefsTransportMapper(): SharedPrefTransportMapper {
+ return SharedPrefTransportMapper()
+ }
+
@Provides
fun presentationTransportMapper(): PresentationTransportMapper {
return PresentationTransportMapper()
diff --git a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/qualifier/AndroidContext.kt b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/qualifier/AndroidContext.kt
new file mode 100644
index 0000000..508ccda
--- /dev/null
+++ b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/qualifier/AndroidContext.kt
@@ -0,0 +1,9 @@
+package com.joaquimley.transporteta.ui.di.qualifier
+
+import javax.inject.Qualifier
+
+interface AndroidContext {
+ @Qualifier
+ @Retention(AnnotationRetention.RUNTIME)
+ annotation class ApplicationContext
+}
\ No newline at end of file
diff --git a/transport-eta-android/versions.gradle b/transport-eta-android/versions.gradle
index f9624a0..2080c72 100644
--- a/transport-eta-android/versions.gradle
+++ b/transport-eta-android/versions.gradle
@@ -6,7 +6,8 @@ ext.deps = [:]
def versions = [:]
// Plugins
-versions.kotlin = "1.2.51"
+versions.kotlin = "1.2.60"
+versions.kotlin_serialization = "0.6.1"
versions.android_gradle_plugin = '3.3.0-alpha03'
// Javax
@@ -60,7 +61,7 @@ versions.mockito = "2.19.1"
versions.mockito_kotlin = "2.0.0-RC1"
versions.atsl_rules = "1.0.1"
versions.atsl_runner = "1.0.2"
-versions.robolectric = "3.4.2"
+versions.robolectric = "3.8" // "4.0-beta-2-SNAPSHOT" // "3.8"
versions.dexmaker_linkedin = "2.19.0"
versions.dexmaker_linkedin_inline = "2.19.0"
@@ -148,6 +149,8 @@ atsl.rules = "com.android.support.test:rules:$versions.atsl_runner"
atsl.orchestrator = "com.android.support.test:orchestrator:$versions.atsl_runner"
deps.atsl = atsl
+deps.robolectric = "org.robolectric:robolectric:$versions.robolectric"
+
def mockito = [:]
mockito.core = "org.mockito:mockito-core:$versions.mockito"
mockito.inline = "org.mockito:mockito-inline:$versions.mockito"
@@ -161,6 +164,8 @@ kotlin.test = "org.jetbrains.kotlin:kotlin-test:$versions.kotlin"
kotlin.stdlib = "org.jetbrains.kotlin:kotlin-stdlib:$versions.kotlin"
kotlin.junit = "org.jetbrains.kotlin:kotlin-test-junit:$versions.kotlin"
kotlin.plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
+kotlin.serialization = "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$versions.kotlin_serialization"
+kotlin.serialization_plugin = "org.jetbrains.kotlinx:kotlinx-gradle-serialization-plugin:$versions.kotlin_serialization"
deps.kotlin = kotlin
def rx = [:]
@@ -175,7 +180,6 @@ dexmaker.linkedin = "com.linkedin.dexmaker:dexmaker-mockito:$versions.dexmaker_l
dexmaker.linkedin_inline = "com.linkedin.dexmaker:dexmaker-mockito-inline:$versions.dexmaker_linkedin_inline"
deps.dexmaker = dexmaker
-deps.robolectric = "org.robolectric:robolectric:$versions.robolectric"
deps.assertj = "org.assertj:assertj-core:$versions.assertj"
deps.glide = "com.github.bumptech.glide:glide:$versions.glide"
deps.constraint_layout = "com.android.support.constraint:constraint-layout:$versions.constraint_layout"
@@ -203,5 +207,7 @@ ext.app_version = app_version
static def addRepos(RepositoryHandler handler) {
handler.google()
handler.jcenter()
+ handler.maven { url "https://kotlin.bintray.com/kotlinx" }
+ handler.maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
ext.addRepos = this.&addRepos
\ No newline at end of file