Adding app module
This commit is contained in:
@@ -74,6 +74,8 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
|
implementation(project(":util"))
|
||||||
|
|
||||||
implementation(Libs.activity_compose)
|
implementation(Libs.activity_compose)
|
||||||
implementation(Libs.navigation_compose)
|
implementation(Libs.navigation_compose)
|
||||||
implementation(Libs.core_ktx)
|
implementation(Libs.core_ktx)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
android:name=".app.App"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.AndroidStudyCase">
|
android:theme="@style/Theme.AndroidStudyCase">
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.ericampire.android.androidstudycase.app
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
|
||||||
|
@HiltAndroidApp
|
||||||
|
class App : Application() {
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.android.library")
|
||||||
|
id("kotlin-android")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdk = Apps.compileSdk
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = Apps.minSdk
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.kts.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="org.zxconnect.android.beserve.i18n">
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string-array name="notification_tabs">
|
||||||
|
<item>Commandes</item>
|
||||||
|
<item>Tout</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="notification_channel_names">
|
||||||
|
<item>Nouveau fournisseur</item>
|
||||||
|
<item>Promotions</item>
|
||||||
|
<item>Commandes</item>
|
||||||
|
<item>Nouvelle mise à jour</item>
|
||||||
|
<item>Globale</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="notification_channel_ids">
|
||||||
|
<item>stores</item>
|
||||||
|
<item>products</item>
|
||||||
|
<item>orders</item>
|
||||||
|
<item>global</item>
|
||||||
|
<item>default</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="notification_channel_descriptions">
|
||||||
|
<item>Être notifié lorsqu\'un nouveau fournisseur est disponible sur Be Served.</item>
|
||||||
|
<item>Être notifié lorsqu\'il y a de nouvelles promotions.</item>
|
||||||
|
<item>Être notifié lorsque l\'état de votre commande change.</item>
|
||||||
|
<item>Être notifié lorsqu\'une nouvelle version de l\'application est disponible.</item>
|
||||||
|
<item>Autres notifications.</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="history_items">
|
||||||
|
<item>En cours</item>
|
||||||
|
<item>Refusée</item>
|
||||||
|
<item>Historique</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">Study case</string>
|
||||||
|
<string name="app_channel_name">Be Served Channel</string>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -16,3 +16,5 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
rootProject.name = "android-study-case"
|
rootProject.name = "android-study-case"
|
||||||
include(":app")
|
include(":app")
|
||||||
|
include(":util")
|
||||||
|
include(":i18n")
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.android.library")
|
||||||
|
id("kotlin-android")
|
||||||
|
kotlin("kapt")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdk = Apps.compileSdk
|
||||||
|
buildToolsVersion = Apps.buildToolsVersion
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = Apps.minSdk
|
||||||
|
targetSdk = Apps.targetSdk
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
api(project(":i18n"))
|
||||||
|
implementation(Libs.core_ktx)
|
||||||
|
|
||||||
|
api(platform(Libs.kotlin_coroutine_bom))
|
||||||
|
api(Libs.kotlin_coroutine_core)
|
||||||
|
|
||||||
|
testImplementation(Libs.junit_jupiter_api)
|
||||||
|
testImplementation(Libs.junit_jupiter_engine)
|
||||||
|
|
||||||
|
testImplementation(Libs.mockk_core)
|
||||||
|
|
||||||
|
api(Libs.hilt_android)
|
||||||
|
kapt(Libs.hilt_android_compiler)
|
||||||
|
|
||||||
|
api(Libs.timber)
|
||||||
|
}
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.zxconnect.android.beserve.util">
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.zxconnect.android.beserve.util
|
||||||
|
|
||||||
|
import javax.inject.Qualifier
|
||||||
|
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class DefaultDispatcher
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class IoDispatcher
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class MainDispatcher
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class MainImmediateDispatcher
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class ApplicationScope
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package org.zxconnect.android.beserve.util
|
||||||
|
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
|
||||||
|
open class Event<out T>(private val content: T) {
|
||||||
|
|
||||||
|
var hasBeenHandled = false
|
||||||
|
private set // Allow external read but not write
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content and prevents its use again.
|
||||||
|
*/
|
||||||
|
fun getContentIfNotHandled(): T? {
|
||||||
|
return if (hasBeenHandled) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
hasBeenHandled = true
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content, even if it's already been handled.
|
||||||
|
*/
|
||||||
|
fun peekContent(): T = content
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
|
||||||
|
* already been handled.
|
||||||
|
*
|
||||||
|
* [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
|
||||||
|
*/
|
||||||
|
class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
|
||||||
|
override fun onChanged(event: Event<T>?) {
|
||||||
|
event?.getContentIfNotHandled()?.let { value ->
|
||||||
|
onEventUnhandledContent(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package org.zxconnect.android.beserve.util
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
|
||||||
|
sealed class Result<out R> {
|
||||||
|
|
||||||
|
data class Success<out T>(val data: T) : Result<T>()
|
||||||
|
data class Error(val exception: Exception) : Result<Nothing>()
|
||||||
|
object Loading : Result<Nothing>()
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return when (this) {
|
||||||
|
is Success<*> -> "Success[data=$data]"
|
||||||
|
is Error -> "Error[exception=$exception]"
|
||||||
|
Loading -> "Loading"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `true` if [Result] is of type [Success] & hoxlds non-null [Success.data].
|
||||||
|
*/
|
||||||
|
val Result<*>.succeeded
|
||||||
|
get() = this is Result.Success && data != null
|
||||||
|
|
||||||
|
fun <T> Result<T>.successOr(fallback: T): T {
|
||||||
|
return (this as? Result.Success<T>)?.data ?: fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
val <T> Result<T>.data: T?
|
||||||
|
get() = (this as? Result.Success)?.data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates value of [liveData] if [Result] is of type [Success]
|
||||||
|
*/
|
||||||
|
inline fun <reified T> Result<T>.updateOnSuccess(liveData: MutableLiveData<T>) {
|
||||||
|
if (this is Result.Success) {
|
||||||
|
liveData.value = data
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package org.zxconnect.android.beserve.util.usecase
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.zxconnect.android.beserve.util.Result
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
|
||||||
|
abstract class CoroutineUseCase<in P, R>(private val coroutineDispatcher: CoroutineDispatcher) {
|
||||||
|
|
||||||
|
/** Executes the use case asynchronously and returns a [Result].
|
||||||
|
*
|
||||||
|
* @return a [Result].
|
||||||
|
*
|
||||||
|
* @param parameters the input parameters to run the use case with
|
||||||
|
*/
|
||||||
|
suspend operator fun invoke(parameters: P): Result<R> {
|
||||||
|
return try {
|
||||||
|
// Moving all use case's executions to the injected dispatcher
|
||||||
|
// In production code, this is usually the Default dispatcher (background thread)
|
||||||
|
// In tests, this becomes a TestCoroutineDispatcher
|
||||||
|
withContext(coroutineDispatcher) {
|
||||||
|
execute(parameters).let {
|
||||||
|
Result.Success(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.d(e)
|
||||||
|
Result.Error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this to set the code to be executed.
|
||||||
|
*/
|
||||||
|
@Throws(RuntimeException::class)
|
||||||
|
protected abstract suspend fun execute(parameters: P): R
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package org.zxconnect.android.beserve.util.usecase
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import org.zxconnect.android.beserve.util.Result
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes business logic in its execute method and keep posting updates to the result as
|
||||||
|
* [Result<R>].
|
||||||
|
* Handling an exception (emit [Result.Error] to the result) is the subclasses's responsibility.
|
||||||
|
*/
|
||||||
|
abstract class FlowUseCase<in P, R>(private val coroutineDispatcher: CoroutineDispatcher) {
|
||||||
|
operator fun invoke(parameters: P): Flow<Result<R>> = execute(parameters)
|
||||||
|
.catch { e -> emit(Result.Error(Exception(e))) }
|
||||||
|
.flowOn(coroutineDispatcher)
|
||||||
|
|
||||||
|
protected abstract fun execute(parameters: P): Flow<Result<R>>
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.zxconnect.android.beserve.util
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
fun addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user