Author: IsaacRF239
Minimum SDK: API 16
Target SDK: API 29
Android test app built in modern Architecture that consumes GitHub API to show sample data. This is part of a coding challenge developed for Xing, you can find the assessment details here
This project uses MVVM (Model - View - Viewmodel) architecture, via new Jetpack ViewModel feature.
This project implements "By feature + layout" structure, Separation of Concerns pattern and SOLID principles.
Project architecture combines the "By feature" structure, consisting on separating all files concerning to a specific feature (for example, an app screen / section) on its own package, plus the "By layout" classic structure, separating all files serving a similar purpose on its own sub-package.
By feature structure complies with the Separation of Concerns and encapsulation patterns, also making the app highly scalable, modular and way easier to manipulate, as deleting or adding features impact only app base layer and refactor is minimum to non-existent.
Testing project replicates the same feature structure to ease test running separation
Data is handled via LiveData / Observable Pattern instead of RxJava, as it's better performant and includes a series of benefits as, for example, avoiding manual app lifecycle management.
RepoListViewModel
val repoList: LiveData<NetworkResource<List<Repo>>> = repoListRepository.getRepos(organizationName)
RepoListActivity
//Observe live data changes and update UI accordingly
repoListViewModel.repoList.observe(this) {
when(it.status) {
Status.LOADING -> {}
Status.SUCCESS -> {}
Status.ERROR -> {}
}
}
Project implements Dependency Injection (SOLID) to isolate modules, avoid inter-dependencies and make testing easier
Dependency Injection is handled via Hilt, a library that uses Dagger under the hood easing its implementation via @ annotations, and is developed and recommended to use by Google.
GithubNdApp (App main class)
@HiltAndroidApp
class GithubNdApp : Application() {}
RepoListActivity
@AndroidEntryPoint
class RepoListActivity : AppCompatActivity(), RepoListItemViewAdapter.OnRepoListener {
private val repoListViewModel: RepoListViewModel by viewModels()
}
RepoListViewModel
class RepoListViewModel @ViewModelInject constructor (
private val repoListRepository: RepoListRepository,
@Assisted private val state: SavedStateHandle
) : ViewModel() {}
API Calls are handled via retrofit, declaring calls via an interface, and automatically deserialized by Gson into model objects.
RepoListService
@Headers("Authorization: token ???????????????")
@GET("/orgs/{organizationName}/repos")
fun getRepos(
@Path("organizationName") organizationName: String,
@Query("page") page: Int
): Call<List<Repo>>
Android mininum SDK has been stablished in API 16 to avoid security conflicts against GitHub API.
GitHub API deprecated the SSLv3 and TLSv1.0 connection protocols for security reasons in favor of TLSv1.3 and TLSv1.2, as can be seen here: https://www.ssllabs.com/ssltest/analyze.html?d=api.github.com
As stated in Google Developer guidelines (https://developer.android.com/reference/javax/net/ssl/SSLSocket.html), TLSv1.2 protocol is only available in Android 4.1+ (not enabled by default, requiring some tweaks), and in Android 5.0+ (enabled by default). So any Android version below 4.1 uses TLSv1.0 or TLSv1.1, and fails to establish a secure SSL connection with the API, being unable to retrieve any data.
The required tweaks to run the api calls on API 14 -> 21 are done via a custom TLS Socked Factory that forces the enabling of TLSv1.2 when available, and also forces the ciphers required by the API.
Repo owner avatar image is rendered and cached using EpicBitmapRenderer, a Java library I also developed.
All business logic and services are tested using Mockito and okhttp Mockwebserver.
Every test is isolated, and all API calls are mocked to avoid test results to depend on external sources, becoming unrealiable and possibly leading to unexpected results.