diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 65afc47..6a9b3f5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,9 +4,12 @@ plugins { } dependencies { + implementation(project(":core:core")) implementation(project(":core:ui")) + implementation(project(":core:navigation")) implementation(project(":core:datastore")) implementation(project(":core:network")) + implementation(project(":core:user")) implementation(project(":feature:auth")) implementation(project(":feature:home")) } \ No newline at end of file diff --git a/app/src/main/java/com/stslex/aproselection/controller/AuthControllerImpl.kt b/app/src/main/java/com/stslex/aproselection/controller/AuthControllerImpl.kt index 5067b85..4a27936 100644 --- a/app/src/main/java/com/stslex/aproselection/controller/AuthControllerImpl.kt +++ b/app/src/main/java/com/stslex/aproselection/controller/AuthControllerImpl.kt @@ -2,7 +2,7 @@ package com.stslex.aproselection.controller import com.stslex.aproselection.core.datastore.AppDataStore import com.stslex.aproselection.core.network.client.NetworkClient -import com.stslex.aproselection.core.ui.core.Logger +import com.stslex.aproselection.core.core.Logger import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/stslex/aproselection/di/AppModule.kt b/app/src/main/java/com/stslex/aproselection/di/AppModule.kt index 782b023..3a6cd23 100644 --- a/app/src/main/java/com/stslex/aproselection/di/AppModule.kt +++ b/app/src/main/java/com/stslex/aproselection/di/AppModule.kt @@ -1,10 +1,7 @@ package com.stslex.aproselection.di -import androidx.navigation.NavHostController import com.stslex.aproselection.controller.AuthController import com.stslex.aproselection.controller.AuthControllerImpl -import com.stslex.aproselection.core.ui.navigation.navigator.Navigator -import com.stslex.aproselection.core.ui.navigation.navigator.NavigatorImpl import com.stslex.aproselection.ui.InitialAppViewModel import org.koin.androidx.viewmodel.dsl.viewModelOf import org.koin.core.module.dsl.bind @@ -15,9 +12,6 @@ val appModule = module { singleOf(::AuthControllerImpl) { bind() } } -fun navigationModule( - navHostController: NavHostController -) = module { - single { NavigatorImpl(navHostController) } +val initialAppModule = module { viewModelOf(::InitialAppViewModel) } \ No newline at end of file diff --git a/app/src/main/java/com/stslex/aproselection/navigation/NavigationHost.kt b/app/src/main/java/com/stslex/aproselection/navigation/NavigationHost.kt index c423e36..30988b3 100644 --- a/app/src/main/java/com/stslex/aproselection/navigation/NavigationHost.kt +++ b/app/src/main/java/com/stslex/aproselection/navigation/NavigationHost.kt @@ -4,7 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost -import com.stslex.aproselection.core.ui.navigation.destination.AppDestination +import com.stslex.aproselection.core.navigation.destination.AppDestination import com.stslex.aproselection.feature.auth.ui.navigation.authRouter import com.stslex.aproselection.feature.home.ui.navigation.homeRouter diff --git a/app/src/main/java/com/stslex/aproselection/ui/InitialApp.kt b/app/src/main/java/com/stslex/aproselection/ui/InitialApp.kt index 9db8cf1..2eceae1 100644 --- a/app/src/main/java/com/stslex/aproselection/ui/InitialApp.kt +++ b/app/src/main/java/com/stslex/aproselection/ui/InitialApp.kt @@ -6,7 +6,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.navigation.NavHostController -import com.stslex.aproselection.core.ui.navigation.destination.AppDestination +import com.stslex.aproselection.core.navigation.destination.AppDestination import com.stslex.aproselection.navigation.NavigationHost import org.koin.androidx.compose.koinViewModel @@ -23,7 +23,7 @@ fun InitialApp( viewModel.init() } - AppDestination + com.stslex.aproselection.core.navigation.destination.AppDestination .getStartDestination(isInitialAuth) ?.let { destination -> NavigationHost( diff --git a/app/src/main/java/com/stslex/aproselection/ui/InitialAppViewModel.kt b/app/src/main/java/com/stslex/aproselection/ui/InitialAppViewModel.kt index 3ac651b..ebb3db2 100644 --- a/app/src/main/java/com/stslex/aproselection/ui/InitialAppViewModel.kt +++ b/app/src/main/java/com/stslex/aproselection/ui/InitialAppViewModel.kt @@ -2,9 +2,9 @@ package com.stslex.aproselection.ui import androidx.lifecycle.viewModelScope import com.stslex.aproselection.controller.AuthController +import com.stslex.aproselection.core.navigation.destination.NavigationScreen +import com.stslex.aproselection.core.navigation.navigator.Navigator import com.stslex.aproselection.core.ui.base.BaseViewModel -import com.stslex.aproselection.core.ui.navigation.destination.NavigationScreen -import com.stslex.aproselection.core.ui.navigation.navigator.Navigator import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow diff --git a/app/src/main/java/com/stslex/aproselection/ui/MainActivity.kt b/app/src/main/java/com/stslex/aproselection/ui/MainActivity.kt index d95a918..e526064 100644 --- a/app/src/main/java/com/stslex/aproselection/ui/MainActivity.kt +++ b/app/src/main/java/com/stslex/aproselection/ui/MainActivity.kt @@ -6,8 +6,9 @@ import androidx.activity.compose.setContent import androidx.compose.runtime.Composable import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController +import com.stslex.aproselection.core.navigation.di.moduleCoreNavigation import com.stslex.aproselection.core.ui.theme.AppTheme -import com.stslex.aproselection.di.navigationModule +import com.stslex.aproselection.di.initialAppModule import org.koin.androidx.compose.getKoin class MainActivity : ComponentActivity() { @@ -30,9 +31,11 @@ class MainActivity : ComponentActivity() { private fun SetupComposeDependencies( navController: NavHostController ) { - val navModule = navigationModule(navController) getKoin().loadModules( - listOf(navModule) + listOf( + moduleCoreNavigation(navController), + initialAppModule + ) ) } } diff --git a/app/src/test/java/com/stslex/aproselection/DiKoinModuleTest.kt b/app/src/test/java/com/stslex/aproselection/DiKoinModuleTest.kt index 767eda5..4a089e5 100644 --- a/app/src/test/java/com/stslex/aproselection/DiKoinModuleTest.kt +++ b/app/src/test/java/com/stslex/aproselection/DiKoinModuleTest.kt @@ -3,7 +3,7 @@ package com.stslex.aproselection import android.content.Context import com.stslex.aproselection.core.datastore.coreDataStoreModule import com.stslex.aproselection.core.network.di.ModuleCoreNetwork.moduleCoreNetwork -import com.stslex.aproselection.core.ui.navigation.destination.NavigationScreen +import com.stslex.aproselection.core.navigation.destination.NavigationScreen import com.stslex.aproselection.di.appModule import com.stslex.aproselection.feature.auth.di.ModuleFeatureAuth.moduleFeatureAuth import com.stslex.aproselection.feature.home.di.moduleFeatureHome @@ -19,7 +19,7 @@ class DiKoinModuleTest : KoinTest { @Test fun checkKoinModules() { - val navigator: (screen: NavigationScreen) -> Unit = {} + val navigator: (screen: com.stslex.aproselection.core.navigation.destination.NavigationScreen) -> Unit = {} koinApplication { diff --git a/core/core/.gitignore b/core/core/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/core/core/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/core/build.gradle.kts b/core/core/build.gradle.kts new file mode 100644 index 0000000..f4f2a24 --- /dev/null +++ b/core/core/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("aproselection.android.library") +} + +android.namespace = "com.stslex.aproselection.core.core" \ No newline at end of file diff --git a/core/core/consumer-rules.pro b/core/core/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/core/core/proguard-rules.pro b/core/core/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/core/core/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/core/core/src/androidTest/java/com/stslex/aproselection/core/core/ExampleInstrumentedTest.kt b/core/core/src/androidTest/java/com/stslex/aproselection/core/core/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..8a94dc0 --- /dev/null +++ b/core/core/src/androidTest/java/com/stslex/aproselection/core/core/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.stslex.aproselection.core.core + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.stslex.aproselection.core.core.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/core/src/main/AndroidManifest.xml b/core/core/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/core/core/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/ui/src/main/java/com/stslex/aproselection/core/ui/core/Logger.kt b/core/core/src/main/java/com/stslex/aproselection/core/core/Logger.kt similarity index 64% rename from core/ui/src/main/java/com/stslex/aproselection/core/ui/core/Logger.kt rename to core/core/src/main/java/com/stslex/aproselection/core/core/Logger.kt index 785c443..dabc345 100644 --- a/core/ui/src/main/java/com/stslex/aproselection/core/ui/core/Logger.kt +++ b/core/core/src/main/java/com/stslex/aproselection/core/core/Logger.kt @@ -1,4 +1,4 @@ -package com.stslex.aproselection.core.ui.core +package com.stslex.aproselection.core.core import android.util.Log @@ -18,4 +18,12 @@ object Logger { throwable, ) } + + fun debug( + message: String, + tag: String? = null, + ) { + val currentTag = "$DEFAULT_TAG:${tag.orEmpty()}" + Log.d(currentTag, message) + } } \ No newline at end of file diff --git a/core/core/src/test/java/com/stslex/aproselection/core/core/ExampleUnitTest.kt b/core/core/src/test/java/com/stslex/aproselection/core/core/ExampleUnitTest.kt new file mode 100644 index 0000000..59ce890 --- /dev/null +++ b/core/core/src/test/java/com/stslex/aproselection/core/core/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.stslex.aproselection.core.core + +import org.junit.Test + +import org.junit.Assert.* + +/** + * 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) + } +} \ No newline at end of file diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts index 6067b9d..4a5a2af 100644 --- a/core/datastore/build.gradle.kts +++ b/core/datastore/build.gradle.kts @@ -5,6 +5,7 @@ plugins { android.namespace = "com.stslex.aproselection.core.datastore" dependencies { + implementation(project(":core:core")) implementation(libs.androidx.datastore.preferences) implementation(libs.androidx.datastore) } \ No newline at end of file diff --git a/core/navigation/.gitignore b/core/navigation/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/core/navigation/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts new file mode 100644 index 0000000..08eefc8 --- /dev/null +++ b/core/navigation/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("aproselection.android.library") +} + +android.namespace = "com.stslex.aproselection.core.navigation" + +dependencies { + implementation(project(":core:core")) +} \ No newline at end of file diff --git a/core/navigation/consumer-rules.pro b/core/navigation/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/core/navigation/proguard-rules.pro b/core/navigation/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/core/navigation/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/core/navigation/src/androidTest/java/com/stslex/aproselection/core/navigation/ExampleInstrumentedTest.kt b/core/navigation/src/androidTest/java/com/stslex/aproselection/core/navigation/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..d5aaeed --- /dev/null +++ b/core/navigation/src/androidTest/java/com/stslex/aproselection/core/navigation/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.stslex.aproselection.core.navigation + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.stslex.aproselection.core.navigation.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/navigation/src/main/AndroidManifest.xml b/core/navigation/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/core/navigation/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/destination/AppArguments.kt b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/destination/AppArguments.kt similarity index 85% rename from core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/destination/AppArguments.kt rename to core/navigation/src/main/java/com/stslex/aproselection/core/navigation/destination/AppArguments.kt index d63c38e..60566dc 100644 --- a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/destination/AppArguments.kt +++ b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/destination/AppArguments.kt @@ -1,4 +1,4 @@ -package com.stslex.aproselection.core.ui.navigation.destination +package com.stslex.aproselection.core.navigation.destination sealed class AppArguments { diff --git a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/destination/AppDestination.kt b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/destination/AppDestination.kt similarity index 92% rename from core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/destination/AppDestination.kt rename to core/navigation/src/main/java/com/stslex/aproselection/core/navigation/destination/AppDestination.kt index 0e3db8a..f3e45f8 100644 --- a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/destination/AppDestination.kt +++ b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/destination/AppDestination.kt @@ -1,4 +1,4 @@ -package com.stslex.aproselection.core.ui.navigation.destination +package com.stslex.aproselection.core.navigation.destination enum class AppDestination( vararg val argsNames: String diff --git a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/destination/NavigationScreen.kt b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/destination/NavigationScreen.kt similarity index 92% rename from core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/destination/NavigationScreen.kt rename to core/navigation/src/main/java/com/stslex/aproselection/core/navigation/destination/NavigationScreen.kt index c9ac2be..a687039 100644 --- a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/destination/NavigationScreen.kt +++ b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/destination/NavigationScreen.kt @@ -1,4 +1,4 @@ -package com.stslex.aproselection.core.ui.navigation.destination +package com.stslex.aproselection.core.navigation.destination sealed interface NavigationScreen { diff --git a/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/di/ModuleCoreNavigation.kt b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/di/ModuleCoreNavigation.kt new file mode 100644 index 0000000..1107ca7 --- /dev/null +++ b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/di/ModuleCoreNavigation.kt @@ -0,0 +1,17 @@ +package com.stslex.aproselection.core.navigation.di + +import androidx.navigation.NavHostController +import com.stslex.aproselection.core.navigation.navigator.Navigator +import com.stslex.aproselection.core.navigation.navigator.NavigatorImpl +import org.koin.core.module.Module +import org.koin.dsl.module + +val moduleCoreNavigation: (navHostController: NavHostController) -> Module = { navHostController -> + module { + single { + NavigatorImpl( + navHostController + ) + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/ext/NavExt.kt b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/ext/NavExt.kt similarity index 80% rename from core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/ext/NavExt.kt rename to core/navigation/src/main/java/com/stslex/aproselection/core/navigation/ext/NavExt.kt index 4dfbda9..9973cf7 100644 --- a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/ext/NavExt.kt +++ b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/ext/NavExt.kt @@ -1,10 +1,10 @@ -package com.stslex.aproselection.core.ui.navigation.ext +package com.stslex.aproselection.core.navigation.ext import androidx.navigation.NamedNavArgument import androidx.navigation.NavBackStackEntry import androidx.navigation.NavType import androidx.navigation.navArgument -import com.stslex.aproselection.core.ui.navigation.destination.AppDestination +import com.stslex.aproselection.core.navigation.destination.AppDestination object NavExt { diff --git a/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/navigator/Navigator.kt b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/navigator/Navigator.kt new file mode 100644 index 0000000..e78b4f9 --- /dev/null +++ b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/navigator/Navigator.kt @@ -0,0 +1,9 @@ +package com.stslex.aproselection.core.navigation.navigator + +import com.stslex.aproselection.core.navigation.destination.NavigationScreen + +interface Navigator { + + fun navigate(screen: NavigationScreen) +} + diff --git a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/navigator/NavigatorImpl.kt b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/navigator/NavigatorImpl.kt similarity index 85% rename from core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/navigator/NavigatorImpl.kt rename to core/navigation/src/main/java/com/stslex/aproselection/core/navigation/navigator/NavigatorImpl.kt index adf649e..409b129 100644 --- a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/navigator/NavigatorImpl.kt +++ b/core/navigation/src/main/java/com/stslex/aproselection/core/navigation/navigator/NavigatorImpl.kt @@ -1,7 +1,7 @@ -package com.stslex.aproselection.core.ui.navigation.navigator +package com.stslex.aproselection.core.navigation.navigator import androidx.navigation.NavHostController -import com.stslex.aproselection.core.ui.navigation.destination.NavigationScreen +import com.stslex.aproselection.core.navigation.destination.NavigationScreen class NavigatorImpl( private val navController: NavHostController diff --git a/core/navigation/src/test/java/com/stslex/aproselection/core/navigation/ExampleUnitTest.kt b/core/navigation/src/test/java/com/stslex/aproselection/core/navigation/ExampleUnitTest.kt new file mode 100644 index 0000000..9627662 --- /dev/null +++ b/core/navigation/src/test/java/com/stslex/aproselection/core/navigation/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.stslex.aproselection.core.navigation + +import org.junit.Test + +import org.junit.Assert.* + +/** + * 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) + } +} \ No newline at end of file diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index 9f18b16..54e50fc 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -6,6 +6,7 @@ plugins { } dependencies { + implementation(project(":core:core")) implementation(project(":core:datastore")) implementation(libs.bundles.ktor) implementation(libs.bundles.okhttp) diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/UserNetworkClient.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/UserNetworkClient.kt new file mode 100644 index 0000000..79c9408 --- /dev/null +++ b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/UserNetworkClient.kt @@ -0,0 +1,13 @@ +package com.stslex.aproselection.core.network.clients.user + +import com.stslex.aproselection.core.network.clients.user.model.UpdateUserInfoBody +import com.stslex.aproselection.core.network.clients.user.model.UserResponse + +interface UserNetworkClient { + + suspend fun getUserList(page: Int, pageSize: Int): List + + suspend fun getUser(uuid: String): UserResponse + + suspend fun updateUserInfo(info: UpdateUserInfoBody): UserResponse +} \ No newline at end of file diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/UserNetworkClientImpl.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/UserNetworkClientImpl.kt new file mode 100644 index 0000000..c95f622 --- /dev/null +++ b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/UserNetworkClientImpl.kt @@ -0,0 +1,50 @@ +package com.stslex.aproselection.core.network.clients.user + +import com.stslex.aproselection.core.network.client.NetworkClient +import com.stslex.aproselection.core.network.clients.user.model.UpdateUserInfoBody +import com.stslex.aproselection.core.network.clients.user.model.UserResponse +import com.stslex.aproselection.core.network.model.PagingRequest +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.post +import io.ktor.client.request.setBody +import io.ktor.http.appendPathSegments +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class UserNetworkClientImpl( + private val networkClient: NetworkClient +) : UserNetworkClient { + + override suspend fun getUserList( + page: Int, + pageSize: Int + ): List = withContext(Dispatchers.IO) { + networkClient.apiClient.get { + url.appendPathSegments("user", "list") + setBody( + PagingRequest( + pageNumber = page, + pageSize = pageSize + ) + ) + }.body() + } + + override suspend fun getUser( + uuid: String + ): UserResponse = withContext(Dispatchers.IO) { + networkClient.apiClient.get { + url.appendPathSegments("user", uuid) + }.body() + } + + override suspend fun updateUserInfo( + info: UpdateUserInfoBody + ): UserResponse = withContext(Dispatchers.IO) { + networkClient.apiClient.post { + url.appendPathSegments("user", "update") + setBody(info) + }.body() + } +} \ No newline at end of file diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/model/UpdateUserInfoBody.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/model/UpdateUserInfoBody.kt new file mode 100644 index 0000000..4d81a10 --- /dev/null +++ b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/model/UpdateUserInfoBody.kt @@ -0,0 +1,10 @@ +package com.stslex.aproselection.core.network.clients.user.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UpdateUserInfoBody( + @SerialName("nickname") + val nickname: String +) diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/model/UserResponse.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/model/UserResponse.kt new file mode 100644 index 0000000..cf7e910 --- /dev/null +++ b/core/network/src/main/java/com/stslex/aproselection/core/network/clients/user/model/UserResponse.kt @@ -0,0 +1,14 @@ +package com.stslex.aproselection.core.network.clients.user.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UserResponse( + @SerialName("uuid") + val uuid: String, + @SerialName("username") + val username: String, + @SerialName("nickname") + val nickname: String +) diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/di/ModuleCoreNetwork.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/di/ModuleCoreNetwork.kt index 65b6b0e..ab7e741 100644 --- a/core/network/src/main/java/com/stslex/aproselection/core/network/di/ModuleCoreNetwork.kt +++ b/core/network/src/main/java/com/stslex/aproselection/core/network/di/ModuleCoreNetwork.kt @@ -4,6 +4,8 @@ import com.stslex.aproselection.core.network.client.NetworkClient import com.stslex.aproselection.core.network.client.NetworkClientImpl import com.stslex.aproselection.core.network.clients.auth.AuthNetworkClient import com.stslex.aproselection.core.network.clients.auth.AuthNetworkClientImpl +import com.stslex.aproselection.core.network.clients.user.UserNetworkClient +import com.stslex.aproselection.core.network.clients.user.UserNetworkClientImpl import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.singleOf import org.koin.dsl.module @@ -13,5 +15,6 @@ object ModuleCoreNetwork { val moduleCoreNetwork = module { singleOf(::NetworkClientImpl) { bind() } singleOf(::AuthNetworkClientImpl) { bind() } + singleOf(::UserNetworkClientImpl) { bind() } } } \ No newline at end of file diff --git a/core/network/src/main/java/com/stslex/aproselection/core/network/model/PagingRequest.kt b/core/network/src/main/java/com/stslex/aproselection/core/network/model/PagingRequest.kt new file mode 100644 index 0000000..d2f139a --- /dev/null +++ b/core/network/src/main/java/com/stslex/aproselection/core/network/model/PagingRequest.kt @@ -0,0 +1,12 @@ +package com.stslex.aproselection.core.network.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class PagingRequest( + @SerialName("page_size") + val pageSize: Int, + @SerialName("page_number") + val pageNumber: Int +) diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 5c9862b..cb3c7b8 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -4,3 +4,7 @@ plugins { } android.namespace = "com.stslex.aproselection.core.ui" + +dependencies { + implementation(project(":core:core")) +} diff --git a/core/ui/src/main/java/com/stslex/aproselection/core/ui/base/BasePagingSource.kt b/core/ui/src/main/java/com/stslex/aproselection/core/ui/base/BasePagingSource.kt new file mode 100644 index 0000000..2e3b0b7 --- /dev/null +++ b/core/ui/src/main/java/com/stslex/aproselection/core/ui/base/BasePagingSource.kt @@ -0,0 +1,37 @@ +package com.stslex.aproselection.core.ui.base + +import androidx.paging.PagingSource +import androidx.paging.PagingState + +class BasePagingSource( + private val getItems: suspend (Int, Int) -> List +) : PagingSource() { + + override fun getRefreshKey(state: PagingState): Int? { + val anchorPosition = state.anchorPosition ?: return null + val anchorPage = state.closestPageToPosition(anchorPosition) ?: return null + return anchorPage.prevKey?.plus(1) ?: anchorPage.nextKey?.minus(1) + } + + override suspend fun load(params: LoadParams): LoadResult { + return try { + val pageNumber = params.key ?: INITIAL_PAGE_NUMBER + val pageSize = params.loadSize + + val photos = getItems(pageNumber, pageSize) + val prevKey = pageNumber.takeIf { it > INITIAL_PAGE_NUMBER }?.dec() + val nextKey = pageNumber.takeIf { photos.isNotEmpty() }?.inc() + LoadResult.Page( + data = photos, + prevKey = prevKey, + nextKey = nextKey + ) + } catch (e: Exception) { + LoadResult.Error(e) + } + } + + companion object { + const val INITIAL_PAGE_NUMBER = 1 + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/navigator/Navigator.kt b/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/navigator/Navigator.kt deleted file mode 100644 index f52061e..0000000 --- a/core/ui/src/main/java/com/stslex/aproselection/core/ui/navigation/navigator/Navigator.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.stslex.aproselection.core.ui.navigation.navigator - -import com.stslex.aproselection.core.ui.navigation.destination.NavigationScreen - -interface Navigator { - - fun navigate(screen: NavigationScreen) -} - diff --git a/core/user/.gitignore b/core/user/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/core/user/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/user/build.gradle.kts b/core/user/build.gradle.kts new file mode 100644 index 0000000..e9fbf92 --- /dev/null +++ b/core/user/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("aproselection.android.library") +} + +android.namespace = "com.stslex.aproselection.core.user" + +dependencies { + implementation(project(":core:core")) + implementation(project(":core:network")) +} \ No newline at end of file diff --git a/core/user/consumer-rules.pro b/core/user/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/core/user/proguard-rules.pro b/core/user/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/core/user/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/core/user/src/androidTest/java/com/stslex/aproselection/core/user/ExampleInstrumentedTest.kt b/core/user/src/androidTest/java/com/stslex/aproselection/core/user/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..93c78bf --- /dev/null +++ b/core/user/src/androidTest/java/com/stslex/aproselection/core/user/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.stslex.aproselection.core.user + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.stslex.aproselection.core.user.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/user/src/main/AndroidManifest.xml b/core/user/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/core/user/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/user/src/test/java/com/stslex/aproselection/core/user/ExampleUnitTest.kt b/core/user/src/test/java/com/stslex/aproselection/core/user/ExampleUnitTest.kt new file mode 100644 index 0000000..c5003e7 --- /dev/null +++ b/core/user/src/test/java/com/stslex/aproselection/core/user/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.stslex.aproselection.core.user + +import org.junit.Test + +import org.junit.Assert.* + +/** + * 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) + } +} \ No newline at end of file diff --git a/feature/auth/build.gradle.kts b/feature/auth/build.gradle.kts index 68a9b2b..88dc0ae 100644 --- a/feature/auth/build.gradle.kts +++ b/feature/auth/build.gradle.kts @@ -1,12 +1,12 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { id("aproselection.android.library") id("aproselection.android.library.compose") } dependencies { + implementation(project(":core:core")) implementation(project(":core:ui")) + implementation(project(":core:navigation")) implementation(project(":core:datastore")) implementation(project(":core:network")) } diff --git a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/AuthViewModel.kt b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/AuthViewModel.kt index 9664ed4..42859b8 100644 --- a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/AuthViewModel.kt +++ b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/AuthViewModel.kt @@ -1,9 +1,8 @@ package com.stslex.aproselection.feature.auth.ui import androidx.lifecycle.viewModelScope +import com.stslex.aproselection.core.navigation.navigator.Navigator import com.stslex.aproselection.core.ui.base.BaseViewModel -import com.stslex.aproselection.core.ui.navigation.destination.NavigationScreen -import com.stslex.aproselection.core.ui.navigation.navigator.Navigator import com.stslex.aproselection.feature.auth.domain.interactor.AuthInteractor import com.stslex.aproselection.feature.auth.ui.model.AuthFieldsState import com.stslex.aproselection.feature.auth.ui.model.ErrorType @@ -123,7 +122,7 @@ class AuthViewModel( } .onEach { setLoadingState(ScreenLoadingState.Content) - navigator.navigate(NavigationScreen.Home) + navigator.navigate(com.stslex.aproselection.core.navigation.destination.NavigationScreen.Home) } .launchIn(viewModelScope) } diff --git a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/navigation/AuthRouter.kt b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/navigation/AuthRouter.kt index 7a52558..ae1d8be 100644 --- a/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/navigation/AuthRouter.kt +++ b/feature/auth/src/main/java/com/stslex/aproselection/feature/auth/ui/navigation/AuthRouter.kt @@ -3,7 +3,7 @@ package com.stslex.aproselection.feature.auth.ui.navigation import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable -import com.stslex.aproselection.core.ui.navigation.destination.AppDestination +import com.stslex.aproselection.core.navigation.destination.AppDestination import com.stslex.aproselection.feature.auth.ui.AuthScreen import com.stslex.aproselection.feature.auth.ui.AuthViewModel import com.stslex.aproselection.feature.auth.ui.model.screen.rememberAuthScreenState @@ -13,7 +13,7 @@ fun NavGraphBuilder.authRouter( modifier: Modifier = Modifier, ) { composable( - route = AppDestination.AUTH.navigationRoute + route = com.stslex.aproselection.core.navigation.destination.AppDestination.AUTH.navigationRoute ) { val viewModel: AuthViewModel = koinViewModel() diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index 2a95b30..ef421d4 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -4,7 +4,10 @@ plugins { } dependencies { + implementation(project(":core:core")) implementation(project(":core:ui")) + implementation(project(":core:navigation")) + implementation(project(":core:user")) implementation(project(":core:datastore")) implementation(project(":core:network")) } diff --git a/feature/home/src/main/java/com/stslex/aproselection/feature/home/domain/HomeScreenInteractor.kt b/feature/home/src/main/java/com/stslex/aproselection/feature/home/domain/HomeScreenInteractor.kt new file mode 100644 index 0000000..a63726b --- /dev/null +++ b/feature/home/src/main/java/com/stslex/aproselection/feature/home/domain/HomeScreenInteractor.kt @@ -0,0 +1,8 @@ +package com.stslex.aproselection.feature.home.domain + +import com.stslex.aproselection.feature.home.domain.model.UserDomain + +interface HomeScreenInteractor { + + suspend fun getAllUsers(page: Int, pageSize: Int): List +} \ No newline at end of file diff --git a/feature/home/src/main/java/com/stslex/aproselection/feature/home/domain/model/UserDomain.kt b/feature/home/src/main/java/com/stslex/aproselection/feature/home/domain/model/UserDomain.kt new file mode 100644 index 0000000..61aa3c7 --- /dev/null +++ b/feature/home/src/main/java/com/stslex/aproselection/feature/home/domain/model/UserDomain.kt @@ -0,0 +1,7 @@ +package com.stslex.aproselection.feature.home.domain.model + +data class UserDomain( + val uuid: String, + val username: String, + val nickname: String +) diff --git a/feature/home/src/main/java/com/stslex/aproselection/feature/home/ui/model/UserUi.kt b/feature/home/src/main/java/com/stslex/aproselection/feature/home/ui/model/UserUi.kt new file mode 100644 index 0000000..2f10c93 --- /dev/null +++ b/feature/home/src/main/java/com/stslex/aproselection/feature/home/ui/model/UserUi.kt @@ -0,0 +1,10 @@ +package com.stslex.aproselection.feature.home.ui.model + +import androidx.compose.runtime.Stable + +@Stable +data class UserUi( + val uuid: String, + val username: String, + val nickname: String +) diff --git a/feature/home/src/main/java/com/stslex/aproselection/feature/home/ui/navigation/HomeRouter.kt b/feature/home/src/main/java/com/stslex/aproselection/feature/home/ui/navigation/HomeRouter.kt index 0dc2923..ad3ec71 100644 --- a/feature/home/src/main/java/com/stslex/aproselection/feature/home/ui/navigation/HomeRouter.kt +++ b/feature/home/src/main/java/com/stslex/aproselection/feature/home/ui/navigation/HomeRouter.kt @@ -3,7 +3,7 @@ package com.stslex.aproselection.feature.home.ui.navigation import androidx.compose.ui.Modifier import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable -import com.stslex.aproselection.core.ui.navigation.destination.AppDestination +import com.stslex.aproselection.core.navigation.destination.AppDestination import com.stslex.aproselection.feature.home.ui.HomeScreen import com.stslex.aproselection.feature.home.ui.HomeViewModel import org.koin.androidx.compose.koinViewModel @@ -12,7 +12,7 @@ fun NavGraphBuilder.homeRouter( modifier: Modifier = Modifier, ) { composable( - route = AppDestination.HOME.navigationRoute + route = com.stslex.aproselection.core.navigation.destination.AppDestination.HOME.navigationRoute ) { val viewModel: HomeViewModel = koinViewModel() diff --git a/settings.gradle.kts b/settings.gradle.kts index 99483fc..f2d7ba7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -24,3 +24,6 @@ include(":feature:auth") include(":core:ui") include(":core:datastore") include(":feature:home") +include(":core:user") +include(":core:core") +include(":core:navigation")