Skip to content

Commit 0b07cd9

Browse files
authored
feat: offline support (#11)
use room to store data locally follow one source of trust pattern, using local data. closes #8
1 parent f265edd commit 0b07cd9

File tree

16 files changed

+575
-77
lines changed

16 files changed

+575
-77
lines changed

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ My way to MVVM using RxJava with new Android databinding
44

55
## Summary
66
* Use [MVVM][1] using [architecture components][6] with to separate Android Framework with a [clean architecture][2] to my domain logic.
7-
* Use [Android Databinding][3] to glue view model and Android
7+
* Use [Android Databinding][3] wih [LiveData][8] to glue [ViewModel][9] and Android
88
* Asynchronous communications implemented with [Rx][4].
99
* Rest API from [ComicVine][5]
10+
* Store data using [Room][7]
1011

1112
## Dependencies
1213
* architecture components
14+
* livedata
15+
* room
16+
* viewmodel
1317
* rx-java
1418
* floating search
1519
* okhttp
@@ -22,8 +26,6 @@ TODO LIST
2226

2327
* Better UI, with Material Design concepts and so on
2428
* Add unit tests, allways fail on that :(
25-
* Implement a local datasource with Realm to test it
26-
2729

2830
Developed By
2931
------------
@@ -40,7 +42,7 @@ Fernando Franco Giráldez - <ffrancogiraldez@gmail.com>
4042
License
4143
-------
4244

43-
Copyright 2015 Fernando Franco Giráldez
45+
Copyright 2018 Fernando Franco Giráldez
4446
Licensed under the Apache License, Version 2.0 (the "License");
4547
you may not use this file except in compliance with the License.
4648
You may obtain a copy of the License at
@@ -59,3 +61,6 @@ License
5961
[4]: http://reactivex.io/
6062
[5]: http://www.comicvine.com/api/
6163
[6]: https://developer.android.com/topic/libraries/architecture/index.html
64+
[7]: https://developer.android.com/topic/libraries/architecture/room.html
65+
[8]: https://developer.android.com/topic/libraries/architecture/livedata.html
66+
[9]: https://developer.android.com/topic/libraries/architecture/viewmodel.html

app/build.gradle

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ android {
3333
dataBinding {
3434
enabled = true
3535
}
36-
36+
3737
compileOptions {
3838
sourceCompatibility JavaVersion.VERSION_1_8
3939
targetCompatibility JavaVersion.VERSION_1_8
@@ -44,11 +44,22 @@ android {
4444
}
4545
}
4646

47+
kapt {
48+
arguments {
49+
arg("room.schemaLocation", "$projectDir/schemas".toString())
50+
}
51+
}
52+
53+
4754
dependencies {
4855
kapt libs.databinding_compiler
56+
kapt libs.arch_comp_room_compiler
57+
kaptTest libs.arch_comp_room_compiler
4958

5059
implementation libs.arch_comp_livedata
5160
implementation libs.arch_comp_viewmodel
61+
implementation libs.arch_comp_room
62+
implementation libs.arch_comp_room_rxjava
5263
implementation libs.constraint
5364
implementation libs.design
5465
implementation libs.floating_search
@@ -64,7 +75,10 @@ dependencies {
6475
implementation libs.retrofit_rx_java
6576
implementation libs.rx_java
6677
implementation libs.rx_android
78+
implementation libs.steho
6779

80+
testImplementation libs.arch_comp_room_test
81+
testImplementation libs.arch_comp_test
6882
testImplementation libs.junit
6983
testImplementation libs.koin_test
7084
testImplementation libs.mockito_kotlin
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
{
2+
"formatVersion": 1,
3+
"database": {
4+
"version": 1,
5+
"identityHash": "843b5908f7967726e53a210136b5cda2",
6+
"entities": [
7+
{
8+
"tableName": "queries",
9+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`query_identifier` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `search_term` TEXT NOT NULL)",
10+
"fields": [
11+
{
12+
"fieldPath": "queryId",
13+
"columnName": "query_identifier",
14+
"affinity": "INTEGER",
15+
"notNull": true
16+
},
17+
{
18+
"fieldPath": "searchTerm",
19+
"columnName": "search_term",
20+
"affinity": "TEXT",
21+
"notNull": true
22+
}
23+
],
24+
"primaryKey": {
25+
"columnNames": [
26+
"query_identifier"
27+
],
28+
"autoGenerate": true
29+
},
30+
"indices": [
31+
{
32+
"name": "idx_query_identifier",
33+
"unique": false,
34+
"columnNames": [
35+
"query_identifier"
36+
],
37+
"createSql": "CREATE INDEX `idx_query_identifier` ON `${TABLE_NAME}` (`query_identifier`)"
38+
},
39+
{
40+
"name": "idx_query_term",
41+
"unique": false,
42+
"columnNames": [
43+
"search_term"
44+
],
45+
"createSql": "CREATE INDEX `idx_query_term` ON `${TABLE_NAME}` (`search_term`)"
46+
}
47+
],
48+
"foreignKeys": []
49+
},
50+
{
51+
"tableName": "suggestions",
52+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`suggestionId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query_id` INTEGER NOT NULL, `title` TEXT NOT NULL, FOREIGN KEY(`query_id`) REFERENCES `queries`(`query_identifier`) ON UPDATE NO ACTION ON DELETE CASCADE )",
53+
"fields": [
54+
{
55+
"fieldPath": "suggestionId",
56+
"columnName": "suggestionId",
57+
"affinity": "INTEGER",
58+
"notNull": true
59+
},
60+
{
61+
"fieldPath": "queryId",
62+
"columnName": "query_id",
63+
"affinity": "INTEGER",
64+
"notNull": true
65+
},
66+
{
67+
"fieldPath": "title",
68+
"columnName": "title",
69+
"affinity": "TEXT",
70+
"notNull": true
71+
}
72+
],
73+
"primaryKey": {
74+
"columnNames": [
75+
"suggestionId"
76+
],
77+
"autoGenerate": true
78+
},
79+
"indices": [
80+
{
81+
"name": "idx_suggestion_id",
82+
"unique": false,
83+
"columnNames": [
84+
"query_id"
85+
],
86+
"createSql": "CREATE INDEX `idx_suggestion_id` ON `${TABLE_NAME}` (`query_id`)"
87+
}
88+
],
89+
"foreignKeys": [
90+
{
91+
"table": "queries",
92+
"onDelete": "CASCADE",
93+
"onUpdate": "NO ACTION",
94+
"columns": [
95+
"query_id"
96+
],
97+
"referencedColumns": [
98+
"query_identifier"
99+
]
100+
}
101+
]
102+
},
103+
{
104+
"tableName": "search",
105+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`search_identifier` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `search_term` TEXT NOT NULL)",
106+
"fields": [
107+
{
108+
"fieldPath": "queryId",
109+
"columnName": "search_identifier",
110+
"affinity": "INTEGER",
111+
"notNull": true
112+
},
113+
{
114+
"fieldPath": "searchTerm",
115+
"columnName": "search_term",
116+
"affinity": "TEXT",
117+
"notNull": true
118+
}
119+
],
120+
"primaryKey": {
121+
"columnNames": [
122+
"search_identifier"
123+
],
124+
"autoGenerate": true
125+
},
126+
"indices": [
127+
{
128+
"name": "idx_search_identifier",
129+
"unique": false,
130+
"columnNames": [
131+
"search_identifier"
132+
],
133+
"createSql": "CREATE INDEX `idx_search_identifier` ON `${TABLE_NAME}` (`search_identifier`)"
134+
},
135+
{
136+
"name": "idx_search_term",
137+
"unique": false,
138+
"columnNames": [
139+
"search_term"
140+
],
141+
"createSql": "CREATE INDEX `idx_search_term` ON `${TABLE_NAME}` (`search_term`)"
142+
}
143+
],
144+
"foreignKeys": []
145+
},
146+
{
147+
"tableName": "volumes",
148+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`suggestionId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `search_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `url` TEXT NOT NULL, FOREIGN KEY(`search_id`) REFERENCES `search`(`search_identifier`) ON UPDATE NO ACTION ON DELETE CASCADE )",
149+
"fields": [
150+
{
151+
"fieldPath": "suggestionId",
152+
"columnName": "suggestionId",
153+
"affinity": "INTEGER",
154+
"notNull": true
155+
},
156+
{
157+
"fieldPath": "queryId",
158+
"columnName": "search_id",
159+
"affinity": "INTEGER",
160+
"notNull": true
161+
},
162+
{
163+
"fieldPath": "title",
164+
"columnName": "title",
165+
"affinity": "TEXT",
166+
"notNull": true
167+
},
168+
{
169+
"fieldPath": "author",
170+
"columnName": "author",
171+
"affinity": "TEXT",
172+
"notNull": true
173+
},
174+
{
175+
"fieldPath": "url",
176+
"columnName": "url",
177+
"affinity": "TEXT",
178+
"notNull": true
179+
}
180+
],
181+
"primaryKey": {
182+
"columnNames": [
183+
"suggestionId"
184+
],
185+
"autoGenerate": true
186+
},
187+
"indices": [
188+
{
189+
"name": "idx_search_id",
190+
"unique": false,
191+
"columnNames": [
192+
"search_id"
193+
],
194+
"createSql": "CREATE INDEX `idx_search_id` ON `${TABLE_NAME}` (`search_id`)"
195+
}
196+
],
197+
"foreignKeys": [
198+
{
199+
"table": "search",
200+
"onDelete": "CASCADE",
201+
"onUpdate": "NO ACTION",
202+
"columns": [
203+
"search_id"
204+
],
205+
"referencedColumns": [
206+
"search_identifier"
207+
]
208+
}
209+
]
210+
}
211+
],
212+
"setupQueries": [
213+
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
214+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"843b5908f7967726e53a210136b5cda2\")"
215+
]
216+
}
217+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package es.ffgiraldez.comicsearch
22

33
import android.app.Application
4+
import com.facebook.stetho.Stetho
45
import es.ffgiraldez.comicsearch.di.comicContext
56
import org.koin.android.ext.android.startKoin
67

78
class ComicApplication : Application() {
89
override fun onCreate() {
910
super.onCreate()
10-
startKoin(this, listOf(comicContext));
11+
startKoin(this, listOf(comicContext))
12+
Stetho.initializeWithDefaults(this)
1113
}
1214
}

0 commit comments

Comments
 (0)