mirror of
https://github.com/melihaksoy/Android-Kotlin-Modulerized-CleanArchitecture.git
synced 2026-04-22 08:48:31 +02:00
Feature/styling (#36)
* Working with material styles * Replaced deprecated ViewModelProviders.of and applied base styles with material components * Various code style fixes and styles.xml improvements
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
package com.melih.repository
|
||||
|
||||
const val DEFAULT_NAME = "Default name"
|
||||
const val EMPTY_STRING = ""
|
||||
const val DEFAULT_IMAGE_SIZE = 480
|
||||
internal const val DEFAULT_NAME = "Default name"
|
||||
internal const val EMPTY_STRING = ""
|
||||
|
||||
@@ -4,10 +4,13 @@ import com.melih.repository.entities.LaunchEntity
|
||||
import com.melih.repository.interactors.base.Result
|
||||
|
||||
/**
|
||||
* Abstract class to create contract in sources to seperate low level business logic from build and return type
|
||||
* Contract for sources to seperate low level business logic from build and return type
|
||||
*/
|
||||
abstract class Repository {
|
||||
|
||||
//region Abstractions
|
||||
|
||||
internal abstract suspend fun getNextLaunches(count: Int, page: Int): Result<List<LaunchEntity>>
|
||||
internal abstract suspend fun getLaunchById(id: Long): Result<LaunchEntity>
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -18,11 +18,16 @@ import javax.inject.Inject
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
class GetLaunchDetails @Inject constructor() : BaseInteractor<LaunchEntity, GetLaunchDetails.Params>() {
|
||||
|
||||
//region Properties
|
||||
|
||||
@field:Inject
|
||||
internal lateinit var networkSource: NetworkSource
|
||||
|
||||
@field:Inject
|
||||
internal lateinit var persistenceSource: PersistenceSource
|
||||
//endregion
|
||||
|
||||
//region Functions
|
||||
|
||||
override suspend fun FlowCollector<Result<LaunchEntity>>.run(params: Params) {
|
||||
val result = persistenceSource.getLaunchById(params.id)
|
||||
@@ -42,6 +47,7 @@ class GetLaunchDetails @Inject constructor() : BaseInteractor<LaunchEntity, GetL
|
||||
emit(result)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
data class Params(
|
||||
val id: Long
|
||||
|
||||
@@ -19,11 +19,16 @@ const val DEFAULT_LAUNCHES_AMOUNT = 15
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
class GetLaunches @Inject constructor() : BaseInteractor<List<LaunchEntity>, GetLaunches.Params>() {
|
||||
|
||||
//region Properties
|
||||
|
||||
@field:Inject
|
||||
internal lateinit var networkSource: NetworkSource
|
||||
|
||||
@field:Inject
|
||||
internal lateinit var persistenceSource: PersistenceSource
|
||||
//endregion
|
||||
|
||||
//region Functions
|
||||
|
||||
override suspend fun FlowCollector<Result<List<LaunchEntity>>>.run(params: Params) {
|
||||
|
||||
@@ -40,6 +45,7 @@ class GetLaunches @Inject constructor() : BaseInteractor<List<LaunchEntity>, Get
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
data class Params(
|
||||
val count: Int = DEFAULT_LAUNCHES_AMOUNT,
|
||||
|
||||
@@ -13,12 +13,12 @@ import kotlinx.coroutines.flow.flowOn
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
abstract class BaseInteractor<T, in P : InteractorParameters> {
|
||||
|
||||
// region Abstractions
|
||||
//region Abstractions
|
||||
|
||||
protected abstract suspend fun FlowCollector<Result<T>>.run(params: P)
|
||||
// endregion
|
||||
//endregion
|
||||
|
||||
// region Functions
|
||||
//region Functions
|
||||
|
||||
operator fun invoke(params: P) =
|
||||
flow<Result<T>> {
|
||||
@@ -26,7 +26,7 @@ abstract class BaseInteractor<T, in P : InteractorParameters> {
|
||||
run(params)
|
||||
emit(State.Loaded())
|
||||
}.flowOn(Dispatchers.IO)
|
||||
// endregion
|
||||
//endregion
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,8 @@ import com.melih.repository.R
|
||||
*/
|
||||
sealed class Reason(@StringRes val messageRes: Int)
|
||||
|
||||
//region Subclasses
|
||||
|
||||
class NetworkError : Reason(R.string.reason_network)
|
||||
class EmptyResultError : Reason(R.string.reason_empty_body)
|
||||
class GenericError : Reason(R.string.reason_generic)
|
||||
@@ -16,3 +18,4 @@ class ResponseError : Reason(R.string.reason_response)
|
||||
class TimeoutError : Reason(R.string.reason_timeout)
|
||||
class PersistenceEmpty : Reason(R.string.reason_persistance_empty)
|
||||
class NoNetworkPersistenceEmpty : Reason(R.string.reason_no_network_persistance_empty)
|
||||
//endregion
|
||||
|
||||
@@ -9,7 +9,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
sealed class Result<out T>
|
||||
|
||||
// region Subclasses
|
||||
//region Subclasses
|
||||
|
||||
class Success<out T>(val successData: T) : Result<T>()
|
||||
class Failure(val errorData: Reason) : Result<Nothing>()
|
||||
@@ -18,9 +18,9 @@ sealed class State : Result<Nothing>() {
|
||||
class Loading : State()
|
||||
class Loaded : State()
|
||||
}
|
||||
// endregion
|
||||
//endregion
|
||||
|
||||
// region Extensions
|
||||
//region Extensions
|
||||
|
||||
inline fun <T> Result<T>.handle(stateBlock: (State) -> Unit, failureBlock: (Reason) -> Unit, successBlock: (T) -> Unit) {
|
||||
when (this) {
|
||||
@@ -50,4 +50,4 @@ inline fun <T> Result<T>.onState(stateBlock: (State) -> Unit): Result<T> {
|
||||
|
||||
return this
|
||||
}
|
||||
// endregion
|
||||
//endregion
|
||||
|
||||
@@ -12,6 +12,8 @@ import retrofit2.http.Query
|
||||
*/
|
||||
internal interface Api {
|
||||
|
||||
//region Get
|
||||
|
||||
@GET("launch/next/{count}")
|
||||
suspend fun getNextLaunches(
|
||||
@Path("count") count: Int,
|
||||
@@ -22,4 +24,5 @@ internal interface Api {
|
||||
suspend fun getLaunchById(
|
||||
@Path("id") id: Long
|
||||
): Response<LaunchEntity>
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ internal const val TIMEOUT_DURATION = 7L
|
||||
|
||||
internal class ApiImpl @Inject constructor() : Api {
|
||||
|
||||
// region Properties
|
||||
//region Properties
|
||||
|
||||
private val service by lazy {
|
||||
val moshi = Moshi.Builder()
|
||||
@@ -39,7 +39,9 @@ internal class ApiImpl @Inject constructor() : Api {
|
||||
.build()
|
||||
.create(Api::class.java)
|
||||
}
|
||||
// endregion
|
||||
//endregion
|
||||
|
||||
//region Functions
|
||||
|
||||
override suspend fun getNextLaunches(
|
||||
count: Int,
|
||||
@@ -51,4 +53,5 @@ internal class ApiImpl @Inject constructor() : Api {
|
||||
id: Long
|
||||
): Response<LaunchEntity> =
|
||||
service.getLaunchById(id)
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ const val DB_NAME = "LaunchesDB"
|
||||
)
|
||||
internal abstract class LaunchesDatabase : RoomDatabase() {
|
||||
|
||||
//region Companion
|
||||
|
||||
companion object {
|
||||
|
||||
private lateinit var instance: LaunchesDatabase
|
||||
@@ -42,6 +44,10 @@ internal abstract class LaunchesDatabase : RoomDatabase() {
|
||||
}
|
||||
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Abstractions
|
||||
|
||||
internal abstract val launchesDao: LaunchesDao
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -10,13 +10,19 @@ import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
*/
|
||||
abstract class BaseConverter<T> {
|
||||
|
||||
//region Abstractions
|
||||
|
||||
abstract fun getAdapter(moshi: Moshi): JsonAdapter<T>
|
||||
//endregion
|
||||
|
||||
//region Properties
|
||||
|
||||
private val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
//endregion
|
||||
|
||||
abstract fun getAdapter(moshi: Moshi): JsonAdapter<T>
|
||||
|
||||
// region Functions
|
||||
//region Functions
|
||||
|
||||
@TypeConverter
|
||||
fun convertFrom(item: T) =
|
||||
@@ -25,5 +31,5 @@ abstract class BaseConverter<T> {
|
||||
@TypeConverter
|
||||
fun convertTo(string: String) =
|
||||
getAdapter(moshi).fromJson(string)
|
||||
// endregion
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -10,13 +10,19 @@ import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
*/
|
||||
abstract class BaseListConverter<T> {
|
||||
|
||||
//region Abstractions
|
||||
|
||||
abstract fun getAdapter(moshi: Moshi): JsonAdapter<List<T>>
|
||||
//endregion
|
||||
|
||||
//region Properties
|
||||
|
||||
private val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
//endregion
|
||||
|
||||
abstract fun getAdapter(moshi: Moshi): JsonAdapter<List<T>>
|
||||
|
||||
// region Functions
|
||||
//region Functions
|
||||
|
||||
@TypeConverter
|
||||
fun convertFrom(items: List<T>) =
|
||||
@@ -25,5 +31,5 @@ abstract class BaseListConverter<T> {
|
||||
@TypeConverter
|
||||
fun convertTo(string: String): List<T>? =
|
||||
getAdapter(moshi).fromJson(string)
|
||||
// endregion
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.squareup.moshi.Moshi
|
||||
* Converts [location][LocationEntity]
|
||||
*/
|
||||
class LocationConverter : BaseConverter<LocationEntity>() {
|
||||
|
||||
override fun getAdapter(moshi: Moshi): JsonAdapter<LocationEntity> =
|
||||
LocationEntityJsonAdapter(moshi)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.squareup.moshi.Moshi
|
||||
* Converts [rocket][RocketEntity]
|
||||
*/
|
||||
class RocketConverter : BaseConverter<RocketEntity>() {
|
||||
|
||||
override fun getAdapter(moshi: Moshi): JsonAdapter<RocketEntity> =
|
||||
RocketEntityJsonAdapter(moshi)
|
||||
RocketEntityJsonAdapter(moshi)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import com.melih.repository.entities.LaunchEntity
|
||||
@Dao
|
||||
internal abstract class LaunchesDao {
|
||||
|
||||
// region Queries
|
||||
//region Queries
|
||||
|
||||
@Query("SELECT * FROM Launches ORDER BY launchStartTime DESC LIMIT :count OFFSET :page*:count")
|
||||
abstract suspend fun getLaunches(count: Int, page: Int): List<LaunchEntity>
|
||||
@@ -22,14 +22,14 @@ internal abstract class LaunchesDao {
|
||||
|
||||
@Query("DELETE FROM Launches")
|
||||
abstract suspend fun nukeLaunches()
|
||||
// endregion
|
||||
//endregion
|
||||
|
||||
// region Insertion
|
||||
//region Insertion
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun saveLaunches(launches: List<LaunchEntity>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun saveLaunch(launch: LaunchEntity)
|
||||
// endregion
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.melih.repository.sources
|
||||
|
||||
import android.net.NetworkInfo
|
||||
import com.melih.repository.DEFAULT_IMAGE_SIZE
|
||||
import com.melih.repository.Repository
|
||||
import com.melih.repository.entities.LaunchEntity
|
||||
import com.melih.repository.interactors.DEFAULT_LAUNCHES_AMOUNT
|
||||
@@ -19,6 +18,8 @@ import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
private const val DEFAULT_IMAGE_SIZE = 480
|
||||
|
||||
/**
|
||||
* NetworkSource for fetching results using api and wrapping them as contracted in [repository][Repository],
|
||||
* returning either [failure][Failure] with proper [reason][Reason] or [success][Success] with data
|
||||
@@ -27,16 +28,17 @@ internal class NetworkSource @Inject constructor(
|
||||
private val apiImpl: ApiImpl,
|
||||
private val networkInfoProvider: Provider<NetworkInfo>
|
||||
) : Repository() {
|
||||
// region Properties
|
||||
|
||||
//region Properties
|
||||
|
||||
private val isNetworkConnected: Boolean
|
||||
get() {
|
||||
val networkInfo = networkInfoProvider.get()
|
||||
return networkInfo != null && networkInfo.isConnected
|
||||
}
|
||||
// endregion
|
||||
//endregion
|
||||
|
||||
// region Functions
|
||||
//region Functions
|
||||
|
||||
override suspend fun getNextLaunches(count: Int, page: Int): Result<List<LaunchEntity>> =
|
||||
safeExecute({
|
||||
@@ -73,8 +75,8 @@ internal class NetworkSource @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend inline fun <T, R> safeExecute(
|
||||
block: suspend () -> Response<T>,
|
||||
private inline fun <T, R> safeExecute(
|
||||
block: () -> Response<T>,
|
||||
transform: (T) -> R
|
||||
) =
|
||||
if (isNetworkConnected) {
|
||||
@@ -112,5 +114,5 @@ internal class NetworkSource @Inject constructor(
|
||||
} catch (e: Exception) {
|
||||
imageUrl
|
||||
}
|
||||
// endregion
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ import javax.inject.Inject
|
||||
internal class PersistenceSource @Inject constructor(
|
||||
ctx: Context
|
||||
) : Repository() {
|
||||
// region Functions
|
||||
|
||||
//region Functions
|
||||
|
||||
private val launchesDatabase = LaunchesDatabase.getInstance(ctx)
|
||||
|
||||
@@ -45,5 +46,5 @@ internal class PersistenceSource @Inject constructor(
|
||||
internal suspend fun saveLaunch(launch: LaunchEntity) {
|
||||
launchesDatabase.launchesDao.saveLaunch(launch)
|
||||
}
|
||||
// endregion
|
||||
//endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user