Skip to content

Commit 9366a48

Browse files
authored
Merge pull request #96 from odaridavid/major-breaking-changes
Add save favorites feature
2 parents 27fe809 + 1557ffa commit 9366a48

File tree

190 files changed

+3781
-1997
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

190 files changed

+3781
-1997
lines changed

.travis.yml

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,31 @@ language: android
22
dist: trusty
33
env:
44
global:
5-
- ANDROID_TARGET=android-22
6-
- ANDROID_ABI=armeabi-v7a
5+
- ANDROID_TARGET=android-22
6+
- ANDROID_ABI=armeabi-v7a
77
android:
88
components:
9-
- tools
10-
- platform-tools
11-
- build-tools-29.0.2
12-
- android-29
13-
- extra
14-
- "$ANDROID_TARGET"
15-
- sys-img-${ANDROID_ABI}-${ANDROID_TARGET}
9+
- tools
10+
- platform-tools
11+
- build-tools-29.0.2
12+
- android-29
13+
- extra
14+
- "$ANDROID_TARGET"
15+
- sys-img-${ANDROID_ABI}-${ANDROID_TARGET}
1616
licenses:
17-
- android-sdk-preview-license-52d11cd2
18-
- android-sdk-license-.+
19-
- google-gdk-license-.+
17+
- android-sdk-preview-license-.+
18+
- android-sdk-license-.+
19+
- google-gdk-license-.+
2020
before_install:
21-
- openssl aes-256-cbc -K $encrypted_50477c21cd2c_key -iv $encrypted_50477c21cd2c_iv
22-
-in secrets.tar.enc -out secrets.tar -d
23-
- tar xvf secrets.tar
21+
- openssl aes-256-cbc -K $encrypted_50477c21cd2c_key -iv $encrypted_50477c21cd2c_iv
22+
-in secrets.tar.enc -out secrets.tar -d
23+
- tar xvf secrets.tar
2424
script:
25-
- "./gradlew build jacocoTestReport assembleAndroidTest"
26-
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
27-
- emulator -avd test -no-audio -no-window &
28-
- android-wait-for-emulator
29-
- adb shell setprop dalvik.vm.dexopt-flags v=n,o=v
30-
- "./gradlew connectedCheck"
25+
- "./gradlew build jacocoTestReport assembleAndroidTest"
26+
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
27+
- emulator -avd test -no-audio -no-window &
28+
- android-wait-for-emulator
29+
- adb shell setprop dalvik.vm.dexopt-flags v=n,o=v
30+
- "./gradlew connectedCheck"
3131
after_success:
32-
- bash <(curl -s https://codecov.io/bash) -t aaf7e8f7-d198-4ac1-9370-e8d4e5840497
32+
- bash <(curl -s https://codecov.io/bash) -t aaf7e8f7-d198-4ac1-9370-e8d4e5840497

README.md

Lines changed: 40 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -85,91 +85,62 @@ that should scale.
8585
The 3 layered architectural approach is majorly guided by clean architecture which provides
8686
a clear separation of concerns with its Abstraction Principle.
8787

88-
The `domain` and `data` layers are java module libraries as the business
89-
logic does not rely on the Android frameworks concrete implementations.
90-
9188
#### Presentation
9289

93-
The application presentation layer contains the Activity,Fragments and
94-
Viewmodels and handles Dependency Injection.
95-
96-
The UI layer `feature` package contains `character_detail` and
97-
`character_search` which contain an activity and corresponding
98-
viewmodel as well as other UI related classes.
99-
100-
The viewmodels are provided by the Koin that uses Kotlin's DSLs
101-
to lazily resolve dependency graph at runtime
102-
103-
The ViewModel then receives data from the use case and updates the
104-
LiveData being observed by the activity,the Activity then makes updates
105-
to the UI as need be depending on the current view state.
106-
107-
The UI utilises a **State pattern** by representing expected view states using sealed classes.
108-
This aids with delegating logic operations to the Viewmodel and makes testing in isolation
109-
easier.
90+
```app``` contains the UI files and handles binding of DI components from other modules.
91+
Binding of data is facilitated by jetpacks data binding by serving data from the viewmodel
92+
to the UI.The data being received is part of a viewstate class that has properties contained in the
93+
relevant state.
11094

11195
#### Domain
11296

113-
The domain layer contains domain model classes which represent the
97+
The ```domain``` module contains domain model classes which represent the
11498
data we will be handling across presentation and data layer.
11599

116100
Use cases are also provided in the domain layer and orchestrate the flow
117101
of data from the data layer onto the presentation layer and a split into
118102
modular pieces serving one particular purpose.
119103

120-
The UseCases use a ```BaseUseCase``` interface that defines the parameters its taking in and output
121-
this helps in creating fakes using in testing.
104+
The UseCases use a ```BaseUseCase``` interface that defines the parameters its taking in and
105+
output this helps in creating fakes using in testing.
122106

123107
#### Data
124108

125-
The Data layer using the **Repository Pattern** will be able to
126-
provide data to the defined use cases which in this case is searching
127-
for characters and viewing details of selected characters.The use-cases
128-
are based on the single responsibility rule.
129-
130-
This provides a more decoupled system,as it is isolated from changes to the
131-
db by abstracting low level implementation details of data sources and
132-
changes to the UI.
109+
- ```data-remote```
133110

134-
The repository classes delegate access of data to the data source of
135-
interest either local data source or a remote data source.
111+
Handles data interacting with the network and is later serverd up to the presentation layer through
112+
domain object
136113

137-
This layer also handles mapping of data entities to their domain
138-
representations which when eventually passed to the presentation layer the
139-
domain will be mapped to the presentation model.
114+
- ```data-local```
140115

141-
## Testing
142-
143-
Testing has been done based on the architectural layers.
116+
Handles persistence of object with Room ORM from.This module is responsible for handling all local related
117+
logic and serves up data to and from the presentation layer through domain objects.
144118

145-
1.Domain
119+
With this separation we can easily swap in new or replace the database being used without causeing
120+
major ripples across the codebase.
146121

147-
Contains tests that encompass domain models and uses mockito to verify
148-
use case behavior.
122+
## Testing
149123

150-
2.Data
124+
Each module has its own tests except for the ```domain``` module which is catered for since its
125+
part of the behavior under test.
151126

152-
Tests in the data inherit from a base test that provides a mock web server
153-
with the api interface to request paths the routing of paths to responses
154-
is handled by a custom mock web server dispatcher.
127+
All server responses in the tests are served by mock web server by appending relative urls to
128+
the localhost and the connected port as the base url.
155129

156-
Json responses have also been provided in the test resource folder they
157-
are similar to the response that will be received from the api
158-
The repository tests serve as integration tests between the data sources
159-
and mappers to the domain models.
160-
Currently the data source tests serve as unit tests verifying the appropriate
161-
responses are received from remote source.
130+
In the ``data-remote`` module the responses are mocked using the mockwebserver and verified that they
131+
are what we expect.
162132

163-
3.Presentation
133+
In the ```data-local``` module an in memory database is being used to run the tests,this makes it a
134+
little faster compared to an actual db.
164135

165-
The Presentation layer contains robolectric jvm tests on for menu items
166-
and instrumentation tests checking on system behaviour as per user
167-
expectation.
136+
In the ```app``` module there are unit tests for the viewmodels and util classes
137+
and connected tests for the UI Screens.
168138

169-
The UI tests display data served from a mock web server running from the
170-
devices localhost,this removes flakiness compared to relying on actual
171-
data from the real server aspects such as internet connection or
172-
network service might bring up issues.
139+
The test instrumentation app uses modules that have been swaped with fakes for
140+
the network module so as to run requests on localhost with mockwebserver,this removes flakiness
141+
compared to relying on actual data from the real server aspects such as internet connection or
142+
network service might bring up issues and an in memory database for local data that also allows
143+
main thread queries since tests should also be fast and we are just asserting stuff works.
173144

174145
View models testing on live data were guided by this [article](https://proandroiddev.com/how-to-easily-test-a-viewmodel-with-livedata-and-coroutines-230c74416047)
175146

@@ -181,13 +152,13 @@ Libraries used in the whole application are:
181152
- [Viewmodel](https://developer.android.com/topic/libraries/architecture/viewmodel) - Manage UI related data in a lifecycle conscious way
182153
and act as a channel between use cases and ui
183154
- [Data Binding](https://developer.android.com/topic/libraries/data-binding) - support library that allows binding of UI components in layouts to data sources,binds character details and search results to UI
155+
- [Room](https://developer.android.com/training/data-storage/room) - Provides abstraction layer over SQLite
184156
- [Retrofit](https://square.github.io/retrofit/) - type safe http client
185157
and supports coroutines out of the box.
186158
- [Moshi](https://github.com/square/moshi) - JSON Parser,used to parse
187159
requests on the data layer for Entities and understands Kotlin non-nullable
188160
and default parameters
189161
- [okhttp-logging-interceptor](https://github.com/square/okhttp/blob/master/okhttp-logging-interceptor/README.md) - logs HTTP request and response data.
190-
- [Mockito](https://site.mockito.org/) - Mocking framework used to provide mocks to verify behaviour in domain usecases tests.
191162
- [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) - Library Support for coroutines,provides `runBlocking` coroutine builder used in tests
192163
- [Truth](https://truth.dev/) - Assertions Library,provides readability as far as assertions are concerned
193164
- [MockWebServer](https://github.com/square/okhttp/tree/master/mockwebserver) - web server for testing HTTP clients ,verify requests and responses on the star wars api with the retrofit client.
@@ -200,18 +171,23 @@ and default parameters
200171
- [Espresso](https://developer.android.com/training/testing/espresso) - Test framework to write UI Tests
201172
- [recyclerview-animators](https://github.com/wasabeef/recyclerview-animators) - Recycler View Animations
202173
- [AboutLibraries](https://github.com/mikepenz/AboutLibraries) -provide info on used open source libraries.
174+
- [Stetho](https://github.com/facebook/stetho) - debug bridge
203175

204176
## Contributors
205177

206178
- Thanks to [Zafer Celaloglu](https://github.com/zfrc) for the Dagger to Koin Refactor and additional test cases.
207179

208-
Feel free to contribute in any way to the project.
180+
Feel free to contribute in any way to the project from typos in docs to code review are all welcome.
209181

210182
## Demo
211183

212-
|<img src="art/s1.png" width=200/>|<img src="art/s2.png" width=200/>|
213-
|:----:|:----:|
184+
The codebase in most cases will be ahead of whats on the store.
214185

186+
|<img src="art/sh1.png" width=200/>|<img src="art/sh2.png" width=200/>|<img src="art/sh3.png" width=200/>|<img src="art/sh4.png" width=200/>|
187+
|:----:|:----:|:----:|:----:|
188+
189+
|<img src="art/app.gif" width=200/>|
190+
|:----:|
215191

216192
<a href='https://play.google.com/store/apps/details?id=com.k0d4black.theforce&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png' width='170'/></a>
217193

@@ -242,5 +218,3 @@ Star Wars and all associated names are copyright Lucasfilm ltd.
242218
See the License for the specific language governing permissions and
243219
limitations under the License.
244220
```
245-
246-

app/build.gradle

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ android {
5656
minifyEnabled false
5757
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
5858
manifestPlaceholders = [
59-
crashlyticsEnabled : true,
60-
appIcon : "@mipmap/ic_launcher",
61-
appIconRound : "@mipmap/ic_launcher_round"
59+
crashlyticsEnabled: true,
60+
appIcon : "@mipmap/ic_launcher",
61+
appIconRound : "@mipmap/ic_launcher_round"
6262
]
6363
signingConfig signingConfigs.release
6464
}
@@ -67,9 +67,9 @@ android {
6767
applicationIdSuffix ".debug"
6868
versionNameSuffix "-debug"
6969
manifestPlaceholders = [
70-
crashlyticsEnabled : false,
71-
appIcon : "@mipmap/ic_debug_launcher",
72-
appIconRound : "@mipmap/ic_debug_launcher_round"
70+
crashlyticsEnabled: false,
71+
appIcon : "@mipmap/ic_debug_launcher",
72+
appIconRound : "@mipmap/ic_debug_launcher_round"
7373
]
7474
testCoverageEnabled true
7575
}
@@ -103,7 +103,9 @@ dependencies {
103103

104104
//Local Libs
105105
implementation project(':domain')
106-
implementation project(':di')
106+
implementation project(':data-local')
107+
implementation project(':data-remote')
108+
107109
implementation fileTree(dir: 'libs', include: ['*.jar'])
108110

109111
//Jetpack
@@ -121,6 +123,9 @@ dependencies {
121123
implementation appDependencies.firebaseAnalytics
122124
implementation appDependencies.crashlytics
123125

126+
//Stetho
127+
implementation appDependencies.stetho
128+
124129
//Animations
125130
implementation appDependencies.recyclerviewAnimations
126131

@@ -133,7 +138,6 @@ dependencies {
133138

134139
// Koin
135140
implementation appDependencies.koinAndroid
136-
implementation appDependencies.koinLifeCycleScope
137141
implementation appDependencies.koinAndroidViewModel
138142

139143
//Test Libs
@@ -155,8 +159,6 @@ dependencies {
155159
androidTestImplementation testDependencies.mockWebServer
156160
androidTestImplementation testDependencies.espressoContrib
157161
androidTestImplementation testDependencies.espressoIntents
158-
//For Fake Network Module
159-
androidTestImplementation project(":data")
160-
162+
androidTestImplementation testDependencies.koinTest
161163
}
162164
apply plugin: 'com.google.gms.google-services'

app/src/androidTest/kotlin/com/k0d4black/theforce/BaseTest.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
package com.k0d4black.theforce
22

3+
import com.github.odaridavid.data.local.dao.FavoritesDao
4+
import com.k0d4black.theforce.domain.models.Favorite
5+
import com.k0d4black.theforce.domain.models.Film
36
import com.k0d4black.theforce.helpers.StarWarsRequestDispatcher
7+
import kotlinx.coroutines.runBlocking
48
import okhttp3.mockwebserver.MockWebServer
59
import org.junit.After
610
import org.junit.Before
11+
import org.junit.BeforeClass
12+
import org.koin.core.inject
13+
import org.koin.test.KoinTest
714

815

9-
open class BaseTest {
16+
open class BaseTest : KoinTest {
1017

1118
private lateinit var mockWebServer: MockWebServer
1219

app/src/androidTest/kotlin/com/k0d4black/theforce/CharacterDetailActivityIntegrationTest.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import androidx.test.espresso.assertion.ViewAssertions.matches
77
import androidx.test.espresso.matcher.ViewMatchers.*
88
import androidx.test.ext.junit.runners.AndroidJUnit4
99
import androidx.test.rule.ActivityTestRule
10-
import com.k0d4black.theforce.commons.CHARACTER_PARCEL_KEY
11-
import com.k0d4black.theforce.features.character_details.CharacterDetailActivity
10+
import com.k0d4black.theforce.commons.NavigationUtils
11+
import com.k0d4black.theforce.activities.CharacterDetailActivity
1212
import com.k0d4black.theforce.models.CharacterPresentation
1313
import org.junit.After
1414
import org.junit.Rule
@@ -22,26 +22,25 @@ internal class CharacterDetailActivityIntegrationTest : BaseTest() {
2222
var activityRule: ActivityTestRule<CharacterDetailActivity> =
2323
ActivityTestRule(CharacterDetailActivity::class.java, false, false)
2424

25-
2625
@Test
2726
fun shouldDisplayErrorOnLaunchWithDefaultId() {
2827
val intent = Intent()
2928
activityRule.launchActivity(intent)
3029
SystemClock.sleep(2000)
3130
onView(withId(com.google.android.material.R.id.snackbar_text))
32-
.check(matches(withText(activityRule.activity.resources.getString(R.string.error_character_details))))
31+
.check(matches(withText(activityRule.activity.resources.getString(R.string.error_loading_character_details))))
3332
}
3433

3534
@Test
3635
fun shouldLoadDataOnLaunchWithValidCharacterId() {
3736
val intent = Intent().putExtra(
38-
CHARACTER_PARCEL_KEY,
37+
NavigationUtils.CHARACTER_PARCEL_KEY,
3938
CharacterPresentation(
4039
name = "Luke",
4140
birthYear = "12BBY",
4241
heightInCm = "234",
4342
heightInInches = "544",
44-
url = "https://swapi.py4e.com/api/people/1/"
43+
url = "/api/people/1/"
4544
)
4645
)
4746
activityRule.launchActivity(intent)

0 commit comments

Comments
 (0)