A simple android application fetches details of a debit or credit card by the card number using the Binlist API. It accepts input from a user either by manual input or scanning via OCR
The app presents a simple walkthrough screen the first time the app is run on a device. The home screen gives the user the option input the card number manually or by scanning via OCR. Once a card is scanned or the user finishes entering the card details, the app should display the card details fetched from the Binlist API
You can download the APK from releases.
The app uses the Bouncer Cardscan SDK to scan cards. Get an api key here
- In the
local.properties
file in your project-level directory, add the following code to the file. ReplaceYOUR_APP_KEY
with your card scan API key.
SCAN_CARD_API_KEY=YOUR_API_KEY
To build this project, you require:
- Android Studio 4.1.0 or higher
- Gradle 6.5 or higher
- Clean Architecture
- Kotlin coroutines with Flow
- Dependency injection with Dagger-Hilt
- API request with Retrofit and Moshi for JSON serialisation
- CI with Github actions
- Code coverage with jacoco with reports uploaded to codecov
- Code lint check with Ktlint using a gradle plugin
- Static code analysis with detekt
- Dependency management with buildSrc (Kotlin DSL)
- Dependency updates with buildSrcVersions
- Git hooks to perform ktlint, detekt and lint checks before committing
The app follows the clean architecture concept in the most minimal way appropriate for the current state of the app. There are three layers in the app. The data layer, the domain and the presentation.
On the data layer, an API request is done with the Retrofit library. Models are defined for the expected response. A mapper class is present to convert the response to the domain layer model.
The domain layer contains the model class and the use case for getting card info. It defines a state class to notify the presentation layer of the loading, success and error states. In the app, asynchronous operations are carried out with coroutines. The domain layer defines a class to abstract the different dispatchers.
The presentation layer is implemented with MVVM
using ViewModel
and LiveData
. A view state data class is defined. The fragment observes the view state which is wrapped in a LiveData
. The view is a Fragment
. The card number is passed to the ViewModel
, which then executes the use case and a reducer, which receives the old state and the results (error, failure, or loading) creates a copy of the new state. This new state is posted to the view.
The app uses the card scan sdk to scan card details. The Activity Results API is used to define the contact used to request and receive results of the scan.
- Material Components - comes with ready made material UI view components
- CardScan API - for scanning card details
- Constraint Layout - it enables creation of layouts flat view hierarchies
- Retrofit for REST api communication, it is actively developed and stable
- Moshi for JSON serialisation and deserialisation, it's lightweight, stable and works well with Retrofit
- Kotlin Coroutines - for asynchronous concurrency because it's easy to use and comes out of the box with kotlin
- ViewModel - to manage UI data in a lifecycle conscious way, surviving configuration change
- LiveData - to hold and observe UI state in a lifecycle aware way in the app
- Navigation Architecture Component - for easy navigation, especially with fragments no need to interact with the fragment manager
- Glide - for image loading and caching with smooth scrolling. It is actively developed and stable.
- ViewBinding - to interact easily with views from XML through an auto-generated binding class.
- LeakCanary - to detecting memory leaks in development
- Mockk for mocking in tests.
- Dagger-Hilt for dependency injection
- Kotlin Flow for concurrency
- Turbine for testing flow
- Ktlint gradle plugin for code lint checks
- Detekt for static code analysis
Testing is done with Junit4 testing framework, and with Google Truth for making assertions. Mockk is used to provide mocks in some of the tests. The tests run on the CI and the code coverage report is generated by jacoco can be tracked here. Instrumentation tests are written with Espresso
The Gradle script uses Kotlin Gradle DSL (buildSrc ) which brings Kotlin's rich language features to Gradle configuration. The project also uses detekt to detect code smells and ktlint to enforce proper code style. Github actions handle continuous integration and run detekt, ktlint, lint and unit tests concurrently. A pre-commit git hook verifies the project's code style before committing code. Test coverage reports are uploaded to codecov.
Other things to do
- Setup UI test CI with Firebase test laba
- Improve Unit test coverage