diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 08a7394e8..d6b32994b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,12 +10,12 @@ plugins { android { namespace = "dev.arkbuilders.rate" - compileSdk = 34 + compileSdk = libs.versions.compileSdk.get().toInt() defaultConfig { applicationId = "dev.arkbuilders.rate" - minSdk = 26 - targetSdk = 34 + minSdk = libs.versions.minSdk.get().toInt() + targetSdk = libs.versions.compileSdk.get().toInt() versionCode = 3 versionName = "1.2.0" setProperty("archivesBaseName", "ark-rate") @@ -94,7 +94,7 @@ android { compose = true } composeOptions { - kotlinCompilerExtensionVersion = "1.5.4" + kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() } packaging { resources { @@ -104,12 +104,22 @@ android { } dependencies { + implementation(project(":core:di")) + implementation(project(":core:domain")) + implementation(project(":core:data")) + implementation(project(":core:presentation")) + implementation(project(":feature:quick")) + implementation(project(":feature:quickwidget")) + implementation(project(":feature:portfolio")) + implementation(project(":feature:pairalert")) + implementation(project(":feature:search")) + implementation(project(":feature:settings")) implementation(project(":fiaticons")) implementation(project(":cryptoicons")) implementation(libs.ark.about) - implementation(libs.androidx.ui) + implementation(libs.androidx.ui) implementation(libs.navigation.compose) implementation(libs.material3) implementation(libs.androidx.ui.tooling.preview) @@ -117,8 +127,9 @@ dependencies { implementation(libs.androidx.activity.compose) implementation(libs.constraintlayout.compose) - implementation(libs.dagger) implementation(libs.androidx.glance.appwidget) + + implementation(libs.dagger) ksp(libs.dagger.compiler) implementation(libs.androidx.room.runtime) @@ -159,13 +170,6 @@ dependencies { debugImplementation(libs.androidx.ui.test.manifest) } -ktlint { - android.set(true) - outputToConsole.set(true) -} - tasks.getByPath(":app:preBuild").dependsOn("ktlintCheck") tasks.getByPath(":app:preBuild").dependsOn("ktlintFormat") - -tasks.getByPath("ktlintCheck").shouldRunAfter("ktlintFormat") diff --git a/app/google-services.json b/app/google-services.json index de6026a2d..a67ed7c6f 100644 --- a/app/google-services.json +++ b/app/google-services.json @@ -1,13 +1,13 @@ { "project_info": { - "project_number": "414351467383", - "project_id": "ark-builders-test", - "storage_bucket": "ark-builders-test.appspot.com" + "project_number": "659862496072", + "project_id": "ark-rate-test", + "storage_bucket": "ark-rate-test.firebasestorage.app" }, "client": [ { "client_info": { - "mobilesdk_app_id": "1:414351467383:android:be354cff9341b81e623772", + "mobilesdk_app_id": "1:659862496072:android:bd9e57c870395c3802897d", "android_client_info": { "package_name": "dev.arkbuilders.rate" } @@ -15,7 +15,7 @@ "oauth_client": [], "api_key": [ { - "current_key": "AIzaSyCn2k1dAY8sqK4rjsjOd38fOr54FPOAdyY" + "current_key": "AIzaSyDYJv31n-7JnZB5FQ1o16zatujTXbfbbpk" } ], "services": { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 59b5efeea..74ea9e69f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,7 +31,7 @@ - diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/TimestampRepoImpl.kt b/app/src/main/java/dev/arkbuilders/rate/data/repo/TimestampRepoImpl.kt deleted file mode 100644 index 1f7aa0808..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/TimestampRepoImpl.kt +++ /dev/null @@ -1,28 +0,0 @@ -package dev.arkbuilders.rate.data.repo - -import dev.arkbuilders.rate.data.db.dao.TimestampDao -import dev.arkbuilders.rate.data.db.entity.RoomFetchTimestamp -import dev.arkbuilders.rate.domain.model.TimestampType -import dev.arkbuilders.rate.domain.repo.TimestampRepo -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import java.time.OffsetDateTime -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TimestampRepoImpl @Inject constructor(private val dao: TimestampDao) : - TimestampRepo { - override suspend fun rememberTimestamp(type: TimestampType) = - dao.insert(RoomFetchTimestamp(type.name, OffsetDateTime.now())) - - override suspend fun getTimestamp(type: TimestampType) = - dao.getTimestamp( - type.name, - )?.timestamp - - override fun timestampFlow(type: TimestampType): Flow = - dao.timestampFlow(type.name).map { - it?.timestamp - } - } diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/CurrencyDataSource.kt b/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/CurrencyDataSource.kt deleted file mode 100644 index b904e3648..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/CurrencyDataSource.kt +++ /dev/null @@ -1,15 +0,0 @@ -package dev.arkbuilders.rate.data.repo.currency - -import arrow.core.Either -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.domain.model.CurrencyRate -import dev.arkbuilders.rate.domain.model.CurrencyType - -interface CurrencyDataSource { - val currencyType: CurrencyType - - suspend fun fetchRemote(): Either> - - suspend fun getCurrencyName(): Map -} diff --git a/app/src/main/java/dev/arkbuilders/rate/di/AppComponent.kt b/app/src/main/java/dev/arkbuilders/rate/di/AppComponent.kt deleted file mode 100644 index 744f71bce..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/di/AppComponent.kt +++ /dev/null @@ -1,85 +0,0 @@ -package dev.arkbuilders.rate.di - -import android.app.Application -import android.content.Context -import dagger.BindsInstance -import dagger.Component -import dev.arkbuilders.rate.data.repo.PortfolioRepoImpl -import dev.arkbuilders.rate.data.repo.QuickRepoImpl -import dev.arkbuilders.rate.data.repo.currency.CurrencyRepoImpl -import dev.arkbuilders.rate.data.worker.AppWorkerFactory -import dev.arkbuilders.rate.data.worker.CurrencyMonitorWorker -import dev.arkbuilders.rate.di.module.ApiModule -import dev.arkbuilders.rate.di.module.DBModule -import dev.arkbuilders.rate.di.module.RepoModule -import dev.arkbuilders.rate.domain.repo.NetworkStatus -import dev.arkbuilders.rate.domain.repo.Prefs -import dev.arkbuilders.rate.domain.usecase.CalcFrequentCurrUseCase -import dev.arkbuilders.rate.domain.usecase.ConvertWithRateUseCase -import dev.arkbuilders.rate.domain.usecase.GetSortedPinnedQuickPairsUseCase -import dev.arkbuilders.rate.presentation.pairalert.AddPairAlertViewModelFactory -import dev.arkbuilders.rate.presentation.pairalert.PairAlertViewModelFactory -import dev.arkbuilders.rate.presentation.portfolio.AddAssetViewModelFactory -import dev.arkbuilders.rate.presentation.portfolio.EditAssetViewModelFactory -import dev.arkbuilders.rate.presentation.portfolio.PortfolioViewModelFactory -import dev.arkbuilders.rate.presentation.quick.AddQuickViewModelFactory -import dev.arkbuilders.rate.presentation.quick.QuickViewModelFactory -import dev.arkbuilders.rate.presentation.search.SearchViewModelFactory -import dev.arkbuilders.rate.presentation.settings.SettingsViewModelFactory -import javax.inject.Singleton - -@Singleton -@Component( - modules = [ - ApiModule::class, - DBModule::class, - RepoModule::class, - ], -) -interface AppComponent { - fun assetsVMFactory(): PortfolioViewModelFactory - - fun addCurrencyVMFactory(): AddAssetViewModelFactory - - fun addQuickVMFactory(): AddQuickViewModelFactory.Factory - - fun pairAlertVMFactory(): PairAlertViewModelFactory - - fun addPairAlertVMFactory(): AddPairAlertViewModelFactory.Factory - - fun quickVMFactory(): QuickViewModelFactory.Factory - - fun editAssetVMFactory(): EditAssetViewModelFactory.Factory - - fun searchVMFactory(): SearchViewModelFactory.Factory - - fun settingsVMFactory(): SettingsViewModelFactory - - fun appWorkerFactory(): AppWorkerFactory - - fun prefs(): Prefs - - fun networkStatus(): NetworkStatus - - fun generalCurrencyRepo(): CurrencyRepoImpl - - fun assetsRepo(): PortfolioRepoImpl - - fun quickRepo(): QuickRepoImpl - - fun convertUseCase(): ConvertWithRateUseCase - - fun inject(currencyMonitorWorker: CurrencyMonitorWorker) - - fun calcFrequentCurrUseCase(): CalcFrequentCurrUseCase - - fun getSortedPinnedQuickPairsUseCase(): GetSortedPinnedQuickPairsUseCase - - @Component.Factory - interface Factory { - fun create( - @BindsInstance application: Application, - @BindsInstance context: Context, - ): AppComponent - } -} diff --git a/app/src/main/java/dev/arkbuilders/rate/di/DIManager.kt b/app/src/main/java/dev/arkbuilders/rate/di/DIManager.kt deleted file mode 100644 index d0c5aff24..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/di/DIManager.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.arkbuilders.rate.di - -import android.app.Application - -object DIManager { - lateinit var component: AppComponent - private set - - fun init(app: Application) { - component = DaggerAppComponent.factory().create(app, app.applicationContext) - } -} diff --git a/app/src/main/java/dev/arkbuilders/rate/di/module/RepoModule.kt b/app/src/main/java/dev/arkbuilders/rate/di/module/RepoModule.kt deleted file mode 100644 index 2cf0d690a..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/di/module/RepoModule.kt +++ /dev/null @@ -1,52 +0,0 @@ -package dev.arkbuilders.rate.di.module - -import dagger.Binds -import dagger.Module -import dev.arkbuilders.rate.data.network.NetworkStatusImpl -import dev.arkbuilders.rate.data.preferences.PrefsImpl -import dev.arkbuilders.rate.data.repo.AnalyticsManagerImpl -import dev.arkbuilders.rate.data.repo.CodeUseStatRepoImpl -import dev.arkbuilders.rate.data.repo.PairAlertRepoImpl -import dev.arkbuilders.rate.data.repo.PortfolioRepoImpl -import dev.arkbuilders.rate.data.repo.QuickRepoImpl -import dev.arkbuilders.rate.data.repo.TimestampRepoImpl -import dev.arkbuilders.rate.data.repo.currency.CurrencyRepoImpl -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.CodeUseStatRepo -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import dev.arkbuilders.rate.domain.repo.NetworkStatus -import dev.arkbuilders.rate.domain.repo.PairAlertRepo -import dev.arkbuilders.rate.domain.repo.PortfolioRepo -import dev.arkbuilders.rate.domain.repo.Prefs -import dev.arkbuilders.rate.domain.repo.QuickRepo -import dev.arkbuilders.rate.domain.repo.TimestampRepo - -@Module -abstract class RepoModule { - @Binds - abstract fun currencyRepo(repo: CurrencyRepoImpl): CurrencyRepo - - @Binds - abstract fun pairAlertRepo(repo: PairAlertRepoImpl): PairAlertRepo - - @Binds - abstract fun portfolioRepo(repo: PortfolioRepoImpl): PortfolioRepo - - @Binds - abstract fun quickRepo(repo: QuickRepoImpl): QuickRepo - - @Binds - abstract fun prefs(prefs: PrefsImpl): Prefs - - @Binds - abstract fun codeUseStatRepo(codeUseStatRepoImpl: CodeUseStatRepoImpl): CodeUseStatRepo - - @Binds - abstract fun analyticsManager(analyticsManagerImpl: AnalyticsManagerImpl): AnalyticsManager - - @Binds - abstract fun timestampRepo(timestampRepoImpl: TimestampRepoImpl): TimestampRepo - - @Binds - abstract fun networkStatus(networkStatusImpl: NetworkStatusImpl): NetworkStatus -} diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/model/CurrencyCode.kt b/app/src/main/java/dev/arkbuilders/rate/domain/model/CurrencyCode.kt deleted file mode 100644 index e145b263a..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/domain/model/CurrencyCode.kt +++ /dev/null @@ -1,3 +0,0 @@ -package dev.arkbuilders.rate.domain.model - -typealias CurrencyCode = String diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/repo/PairAlertRepo.kt b/app/src/main/java/dev/arkbuilders/rate/domain/repo/PairAlertRepo.kt deleted file mode 100644 index 12f823259..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/domain/repo/PairAlertRepo.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.arkbuilders.rate.domain.repo - -import dev.arkbuilders.rate.domain.model.PairAlert -import kotlinx.coroutines.flow.Flow - -interface PairAlertRepo { - suspend fun insert(pairAlert: PairAlert): Long - - suspend fun getById(id: Long): PairAlert? - - suspend fun getAll(): List - - fun getAllFlow(): Flow> - - suspend fun delete(id: Long): Boolean -} diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/repo/PortfolioRepo.kt b/app/src/main/java/dev/arkbuilders/rate/domain/repo/PortfolioRepo.kt deleted file mode 100644 index dd7c488b0..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/domain/repo/PortfolioRepo.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.arkbuilders.rate.domain.repo - -import dev.arkbuilders.rate.domain.model.Asset -import kotlinx.coroutines.flow.Flow - -interface PortfolioRepo { - suspend fun allAssets(): List - - fun allAssetsFlow(): Flow> - - suspend fun getById(id: Long): Asset? - - suspend fun setAsset(asset: Asset) - - suspend fun setAssetsList(list: List) - - suspend fun removeAsset(id: Long): Boolean -} diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/App.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/App.kt index f17fd36e8..b7524d8f3 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/App.kt +++ b/app/src/main/java/dev/arkbuilders/rate/presentation/App.kt @@ -11,22 +11,37 @@ import androidx.work.WorkManager import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase import dev.arkbuilders.rate.BuildConfig -import dev.arkbuilders.rate.data.worker.CurrencyMonitorWorker -import dev.arkbuilders.rate.data.worker.QuickPairsWidgetRefreshWorker -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.domain.AppConfig -import dev.arkbuilders.rate.domain.repo.PreferenceKey +import dev.arkbuilders.rate.core.di.CoreComponent +import dev.arkbuilders.rate.core.di.CoreComponentProvider +import dev.arkbuilders.rate.core.di.DaggerCoreComponent +import dev.arkbuilders.rate.core.domain.AppConfig +import dev.arkbuilders.rate.core.domain.BuildConfigFields +import dev.arkbuilders.rate.core.domain.repo.PreferenceKey +import dev.arkbuilders.rate.feature.pairalert.data.worker.CurrencyMonitorWorker +import dev.arkbuilders.rate.feature.pairalert.di.PairAlertComponentHolder +import dev.arkbuilders.rate.feature.quickwidget.worker.QuickPairsWidgetRefreshWorker import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import timber.log.Timber import java.util.concurrent.TimeUnit -class App : Application(), Configuration.Provider { +class App : Application(), Configuration.Provider, CoreComponentProvider { + lateinit var coreComponent: CoreComponent + override fun onCreate() { super.onCreate() Timber.plant(Timber.DebugTree()) - DIManager.init(this) + coreComponent = DaggerCoreComponent.factory().create(this, applicationContext) + coreComponent.buildConfigFieldsProvider().init( + BuildConfigFields( + buildType = BuildConfig.BUILD_TYPE, + versionCode = BuildConfig.VERSION_CODE, + versionName = BuildConfig.VERSION_NAME, + isGooglePlayBuild = BuildConfig.GOOGLE_PLAY_BUILD, + ), + ) + instance = this initCrashlytics() initWorker(CurrencyMonitorWorker::class.java, CurrencyMonitorWorker.NAME) @@ -40,7 +55,7 @@ class App : Application(), Configuration.Provider { if (BuildConfig.GOOGLE_PLAY_BUILD) true else - DIManager.component.prefs().get(PreferenceKey.CollectCrashReports) + coreComponent.prefs().get(PreferenceKey.CollectCrashReports) Firebase.crashlytics.setCrashlyticsCollectionEnabled(collect) } @@ -73,6 +88,18 @@ class App : Application(), Configuration.Provider { override fun getWorkManagerConfiguration() = Configuration.Builder() .setMinimumLoggingLevel(android.util.Log.INFO) - .setWorkerFactory(DIManager.component.appWorkerFactory()) + .setWorkerFactory(buildAppWorkerFactory()) .build() + + override fun provideCoreComponent() = coreComponent + + private fun buildAppWorkerFactory() = + AppWorkerFactory( + PairAlertComponentHolder.provide(this).handlePairAlertCheckUseCase(), + coreComponent.timestampRepo(), + ) + + companion object { + lateinit var instance: App + } } diff --git a/app/src/main/java/dev/arkbuilders/rate/data/worker/AppWorkerFactory.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/AppWorkerFactory.kt similarity index 54% rename from app/src/main/java/dev/arkbuilders/rate/data/worker/AppWorkerFactory.kt rename to app/src/main/java/dev/arkbuilders/rate/presentation/AppWorkerFactory.kt index 9ad586c0a..7f40c4c6a 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/worker/AppWorkerFactory.kt +++ b/app/src/main/java/dev/arkbuilders/rate/presentation/AppWorkerFactory.kt @@ -1,13 +1,12 @@ -package dev.arkbuilders.rate.data.worker +package dev.arkbuilders.rate.presentation import androidx.work.DelegatingWorkerFactory -import dev.arkbuilders.rate.domain.repo.TimestampRepo -import dev.arkbuilders.rate.domain.usecase.HandlePairAlertCheckUseCase -import javax.inject.Inject -import javax.inject.Singleton +import dev.arkbuilders.rate.core.domain.repo.TimestampRepo +import dev.arkbuilders.rate.feature.pairalert.data.worker.CurrencyMonitorWorkerFactory +import dev.arkbuilders.rate.feature.pairalert.domain.usecase.HandlePairAlertCheckUseCase +import dev.arkbuilders.rate.feature.quickwidget.worker.QuickPairsWidgetRefreshWorkerFactory -@Singleton -class AppWorkerFactory @Inject constructor( +class AppWorkerFactory( private val handlePairAlertCheckUseCase: HandlePairAlertCheckUseCase, private val timestampRepo: TimestampRepo, ) : DelegatingWorkerFactory() { diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/MainActivity.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/MainActivity.kt index f69afdb07..9266db613 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/MainActivity.kt +++ b/app/src/main/java/dev/arkbuilders/rate/presentation/MainActivity.kt @@ -6,8 +6,8 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.core.view.WindowCompat -import dev.arkbuilders.rate.presentation.quick.glancewidget.QuickPairsWidgetReceiver -import dev.arkbuilders.rate.presentation.theme.ARKRateTheme +import dev.arkbuilders.rate.core.presentation.theme.ARKRateTheme +import dev.arkbuilders.rate.feature.quickwidget.presentation.QuickPairsWidgetReceiver class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/MainScreen.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/MainScreen.kt index 11dd34cf5..cbde7fe92 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/MainScreen.kt +++ b/app/src/main/java/dev/arkbuilders/rate/presentation/MainScreen.kt @@ -1,5 +1,4 @@ @file:OptIn( - ExperimentalMaterialNavigationApi::class, ExperimentalAnimationApi::class, ) @@ -22,28 +21,26 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.compose.currentBackStackEntryAsState -import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi import com.ramcosta.composedestinations.DestinationsNavHost import com.ramcosta.composedestinations.animations.defaults.RootNavGraphDefaultAnimations import com.ramcosta.composedestinations.animations.rememberAnimatedNavHostEngine -import com.ramcosta.composedestinations.navigation.navigate -import com.ramcosta.composedestinations.utils.startDestination -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.presentation.destinations.AddQuickScreenDestination -import dev.arkbuilders.rate.presentation.destinations.PairAlertConditionScreenDestination -import dev.arkbuilders.rate.presentation.destinations.PortfolioScreenDestination -import dev.arkbuilders.rate.presentation.destinations.QuickScreenDestination -import dev.arkbuilders.rate.presentation.destinations.SettingsScreenDestination -import dev.arkbuilders.rate.presentation.quick.glancewidget.action.AddNewPairAction.Companion.ADD_NEW_PAIR -import dev.arkbuilders.rate.presentation.quick.glancewidget.action.AddNewPairAction.Companion.ADD_NEW_PAIR_GROUP_KEY -import dev.arkbuilders.rate.presentation.ui.AnimatedRateBottomNavigation -import dev.arkbuilders.rate.presentation.ui.ConnectivityOfflineSnackbar -import dev.arkbuilders.rate.presentation.ui.ConnectivityOfflineSnackbarVisuals -import dev.arkbuilders.rate.presentation.ui.ConnectivityOnlineSnackbar -import dev.arkbuilders.rate.presentation.ui.ConnectivityOnlineSnackbarVisuals -import dev.arkbuilders.rate.presentation.utils.findActivity -import dev.arkbuilders.rate.presentation.utils.keyboardAsState +import dev.arkbuilders.rate.core.presentation.ui.ConnectivityOfflineSnackbar +import dev.arkbuilders.rate.core.presentation.ui.ConnectivityOfflineSnackbarVisuals +import dev.arkbuilders.rate.core.presentation.ui.ConnectivityOnlineSnackbar +import dev.arkbuilders.rate.core.presentation.ui.ConnectivityOnlineSnackbarVisuals +import dev.arkbuilders.rate.core.presentation.utils.findActivity +import dev.arkbuilders.rate.core.presentation.utils.keyboardAsState +import dev.arkbuilders.rate.feature.pairalert.presentation.destinations.PairAlertConditionScreenDestination +import dev.arkbuilders.rate.feature.portfolio.presentation.destinations.PortfolioScreenDestination +import dev.arkbuilders.rate.feature.quick.presentation.destinations.AddQuickScreenDestination +import dev.arkbuilders.rate.feature.quick.presentation.destinations.QuickScreenDestination +import dev.arkbuilders.rate.feature.quickwidget.presentation.action.AddNewPairAction.Companion.ADD_NEW_PAIR +import dev.arkbuilders.rate.feature.quickwidget.presentation.action.AddNewPairAction.Companion.ADD_NEW_PAIR_GROUP_KEY +import dev.arkbuilders.rate.feature.settings.presentation.destinations.SettingsScreenDestination +import dev.arkbuilders.rate.presentation.navigation.AnimatedRateBottomNavigation +import dev.arkbuilders.rate.presentation.navigation.NavGraphs import kotlinx.coroutines.flow.drop +import timber.log.Timber @Composable fun MainScreen() { @@ -67,7 +64,7 @@ fun MainScreen() { } } LaunchedEffect(key1 = Unit) { - DIManager.component.networkStatus().onlineStatus + App.instance.coreComponent.networkStatus().onlineStatus .drop(1) .collect { online -> val visuals = @@ -83,16 +80,15 @@ fun MainScreen() { val bottomBarVisible = rememberSaveable { mutableStateOf(false) } val navBackStackEntry by navController.currentBackStackEntryAsState() - val destination = - navController.appCurrentDestinationAsState().value - ?: NavGraphs.root.startAppDestination + val currentRoute = navBackStackEntry?.destination?.route ?: NavGraphs.root.startRoute.route + Timber.d("ALLO $currentRoute") bottomBarVisible.value = - when (navBackStackEntry?.destination?.route) { - QuickScreenDestination.route -> true - PortfolioScreenDestination.route -> true - PairAlertConditionScreenDestination.route -> true - SettingsScreenDestination.route -> true + when { + currentRoute.startsWith(QuickScreenDestination.route) -> true + currentRoute.startsWith(PortfolioScreenDestination.route) -> true + currentRoute.startsWith(PairAlertConditionScreenDestination.route) -> true + currentRoute.startsWith(SettingsScreenDestination.route) -> true else -> false } @@ -119,7 +115,7 @@ fun MainScreen() { }, bottomBar = { AnimatedRateBottomNavigation( - currentDestination = destination, + currentRoute = currentRoute, onBottomBarItemClick = { navController.navigate(it) { // Pop up to the start destination of the graph to @@ -144,7 +140,6 @@ fun MainScreen() { navController = navController, navGraph = NavGraphs.root, modifier = Modifier.padding(it), - startRoute = QuickScreenDestination.startDestination, ) } } diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/BottomNavigation.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/navigation/BottomNavigation.kt similarity index 75% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/BottomNavigation.kt rename to app/src/main/java/dev/arkbuilders/rate/presentation/navigation/BottomNavigation.kt index 82ccf0699..63752b75d 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/BottomNavigation.kt +++ b/app/src/main/java/dev/arkbuilders/rate/presentation/navigation/BottomNavigation.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalAnimationApi::class) -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.presentation.navigation import androidx.annotation.DrawableRes import androidx.annotation.StringRes @@ -25,13 +25,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.destinations.Destination -import dev.arkbuilders.rate.presentation.destinations.PairAlertConditionScreenDestination -import dev.arkbuilders.rate.presentation.destinations.PortfolioScreenDestination -import dev.arkbuilders.rate.presentation.destinations.QuickScreenDestination -import dev.arkbuilders.rate.presentation.destinations.SettingsScreenDestination -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.feature.pairalert.presentation.destinations.PairAlertConditionScreenDestination +import dev.arkbuilders.rate.feature.portfolio.presentation.destinations.PortfolioScreenDestination +import dev.arkbuilders.rate.feature.quick.presentation.destinations.QuickScreenDestination +import dev.arkbuilders.rate.feature.settings.presentation.destinations.SettingsScreenDestination sealed class BottomNavItem( @StringRes val title: Int, @@ -40,37 +40,37 @@ sealed class BottomNavItem( val route: String, ) { data object Assets : BottomNavItem( - R.string.bottom_nav_portfolio, - R.drawable.ic_nav_portfolio_disabled, - R.drawable.ic_nav_portfolio_enabled, + CoreRString.bottom_nav_portfolio, + CoreRDrawable.ic_nav_portfolio_disabled, + CoreRDrawable.ic_nav_portfolio_enabled, PortfolioScreenDestination.route, ) data object PairAlert : BottomNavItem( - R.string.bottom_nav_alerts, - R.drawable.ic_nav_alerts_disabled, - R.drawable.ic_nav_alerts_enabled, + CoreRString.bottom_nav_alerts, + CoreRDrawable.ic_nav_alerts_disabled, + CoreRDrawable.ic_nav_alerts_enabled, PairAlertConditionScreenDestination.route, ) data object Quick : BottomNavItem( - R.string.bottom_nav_quick, - R.drawable.ic_nav_quick_disabled, - R.drawable.ic_nav_quick_enabled, + CoreRString.bottom_nav_quick, + CoreRDrawable.ic_nav_quick_disabled, + CoreRDrawable.ic_nav_quick_enabled, QuickScreenDestination.route, ) data object Settings : BottomNavItem( - R.string.bottom_nav_settings, - R.drawable.ic_nav_settings_disabled, - R.drawable.ic_nav_settings_enabled, + CoreRString.bottom_nav_settings, + CoreRDrawable.ic_nav_settings_disabled, + CoreRDrawable.ic_nav_settings_enabled, SettingsScreenDestination.route, ) } @Composable fun AnimatedRateBottomNavigation( - currentDestination: Destination, + currentRoute: String, onBottomBarItemClick: (String) -> Unit, bottomBarVisible: State, ) { @@ -82,13 +82,13 @@ fun AnimatedRateBottomNavigation( }, ) { expanded -> if (expanded) - RateBottomNavigation(currentDestination, onBottomBarItemClick) + RateBottomNavigation(currentRoute, onBottomBarItemClick) } } @Composable fun RateBottomNavigation( - currentDestination: Destination, + currentRoute: String, onBottomBarItemClick: (String) -> Unit, ) { val items = @@ -104,7 +104,7 @@ fun RateBottomNavigation( containerColor = Color.White, ) { items.forEach { item -> - val selected = item.route.contains(currentDestination.route) + val selected = currentRoute.contains(item.route) NavigationBarItem( selected = selected, onClick = { onBottomBarItemClick(item.route) }, diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/navigation/NavGraphs.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/navigation/NavGraphs.kt new file mode 100644 index 000000000..08399f0f6 --- /dev/null +++ b/app/src/main/java/dev/arkbuilders/rate/presentation/navigation/NavGraphs.kt @@ -0,0 +1,38 @@ +package dev.arkbuilders.rate.presentation.navigation + +import com.ramcosta.composedestinations.spec.DestinationSpec +import com.ramcosta.composedestinations.spec.NavGraphSpec +import dev.arkbuilders.rate.feature.pairalert.presentation.destinations.AddPairAlertScreenDestination +import dev.arkbuilders.rate.feature.pairalert.presentation.destinations.PairAlertConditionScreenDestination +import dev.arkbuilders.rate.feature.portfolio.presentation.destinations.AddAssetScreenDestination +import dev.arkbuilders.rate.feature.portfolio.presentation.destinations.EditAssetScreenDestination +import dev.arkbuilders.rate.feature.portfolio.presentation.destinations.PortfolioScreenDestination +import dev.arkbuilders.rate.feature.quick.presentation.destinations.AddQuickScreenDestination +import dev.arkbuilders.rate.feature.quick.presentation.destinations.QuickScreenDestination +import dev.arkbuilders.rate.feature.search.presentation.destinations.SearchCurrencyScreenDestination +import dev.arkbuilders.rate.feature.settings.presentation.destinations.AboutScreenDestination +import dev.arkbuilders.rate.feature.settings.presentation.destinations.SettingsScreenDestination + +// https://github.com/raamcosta/compose-destinations/issues/410 +object NavGraphs { + val root = + object : NavGraphSpec { + override val route = "root" + + override val destinationsByRoute = + listOf>( + QuickScreenDestination, + AddQuickScreenDestination, + PortfolioScreenDestination, + AddAssetScreenDestination, + EditAssetScreenDestination, + PairAlertConditionScreenDestination, + AddPairAlertScreenDestination, + SearchCurrencyScreenDestination, + SettingsScreenDestination, + AboutScreenDestination, + ).associateBy { it.route } + + override val startRoute = QuickScreenDestination + } +} diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/settings/AboutScreen.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/settings/AboutScreen.kt deleted file mode 100644 index 1b46a4a10..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/settings/AboutScreen.kt +++ /dev/null @@ -1,31 +0,0 @@ -package dev.arkbuilders.rate.presentation.settings - -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import dev.arkbuilders.components.about.presentation.ArkAbout -import dev.arkbuilders.rate.BuildConfig -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.ui.AppTopBarBack - -@Destination -@Composable -fun AboutScreen(navigator: DestinationsNavigator) { - Scaffold( - topBar = { - AppTopBarBack(title = stringResource(R.string.about), navigator) - }, - ) { - ArkAbout( - modifier = Modifier.padding(it), - appName = stringResource(id = R.string.app_name), - appLogoResId = R.drawable.ic_about_logo, - versionName = BuildConfig.VERSION_NAME, - privacyPolicyUrl = stringResource(R.string.privacy_policy_url), - ) - } -} diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/settings/QRCryptoDialog.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/settings/QRCryptoDialog.kt deleted file mode 100644 index c65300585..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/settings/QRCryptoDialog.kt +++ /dev/null @@ -1,259 +0,0 @@ -package dev.arkbuilders.rate.presentation.settings - -import android.annotation.SuppressLint -import android.widget.Toast -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts -import androidx.annotation.DrawableRes -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.ClickableText -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.OutlinedButton -import androidx.compose.material3.Text -import androidx.compose.material3.VerticalDivider -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalClipboardManager -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor -import dev.arkbuilders.rate.presentation.utils.openEmail - -@Composable -fun QRCryptoDialog( - visible: Boolean, - title: String, - wallet: String, - fileName: String, - @DrawableRes qrBitmap: Int, - onDismiss: () -> Unit, -) { - if (visible.not()) - return - - Dialog( - onDismissRequest = onDismiss, - properties = DialogProperties(usePlatformDefaultWidth = false), - ) { - Content( - title = title, - wallet = wallet, - fileName = fileName, - qrBitmap = qrBitmap, - onDismiss = onDismiss, - ) - } -} - -@SuppressLint("ResourceType") -@Composable -private fun Content( - title: String, - wallet: String, - fileName: String, - @DrawableRes qrBitmap: Int, - onDismiss: () -> Unit, -) { - val ctx = LocalContext.current - val clipboardManager = LocalClipboardManager.current - val launcher = - rememberLauncherForActivityResult( - ActivityResultContracts.CreateDocument("image/jpg"), - ) { uri -> - uri ?: return@rememberLauncherForActivityResult - val input = ctx.resources.openRawResource(qrBitmap) - ctx.contentResolver.openOutputStream(uri).use { output -> - output?.let { - input.copyTo(output) - } - } - input.close() - } - - Box( - modifier = - Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp) - .background(Color.White, RoundedCornerShape(12.dp)), - ) { - Column( - modifier = - Modifier - .padding(horizontal = 16.dp) - .verticalScroll(rememberScrollState()), - ) { - Row { - Text( - modifier = Modifier.padding(top = 20.dp), - text = title, - fontWeight = FontWeight.SemiBold, - color = ArkColor.TextPrimary, - fontSize = 18.sp, - ) - } - val emailText = - buildAnnotatedString { - append(stringResource(R.string.about_send_email_part_1)) - pushStringAnnotation( - tag = "email", - annotation = ctx.getString(R.string.ark_support_email), - ) - withStyle(style = SpanStyle(textDecoration = TextDecoration.Underline)) { - append(ctx.getString(R.string.ark_support_email)) - } - pop() - append(stringResource(R.string.about_send_email_part_2)) - } - ClickableText( - modifier = Modifier.padding(top = 4.dp), - text = emailText, - style = TextStyle.Default.copy(color = ArkColor.TextTertiary), - ) { offset -> - emailText - .getStringAnnotations(tag = "email", offset, offset) - .firstOrNull() - ?.let { - ctx.openEmail(ctx.getString(R.string.ark_support_email)) - } - } - Image( - modifier = - Modifier - .fillMaxWidth() - .padding(top = 16.dp), - painter = painterResource(qrBitmap), - contentDescription = "", - contentScale = ContentScale.FillWidth, - ) - Row( - modifier = - Modifier - .padding(top = 24.dp) - .fillMaxWidth() - .height(45.dp) - .clip(RoundedCornerShape(8.dp)) - .border(1.dp, ArkColor.Border, RoundedCornerShape(8.dp)), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - modifier = - Modifier - .weight(1f) - .padding(horizontal = 12.dp, vertical = 8.dp), - text = wallet, - fontSize = 16.sp, - maxLines = 1, - color = ArkColor.TextPlaceHolder, - overflow = TextOverflow.Ellipsis, - ) - VerticalDivider( - modifier = Modifier.height(45.dp), - color = ArkColor.Border, - ) - Row( - modifier = - Modifier - .clickable { - clipboardManager.setText(AnnotatedString(wallet)) - Toast - .makeText( - ctx, - ctx.getString(R.string.about_wallet_copied), - Toast.LENGTH_SHORT, - ) - .show() - } - .padding( - horizontal = 16.dp, - vertical = 12.dp, - ), - ) { - Icon( - painter = painterResource(R.drawable.ic_copy), - contentDescription = "", - tint = ArkColor.TextSecondary, - ) - Text( - modifier = Modifier.padding(start = 6.dp), - text = stringResource(R.string.copy), - color = ArkColor.TextSecondary, - fontWeight = FontWeight.SemiBold, - ) - } - } - OutlinedButton( - modifier = - Modifier - .padding(top = 12.dp, bottom = 16.dp) - .fillMaxWidth(), - onClick = { launcher.launch(fileName) }, - border = - BorderStroke( - width = 1.dp, - color = ArkColor.BorderSecondary, - ), - shape = RoundedCornerShape(8.dp), - ) { - Icon( - painter = painterResource(R.drawable.ic_download), - contentDescription = "", - tint = ArkColor.TextSecondary, - ) - Text( - modifier = Modifier.padding(start = 8.dp), - text = stringResource(R.string.about_download_qr_image), - fontWeight = FontWeight.SemiBold, - fontSize = 16.sp, - color = ArkColor.TextSecondary, - ) - } - } - IconButton( - modifier = - Modifier - .padding(end = 12.dp, top = 12.dp) - .align(Alignment.TopEnd), - onClick = { onDismiss() }, - ) { - Icon( - modifier = Modifier, - painter = painterResource(id = R.drawable.ic_close), - contentDescription = "", - tint = ArkColor.FGQuinary, - ) - } - } -} diff --git a/app/src/main/java/dev/arkbuilders/rate/utils/CollectionsUtils.kt b/app/src/main/java/dev/arkbuilders/rate/utils/CollectionsUtils.kt deleted file mode 100644 index ac0a0150a..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/utils/CollectionsUtils.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.arkbuilders.rate.utils - -fun List.replace( - targetItem: T, - newItem: T, -) = map { - if (it == targetItem) { - newItem - } else { - it - } -} diff --git a/app/src/main/java/dev/arkbuilders/rate/utils/Constants.kt b/app/src/main/java/dev/arkbuilders/rate/utils/Constants.kt deleted file mode 100644 index 086540fed..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/utils/Constants.kt +++ /dev/null @@ -1,4 +0,0 @@ -package dev.arkbuilders.rate.utils - -// shared preferences -const val CRASH_REPORT_ENABLE = "crash_report_enable" diff --git a/app/src/main/java/dev/arkbuilders/rate/utils/CoroutineUtils.kt b/app/src/main/java/dev/arkbuilders/rate/utils/CoroutineUtils.kt deleted file mode 100644 index 075f0521d..000000000 --- a/app/src/main/java/dev/arkbuilders/rate/utils/CoroutineUtils.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.arkbuilders.rate.utils - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.withContext -import kotlin.coroutines.CoroutineContext - -suspend fun withContextAndLock( - context: CoroutineContext, - mutex: Mutex, - block: suspend CoroutineScope.() -> T, -): T = - withContext(context) { - mutex.withLock { - block() - } - } diff --git a/app/src/main/res/drawable/ic_about_coffee.xml b/app/src/main/res/drawable/ic_about_coffee.xml deleted file mode 100644 index cff5abf5f..000000000 --- a/app/src/main/res/drawable/ic_about_coffee.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_about_discord.xml b/app/src/main/res/drawable/ic_about_discord.xml deleted file mode 100644 index b41f6cbcb..000000000 --- a/app/src/main/res/drawable/ic_about_discord.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_about_patreon.xml b/app/src/main/res/drawable/ic_about_patreon.xml deleted file mode 100644 index e6aa6e2ef..000000000 --- a/app/src/main/res/drawable/ic_about_patreon.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_about_site.xml b/app/src/main/res/drawable/ic_about_site.xml deleted file mode 100644 index 8a2bbd891..000000000 --- a/app/src/main/res/drawable/ic_about_site.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_about_telegram.xml b/app/src/main/res/drawable/ic_about_telegram.xml deleted file mode 100644 index 03c2dae94..000000000 --- a/app/src/main/res/drawable/ic_about_telegram.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/qr_btc.jpg b/app/src/main/res/drawable/qr_btc.jpg deleted file mode 100644 index c6c022979..000000000 Binary files a/app/src/main/res/drawable/qr_btc.jpg and /dev/null differ diff --git a/app/src/main/res/drawable/qr_eth.jpg b/app/src/main/res/drawable/qr_eth.jpg deleted file mode 100644 index 7607b3e85..000000000 Binary files a/app/src/main/res/drawable/qr_eth.jpg and /dev/null differ diff --git a/app/src/main/res/xml/glance_widget_provider.xml b/app/src/main/res/xml/glance_widget_provider.xml index b02129196..8dcc62490 100644 --- a/app/src/main/res/xml/glance_widget_provider.xml +++ b/app/src/main/res/xml/glance_widget_provider.xml @@ -2,12 +2,12 @@ \ No newline at end of file + android:targetCellHeight="3" /> diff --git a/build.gradle.kts b/build.gradle.kts index 7e2642b51..dfe74c414 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,10 +1,21 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { id("com.android.application") version "8.1.3" apply false - id("com.android.library") version "8.1.3" apply false - id("org.jetbrains.kotlin.android") version "1.9.20" apply false - id("com.google.devtools.ksp") version "1.9.22-1.0.17" + alias(libs.plugins.android.library) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.ksp) id("com.google.gms.google-services") version "4.4.2" apply false id("com.google.firebase.crashlytics") version "3.0.1" apply false id("org.jlleitschuh.gradle.ktlint") version "12.1.1" } + +allprojects { + plugins.apply("org.jlleitschuh.gradle.ktlint") +} + +ktlint { + android.set(true) + outputToConsole.set(true) +} + +tasks.getByPath("ktlintCheck").shouldRunAfter("ktlintFormat") diff --git a/core/data/.gitignore b/core/data/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/core/data/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts new file mode 100644 index 000000000..caa5bd326 --- /dev/null +++ b/core/data/build.gradle.kts @@ -0,0 +1,70 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) +} + +android { + namespace = "dev.arkbuilders.rate.core.data" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + 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_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(project(":core:domain")) + implementation(project(":core:db")) + + implementation(libs.androidx.core.ktx) + + implementation(libs.androidx.datastore.preferences) + implementation(libs.androidx.work.runtime.ktx) + + implementation(libs.dagger) + ksp(libs.dagger.compiler) + + implementation(libs.androidx.room.runtime) + implementation(libs.androidx.room.ktx) + + implementation(libs.timber) + + implementation(libs.arrow.core) + implementation(libs.arrow.fx.coroutines) + + implementation(libs.retrofit) + implementation(libs.converter.gson) + implementation(libs.logging.interceptor) + + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.analytics) + implementation(libs.firebase.crashlytics) + + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":core:data:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":core:data:preBuild").dependsOn("ktlintFormat") diff --git a/core/data/consumer-rules.pro b/core/data/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/core/data/proguard-rules.pro b/core/data/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/core/data/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/data/schemas/dev.arkbuilders.rate.core.data.db.Database/14.json b/core/data/schemas/dev.arkbuilders.rate.core.data.db.Database/14.json new file mode 100644 index 000000000..b71d294f0 --- /dev/null +++ b/core/data/schemas/dev.arkbuilders.rate.core.data.db.Database/14.json @@ -0,0 +1,272 @@ +{ + "formatVersion": 1, + "database": { + "version": 14, + "identityHash": "4309cb10f28a58d6c6611b5b58732a82", + "entities": [ + { + "tableName": "RoomAsset", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `code` TEXT NOT NULL, `amount` TEXT NOT NULL, `group` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RoomCurrencyRate", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`code` TEXT NOT NULL, `currencyType` TEXT NOT NULL, `rate` TEXT NOT NULL, PRIMARY KEY(`code`))", + "fields": [ + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currencyType", + "columnName": "currencyType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "code" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RoomFetchTimestamp", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `timestamp` TEXT NOT NULL, PRIMARY KEY(`type`))", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "type" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RoomPairAlert", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `targetCode` TEXT NOT NULL, `baseCode` TEXT NOT NULL, `targetPrice` TEXT NOT NULL, `startPrice` TEXT NOT NULL, `alertPercent` REAL, `oneTimeNotRecurrent` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `lastDateTriggered` TEXT, `group` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "targetCode", + "columnName": "targetCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "baseCode", + "columnName": "baseCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "targetPrice", + "columnName": "targetPrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "startPrice", + "columnName": "startPrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "alertPercent", + "columnName": "alertPercent", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "oneTimeNotRecurrent", + "columnName": "oneTimeNotRecurrent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastDateTriggered", + "columnName": "lastDateTriggered", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RoomQuickPair", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `from` TEXT NOT NULL, `amount` TEXT NOT NULL, `to` TEXT NOT NULL, `calculatedDate` TEXT NOT NULL, `pinnedDate` TEXT, `group` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "from", + "columnName": "from", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "to", + "columnName": "to", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "calculatedDate", + "columnName": "calculatedDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pinnedDate", + "columnName": "pinnedDate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RoomCodeUseStat", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`code` TEXT NOT NULL, `count` INTEGER NOT NULL, `lastUsedDate` TEXT NOT NULL, PRIMARY KEY(`code`))", + "fields": [ + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "count", + "columnName": "count", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUsedDate", + "columnName": "lastUsedDate", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "code" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4309cb10f28a58d6c6611b5b58732a82')" + ] + } +} \ No newline at end of file diff --git a/fiaticons/src/androidTest/java/dev/arkbuilders/rate/fiaticons/ExampleInstrumentedTest.kt b/core/data/src/androidTest/java/dev/arkbuilders/rate/core/data/ExampleInstrumentedTest.kt similarity index 79% rename from fiaticons/src/androidTest/java/dev/arkbuilders/rate/fiaticons/ExampleInstrumentedTest.kt rename to core/data/src/androidTest/java/dev/arkbuilders/rate/core/data/ExampleInstrumentedTest.kt index b85253043..15558e081 100644 --- a/fiaticons/src/androidTest/java/dev/arkbuilders/rate/fiaticons/ExampleInstrumentedTest.kt +++ b/core/data/src/androidTest/java/dev/arkbuilders/rate/core/data/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ -package dev.arkbuilders.rate.fiaticons +package dev.arkbuilders.rate.core.data -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * @@ -19,6 +17,6 @@ class ExampleInstrumentedTest { fun useAppContext() { // Context of the app under test. val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("dev.arkbuilders.rate.fiaticons.test", appContext.packageName) + assertEquals("dev.arkbuilders.rate.core.data.test", appContext.packageName) } -} \ No newline at end of file +} diff --git a/core/data/src/main/AndroidManifest.xml b/core/data/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/core/data/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/java/dev/arkbuilders/rate/data/network/NetworkStatusImpl.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/NetworkStatusImpl.kt similarity index 91% rename from app/src/main/java/dev/arkbuilders/rate/data/network/NetworkStatusImpl.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/network/NetworkStatusImpl.kt index b03be7fc1..568012714 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/network/NetworkStatusImpl.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/NetworkStatusImpl.kt @@ -1,19 +1,18 @@ -package dev.arkbuilders.rate.data.network +package dev.arkbuilders.rate.core.data.network +import android.annotation.SuppressLint import android.content.Context import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest import android.os.Build -import dev.arkbuilders.rate.domain.repo.NetworkStatus +import dev.arkbuilders.rate.core.domain.repo.NetworkStatus import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import javax.inject.Inject -import javax.inject.Singleton -@Singleton -class NetworkStatusImpl @Inject constructor( +@SuppressLint("MissingPermission") +class NetworkStatusImpl( private val context: Context, ) : NetworkStatus { private val cm = diff --git a/app/src/main/java/dev/arkbuilders/rate/data/network/OkHttpClientBuilder.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/OkHttpClientBuilder.kt similarity index 94% rename from app/src/main/java/dev/arkbuilders/rate/data/network/OkHttpClientBuilder.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/network/OkHttpClientBuilder.kt index f3edca0e8..54e654e89 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/network/OkHttpClientBuilder.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/OkHttpClientBuilder.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.data.network +package dev.arkbuilders.rate.core.data.network import android.content.Context import android.webkit.WebSettings diff --git a/app/src/main/java/dev/arkbuilders/rate/data/network/api/CryptoAPI.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/api/CryptoAPI.kt similarity index 59% rename from app/src/main/java/dev/arkbuilders/rate/data/network/api/CryptoAPI.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/network/api/CryptoAPI.kt index 178ce4777..313391d5f 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/network/api/CryptoAPI.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/api/CryptoAPI.kt @@ -1,6 +1,6 @@ -package dev.arkbuilders.rate.data.network.api +package dev.arkbuilders.rate.core.data.network.api -import dev.arkbuilders.rate.data.network.dto.CryptoRateResponse +import dev.arkbuilders.rate.core.data.network.dto.CryptoRateResponse import retrofit2.http.GET interface CryptoAPI { diff --git a/app/src/main/java/dev/arkbuilders/rate/data/network/api/FiatAPI.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/api/FiatAPI.kt similarity index 56% rename from app/src/main/java/dev/arkbuilders/rate/data/network/api/FiatAPI.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/network/api/FiatAPI.kt index 0d24cba55..a3de37b58 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/network/api/FiatAPI.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/api/FiatAPI.kt @@ -1,6 +1,6 @@ -package dev.arkbuilders.rate.data.network.api +package dev.arkbuilders.rate.core.data.network.api -import dev.arkbuilders.rate.data.network.dto.FiatRateResponse +import dev.arkbuilders.rate.core.data.network.dto.FiatRateResponse import retrofit2.http.GET interface FiatAPI { diff --git a/app/src/main/java/dev/arkbuilders/rate/data/network/dto/CryptoRateResponse.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/dto/CryptoRateResponse.kt similarity index 71% rename from app/src/main/java/dev/arkbuilders/rate/data/network/dto/CryptoRateResponse.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/network/dto/CryptoRateResponse.kt index b5bd628be..1cc34c00c 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/network/dto/CryptoRateResponse.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/dto/CryptoRateResponse.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.data.network.dto +package dev.arkbuilders.rate.core.data.network.dto import androidx.annotation.Keep diff --git a/app/src/main/java/dev/arkbuilders/rate/data/network/dto/FiatRateResponse.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/dto/FiatRateResponse.kt similarity index 72% rename from app/src/main/java/dev/arkbuilders/rate/data/network/dto/FiatRateResponse.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/network/dto/FiatRateResponse.kt index 86aa05d8f..b06b289d1 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/network/dto/FiatRateResponse.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/network/dto/FiatRateResponse.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.data.network.dto +package dev.arkbuilders.rate.core.data.network.dto import androidx.annotation.Keep diff --git a/app/src/main/java/dev/arkbuilders/rate/data/preferences/PrefsImpl.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/preferences/PrefsImpl.kt similarity index 85% rename from app/src/main/java/dev/arkbuilders/rate/data/preferences/PrefsImpl.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/preferences/PrefsImpl.kt index dd641020f..d02cf8fa7 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/preferences/PrefsImpl.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/preferences/PrefsImpl.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.data.preferences +package dev.arkbuilders.rate.core.data.preferences import android.content.Context import androidx.datastore.preferences.core.Preferences @@ -6,15 +6,12 @@ import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore -import dev.arkbuilders.rate.domain.repo.PreferenceKey -import dev.arkbuilders.rate.domain.repo.Prefs +import dev.arkbuilders.rate.core.domain.repo.PreferenceKey +import dev.arkbuilders.rate.core.domain.repo.Prefs import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map -import javax.inject.Inject -import javax.inject.Singleton -@Singleton -class PrefsImpl @Inject constructor(val context: Context) : Prefs { +class PrefsImpl(val context: Context) : Prefs { private val sharedPreferencesKey = "user_preferences" private val Context.preferencesDatastore by preferencesDataStore( diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/AnalyticsManagerImpl.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/AnalyticsManagerImpl.kt similarity index 73% rename from app/src/main/java/dev/arkbuilders/rate/data/repo/AnalyticsManagerImpl.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/AnalyticsManagerImpl.kt index 4c34b5552..a12e2acce 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/AnalyticsManagerImpl.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/AnalyticsManagerImpl.kt @@ -1,20 +1,17 @@ -package dev.arkbuilders.rate.data.repo +package dev.arkbuilders.rate.core.data.repo import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.analytics.ktx.analytics import com.google.firebase.analytics.logEvent import com.google.firebase.ktx.Firebase -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.PreferenceKey -import dev.arkbuilders.rate.domain.repo.Prefs +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.PreferenceKey +import dev.arkbuilders.rate.core.domain.repo.Prefs import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import javax.inject.Inject -import javax.inject.Singleton -@Singleton -class AnalyticsManagerImpl @Inject constructor( +class AnalyticsManagerImpl( private val prefs: Prefs, ) : AnalyticsManager { private val scope = CoroutineScope(Dispatchers.IO) diff --git a/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/BuildConfigFieldsProviderImpl.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/BuildConfigFieldsProviderImpl.kt new file mode 100644 index 000000000..d35122bf1 --- /dev/null +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/BuildConfigFieldsProviderImpl.kt @@ -0,0 +1,18 @@ +package dev.arkbuilders.rate.core.data.repo + +import dev.arkbuilders.rate.core.domain.BuildConfigFields +import dev.arkbuilders.rate.core.domain.BuildConfigFieldsProvider + +class BuildConfigFieldsProviderImpl : BuildConfigFieldsProvider { + private var fields: BuildConfigFields? = null + + @Synchronized + override fun init(fields: BuildConfigFields) { + this.fields = fields + } + + @Synchronized + override fun provide(): BuildConfigFields { + return fields ?: error("BuildConfigFields not initialized") + } +} diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/CodeUseStatRepoImpl.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/CodeUseStatRepoImpl.kt similarity index 68% rename from app/src/main/java/dev/arkbuilders/rate/data/repo/CodeUseStatRepoImpl.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/CodeUseStatRepoImpl.kt index 272341bfb..227cf8483 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/CodeUseStatRepoImpl.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/CodeUseStatRepoImpl.kt @@ -1,18 +1,15 @@ -package dev.arkbuilders.rate.data.repo +package dev.arkbuilders.rate.core.data.repo -import dev.arkbuilders.rate.data.db.dao.CodeUseStatDao -import dev.arkbuilders.rate.data.db.entity.RoomCodeUseStat -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.stats.CodeUseStat -import dev.arkbuilders.rate.domain.repo.CodeUseStatRepo +import dev.arkbuilders.rate.core.db.dao.CodeUseStatDao +import dev.arkbuilders.rate.core.db.entity.RoomCodeUseStat +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.stats.CodeUseStat +import dev.arkbuilders.rate.core.domain.repo.CodeUseStatRepo import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import java.time.OffsetDateTime -import javax.inject.Inject -import javax.inject.Singleton -@Singleton -class CodeUseStatRepoImpl @Inject constructor( +class CodeUseStatRepoImpl( private val dao: CodeUseStatDao, ) : CodeUseStatRepo { override suspend fun codesUsed(vararg codes: CurrencyCode) { diff --git a/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/TimestampRepoImpl.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/TimestampRepoImpl.kt new file mode 100644 index 000000000..0c578c0ca --- /dev/null +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/TimestampRepoImpl.kt @@ -0,0 +1,32 @@ +package dev.arkbuilders.rate.core.data.repo + +import dev.arkbuilders.rate.core.db.dao.TimestampDao +import dev.arkbuilders.rate.core.db.entity.RoomFetchTimestamp +import dev.arkbuilders.rate.core.domain.model.TimestampType +import dev.arkbuilders.rate.core.domain.repo.TimestampRepo +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import java.time.OffsetDateTime +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TimestampRepoImpl @Inject constructor(private val dao: TimestampDao) : TimestampRepo { + override suspend fun rememberTimestamp(type: TimestampType) = + dao.insert( + RoomFetchTimestamp( + type.name, + OffsetDateTime.now(), + ), + ) + + override suspend fun getTimestamp(type: TimestampType) = + dao.getTimestamp( + type.name, + )?.timestamp + + override fun timestampFlow(type: TimestampType): Flow = + dao.timestampFlow(type.name).map { + it?.timestamp + } +} diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/CryptoCurrencyDataSource.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/CryptoCurrencyDataSource.kt similarity index 72% rename from app/src/main/java/dev/arkbuilders/rate/data/repo/currency/CryptoCurrencyDataSource.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/CryptoCurrencyDataSource.kt index 06a3b64cf..d33089fce 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/CryptoCurrencyDataSource.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/CryptoCurrencyDataSource.kt @@ -1,13 +1,13 @@ -package dev.arkbuilders.rate.data.repo.currency +package dev.arkbuilders.rate.core.data.repo.currency import arrow.core.Either import arrow.core.left import arrow.core.right -import dev.arkbuilders.rate.data.network.api.CryptoAPI -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.domain.model.CurrencyRate -import dev.arkbuilders.rate.domain.model.CurrencyType +import dev.arkbuilders.rate.core.data.network.api.CryptoAPI +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.domain.model.CurrencyRate +import dev.arkbuilders.rate.core.domain.model.CurrencyType import java.math.BigDecimal import javax.inject.Inject import javax.inject.Singleton diff --git a/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/CurrencyDataSource.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/CurrencyDataSource.kt new file mode 100644 index 000000000..081d3ac5b --- /dev/null +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/CurrencyDataSource.kt @@ -0,0 +1,15 @@ +package dev.arkbuilders.rate.core.data.repo.currency + +import arrow.core.Either +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.domain.model.CurrencyRate +import dev.arkbuilders.rate.core.domain.model.CurrencyType + +interface CurrencyDataSource { + val currencyType: CurrencyType + + suspend fun fetchRemote(): Either> + + suspend fun getCurrencyName(): Map +} diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/CurrencyRepoImpl.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/CurrencyRepoImpl.kt similarity index 86% rename from app/src/main/java/dev/arkbuilders/rate/data/repo/currency/CurrencyRepoImpl.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/CurrencyRepoImpl.kt index 1fd6ecf5b..8d7bb1153 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/CurrencyRepoImpl.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/CurrencyRepoImpl.kt @@ -1,16 +1,16 @@ -package dev.arkbuilders.rate.data.repo.currency +package dev.arkbuilders.rate.core.data.repo.currency import arrow.core.Either import arrow.core.left import arrow.core.right -import dev.arkbuilders.rate.domain.AppConfig -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.domain.model.CurrencyRate -import dev.arkbuilders.rate.domain.model.CurrencyType -import dev.arkbuilders.rate.domain.model.TimestampType -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import dev.arkbuilders.rate.domain.repo.NetworkStatus -import dev.arkbuilders.rate.domain.repo.TimestampRepo +import dev.arkbuilders.rate.core.domain.AppConfig +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.domain.model.CurrencyRate +import dev.arkbuilders.rate.core.domain.model.CurrencyType +import dev.arkbuilders.rate.core.domain.model.TimestampType +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.repo.NetworkStatus +import dev.arkbuilders.rate.core.domain.repo.TimestampRepo import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/FiatCurrencyDataSource.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/FiatCurrencyDataSource.kt similarity index 94% rename from app/src/main/java/dev/arkbuilders/rate/data/repo/currency/FiatCurrencyDataSource.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/FiatCurrencyDataSource.kt index e90cfd36a..9ad4c35c3 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/FiatCurrencyDataSource.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/FiatCurrencyDataSource.kt @@ -1,14 +1,14 @@ -package dev.arkbuilders.rate.data.repo.currency +package dev.arkbuilders.rate.core.data.repo.currency import arrow.core.Either import arrow.core.left import arrow.core.right -import dev.arkbuilders.rate.data.divideArk -import dev.arkbuilders.rate.data.network.api.FiatAPI -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.domain.model.CurrencyRate -import dev.arkbuilders.rate.domain.model.CurrencyType +import dev.arkbuilders.rate.core.data.network.api.FiatAPI +import dev.arkbuilders.rate.core.domain.divideArk +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.domain.model.CurrencyRate +import dev.arkbuilders.rate.core.domain.model.CurrencyType import java.math.BigDecimal import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/LocalCurrencyDataSource.kt b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/LocalCurrencyDataSource.kt similarity index 58% rename from app/src/main/java/dev/arkbuilders/rate/data/repo/currency/LocalCurrencyDataSource.kt rename to core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/LocalCurrencyDataSource.kt index ed5af4985..a33fda3ad 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/currency/LocalCurrencyDataSource.kt +++ b/core/data/src/main/java/dev/arkbuilders/rate/core/data/repo/currency/LocalCurrencyDataSource.kt @@ -1,9 +1,9 @@ -package dev.arkbuilders.rate.data.repo.currency +package dev.arkbuilders.rate.core.data.repo.currency -import dev.arkbuilders.rate.data.db.dao.CurrencyRateDao -import dev.arkbuilders.rate.data.db.entity.RoomCurrencyRate -import dev.arkbuilders.rate.domain.model.CurrencyRate -import dev.arkbuilders.rate.domain.model.CurrencyType +import dev.arkbuilders.rate.core.db.dao.CurrencyRateDao +import dev.arkbuilders.rate.core.db.entity.RoomCurrencyRate +import dev.arkbuilders.rate.core.domain.model.CurrencyRate +import dev.arkbuilders.rate.core.domain.model.CurrencyType import javax.inject.Inject class LocalCurrencyDataSource @Inject constructor(val dao: CurrencyRateDao) { @@ -19,4 +19,4 @@ class LocalCurrencyDataSource @Inject constructor(val dao: CurrencyRateDao) { private fun RoomCurrencyRate.toCurrencyRate() = CurrencyRate(CurrencyType.valueOf(currencyType), code, rate) -private fun CurrencyRate.toRoom() = RoomCurrencyRate(code, type.name, rate) +private fun CurrencyRate.toRoom(): RoomCurrencyRate = RoomCurrencyRate(code, type.name, rate) diff --git a/core/data/src/test/java/dev/arkbuilders/rate/core/data/ExampleUnitTest.kt b/core/data/src/test/java/dev/arkbuilders/rate/core/data/ExampleUnitTest.kt new file mode 100644 index 000000000..cfce2de16 --- /dev/null +++ b/core/data/src/test/java/dev/arkbuilders/rate/core/data/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package dev.arkbuilders.rate.core.data + +import org.junit.Assert.assertEquals +import org.junit.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) + } +} diff --git a/core/db/.gitignore b/core/db/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/core/db/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/db/build.gradle.kts b/core/db/build.gradle.kts new file mode 100644 index 000000000..ffbeec023 --- /dev/null +++ b/core/db/build.gradle.kts @@ -0,0 +1,57 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) +} + +android { + namespace = "dev.arkbuilders.rate.core.db" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + + ksp { + arg("room.schemaLocation", "$projectDir/schemas") + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(project(":core:domain")) + + implementation(libs.androidx.core.ktx) + implementation(libs.gson) + + implementation(libs.androidx.room.runtime) + implementation(libs.androidx.room.ktx) + ksp(libs.androidx.room.compiler) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":core:db:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":core:db:preBuild").dependsOn("ktlintFormat") diff --git a/core/db/consumer-rules.pro b/core/db/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/core/db/proguard-rules.pro b/core/db/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/core/db/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/db/schemas/dev.arkbuilders.rate.core.db.Database/14.json b/core/db/schemas/dev.arkbuilders.rate.core.db.Database/14.json new file mode 100644 index 000000000..b71d294f0 --- /dev/null +++ b/core/db/schemas/dev.arkbuilders.rate.core.db.Database/14.json @@ -0,0 +1,272 @@ +{ + "formatVersion": 1, + "database": { + "version": 14, + "identityHash": "4309cb10f28a58d6c6611b5b58732a82", + "entities": [ + { + "tableName": "RoomAsset", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `code` TEXT NOT NULL, `amount` TEXT NOT NULL, `group` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RoomCurrencyRate", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`code` TEXT NOT NULL, `currencyType` TEXT NOT NULL, `rate` TEXT NOT NULL, PRIMARY KEY(`code`))", + "fields": [ + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currencyType", + "columnName": "currencyType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "code" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RoomFetchTimestamp", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `timestamp` TEXT NOT NULL, PRIMARY KEY(`type`))", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "type" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RoomPairAlert", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `targetCode` TEXT NOT NULL, `baseCode` TEXT NOT NULL, `targetPrice` TEXT NOT NULL, `startPrice` TEXT NOT NULL, `alertPercent` REAL, `oneTimeNotRecurrent` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `lastDateTriggered` TEXT, `group` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "targetCode", + "columnName": "targetCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "baseCode", + "columnName": "baseCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "targetPrice", + "columnName": "targetPrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "startPrice", + "columnName": "startPrice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "alertPercent", + "columnName": "alertPercent", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "oneTimeNotRecurrent", + "columnName": "oneTimeNotRecurrent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastDateTriggered", + "columnName": "lastDateTriggered", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RoomQuickPair", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `from` TEXT NOT NULL, `amount` TEXT NOT NULL, `to` TEXT NOT NULL, `calculatedDate` TEXT NOT NULL, `pinnedDate` TEXT, `group` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "from", + "columnName": "from", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "to", + "columnName": "to", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "calculatedDate", + "columnName": "calculatedDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pinnedDate", + "columnName": "pinnedDate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "RoomCodeUseStat", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`code` TEXT NOT NULL, `count` INTEGER NOT NULL, `lastUsedDate` TEXT NOT NULL, PRIMARY KEY(`code`))", + "fields": [ + { + "fieldPath": "code", + "columnName": "code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "count", + "columnName": "count", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUsedDate", + "columnName": "lastUsedDate", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "code" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4309cb10f28a58d6c6611b5b58732a82')" + ] + } +} \ No newline at end of file diff --git a/core/db/src/androidTest/java/dev/arkbuilders/rate/core/db/ExampleInstrumentedTest.kt b/core/db/src/androidTest/java/dev/arkbuilders/rate/core/db/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..67ae6e6c4 --- /dev/null +++ b/core/db/src/androidTest/java/dev/arkbuilders/rate/core/db/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package dev.arkbuilders.rate.core.db + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * 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("dev.arkbuilders.rate.core.db.test", appContext.packageName) + } +} diff --git a/core/db/src/main/AndroidManifest.xml b/core/db/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/core/db/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/Database.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/Database.kt similarity index 50% rename from app/src/main/java/dev/arkbuilders/rate/data/db/Database.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/Database.kt index 04a7dd071..3b44c1061 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/Database.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/Database.kt @@ -1,22 +1,22 @@ -package dev.arkbuilders.rate.data.db +package dev.arkbuilders.rate.core.db import androidx.room.RoomDatabase import androidx.room.TypeConverters -import dev.arkbuilders.rate.data.db.dao.CodeUseStatDao -import dev.arkbuilders.rate.data.db.dao.CurrencyRateDao -import dev.arkbuilders.rate.data.db.dao.PairAlertDao -import dev.arkbuilders.rate.data.db.dao.PortfolioDao -import dev.arkbuilders.rate.data.db.dao.QuickPairDao -import dev.arkbuilders.rate.data.db.dao.TimestampDao -import dev.arkbuilders.rate.data.db.entity.RoomAsset -import dev.arkbuilders.rate.data.db.entity.RoomCodeUseStat -import dev.arkbuilders.rate.data.db.entity.RoomCurrencyRate -import dev.arkbuilders.rate.data.db.entity.RoomFetchTimestamp -import dev.arkbuilders.rate.data.db.entity.RoomPairAlert -import dev.arkbuilders.rate.data.db.entity.RoomQuickPair -import dev.arkbuilders.rate.data.db.typeconverters.BigDecimalTypeConverter -import dev.arkbuilders.rate.data.db.typeconverters.ListAmountTypeConverter -import dev.arkbuilders.rate.data.db.typeconverters.OffsetDateTimeTypeConverter +import dev.arkbuilders.rate.core.db.dao.CodeUseStatDao +import dev.arkbuilders.rate.core.db.dao.CurrencyRateDao +import dev.arkbuilders.rate.core.db.dao.PairAlertDao +import dev.arkbuilders.rate.core.db.dao.PortfolioDao +import dev.arkbuilders.rate.core.db.dao.QuickPairDao +import dev.arkbuilders.rate.core.db.dao.TimestampDao +import dev.arkbuilders.rate.core.db.entity.RoomAsset +import dev.arkbuilders.rate.core.db.entity.RoomCodeUseStat +import dev.arkbuilders.rate.core.db.entity.RoomCurrencyRate +import dev.arkbuilders.rate.core.db.entity.RoomFetchTimestamp +import dev.arkbuilders.rate.core.db.entity.RoomPairAlert +import dev.arkbuilders.rate.core.db.entity.RoomQuickPair +import dev.arkbuilders.rate.core.db.typeconverters.BigDecimalTypeConverter +import dev.arkbuilders.rate.core.db.typeconverters.ListAmountTypeConverter +import dev.arkbuilders.rate.core.db.typeconverters.OffsetDateTimeTypeConverter @androidx.room.Database( entities = [ diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/CodeUseStatDao.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/CodeUseStatDao.kt similarity index 76% rename from app/src/main/java/dev/arkbuilders/rate/data/db/dao/CodeUseStatDao.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/CodeUseStatDao.kt index 5492199c8..766708e07 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/CodeUseStatDao.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/CodeUseStatDao.kt @@ -1,10 +1,10 @@ -package dev.arkbuilders.rate.data.db.dao +package dev.arkbuilders.rate.core.db.dao import androidx.room.Dao import androidx.room.Query import androidx.room.Upsert -import dev.arkbuilders.rate.data.db.entity.RoomCodeUseStat -import dev.arkbuilders.rate.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.db.entity.RoomCodeUseStat +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import kotlinx.coroutines.flow.Flow @Dao diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/CurrencyRateDao.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/CurrencyRateDao.kt similarity index 81% rename from app/src/main/java/dev/arkbuilders/rate/data/db/dao/CurrencyRateDao.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/CurrencyRateDao.kt index e9e80cdfd..8d2d98923 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/CurrencyRateDao.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/CurrencyRateDao.kt @@ -1,9 +1,9 @@ -package dev.arkbuilders.rate.data.db.dao +package dev.arkbuilders.rate.core.db.dao import androidx.room.Dao import androidx.room.Query import androidx.room.Upsert -import dev.arkbuilders.rate.data.db.entity.RoomCurrencyRate +import dev.arkbuilders.rate.core.db.entity.RoomCurrencyRate @Dao interface CurrencyRateDao { diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/PairAlertDao.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/PairAlertDao.kt similarity index 85% rename from app/src/main/java/dev/arkbuilders/rate/data/db/dao/PairAlertDao.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/PairAlertDao.kt index 5e94949a5..c90ab410f 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/PairAlertDao.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/PairAlertDao.kt @@ -1,9 +1,9 @@ -package dev.arkbuilders.rate.data.db.dao +package dev.arkbuilders.rate.core.db.dao import androidx.room.Dao import androidx.room.Query import androidx.room.Upsert -import dev.arkbuilders.rate.data.db.entity.RoomPairAlert +import dev.arkbuilders.rate.core.db.entity.RoomPairAlert import kotlinx.coroutines.flow.Flow @Dao diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/PortfolioDao.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/PortfolioDao.kt similarity index 88% rename from app/src/main/java/dev/arkbuilders/rate/data/db/dao/PortfolioDao.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/PortfolioDao.kt index f814eb10c..2e2115e0e 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/PortfolioDao.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/PortfolioDao.kt @@ -1,11 +1,11 @@ -package dev.arkbuilders.rate.data.db.dao +package dev.arkbuilders.rate.core.db.dao import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Upsert -import dev.arkbuilders.rate.data.db.entity.RoomAsset +import dev.arkbuilders.rate.core.db.entity.RoomAsset import kotlinx.coroutines.flow.Flow @Dao diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/QuickPairDao.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/QuickPairDao.kt similarity index 85% rename from app/src/main/java/dev/arkbuilders/rate/data/db/dao/QuickPairDao.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/QuickPairDao.kt index 7b9b7494c..b9b21f623 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/QuickPairDao.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/QuickPairDao.kt @@ -1,9 +1,9 @@ -package dev.arkbuilders.rate.data.db.dao +package dev.arkbuilders.rate.core.db.dao import androidx.room.Dao import androidx.room.Query import androidx.room.Upsert -import dev.arkbuilders.rate.data.db.entity.RoomQuickPair +import dev.arkbuilders.rate.core.db.entity.RoomQuickPair import kotlinx.coroutines.flow.Flow @Dao diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/TimestampDao.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/TimestampDao.kt similarity index 82% rename from app/src/main/java/dev/arkbuilders/rate/data/db/dao/TimestampDao.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/TimestampDao.kt index 8419448d5..f5696f5db 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/dao/TimestampDao.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/dao/TimestampDao.kt @@ -1,9 +1,9 @@ -package dev.arkbuilders.rate.data.db.dao +package dev.arkbuilders.rate.core.db.dao import androidx.room.Dao import androidx.room.Query import androidx.room.Upsert -import dev.arkbuilders.rate.data.db.entity.RoomFetchTimestamp +import dev.arkbuilders.rate.core.db.entity.RoomFetchTimestamp import kotlinx.coroutines.flow.Flow @Dao diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomAsset.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomAsset.kt similarity index 71% rename from app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomAsset.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomAsset.kt index 56e8a59ea..aa4465ea2 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomAsset.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomAsset.kt @@ -1,8 +1,8 @@ -package dev.arkbuilders.rate.data.db.entity +package dev.arkbuilders.rate.core.db.entity import androidx.room.Entity import androidx.room.PrimaryKey -import dev.arkbuilders.rate.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import java.math.BigDecimal @Entity diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomCodeUseStat.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomCodeUseStat.kt similarity index 69% rename from app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomCodeUseStat.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomCodeUseStat.kt index 2d6586484..88d8a899f 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomCodeUseStat.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomCodeUseStat.kt @@ -1,8 +1,8 @@ -package dev.arkbuilders.rate.data.db.entity +package dev.arkbuilders.rate.core.db.entity import androidx.room.Entity import androidx.room.PrimaryKey -import dev.arkbuilders.rate.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import java.time.OffsetDateTime @Entity diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomCurrencyRate.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomCurrencyRate.kt similarity index 68% rename from app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomCurrencyRate.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomCurrencyRate.kt index dbb03f6af..a55b0c992 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomCurrencyRate.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomCurrencyRate.kt @@ -1,8 +1,8 @@ -package dev.arkbuilders.rate.data.db.entity +package dev.arkbuilders.rate.core.db.entity import androidx.room.Entity import androidx.room.PrimaryKey -import dev.arkbuilders.rate.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import java.math.BigDecimal @Entity diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomFetchTimestamp.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomFetchTimestamp.kt similarity index 82% rename from app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomFetchTimestamp.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomFetchTimestamp.kt index 8d007a26b..c930cd78a 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomFetchTimestamp.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomFetchTimestamp.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.data.db.entity +package dev.arkbuilders.rate.core.db.entity import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomPairAlert.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomPairAlert.kt similarity index 83% rename from app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomPairAlert.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomPairAlert.kt index 3e9970304..4f8980335 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomPairAlert.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomPairAlert.kt @@ -1,8 +1,8 @@ -package dev.arkbuilders.rate.data.db.entity +package dev.arkbuilders.rate.core.db.entity import androidx.room.Entity import androidx.room.PrimaryKey -import dev.arkbuilders.rate.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import java.math.BigDecimal import java.time.OffsetDateTime diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomQuickPair.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomQuickPair.kt similarity index 71% rename from app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomQuickPair.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomQuickPair.kt index 9cdeeedd9..3ba9a3773 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/entity/RoomQuickPair.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/entity/RoomQuickPair.kt @@ -1,9 +1,9 @@ -package dev.arkbuilders.rate.data.db.entity +package dev.arkbuilders.rate.core.db.entity import androidx.room.Entity import androidx.room.PrimaryKey -import dev.arkbuilders.rate.domain.model.Amount -import dev.arkbuilders.rate.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.Amount +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import java.math.BigDecimal import java.time.OffsetDateTime diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/typeconverters/BigDecimalTypeConverter.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/typeconverters/BigDecimalTypeConverter.kt similarity index 85% rename from app/src/main/java/dev/arkbuilders/rate/data/db/typeconverters/BigDecimalTypeConverter.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/typeconverters/BigDecimalTypeConverter.kt index 499bde510..c62977994 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/typeconverters/BigDecimalTypeConverter.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/typeconverters/BigDecimalTypeConverter.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.data.db.typeconverters +package dev.arkbuilders.rate.core.db.typeconverters import androidx.room.TypeConverter import java.math.BigDecimal diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/typeconverters/ListAmountTypeConverter.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/typeconverters/ListAmountTypeConverter.kt similarity index 68% rename from app/src/main/java/dev/arkbuilders/rate/data/db/typeconverters/ListAmountTypeConverter.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/typeconverters/ListAmountTypeConverter.kt index efb2f598e..d5864b8d6 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/typeconverters/ListAmountTypeConverter.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/typeconverters/ListAmountTypeConverter.kt @@ -1,12 +1,12 @@ -package dev.arkbuilders.rate.data.db.typeconverters +package dev.arkbuilders.rate.core.db.typeconverters import androidx.room.TypeConverter import com.google.gson.Gson import com.google.gson.reflect.TypeToken -import dev.arkbuilders.rate.domain.model.Amount -import dev.arkbuilders.rate.domain.model.AmountStr -import dev.arkbuilders.rate.domain.model.toAmount -import dev.arkbuilders.rate.domain.model.toStrAmount +import dev.arkbuilders.rate.core.domain.model.Amount +import dev.arkbuilders.rate.core.domain.model.AmountStr +import dev.arkbuilders.rate.core.domain.model.toAmount +import dev.arkbuilders.rate.core.domain.model.toStrAmount class ListAmountTypeConverter { @TypeConverter diff --git a/app/src/main/java/dev/arkbuilders/rate/data/db/typeconverters/OffsetDateTimeTypeConverter.kt b/core/db/src/main/java/dev/arkbuilders/rate/core/db/typeconverters/OffsetDateTimeTypeConverter.kt similarity index 85% rename from app/src/main/java/dev/arkbuilders/rate/data/db/typeconverters/OffsetDateTimeTypeConverter.kt rename to core/db/src/main/java/dev/arkbuilders/rate/core/db/typeconverters/OffsetDateTimeTypeConverter.kt index cb57c2a39..a123891f2 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/db/typeconverters/OffsetDateTimeTypeConverter.kt +++ b/core/db/src/main/java/dev/arkbuilders/rate/core/db/typeconverters/OffsetDateTimeTypeConverter.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.data.db.typeconverters +package dev.arkbuilders.rate.core.db.typeconverters import androidx.room.TypeConverter import java.time.OffsetDateTime diff --git a/cryptoicons/src/test/java/dev/arkbuilders/rate/cryptoicons/ExampleUnitTest.kt b/core/db/src/test/java/dev/arkbuilders/rate/core/db/ExampleUnitTest.kt similarity index 79% rename from cryptoicons/src/test/java/dev/arkbuilders/rate/cryptoicons/ExampleUnitTest.kt rename to core/db/src/test/java/dev/arkbuilders/rate/core/db/ExampleUnitTest.kt index 8aceca355..cb1681e8a 100644 --- a/cryptoicons/src/test/java/dev/arkbuilders/rate/cryptoicons/ExampleUnitTest.kt +++ b/core/db/src/test/java/dev/arkbuilders/rate/core/db/ExampleUnitTest.kt @@ -1,9 +1,8 @@ -package dev.arkbuilders.rate.cryptoicons +package dev.arkbuilders.rate.core.db +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). * @@ -14,4 +13,4 @@ class ExampleUnitTest { fun addition_isCorrect() { assertEquals(4, 2 + 2) } -} \ No newline at end of file +} diff --git a/core/di/.gitignore b/core/di/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/core/di/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/di/build.gradle.kts b/core/di/build.gradle.kts new file mode 100644 index 000000000..dabd26a6d --- /dev/null +++ b/core/di/build.gradle.kts @@ -0,0 +1,58 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) +} + +android { + namespace = "dev.arkbuilders.rate.core.di" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + 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_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(project(":core:db")) + implementation(project(":core:data")) + implementation(project(":core:domain")) + + implementation(libs.androidx.core.ktx) + + implementation(libs.androidx.room.runtime) + implementation(libs.retrofit) + implementation(libs.converter.gson) + implementation(libs.logging.interceptor) + + implementation(libs.dagger) + ksp(libs.dagger.compiler) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":core:di:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":core:di:preBuild").dependsOn("ktlintFormat") diff --git a/core/di/consumer-rules.pro b/core/di/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/core/di/proguard-rules.pro b/core/di/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/core/di/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/di/src/androidTest/java/dev/arkbuilders/rate/core/di/ExampleInstrumentedTest.kt b/core/di/src/androidTest/java/dev/arkbuilders/rate/core/di/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..f3a1466f7 --- /dev/null +++ b/core/di/src/androidTest/java/dev/arkbuilders/rate/core/di/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package dev.arkbuilders.rate.core.di + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * 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("dev.arkbuilders.rate.core.di.test", appContext.packageName) + } +} diff --git a/core/di/src/main/AndroidManifest.xml b/core/di/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/core/di/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/core/di/src/main/java/dev/arkbuilders/rate/core/di/CoreComponent.kt b/core/di/src/main/java/dev/arkbuilders/rate/core/di/CoreComponent.kt new file mode 100644 index 000000000..4dd4d8bca --- /dev/null +++ b/core/di/src/main/java/dev/arkbuilders/rate/core/di/CoreComponent.kt @@ -0,0 +1,64 @@ +package dev.arkbuilders.rate.core.di + +import android.app.Application +import android.content.Context +import dagger.BindsInstance +import dagger.Component +import dev.arkbuilders.rate.core.db.dao.PairAlertDao +import dev.arkbuilders.rate.core.db.dao.PortfolioDao +import dev.arkbuilders.rate.core.db.dao.QuickPairDao +import dev.arkbuilders.rate.core.domain.BuildConfigFieldsProvider +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.CodeUseStatRepo +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.repo.NetworkStatus +import dev.arkbuilders.rate.core.domain.repo.Prefs +import dev.arkbuilders.rate.core.domain.repo.TimestampRepo +import dev.arkbuilders.rate.core.domain.usecase.CalcFrequentCurrUseCase +import dev.arkbuilders.rate.core.domain.usecase.ConvertWithRateUseCase +import dev.arkbuilders.rate.core.domain.usecase.GetTopResultUseCase +import javax.inject.Singleton + +@Singleton +@Component( + modules = [ + CoreDataModule::class, + ], +) +interface CoreComponent { + fun quickDao(): QuickPairDao + + fun portfolioDao(): PortfolioDao + + fun pairAlertDao(): PairAlertDao + + fun codesUseStatRepo(): CodeUseStatRepo + + fun appContext(): Context + + fun buildConfigFieldsProvider(): BuildConfigFieldsProvider + + fun currencyRepo(): CurrencyRepo + + fun timestampRepo(): TimestampRepo + + fun prefs(): Prefs + + fun networkStatus(): NetworkStatus + + fun convertUseCase(): ConvertWithRateUseCase + + fun calcFrequentCurrUseCase(): CalcFrequentCurrUseCase + + fun getTopResultUseCase(): GetTopResultUseCase + + fun analyticsManager(): AnalyticsManager + + @Component.Factory + interface Factory { + fun create( + @BindsInstance application: Application, + @BindsInstance context: Context, + ): CoreComponent + } +} diff --git a/core/di/src/main/java/dev/arkbuilders/rate/core/di/CoreComponentProvider.kt b/core/di/src/main/java/dev/arkbuilders/rate/core/di/CoreComponentProvider.kt new file mode 100644 index 000000000..0d587086f --- /dev/null +++ b/core/di/src/main/java/dev/arkbuilders/rate/core/di/CoreComponentProvider.kt @@ -0,0 +1,5 @@ +package dev.arkbuilders.rate.core.di + +interface CoreComponentProvider { + fun provideCoreComponent(): CoreComponent +} diff --git a/core/di/src/main/java/dev/arkbuilders/rate/core/di/CoreDataModule.kt b/core/di/src/main/java/dev/arkbuilders/rate/core/di/CoreDataModule.kt new file mode 100644 index 000000000..a57c2e436 --- /dev/null +++ b/core/di/src/main/java/dev/arkbuilders/rate/core/di/CoreDataModule.kt @@ -0,0 +1,17 @@ +package dev.arkbuilders.rate.core.di + +import dagger.Module +import dev.arkbuilders.rate.core.di.modules.ApiModule +import dev.arkbuilders.rate.core.di.modules.DBModule +import dev.arkbuilders.rate.core.di.modules.RepoModule +import dev.arkbuilders.rate.core.di.modules.UseCaseModule + +@Module( + includes = [ + ApiModule::class, + DBModule::class, + RepoModule::class, + UseCaseModule::class, + ], +) +class CoreDataModule diff --git a/app/src/main/java/dev/arkbuilders/rate/di/module/ApiModule.kt b/core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/ApiModule.kt similarity index 82% rename from app/src/main/java/dev/arkbuilders/rate/di/module/ApiModule.kt rename to core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/ApiModule.kt index 0a62664a1..9339fe4e3 100644 --- a/app/src/main/java/dev/arkbuilders/rate/di/module/ApiModule.kt +++ b/core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/ApiModule.kt @@ -1,11 +1,11 @@ -package dev.arkbuilders.rate.di.module +package dev.arkbuilders.rate.core.di.modules import com.google.gson.GsonBuilder import dagger.Module import dagger.Provides -import dev.arkbuilders.rate.data.network.OkHttpClientBuilder -import dev.arkbuilders.rate.data.network.api.CryptoAPI -import dev.arkbuilders.rate.data.network.api.FiatAPI +import dev.arkbuilders.rate.core.data.network.OkHttpClientBuilder +import dev.arkbuilders.rate.core.data.network.api.CryptoAPI +import dev.arkbuilders.rate.core.data.network.api.FiatAPI import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import javax.inject.Singleton diff --git a/app/src/main/java/dev/arkbuilders/rate/di/module/DBModule.kt b/core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/DBModule.kt similarity index 83% rename from app/src/main/java/dev/arkbuilders/rate/di/module/DBModule.kt rename to core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/DBModule.kt index 0cb3f4271..abe2611a6 100644 --- a/app/src/main/java/dev/arkbuilders/rate/di/module/DBModule.kt +++ b/core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/DBModule.kt @@ -1,11 +1,11 @@ -package dev.arkbuilders.rate.di.module +package dev.arkbuilders.rate.core.di.modules import android.app.Application import androidx.room.Room import dagger.Module import dagger.Provides -import dev.arkbuilders.rate.data.db.Database -import dev.arkbuilders.rate.data.db.Database.Companion.DB_NAME +import dev.arkbuilders.rate.core.db.Database +import dev.arkbuilders.rate.core.db.Database.Companion.DB_NAME import javax.inject.Singleton @Module diff --git a/core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/RepoModule.kt b/core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/RepoModule.kt new file mode 100644 index 000000000..58e29a238 --- /dev/null +++ b/core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/RepoModule.kt @@ -0,0 +1,70 @@ +package dev.arkbuilders.rate.core.di.modules + +import android.content.Context +import dagger.Module +import dagger.Provides +import dev.arkbuilders.rate.core.data.network.NetworkStatusImpl +import dev.arkbuilders.rate.core.data.preferences.PrefsImpl +import dev.arkbuilders.rate.core.data.repo.AnalyticsManagerImpl +import dev.arkbuilders.rate.core.data.repo.BuildConfigFieldsProviderImpl +import dev.arkbuilders.rate.core.data.repo.CodeUseStatRepoImpl +import dev.arkbuilders.rate.core.data.repo.TimestampRepoImpl +import dev.arkbuilders.rate.core.data.repo.currency.CryptoCurrencyDataSource +import dev.arkbuilders.rate.core.data.repo.currency.CurrencyRepoImpl +import dev.arkbuilders.rate.core.data.repo.currency.FiatCurrencyDataSource +import dev.arkbuilders.rate.core.data.repo.currency.LocalCurrencyDataSource +import dev.arkbuilders.rate.core.db.dao.CodeUseStatDao +import dev.arkbuilders.rate.core.db.dao.TimestampDao +import dev.arkbuilders.rate.core.domain.BuildConfigFieldsProvider +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.CodeUseStatRepo +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.repo.NetworkStatus +import dev.arkbuilders.rate.core.domain.repo.Prefs +import dev.arkbuilders.rate.core.domain.repo.TimestampRepo +import javax.inject.Singleton + +@Module +class RepoModule { + @Singleton + @Provides + fun currencyRepo( + fiatCurrencyDataSource: FiatCurrencyDataSource, + cryptoCurrencyDataSource: CryptoCurrencyDataSource, + localCurrencyDataSource: LocalCurrencyDataSource, + timestampRepo: TimestampRepo, + networkStatus: NetworkStatus, + ): CurrencyRepo = + CurrencyRepoImpl( + fiatCurrencyDataSource, + cryptoCurrencyDataSource, + localCurrencyDataSource, + timestampRepo, + networkStatus, + ) + + @Singleton + @Provides + fun prefs(context: Context): Prefs = PrefsImpl(context) + + @Singleton + @Provides + fun codeUseStatRepo(codeUseStatDao: CodeUseStatDao): CodeUseStatRepo = + CodeUseStatRepoImpl(codeUseStatDao) + + @Singleton + @Provides + fun analyticsManager(prefs: Prefs): AnalyticsManager = AnalyticsManagerImpl(prefs) + + @Singleton + @Provides + fun timestampRepo(timestampDao: TimestampDao): TimestampRepo = TimestampRepoImpl(timestampDao) + + @Singleton + @Provides + fun networkStatus(context: Context): NetworkStatus = NetworkStatusImpl(context) + + @Singleton + @Provides + fun buildConfigFieldsProvider(): BuildConfigFieldsProvider = BuildConfigFieldsProviderImpl() +} diff --git a/core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/UseCaseModule.kt b/core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/UseCaseModule.kt new file mode 100644 index 000000000..444bddb0f --- /dev/null +++ b/core/di/src/main/java/dev/arkbuilders/rate/core/di/modules/UseCaseModule.kt @@ -0,0 +1,29 @@ +package dev.arkbuilders.rate.core.di.modules + +import dagger.Module +import dagger.Provides +import dev.arkbuilders.rate.core.domain.repo.CodeUseStatRepo +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.usecase.CalcFrequentCurrUseCase +import dev.arkbuilders.rate.core.domain.usecase.ConvertWithRateUseCase +import dev.arkbuilders.rate.core.domain.usecase.GetTopResultUseCase +import javax.inject.Singleton + +@Module +class UseCaseModule { + @Singleton + @Provides + fun calcFrequentCurrUseCase(codeUseStatRepo: CodeUseStatRepo) = + CalcFrequentCurrUseCase(codeUseStatRepo) + + @Singleton + @Provides + fun convertWithRateUseCase(currencyRepo: CurrencyRepo) = ConvertWithRateUseCase(currencyRepo) + + @Singleton + @Provides + fun getTopResultUseCase( + currencyRepo: CurrencyRepo, + calcFrequentCurrUseCase: CalcFrequentCurrUseCase, + ) = GetTopResultUseCase(currencyRepo, calcFrequentCurrUseCase) +} diff --git a/fiaticons/src/test/java/dev/arkbuilders/rate/fiaticons/ExampleUnitTest.kt b/core/di/src/test/java/dev/arkbuilders/rate/core/di/ExampleUnitTest.kt similarity index 79% rename from fiaticons/src/test/java/dev/arkbuilders/rate/fiaticons/ExampleUnitTest.kt rename to core/di/src/test/java/dev/arkbuilders/rate/core/di/ExampleUnitTest.kt index 153ecf85a..924f8630e 100644 --- a/fiaticons/src/test/java/dev/arkbuilders/rate/fiaticons/ExampleUnitTest.kt +++ b/core/di/src/test/java/dev/arkbuilders/rate/core/di/ExampleUnitTest.kt @@ -1,9 +1,8 @@ -package dev.arkbuilders.rate.fiaticons +package dev.arkbuilders.rate.core.di +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). * @@ -14,4 +13,4 @@ class ExampleUnitTest { fun addition_isCorrect() { assertEquals(4, 2 + 2) } -} \ No newline at end of file +} diff --git a/core/domain/.gitignore b/core/domain/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/core/domain/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/domain/build.gradle.kts b/core/domain/build.gradle.kts new file mode 100644 index 000000000..5e21834a6 --- /dev/null +++ b/core/domain/build.gradle.kts @@ -0,0 +1,48 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + id("kotlin-parcelize") +} + +android { + namespace = "dev.arkbuilders.rate.core.domain" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + 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_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + + implementation(libs.arrow.core) + implementation(libs.arrow.fx.coroutines) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":core:domain:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":core:domain:preBuild").dependsOn("ktlintFormat") diff --git a/core/domain/consumer-rules.pro b/core/domain/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/core/domain/proguard-rules.pro b/core/domain/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/core/domain/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/cryptoicons/src/androidTest/java/dev/arkbuilders/rate/cryptoicons/ExampleInstrumentedTest.kt b/core/domain/src/androidTest/java/dev/arkbuilders/rate/core/domain/ExampleInstrumentedTest.kt similarity index 79% rename from cryptoicons/src/androidTest/java/dev/arkbuilders/rate/cryptoicons/ExampleInstrumentedTest.kt rename to core/domain/src/androidTest/java/dev/arkbuilders/rate/core/domain/ExampleInstrumentedTest.kt index e356f2867..cfac70c03 100644 --- a/cryptoicons/src/androidTest/java/dev/arkbuilders/rate/cryptoicons/ExampleInstrumentedTest.kt +++ b/core/domain/src/androidTest/java/dev/arkbuilders/rate/core/domain/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ -package dev.arkbuilders.rate.cryptoicons +package dev.arkbuilders.rate.core.domain -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * @@ -19,6 +17,6 @@ class ExampleInstrumentedTest { fun useAppContext() { // Context of the app under test. val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("dev.arkbuilders.rate.cryptoicons.test", appContext.packageName) + assertEquals("dev.arkbuilders.rate.core.domain.test", appContext.packageName) } -} \ No newline at end of file +} diff --git a/core/domain/src/main/AndroidManifest.xml b/core/domain/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/core/domain/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/AppConfig.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/AppConfig.kt similarity index 65% rename from app/src/main/java/dev/arkbuilders/rate/domain/AppConfig.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/AppConfig.kt index b0784411e..848a44dcc 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/AppConfig.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/AppConfig.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.domain +package dev.arkbuilders.rate.core.domain object AppConfig { const val CURRENCY_RATES_UPDATE_INTERVAL_HOURS = 2L diff --git a/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/BuildConfigFields.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/BuildConfigFields.kt new file mode 100644 index 000000000..1dea8ab60 --- /dev/null +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/BuildConfigFields.kt @@ -0,0 +1,14 @@ +package dev.arkbuilders.rate.core.domain + +data class BuildConfigFields( + val buildType: String, + val versionCode: Int, + val versionName: String, + val isGooglePlayBuild: Boolean, +) + +interface BuildConfigFieldsProvider { + fun init(fields: BuildConfigFields) + + fun provide(): BuildConfigFields +} diff --git a/app/src/main/java/dev/arkbuilders/rate/data/CurrUtils.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/CurrUtils.kt similarity index 96% rename from app/src/main/java/dev/arkbuilders/rate/data/CurrUtils.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/CurrUtils.kt index cf4f4a535..ec0bd4d11 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/CurrUtils.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/CurrUtils.kt @@ -1,7 +1,7 @@ -package dev.arkbuilders.rate.data +package dev.arkbuilders.rate.core.domain import android.icu.util.Currency -import dev.arkbuilders.rate.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import java.math.BigDecimal import java.math.RoundingMode import java.text.DecimalFormat diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/model/Amount.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/Amount.kt similarity index 73% rename from app/src/main/java/dev/arkbuilders/rate/domain/model/Amount.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/Amount.kt index 9f064dcc5..1f147b290 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/model/Amount.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/Amount.kt @@ -1,6 +1,6 @@ -package dev.arkbuilders.rate.domain.model +package dev.arkbuilders.rate.core.domain.model -import dev.arkbuilders.rate.data.toBigDecimalArk +import dev.arkbuilders.rate.core.domain.toBigDecimalArk import java.math.BigDecimal data class Amount(val code: CurrencyCode, val value: BigDecimal) diff --git a/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/CurrencyCode.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/CurrencyCode.kt new file mode 100644 index 000000000..685c4c911 --- /dev/null +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/CurrencyCode.kt @@ -0,0 +1,3 @@ +package dev.arkbuilders.rate.core.domain.model + +typealias CurrencyCode = String diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/model/CurrencyName.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/CurrencyName.kt similarity index 76% rename from app/src/main/java/dev/arkbuilders/rate/domain/model/CurrencyName.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/CurrencyName.kt index 05853e8e2..9a6549a25 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/model/CurrencyName.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/CurrencyName.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.domain.model +package dev.arkbuilders.rate.core.domain.model data class CurrencyName( val code: CurrencyCode, diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/model/CurrencyRate.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/CurrencyRate.kt similarity index 80% rename from app/src/main/java/dev/arkbuilders/rate/domain/model/CurrencyRate.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/CurrencyRate.kt index f5a7dbff9..5c65a98ee 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/model/CurrencyRate.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/CurrencyRate.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.domain.model +package dev.arkbuilders.rate.core.domain.model import java.math.BigDecimal diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/model/TimestampType.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/TimestampType.kt similarity index 58% rename from app/src/main/java/dev/arkbuilders/rate/domain/model/TimestampType.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/TimestampType.kt index 914a60fa4..c92a4998d 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/model/TimestampType.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/TimestampType.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.domain.model +package dev.arkbuilders.rate.core.domain.model enum class TimestampType { FetchRates, diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/model/stats/CodeUseStat.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/stats/CodeUseStat.kt similarity index 56% rename from app/src/main/java/dev/arkbuilders/rate/domain/model/stats/CodeUseStat.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/stats/CodeUseStat.kt index 94d025101..40a9f85d5 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/model/stats/CodeUseStat.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/model/stats/CodeUseStat.kt @@ -1,6 +1,6 @@ -package dev.arkbuilders.rate.domain.model.stats +package dev.arkbuilders.rate.core.domain.model.stats -import dev.arkbuilders.rate.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import java.time.OffsetDateTime data class CodeUseStat( diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/repo/AnalyticsManager.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/AnalyticsManager.kt similarity index 58% rename from app/src/main/java/dev/arkbuilders/rate/domain/repo/AnalyticsManager.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/AnalyticsManager.kt index 7ac38f278..2286f0f21 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/repo/AnalyticsManager.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/AnalyticsManager.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.domain.repo +package dev.arkbuilders.rate.core.domain.repo interface AnalyticsManager { fun trackScreen(name: String) diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/repo/CodeUseStatRepo.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/CodeUseStatRepo.kt similarity index 55% rename from app/src/main/java/dev/arkbuilders/rate/domain/repo/CodeUseStatRepo.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/CodeUseStatRepo.kt index 20a6fd9bd..acf405357 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/repo/CodeUseStatRepo.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/CodeUseStatRepo.kt @@ -1,7 +1,7 @@ -package dev.arkbuilders.rate.domain.repo +package dev.arkbuilders.rate.core.domain.repo -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.stats.CodeUseStat +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.stats.CodeUseStat import kotlinx.coroutines.flow.Flow interface CodeUseStatRepo { diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/repo/CurrencyRepo.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/CurrencyRepo.kt similarity index 84% rename from app/src/main/java/dev/arkbuilders/rate/domain/repo/CurrencyRepo.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/CurrencyRepo.kt index 8eadbb7c1..c6ff35c21 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/repo/CurrencyRepo.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/CurrencyRepo.kt @@ -1,11 +1,11 @@ -package dev.arkbuilders.rate.domain.repo +package dev.arkbuilders.rate.core.domain.repo import arrow.core.Either import arrow.core.left import arrow.core.right -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.domain.model.CurrencyRate +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.domain.model.CurrencyRate interface CurrencyRepo { suspend fun getCurrencyRate(): Either> diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/repo/NetworkStatus.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/NetworkStatus.kt similarity index 76% rename from app/src/main/java/dev/arkbuilders/rate/domain/repo/NetworkStatus.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/NetworkStatus.kt index f372aa621..4d81b5122 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/repo/NetworkStatus.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/NetworkStatus.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.domain.repo +package dev.arkbuilders.rate.core.domain.repo import kotlinx.coroutines.flow.StateFlow diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/repo/Prefs.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/Prefs.kt similarity index 82% rename from app/src/main/java/dev/arkbuilders/rate/domain/repo/Prefs.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/Prefs.kt index 12ed513fc..5330a954d 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/repo/Prefs.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/Prefs.kt @@ -1,6 +1,6 @@ -package dev.arkbuilders.rate.domain.repo +package dev.arkbuilders.rate.core.domain.repo -import dev.arkbuilders.rate.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import kotlinx.coroutines.flow.Flow sealed class PreferenceKey(val defaultValue: T) { diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/repo/TimestampRepo.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/TimestampRepo.kt similarity index 73% rename from app/src/main/java/dev/arkbuilders/rate/domain/repo/TimestampRepo.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/TimestampRepo.kt index 328f62b85..f12f10481 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/repo/TimestampRepo.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/repo/TimestampRepo.kt @@ -1,6 +1,6 @@ -package dev.arkbuilders.rate.domain.repo +package dev.arkbuilders.rate.core.domain.repo -import dev.arkbuilders.rate.domain.model.TimestampType +import dev.arkbuilders.rate.core.domain.model.TimestampType import kotlinx.coroutines.flow.Flow import java.time.OffsetDateTime diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/usecase/CalcFrequentCurrUseCase.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/usecase/CalcFrequentCurrUseCase.kt similarity index 76% rename from app/src/main/java/dev/arkbuilders/rate/domain/usecase/CalcFrequentCurrUseCase.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/usecase/CalcFrequentCurrUseCase.kt index 53b726f29..8b169312d 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/usecase/CalcFrequentCurrUseCase.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/usecase/CalcFrequentCurrUseCase.kt @@ -1,19 +1,16 @@ -package dev.arkbuilders.rate.domain.usecase +package dev.arkbuilders.rate.core.domain.usecase -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.stats.CodeUseStat -import dev.arkbuilders.rate.domain.repo.CodeUseStatRepo +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.stats.CodeUseStat +import dev.arkbuilders.rate.core.domain.repo.CodeUseStatRepo import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import java.time.Duration import java.time.OffsetDateTime -import javax.inject.Inject -import javax.inject.Singleton private const val FREQUENT_LIMIT = 5 -@Singleton -class CalcFrequentCurrUseCase @Inject constructor( +class CalcFrequentCurrUseCase( private val codeUseStatRepo: CodeUseStatRepo, ) { suspend operator fun invoke(stats: List? = null): List { diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/usecase/ConvertWithRateUseCase.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/usecase/ConvertWithRateUseCase.kt similarity index 69% rename from app/src/main/java/dev/arkbuilders/rate/domain/usecase/ConvertWithRateUseCase.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/usecase/ConvertWithRateUseCase.kt index 743f786e9..be09e8ad9 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/usecase/ConvertWithRateUseCase.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/usecase/ConvertWithRateUseCase.kt @@ -1,13 +1,11 @@ -package dev.arkbuilders.rate.domain.usecase +package dev.arkbuilders.rate.core.domain.usecase -import dev.arkbuilders.rate.data.divideArk -import dev.arkbuilders.rate.domain.model.Amount -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.CurrencyRate -import dev.arkbuilders.rate.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.divideArk +import dev.arkbuilders.rate.core.domain.model.Amount +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyRate +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo import java.math.BigDecimal -import javax.inject.Inject -import javax.inject.Singleton // Example // rate BTC = 67428 USD @@ -15,8 +13,7 @@ import javax.inject.Singleton // rate BTC = 67428 / 1.07 = 63016 EUR // 2 BTC to EUR = 2 * 63016 = 126032 -@Singleton -class ConvertWithRateUseCase @Inject constructor( +class ConvertWithRateUseCase( private val currencyRepo: CurrencyRepo, ) { suspend operator fun invoke( diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/usecase/GetTopResultUseCase.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/usecase/GetTopResultUseCase.kt similarity index 61% rename from app/src/main/java/dev/arkbuilders/rate/domain/usecase/GetTopResultUseCase.kt rename to core/domain/src/main/java/dev/arkbuilders/rate/core/domain/usecase/GetTopResultUseCase.kt index 828ab35b8..83fb84c6f 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/usecase/GetTopResultUseCase.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/usecase/GetTopResultUseCase.kt @@ -1,12 +1,9 @@ -package dev.arkbuilders.rate.domain.usecase +package dev.arkbuilders.rate.core.domain.usecase -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import javax.inject.Inject -import javax.inject.Singleton +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo -@Singleton -class GetTopResultUseCase @Inject constructor( +class GetTopResultUseCase( private val currencyRepo: CurrencyRepo, private val calcFrequentCurrUseCase: CalcFrequentCurrUseCase, ) { diff --git a/core/domain/src/test/java/dev/arkbuilders/rate/core/domain/ExampleUnitTest.kt b/core/domain/src/test/java/dev/arkbuilders/rate/core/domain/ExampleUnitTest.kt new file mode 100644 index 000000000..084f614d3 --- /dev/null +++ b/core/domain/src/test/java/dev/arkbuilders/rate/core/domain/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package dev.arkbuilders.rate.core.domain + +import org.junit.Assert.assertEquals +import org.junit.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) + } +} diff --git a/core/presentation/.gitignore b/core/presentation/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/core/presentation/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/presentation/build.gradle.kts b/core/presentation/build.gradle.kts new file mode 100644 index 000000000..aac0be428 --- /dev/null +++ b/core/presentation/build.gradle.kts @@ -0,0 +1,62 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "dev.arkbuilders.rate.core.presentation" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + 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_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() + } +} + +dependencies { + implementation(project(":core:domain")) + + implementation(libs.compose.destinations.animations) + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.ui) + implementation(libs.navigation.compose) + implementation(libs.material3) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(libs.constraintlayout.compose) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":core:presentation:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":core:presentation:preBuild").dependsOn("ktlintFormat") diff --git a/core/presentation/consumer-rules.pro b/core/presentation/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/core/presentation/proguard-rules.pro b/core/presentation/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/core/presentation/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/presentation/src/androidTest/java/dev/arkbuilders/rate/core/presentation/ExampleInstrumentedTest.kt b/core/presentation/src/androidTest/java/dev/arkbuilders/rate/core/presentation/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..a9a0dec0d --- /dev/null +++ b/core/presentation/src/androidTest/java/dev/arkbuilders/rate/core/presentation/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package dev.arkbuilders.rate.core.presentation + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * 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("dev.arkbuilders.rate.core.presentation.test", appContext.packageName) + } +} diff --git a/core/presentation/src/main/AndroidManifest.xml b/core/presentation/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/core/presentation/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/shared/AppSharedFlow.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/AppSharedFlow.kt similarity index 87% rename from app/src/main/java/dev/arkbuilders/rate/presentation/shared/AppSharedFlow.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/AppSharedFlow.kt index fcb9c270d..112de10ef 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/shared/AppSharedFlow.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/AppSharedFlow.kt @@ -1,7 +1,7 @@ -package dev.arkbuilders.rate.presentation.shared +package dev.arkbuilders.rate.core.presentation -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.presentation.ui.NotifyAddedSnackbarVisuals import kotlinx.coroutines.flow.MutableSharedFlow sealed class AppSharedFlow(val flow: MutableSharedFlow) { diff --git a/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/CoreR.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/CoreR.kt new file mode 100644 index 000000000..3b65cd697 --- /dev/null +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/CoreR.kt @@ -0,0 +1,5 @@ +package dev.arkbuilders.rate.core.presentation + +// https://youtrack.jetbrains.com/issue/KT-47509/Typealias-for-Android-Resources-R-file-doesnt-refered-to-generated-file +typealias CoreRString = R.string +typealias CoreRDrawable = R.drawable diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/theme/ArkColor.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/ArkColor.kt similarity index 96% rename from app/src/main/java/dev/arkbuilders/rate/presentation/theme/ArkColor.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/ArkColor.kt index 6c4911da6..8d3cc6438 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/theme/ArkColor.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/ArkColor.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.theme +package dev.arkbuilders.rate.core.presentation.theme import androidx.compose.ui.graphics.Color diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/theme/Theme.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/Theme.kt similarity index 93% rename from app/src/main/java/dev/arkbuilders/rate/presentation/theme/Theme.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/Theme.kt index 7e2256beb..78f1d3daf 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/theme/Theme.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/Theme.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.theme +package dev.arkbuilders.rate.core.presentation.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/theme/Type.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/Type.kt similarity index 95% rename from app/src/main/java/dev/arkbuilders/rate/presentation/theme/Type.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/Type.kt index 262218acf..e656f1d7f 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/theme/Type.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/Type.kt @@ -1,10 +1,10 @@ -package dev.arkbuilders.rate.presentation.theme +package dev.arkbuilders.rate.core.presentation.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight -import dev.arkbuilders.rate.R +import dev.arkbuilders.rate.core.presentation.R private val interFontFamily = FontFamily( diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/AppButton.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/AppButton.kt similarity index 93% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/AppButton.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/AppButton.kt index 14393c422..ccb38bf30 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/AppButton.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/AppButton.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.height diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/AppDividers.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/AppDividers.kt similarity index 84% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/AppDividers.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/AppDividers.kt index dc316a1f3..e00b4139f 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/AppDividers.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/AppDividers.kt @@ -1,11 +1,11 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.layout.padding import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Composable fun AppHorDiv16(modifier: Modifier = Modifier) { diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/AppTopBar.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/AppTopBar.kt similarity index 84% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/AppTopBar.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/AppTopBar.kt index 1e0932c67..d480e01dd 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/AppTopBar.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/AppTopBar.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -19,16 +19,14 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.sp -import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Preview(showBackground = true) @Composable fun AppTopBarBack( title: String = "Title", - navigator: DestinationsNavigator = EmptyDestinationsNavigator, + onBackClick: () -> Unit = {}, ) { Column { TopAppBar( @@ -41,7 +39,7 @@ fun AppTopBarBack( ) }, navigationIcon = { - IconButton(onClick = { navigator.popBackStack() }) { + IconButton(onClick = { onBackClick() }) { Icon( modifier = Modifier, painter = painterResource(id = R.drawable.ic_back), diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/ArkBasicTextField.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ArkBasicTextField.kt similarity index 97% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/ArkBasicTextField.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ArkBasicTextField.kt index 86695fda3..7a1913421 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/ArkBasicTextField.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ArkBasicTextField.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.PaddingValues diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/ArkLargeTextField.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ArkLargeTextField.kt similarity index 91% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/ArkLargeTextField.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ArkLargeTextField.kt index 3ec3f3cce..990659640 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/ArkLargeTextField.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ArkLargeTextField.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.defaultMinSize @@ -12,7 +12,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Composable fun ArkLargeTextField( diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/ConnectivitySnackbar.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ConnectivitySnackbar.kt similarity index 96% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/ConnectivitySnackbar.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ConnectivitySnackbar.kt index 8e296cc6f..1a415574b 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/ConnectivitySnackbar.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ConnectivitySnackbar.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -20,8 +20,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor object ConnectivityOnlineSnackbarVisuals : SnackbarVisuals { override val actionLabel = "" diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/CurrIcon.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/CurrIcon.kt similarity index 77% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/CurrIcon.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/CurrIcon.kt index 4f98efd0e..115b3a94c 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/CurrIcon.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/CurrIcon.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.material3.Icon import androidx.compose.runtime.Composable @@ -6,8 +6,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.presentation.utils.IconUtils +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.presentation.utils.IconUtils @Composable fun CurrIcon( diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/CurrencyInfoItem.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/CurrencyInfoItem.kt similarity index 91% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/CurrencyInfoItem.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/CurrencyInfoItem.kt index d2577bb86..c5b122e38 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/CurrencyInfoItem.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/CurrencyInfoItem.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -14,8 +14,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Composable fun CurrencyInfoItem( diff --git a/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/DropDown.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/DropDown.kt new file mode 100644 index 000000000..9d3694185 --- /dev/null +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/DropDown.kt @@ -0,0 +1,64 @@ +package dev.arkbuilders.rate.core.presentation.ui + +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor + +@Composable +fun DropDownWithIcon( + modifier: Modifier, + onClick: () -> Unit, + title: String, + icon: Painter, +) { + Row( + modifier = + modifier + .height(44.dp) + .border( + 1.dp, + ArkColor.Border, + RoundedCornerShape(8.dp), + ) + .clip(RoundedCornerShape(8.dp)) + .clickable { onClick() }, + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + modifier = Modifier.padding(start = 16.dp), + painter = icon, + contentDescription = "", + tint = ArkColor.FGSecondary, + ) + Text( + modifier = + Modifier + .padding(start = 8.dp) + .weight(1f), + text = title, + fontSize = 16.sp, + color = ArkColor.TextPlaceHolder, + ) + Icon( + modifier = Modifier.padding(end = 20.dp), + painter = painterResource(id = R.drawable.ic_chevron), + contentDescription = "", + tint = ArkColor.FGSecondary, + ) + } +} diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/GroupCreateDialog.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/GroupCreateDialog.kt similarity index 98% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/GroupCreateDialog.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/GroupCreateDialog.kt index 885227264..f4fe3417a 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/GroupCreateDialog.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/GroupCreateDialog.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background @@ -36,8 +36,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Composable fun GroupCreateDialog( diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/GroupSelectPopup.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/GroupSelectPopup.kt similarity index 95% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/GroupSelectPopup.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/GroupSelectPopup.kt index e5734d625..5374d38d7 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/GroupSelectPopup.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/GroupSelectPopup.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row @@ -21,8 +21,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Preview(showBackground = true, widthDp = 400) @Composable diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/GroupViewPager.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/GroupViewPager.kt similarity index 95% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/GroupViewPager.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/GroupViewPager.kt index d684ac91c..fa8699d0d 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/GroupViewPager.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/GroupViewPager.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalFoundationApi::class) -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box @@ -22,8 +22,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor import kotlinx.coroutines.launch @Composable diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/InfoMarketCapitalizationDialog.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/InfoMarketCapitalizationDialog.kt similarity index 96% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/InfoMarketCapitalizationDialog.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/InfoMarketCapitalizationDialog.kt index 83875cebf..dcc3dfee1 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/InfoMarketCapitalizationDialog.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/InfoMarketCapitalizationDialog.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -21,8 +21,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Composable fun InfoMarketCapitalizationDialog(onDismiss: () -> Unit) { diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/LargeNumberText.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/LargeNumberText.kt similarity index 96% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/LargeNumberText.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/LargeNumberText.kt index ee1c888a1..e8fede520 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/LargeNumberText.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/LargeNumberText.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.Text @@ -18,8 +18,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.TextUnit -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.data.divideArk +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.domain.divideArk import java.math.BigDecimal @Composable diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/LargeNumberTooltipBox.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/LargeNumberTooltipBox.kt similarity index 91% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/LargeNumberTooltipBox.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/LargeNumberTooltipBox.kt index be63fc2de..1a023ad50 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/LargeNumberTooltipBox.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/LargeNumberTooltipBox.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.PlainTooltip @@ -10,7 +10,7 @@ import androidx.compose.material3.TooltipDefaults import androidx.compose.material3.rememberTooltipState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import dev.arkbuilders.rate.data.CurrUtils +import dev.arkbuilders.rate.core.domain.CurrUtils import java.math.BigDecimal @Composable diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/ListHeader.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ListHeader.kt similarity index 84% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/ListHeader.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ListHeader.kt index 8effd4be5..0944bb495 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/ListHeader.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/ListHeader.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text @@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Composable fun ListHeader(text: String) { diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/LoadingScreen.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/LoadingScreen.kt similarity index 97% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/LoadingScreen.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/LoadingScreen.kt index ab5e2eb7a..6a67169cd 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/LoadingScreen.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/LoadingScreen.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.annotation.IntRange import androidx.compose.animation.core.Animatable @@ -34,12 +34,15 @@ import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.rotate import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Composable fun LoadingScreen() { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - InstaSpinner(size = 60.dp, color = ArkColor.Secondary) + InstaSpinner( + size = 60.dp, + color = dev.arkbuilders.rate.core.presentation.theme.ArkColor.Secondary, + ) } } diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/NoInternetScreen.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NoInternetScreen.kt similarity index 94% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/NoInternetScreen.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NoInternetScreen.kt index cbf2a2907..54e2da8e8 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/NoInternetScreen.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NoInternetScreen.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -17,8 +17,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Composable fun NoInternetScreen(onRefreshClick: () -> Unit) { diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/NoResult.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NoResult.kt similarity index 88% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/NoResult.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NoResult.kt index 535a2249c..be0ab9d13 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/NoResult.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NoResult.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -14,8 +14,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Composable fun NoResult() { diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/NotifyAddedSnackbar.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NotifyAddedSnackbar.kt similarity index 95% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/NotifyAddedSnackbar.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NotifyAddedSnackbar.kt index e8098e523..7a065c1a5 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/NotifyAddedSnackbar.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NotifyAddedSnackbar.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -24,8 +24,8 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor class NotifyAddedSnackbarVisuals( val title: String, diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/NotifyRemovedSnackbar.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NotifyRemovedSnackbar.kt similarity index 91% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/NotifyRemovedSnackbar.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NotifyRemovedSnackbar.kt index 66a6685f3..a1c5b085e 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/NotifyRemovedSnackbar.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/NotifyRemovedSnackbar.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -10,13 +10,13 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarVisuals import androidx.compose.material3.Text +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -28,8 +28,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor class NotifyRemovedSnackbarVisuals( val title: String, @@ -112,10 +112,7 @@ fun NotifyRemovedSnackbarContent( .clip(RoundedCornerShape(8.dp)) .clickable( interactionSource = remember { MutableInteractionSource() }, - indication = - rememberRipple( - color = MaterialTheme.colorScheme.primary, - ), + indication = ripple(color = MaterialTheme.colorScheme.primary), ) { visuals.onUndo() onDismiss() diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/RateSnackbarHost.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/RateSnackbarHost.kt similarity index 93% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/RateSnackbarHost.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/RateSnackbarHost.kt index 62c69801a..064773ada 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/RateSnackbarHost.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/RateSnackbarHost.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/Search.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/Search.kt similarity index 93% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/Search.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/Search.kt index f18b096a6..d36239a9c 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/Search.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/Search.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.border import androidx.compose.foundation.layout.Row @@ -18,8 +18,8 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor @Preview(showBackground = true) @Composable diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/SwipeToDismissItem.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/SwipeToDismissItem.kt similarity index 94% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/SwipeToDismissItem.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/SwipeToDismissItem.kt index d0337541b..bdfbe7901 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/SwipeToDismissItem.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/SwipeToDismissItem.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.core.presentation.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -23,8 +23,8 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor // bug: callbacks from swipe called multiply times @Composable diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/utils/ContextUtils.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/ContextUtils.kt similarity index 92% rename from app/src/main/java/dev/arkbuilders/rate/presentation/utils/ContextUtils.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/ContextUtils.kt index c1b93f810..60765dd57 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/utils/ContextUtils.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/ContextUtils.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.utils +package dev.arkbuilders.rate.core.presentation.utils import android.app.Activity import android.content.Context @@ -6,7 +6,7 @@ import android.content.ContextWrapper import android.content.Intent import android.net.Uri import android.widget.Toast -import dev.arkbuilders.rate.R +import dev.arkbuilders.rate.core.presentation.R fun Context.openLink( url: String, diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/utils/DateFormatUtils.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/DateFormatUtils.kt similarity index 95% rename from app/src/main/java/dev/arkbuilders/rate/presentation/utils/DateFormatUtils.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/DateFormatUtils.kt index 0c4f7d38d..7f88c5ac0 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/utils/DateFormatUtils.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/DateFormatUtils.kt @@ -1,7 +1,7 @@ -package dev.arkbuilders.rate.presentation.utils +package dev.arkbuilders.rate.core.presentation.utils import android.content.Context -import dev.arkbuilders.rate.R +import dev.arkbuilders.rate.core.presentation.R import java.time.Duration import java.time.OffsetDateTime import java.time.format.DateTimeFormatter diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/utils/IconUtils.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/IconUtils.kt similarity index 80% rename from app/src/main/java/dev/arkbuilders/rate/presentation/utils/IconUtils.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/IconUtils.kt index 90fdcb5bd..faf5874ab 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/utils/IconUtils.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/IconUtils.kt @@ -1,8 +1,8 @@ -package dev.arkbuilders.rate.presentation.utils +package dev.arkbuilders.rate.core.presentation.utils import android.content.Context -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.presentation.R object IconUtils { fun iconForCurrCode( diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/utils/UIUtils.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/UIUtils.kt similarity index 96% rename from app/src/main/java/dev/arkbuilders/rate/presentation/utils/UIUtils.kt rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/UIUtils.kt index f02dacede..1daa72322 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/utils/UIUtils.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/UIUtils.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.utils +package dev.arkbuilders.rate.core.presentation.utils import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.ime diff --git a/app/src/main/res/drawable/ic_about_logo.xml b/core/presentation/src/main/res/drawable/ic_about_logo.xml similarity index 100% rename from app/src/main/res/drawable/ic_about_logo.xml rename to core/presentation/src/main/res/drawable/ic_about_logo.xml diff --git a/app/src/main/res/drawable/ic_add.xml b/core/presentation/src/main/res/drawable/ic_add.xml similarity index 100% rename from app/src/main/res/drawable/ic_add.xml rename to core/presentation/src/main/res/drawable/ic_add.xml diff --git a/app/src/main/res/drawable/ic_add_circle.xml b/core/presentation/src/main/res/drawable/ic_add_circle.xml similarity index 100% rename from app/src/main/res/drawable/ic_add_circle.xml rename to core/presentation/src/main/res/drawable/ic_add_circle.xml diff --git a/app/src/main/res/drawable/ic_back.xml b/core/presentation/src/main/res/drawable/ic_back.xml similarity index 100% rename from app/src/main/res/drawable/ic_back.xml rename to core/presentation/src/main/res/drawable/ic_back.xml diff --git a/app/src/main/res/drawable/ic_chevron.xml b/core/presentation/src/main/res/drawable/ic_chevron.xml similarity index 100% rename from app/src/main/res/drawable/ic_chevron.xml rename to core/presentation/src/main/res/drawable/ic_chevron.xml diff --git a/app/src/main/res/drawable/ic_chevron_left.xml b/core/presentation/src/main/res/drawable/ic_chevron_left.xml similarity index 100% rename from app/src/main/res/drawable/ic_chevron_left.xml rename to core/presentation/src/main/res/drawable/ic_chevron_left.xml diff --git a/app/src/main/res/drawable/ic_chevron_right.xml b/core/presentation/src/main/res/drawable/ic_chevron_right.xml similarity index 100% rename from app/src/main/res/drawable/ic_chevron_right.xml rename to core/presentation/src/main/res/drawable/ic_chevron_right.xml diff --git a/app/src/main/res/drawable/ic_chevron_up.xml b/core/presentation/src/main/res/drawable/ic_chevron_up.xml similarity index 100% rename from app/src/main/res/drawable/ic_chevron_up.xml rename to core/presentation/src/main/res/drawable/ic_chevron_up.xml diff --git a/app/src/main/res/drawable/ic_close.xml b/core/presentation/src/main/res/drawable/ic_close.xml similarity index 100% rename from app/src/main/res/drawable/ic_close.xml rename to core/presentation/src/main/res/drawable/ic_close.xml diff --git a/app/src/main/res/drawable/ic_copy.xml b/core/presentation/src/main/res/drawable/ic_copy.xml similarity index 100% rename from app/src/main/res/drawable/ic_copy.xml rename to core/presentation/src/main/res/drawable/ic_copy.xml diff --git a/app/src/main/res/drawable/ic_delete.xml b/core/presentation/src/main/res/drawable/ic_delete.xml similarity index 100% rename from app/src/main/res/drawable/ic_delete.xml rename to core/presentation/src/main/res/drawable/ic_delete.xml diff --git a/app/src/main/res/drawable/ic_download.xml b/core/presentation/src/main/res/drawable/ic_download.xml similarity index 100% rename from app/src/main/res/drawable/ic_download.xml rename to core/presentation/src/main/res/drawable/ic_download.xml diff --git a/app/src/main/res/drawable/ic_earth.xml b/core/presentation/src/main/res/drawable/ic_earth.xml similarity index 100% rename from app/src/main/res/drawable/ic_earth.xml rename to core/presentation/src/main/res/drawable/ic_earth.xml diff --git a/app/src/main/res/drawable/ic_edit.xml b/core/presentation/src/main/res/drawable/ic_edit.xml similarity index 100% rename from app/src/main/res/drawable/ic_edit.xml rename to core/presentation/src/main/res/drawable/ic_edit.xml diff --git a/app/src/main/res/drawable/ic_empty_pair.xml b/core/presentation/src/main/res/drawable/ic_empty_pair.xml similarity index 100% rename from app/src/main/res/drawable/ic_empty_pair.xml rename to core/presentation/src/main/res/drawable/ic_empty_pair.xml diff --git a/app/src/main/res/drawable/ic_empty_portfolio.xml b/core/presentation/src/main/res/drawable/ic_empty_portfolio.xml similarity index 100% rename from app/src/main/res/drawable/ic_empty_portfolio.xml rename to core/presentation/src/main/res/drawable/ic_empty_portfolio.xml diff --git a/app/src/main/res/drawable/ic_empty_quick.xml b/core/presentation/src/main/res/drawable/ic_empty_quick.xml similarity index 100% rename from app/src/main/res/drawable/ic_empty_quick.xml rename to core/presentation/src/main/res/drawable/ic_empty_quick.xml diff --git a/app/src/main/res/drawable/ic_external.xml b/core/presentation/src/main/res/drawable/ic_external.xml similarity index 100% rename from app/src/main/res/drawable/ic_external.xml rename to core/presentation/src/main/res/drawable/ic_external.xml diff --git a/app/src/main/res/drawable/ic_group.xml b/core/presentation/src/main/res/drawable/ic_group.xml similarity index 100% rename from app/src/main/res/drawable/ic_group.xml rename to core/presentation/src/main/res/drawable/ic_group.xml diff --git a/app/src/main/res/drawable/ic_group_add.xml b/core/presentation/src/main/res/drawable/ic_group_add.xml similarity index 100% rename from app/src/main/res/drawable/ic_group_add.xml rename to core/presentation/src/main/res/drawable/ic_group_add.xml diff --git a/app/src/main/res/drawable/ic_info.xml b/core/presentation/src/main/res/drawable/ic_info.xml similarity index 100% rename from app/src/main/res/drawable/ic_info.xml rename to core/presentation/src/main/res/drawable/ic_info.xml diff --git a/app/src/main/res/drawable/ic_info_bg.xml b/core/presentation/src/main/res/drawable/ic_info_bg.xml similarity index 100% rename from app/src/main/res/drawable/ic_info_bg.xml rename to core/presentation/src/main/res/drawable/ic_info_bg.xml diff --git a/app/src/main/res/drawable/ic_info_red.xml b/core/presentation/src/main/res/drawable/ic_info_red.xml similarity index 100% rename from app/src/main/res/drawable/ic_info_red.xml rename to core/presentation/src/main/res/drawable/ic_info_red.xml diff --git a/app/src/main/res/drawable/ic_launcher_debug_foreground.xml b/core/presentation/src/main/res/drawable/ic_launcher_debug_foreground.xml similarity index 100% rename from app/src/main/res/drawable/ic_launcher_debug_foreground.xml rename to core/presentation/src/main/res/drawable/ic_launcher_debug_foreground.xml diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/core/presentation/src/main/res/drawable/ic_launcher_foreground.xml similarity index 100% rename from app/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to core/presentation/src/main/res/drawable/ic_launcher_foreground.xml diff --git a/app/src/main/res/drawable/ic_nav_alerts_disabled.xml b/core/presentation/src/main/res/drawable/ic_nav_alerts_disabled.xml similarity index 100% rename from app/src/main/res/drawable/ic_nav_alerts_disabled.xml rename to core/presentation/src/main/res/drawable/ic_nav_alerts_disabled.xml diff --git a/app/src/main/res/drawable/ic_nav_alerts_enabled.xml b/core/presentation/src/main/res/drawable/ic_nav_alerts_enabled.xml similarity index 100% rename from app/src/main/res/drawable/ic_nav_alerts_enabled.xml rename to core/presentation/src/main/res/drawable/ic_nav_alerts_enabled.xml diff --git a/app/src/main/res/drawable/ic_nav_portfolio_disabled.xml b/core/presentation/src/main/res/drawable/ic_nav_portfolio_disabled.xml similarity index 100% rename from app/src/main/res/drawable/ic_nav_portfolio_disabled.xml rename to core/presentation/src/main/res/drawable/ic_nav_portfolio_disabled.xml diff --git a/app/src/main/res/drawable/ic_nav_portfolio_enabled.xml b/core/presentation/src/main/res/drawable/ic_nav_portfolio_enabled.xml similarity index 100% rename from app/src/main/res/drawable/ic_nav_portfolio_enabled.xml rename to core/presentation/src/main/res/drawable/ic_nav_portfolio_enabled.xml diff --git a/app/src/main/res/drawable/ic_nav_quick_disabled.xml b/core/presentation/src/main/res/drawable/ic_nav_quick_disabled.xml similarity index 100% rename from app/src/main/res/drawable/ic_nav_quick_disabled.xml rename to core/presentation/src/main/res/drawable/ic_nav_quick_disabled.xml diff --git a/app/src/main/res/drawable/ic_nav_quick_enabled.xml b/core/presentation/src/main/res/drawable/ic_nav_quick_enabled.xml similarity index 100% rename from app/src/main/res/drawable/ic_nav_quick_enabled.xml rename to core/presentation/src/main/res/drawable/ic_nav_quick_enabled.xml diff --git a/app/src/main/res/drawable/ic_nav_settings_disabled.xml b/core/presentation/src/main/res/drawable/ic_nav_settings_disabled.xml similarity index 100% rename from app/src/main/res/drawable/ic_nav_settings_disabled.xml rename to core/presentation/src/main/res/drawable/ic_nav_settings_disabled.xml diff --git a/app/src/main/res/drawable/ic_nav_settings_enabled.xml b/core/presentation/src/main/res/drawable/ic_nav_settings_enabled.xml similarity index 100% rename from app/src/main/res/drawable/ic_nav_settings_enabled.xml rename to core/presentation/src/main/res/drawable/ic_nav_settings_enabled.xml diff --git a/app/src/main/res/drawable/ic_notifications.xml b/core/presentation/src/main/res/drawable/ic_notifications.xml similarity index 100% rename from app/src/main/res/drawable/ic_notifications.xml rename to core/presentation/src/main/res/drawable/ic_notifications.xml diff --git a/app/src/main/res/drawable/ic_pair_alert_dec.xml b/core/presentation/src/main/res/drawable/ic_pair_alert_dec.xml similarity index 100% rename from app/src/main/res/drawable/ic_pair_alert_dec.xml rename to core/presentation/src/main/res/drawable/ic_pair_alert_dec.xml diff --git a/app/src/main/res/drawable/ic_pair_alert_inc.xml b/core/presentation/src/main/res/drawable/ic_pair_alert_inc.xml similarity index 100% rename from app/src/main/res/drawable/ic_pair_alert_inc.xml rename to core/presentation/src/main/res/drawable/ic_pair_alert_inc.xml diff --git a/app/src/main/res/drawable/ic_pin.xml b/core/presentation/src/main/res/drawable/ic_pin.xml similarity index 100% rename from app/src/main/res/drawable/ic_pin.xml rename to core/presentation/src/main/res/drawable/ic_pin.xml diff --git a/app/src/main/res/drawable/ic_refresh2.xml b/core/presentation/src/main/res/drawable/ic_refresh2.xml similarity index 100% rename from app/src/main/res/drawable/ic_refresh2.xml rename to core/presentation/src/main/res/drawable/ic_refresh2.xml diff --git a/app/src/main/res/drawable/ic_reuse.xml b/core/presentation/src/main/res/drawable/ic_reuse.xml similarity index 100% rename from app/src/main/res/drawable/ic_reuse.xml rename to core/presentation/src/main/res/drawable/ic_reuse.xml diff --git a/app/src/main/res/drawable/ic_search.xml b/core/presentation/src/main/res/drawable/ic_search.xml similarity index 100% rename from app/src/main/res/drawable/ic_search.xml rename to core/presentation/src/main/res/drawable/ic_search.xml diff --git a/app/src/main/res/drawable/ic_search_refraction.xml b/core/presentation/src/main/res/drawable/ic_search_refraction.xml similarity index 100% rename from app/src/main/res/drawable/ic_search_refraction.xml rename to core/presentation/src/main/res/drawable/ic_search_refraction.xml diff --git a/app/src/main/res/drawable/ic_snackbar_done.xml b/core/presentation/src/main/res/drawable/ic_snackbar_done.xml similarity index 100% rename from app/src/main/res/drawable/ic_snackbar_done.xml rename to core/presentation/src/main/res/drawable/ic_snackbar_done.xml diff --git a/app/src/main/res/drawable/ic_wifi_off.xml b/core/presentation/src/main/res/drawable/ic_wifi_off.xml similarity index 100% rename from app/src/main/res/drawable/ic_wifi_off.xml rename to core/presentation/src/main/res/drawable/ic_wifi_off.xml diff --git a/app/src/main/res/font/inter.xml b/core/presentation/src/main/res/font/inter.xml similarity index 100% rename from app/src/main/res/font/inter.xml rename to core/presentation/src/main/res/font/inter.xml diff --git a/app/src/main/res/font/inter_black.xml b/core/presentation/src/main/res/font/inter_black.xml similarity index 100% rename from app/src/main/res/font/inter_black.xml rename to core/presentation/src/main/res/font/inter_black.xml diff --git a/app/src/main/res/font/inter_bold.xml b/core/presentation/src/main/res/font/inter_bold.xml similarity index 100% rename from app/src/main/res/font/inter_bold.xml rename to core/presentation/src/main/res/font/inter_bold.xml diff --git a/app/src/main/res/font/inter_extrabold.xml b/core/presentation/src/main/res/font/inter_extrabold.xml similarity index 100% rename from app/src/main/res/font/inter_extrabold.xml rename to core/presentation/src/main/res/font/inter_extrabold.xml diff --git a/app/src/main/res/font/inter_extralight.xml b/core/presentation/src/main/res/font/inter_extralight.xml similarity index 100% rename from app/src/main/res/font/inter_extralight.xml rename to core/presentation/src/main/res/font/inter_extralight.xml diff --git a/app/src/main/res/font/inter_light.xml b/core/presentation/src/main/res/font/inter_light.xml similarity index 100% rename from app/src/main/res/font/inter_light.xml rename to core/presentation/src/main/res/font/inter_light.xml diff --git a/app/src/main/res/font/inter_medium.xml b/core/presentation/src/main/res/font/inter_medium.xml similarity index 100% rename from app/src/main/res/font/inter_medium.xml rename to core/presentation/src/main/res/font/inter_medium.xml diff --git a/app/src/main/res/font/inter_semibold.xml b/core/presentation/src/main/res/font/inter_semibold.xml similarity index 100% rename from app/src/main/res/font/inter_semibold.xml rename to core/presentation/src/main/res/font/inter_semibold.xml diff --git a/app/src/main/res/font/inter_thin.xml b/core/presentation/src/main/res/font/inter_thin.xml similarity index 100% rename from app/src/main/res/font/inter_thin.xml rename to core/presentation/src/main/res/font/inter_thin.xml diff --git a/app/src/main/res/values/font_certs.xml b/core/presentation/src/main/res/values/font_certs.xml similarity index 100% rename from app/src/main/res/values/font_certs.xml rename to core/presentation/src/main/res/values/font_certs.xml diff --git a/app/src/main/res/values/preloaded_fonts.xml b/core/presentation/src/main/res/values/preloaded_fonts.xml similarity index 100% rename from app/src/main/res/values/preloaded_fonts.xml rename to core/presentation/src/main/res/values/preloaded_fonts.xml diff --git a/app/src/main/res/values/strings.xml b/core/presentation/src/main/res/values/strings.xml similarity index 100% rename from app/src/main/res/values/strings.xml rename to core/presentation/src/main/res/values/strings.xml diff --git a/app/src/main/res/values/time_plurals.xml b/core/presentation/src/main/res/values/time_plurals.xml similarity index 100% rename from app/src/main/res/values/time_plurals.xml rename to core/presentation/src/main/res/values/time_plurals.xml diff --git a/core/presentation/src/test/java/dev/arkbuilders/rate/core/presentation/ExampleUnitTest.kt b/core/presentation/src/test/java/dev/arkbuilders/rate/core/presentation/ExampleUnitTest.kt new file mode 100644 index 000000000..7819b98b9 --- /dev/null +++ b/core/presentation/src/test/java/dev/arkbuilders/rate/core/presentation/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package dev.arkbuilders.rate.core.presentation + +import org.junit.Assert.assertEquals +import org.junit.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) + } +} diff --git a/cryptoicons/build.gradle.kts b/cryptoicons/build.gradle.kts index 3918cc8a6..a7ea87247 100644 --- a/cryptoicons/build.gradle.kts +++ b/cryptoicons/build.gradle.kts @@ -19,16 +19,16 @@ android { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" + "proguard-rules.pro", ) } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "17" } } @@ -40,4 +40,4 @@ dependencies { testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") -} \ No newline at end of file +} diff --git a/feature/pairalert/.gitignore b/feature/pairalert/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/feature/pairalert/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/pairalert/build.gradle.kts b/feature/pairalert/build.gradle.kts new file mode 100644 index 000000000..c1fcffcc8 --- /dev/null +++ b/feature/pairalert/build.gradle.kts @@ -0,0 +1,85 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) +} + +android { + namespace = "dev.arkbuilders.rate.feature.pairalert" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + ksp { + arg("compose-destinations.mode", "destinations") + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(project(":core:di")) + implementation(project(":core:db")) + implementation(project(":core:domain")) + implementation(project(":core:presentation")) + implementation(project(":feature:search")) + + implementation(libs.androidx.core.ktx) + + implementation(libs.androidx.ui) + implementation(libs.navigation.compose) + implementation(libs.material3) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(libs.constraintlayout.compose) + + implementation(libs.androidx.work.runtime.ktx) + + implementation(libs.timber) + + implementation(libs.dagger) + ksp(libs.dagger.compiler) + + implementation(libs.orbit.compose) + implementation(libs.orbit.viewmodel) + + implementation(libs.arrow.core) + implementation(libs.arrow.fx.coroutines) + + implementation(libs.compose.destinations.animations) + ksp(libs.compose.destinations.compiler) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":feature:pairalert:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":feature:pairalert:preBuild").dependsOn("ktlintFormat") diff --git a/feature/pairalert/consumer-rules.pro b/feature/pairalert/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/feature/pairalert/proguard-rules.pro b/feature/pairalert/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/feature/pairalert/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/feature/pairalert/src/androidTest/java/dev/arkbuilders/rate/feature/pairalert/ExampleInstrumentedTest.kt b/feature/pairalert/src/androidTest/java/dev/arkbuilders/rate/feature/pairalert/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..7e55a7985 --- /dev/null +++ b/feature/pairalert/src/androidTest/java/dev/arkbuilders/rate/feature/pairalert/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package dev.arkbuilders.rate.feature.pairalert + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * 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("dev.arkbuilders.rate.feature.pairalert.test", appContext.packageName) + } +} diff --git a/feature/pairalert/src/main/AndroidManifest.xml b/feature/pairalert/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/feature/pairalert/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/java/dev/arkbuilders/rate/data/permission/NotificationPermissionHelper.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/permission/NotificationPermissionHelper.kt similarity index 80% rename from app/src/main/java/dev/arkbuilders/rate/data/permission/NotificationPermissionHelper.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/permission/NotificationPermissionHelper.kt index 24cc82141..2606f4d29 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/permission/NotificationPermissionHelper.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/permission/NotificationPermissionHelper.kt @@ -1,14 +1,14 @@ -package dev.arkbuilders.rate.data.permission +package dev.arkbuilders.rate.feature.pairalert.data.permission import android.Manifest import android.content.Context import android.content.pm.PackageManager import android.os.Build import androidx.core.content.ContextCompat +import dev.arkbuilders.rate.feature.pairalert.di.PairAlertScope import javax.inject.Inject -import javax.inject.Singleton -@Singleton +@PairAlertScope class NotificationPermissionHelper @Inject constructor( private val ctx: Context, ) { diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/PairAlertRepoImpl.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/repo/PairAlertRepoImpl.kt similarity index 78% rename from app/src/main/java/dev/arkbuilders/rate/data/repo/PairAlertRepoImpl.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/repo/PairAlertRepoImpl.kt index 9db2b57db..ac248bba1 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/PairAlertRepoImpl.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/repo/PairAlertRepoImpl.kt @@ -1,9 +1,9 @@ -package dev.arkbuilders.rate.data.repo +package dev.arkbuilders.rate.feature.pairalert.data.repo -import dev.arkbuilders.rate.data.db.dao.PairAlertDao -import dev.arkbuilders.rate.data.db.entity.RoomPairAlert -import dev.arkbuilders.rate.domain.model.PairAlert -import dev.arkbuilders.rate.domain.repo.PairAlertRepo +import dev.arkbuilders.rate.core.db.dao.PairAlertDao +import dev.arkbuilders.rate.core.db.entity.RoomPairAlert +import dev.arkbuilders.rate.feature.pairalert.domain.model.PairAlert +import dev.arkbuilders.rate.feature.pairalert.domain.repo.PairAlertRepo import kotlinx.coroutines.flow.map import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/dev/arkbuilders/rate/data/worker/CurrencyMonitorWorker.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/worker/CurrencyMonitorWorker.kt similarity index 69% rename from app/src/main/java/dev/arkbuilders/rate/data/worker/CurrencyMonitorWorker.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/worker/CurrencyMonitorWorker.kt index 68ef39e26..f59318a6f 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/worker/CurrencyMonitorWorker.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/worker/CurrencyMonitorWorker.kt @@ -1,13 +1,13 @@ -package dev.arkbuilders.rate.data.worker +package dev.arkbuilders.rate.feature.pairalert.data.worker import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import dev.arkbuilders.rate.domain.model.PairAlert -import dev.arkbuilders.rate.domain.model.TimestampType -import dev.arkbuilders.rate.domain.repo.TimestampRepo -import dev.arkbuilders.rate.domain.usecase.HandlePairAlertCheckUseCase -import dev.arkbuilders.rate.presentation.utils.NotificationUtils +import dev.arkbuilders.rate.core.domain.model.TimestampType +import dev.arkbuilders.rate.core.domain.repo.TimestampRepo +import dev.arkbuilders.rate.feature.pairalert.domain.model.PairAlert +import dev.arkbuilders.rate.feature.pairalert.domain.usecase.HandlePairAlertCheckUseCase +import dev.arkbuilders.rate.feature.pairalert.presentation.utils.NotificationUtils class CurrencyMonitorWorker( private val context: Context, diff --git a/app/src/main/java/dev/arkbuilders/rate/data/worker/CurrencyMonitorWorkerFactory.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/worker/CurrencyMonitorWorkerFactory.kt similarity index 80% rename from app/src/main/java/dev/arkbuilders/rate/data/worker/CurrencyMonitorWorkerFactory.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/worker/CurrencyMonitorWorkerFactory.kt index bc1fc3492..f356bc67f 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/worker/CurrencyMonitorWorkerFactory.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/data/worker/CurrencyMonitorWorkerFactory.kt @@ -1,11 +1,11 @@ -package dev.arkbuilders.rate.data.worker +package dev.arkbuilders.rate.feature.pairalert.data.worker import android.content.Context import androidx.work.ListenableWorker import androidx.work.WorkerFactory import androidx.work.WorkerParameters -import dev.arkbuilders.rate.domain.repo.TimestampRepo -import dev.arkbuilders.rate.domain.usecase.HandlePairAlertCheckUseCase +import dev.arkbuilders.rate.core.domain.repo.TimestampRepo +import dev.arkbuilders.rate.feature.pairalert.domain.usecase.HandlePairAlertCheckUseCase class CurrencyMonitorWorkerFactory( private val handlePairAlertCheckUseCase: HandlePairAlertCheckUseCase, diff --git a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertComponent.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertComponent.kt new file mode 100644 index 000000000..25ab1796d --- /dev/null +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertComponent.kt @@ -0,0 +1,32 @@ +package dev.arkbuilders.rate.feature.pairalert.di + +import android.content.Context +import dagger.Component +import dev.arkbuilders.rate.core.db.dao.PairAlertDao +import dev.arkbuilders.rate.core.di.CoreComponent +import dev.arkbuilders.rate.core.domain.repo.CodeUseStatRepo +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.feature.pairalert.domain.repo.PairAlertRepo +import dev.arkbuilders.rate.feature.pairalert.domain.usecase.HandlePairAlertCheckUseCase +import dev.arkbuilders.rate.feature.pairalert.presentation.add.AddPairAlertViewModelFactory +import dev.arkbuilders.rate.feature.pairalert.presentation.main.PairAlertViewModelFactory + +@PairAlertScope +@Component(dependencies = [CoreComponent::class], modules = [PairAlertModule::class]) +interface PairAlertComponent { + fun pairAlertVMFactory(): PairAlertViewModelFactory + + fun addPairAlertVMFactory(): AddPairAlertViewModelFactory.Factory + + fun currencyRepo(): CurrencyRepo + + fun pairAlertRepo(): PairAlertRepo + + fun pairAlertDao(): PairAlertDao + + fun codeUseStatRepo(): CodeUseStatRepo + + fun ctx(): Context + + fun handlePairAlertCheckUseCase(): HandlePairAlertCheckUseCase +} diff --git a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertComponentHolder.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertComponentHolder.kt new file mode 100644 index 000000000..7b17d2463 --- /dev/null +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertComponentHolder.kt @@ -0,0 +1,19 @@ +package dev.arkbuilders.rate.feature.pairalert.di + +import android.content.Context +import dev.arkbuilders.rate.core.di.CoreComponentProvider + +object PairAlertComponentHolder { + private var component: PairAlertComponent? = null + + fun provide(ctx: Context): PairAlertComponent { + component ?: let { + val app = ctx.applicationContext + val coreComponent = (app as CoreComponentProvider).provideCoreComponent() + component = + DaggerPairAlertComponent.builder().coreComponent(coreComponent) + .pairAlertModule(PairAlertModule()).build() + } + return component!! + } +} diff --git a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertModule.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertModule.kt new file mode 100644 index 000000000..af25602b5 --- /dev/null +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertModule.kt @@ -0,0 +1,29 @@ +package dev.arkbuilders.rate.feature.pairalert.di + +import dagger.Module +import dagger.Provides +import dev.arkbuilders.rate.core.db.dao.PairAlertDao +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.usecase.ConvertWithRateUseCase +import dev.arkbuilders.rate.feature.pairalert.data.repo.PairAlertRepoImpl +import dev.arkbuilders.rate.feature.pairalert.domain.repo.PairAlertRepo +import dev.arkbuilders.rate.feature.pairalert.domain.usecase.HandlePairAlertCheckUseCase + +@Module +class PairAlertModule { + @PairAlertScope + @Provides + fun pairAlertRepo(pairAlertDao: PairAlertDao): PairAlertRepo = PairAlertRepoImpl(pairAlertDao) + + @PairAlertScope + @Provides + fun handlePairAlertCheckUseCase( + currencyRepo: CurrencyRepo, + pairAlertRepo: PairAlertRepo, + convertWithRateUseCase: ConvertWithRateUseCase, + ) = HandlePairAlertCheckUseCase( + currencyRepo, + pairAlertRepo, + convertWithRateUseCase, + ) +} diff --git a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertScope.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertScope.kt new file mode 100644 index 000000000..061f863a9 --- /dev/null +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/di/PairAlertScope.kt @@ -0,0 +1,7 @@ +package dev.arkbuilders.rate.feature.pairalert.di + +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.SOURCE) +annotation class PairAlertScope diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/model/PairAlert.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/domain/model/PairAlert.kt similarity index 81% rename from app/src/main/java/dev/arkbuilders/rate/domain/model/PairAlert.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/domain/model/PairAlert.kt index 8b2b2afff..2c54320cd 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/model/PairAlert.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/domain/model/PairAlert.kt @@ -1,5 +1,6 @@ -package dev.arkbuilders.rate.domain.model +package dev.arkbuilders.rate.feature.pairalert.domain.model +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import java.math.BigDecimal import java.time.OffsetDateTime diff --git a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/domain/repo/PairAlertRepo.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/domain/repo/PairAlertRepo.kt new file mode 100644 index 000000000..f30813208 --- /dev/null +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/domain/repo/PairAlertRepo.kt @@ -0,0 +1,17 @@ +package dev.arkbuilders.rate.feature.pairalert.domain.repo + +import kotlinx.coroutines.flow.Flow + +interface PairAlertRepo { + suspend fun insert( + pairAlert: dev.arkbuilders.rate.feature.pairalert.domain.model.PairAlert, + ): Long + + suspend fun getById(id: Long): dev.arkbuilders.rate.feature.pairalert.domain.model.PairAlert? + + suspend fun getAll(): List + + fun getAllFlow(): Flow> + + suspend fun delete(id: Long): Boolean +} diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/usecase/HandlePairAlertCheckUseCase.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/domain/usecase/HandlePairAlertCheckUseCase.kt similarity index 83% rename from app/src/main/java/dev/arkbuilders/rate/domain/usecase/HandlePairAlertCheckUseCase.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/domain/usecase/HandlePairAlertCheckUseCase.kt index 0ec162cfb..b2f7c15c4 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/usecase/HandlePairAlertCheckUseCase.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/domain/usecase/HandlePairAlertCheckUseCase.kt @@ -1,23 +1,21 @@ -package dev.arkbuilders.rate.domain.usecase +package dev.arkbuilders.rate.feature.pairalert.domain.usecase import arrow.core.Either import arrow.core.getOrElse import arrow.core.left import arrow.core.right -import dev.arkbuilders.rate.data.divideArk -import dev.arkbuilders.rate.domain.model.Amount -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.CurrencyRate -import dev.arkbuilders.rate.domain.model.PairAlert -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import dev.arkbuilders.rate.domain.repo.PairAlertRepo +import dev.arkbuilders.rate.core.domain.divideArk +import dev.arkbuilders.rate.core.domain.model.Amount +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyRate +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.usecase.ConvertWithRateUseCase +import dev.arkbuilders.rate.feature.pairalert.domain.model.PairAlert +import dev.arkbuilders.rate.feature.pairalert.domain.repo.PairAlertRepo import java.math.BigDecimal import java.time.OffsetDateTime -import javax.inject.Inject -import javax.inject.Singleton -@Singleton -class HandlePairAlertCheckUseCase @Inject constructor( +class HandlePairAlertCheckUseCase( private val currencyRepo: CurrencyRepo, private val pairAlertRepo: PairAlertRepo, private val convertUseCase: ConvertWithRateUseCase, diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/AddPairAlertScreen.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertScreen.kt similarity index 80% rename from app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/AddPairAlertScreen.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertScreen.kt index 60777a70a..719227c14 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/AddPairAlertScreen.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertScreen.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.pairalert +package dev.arkbuilders.rate.feature.pairalert.presentation.add import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -33,7 +33,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource @@ -47,21 +46,24 @@ import androidx.compose.ui.window.PopupProperties import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.presentation.destinations.SearchCurrencyScreenDestination -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow -import dev.arkbuilders.rate.presentation.shared.AppSharedFlowKey -import dev.arkbuilders.rate.presentation.theme.ArkColor -import dev.arkbuilders.rate.presentation.ui.AppButton -import dev.arkbuilders.rate.presentation.ui.AppTopBarBack -import dev.arkbuilders.rate.presentation.ui.ArkLargeTextField -import dev.arkbuilders.rate.presentation.ui.GroupCreateDialog -import dev.arkbuilders.rate.presentation.ui.GroupSelectPopup -import dev.arkbuilders.rate.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.core.presentation.AppSharedFlowKey +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.ui.AppButton +import dev.arkbuilders.rate.core.presentation.ui.AppTopBarBack +import dev.arkbuilders.rate.core.presentation.ui.ArkLargeTextField +import dev.arkbuilders.rate.core.presentation.ui.DropDownWithIcon +import dev.arkbuilders.rate.core.presentation.ui.GroupCreateDialog +import dev.arkbuilders.rate.core.presentation.ui.GroupSelectPopup +import dev.arkbuilders.rate.core.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.feature.pairalert.di.PairAlertComponentHolder +import dev.arkbuilders.rate.feature.search.presentation.destinations.SearchCurrencyScreenDestination import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect +import dev.arkbuilders.rate.core.presentation.R as CoreR @Destination @Composable @@ -70,9 +72,13 @@ fun AddPairAlertScreen( navigator: DestinationsNavigator, ) { val ctx = LocalContext.current + val component = + remember { + PairAlertComponentHolder.provide(ctx) + } val viewModel: AddPairAlertViewModel = viewModel( - factory = DIManager.component.addPairAlertVMFactory().create(pairAlertId), + factory = component.addPairAlertVMFactory().create(pairAlertId), ) val state by viewModel.collectAsState() @@ -84,19 +90,19 @@ fun AddPairAlertScreen( val pair = effect.pair val aboveOrBelow = if (pair.above()) - ctx.getString(R.string.above) + ctx.getString(CoreRString.above) else - ctx.getString(R.string.below) + ctx.getString(CoreRString.below) val visuals = NotifyAddedSnackbarVisuals( title = ctx.getString( - R.string.alert_snackbar_new_title, + CoreRString.alert_snackbar_new_title, pair.targetCode, ), description = ctx.getString( - R.string.alert_snackbar_new_desc, + CoreRString.alert_snackbar_new_desc, pair.targetCode, aboveOrBelow, CurrUtils.prepareToDisplay(pair.targetPrice), @@ -113,10 +119,10 @@ fun AddPairAlertScreen( AppTopBarBack( title = if (state.editExisting) - stringResource(R.string.alert_edit_alert) + stringResource(CoreRString.alert_edit_alert) else - stringResource(R.string.add_new_alert), - navigator = navigator, + stringResource(CoreRString.add_new_alert), + onBackClick = { navigator.popBackStack() }, ) }, ) { @@ -167,8 +173,8 @@ private fun Content( onClick = { showGroupsPopup = !showGroupsPopup }, title = state.group?.let { state.group } - ?: stringResource(R.string.add_group), - icon = painterResource(id = R.drawable.ic_group), + ?: stringResource(CoreRString.add_group), + icon = painterResource(id = CoreR.drawable.ic_group), ) if (showGroupsPopup) { Box( @@ -207,9 +213,9 @@ private fun Content( Text( text = if (state.editExisting) - stringResource(R.string.save) + stringResource(CoreRString.save) else - stringResource(R.string.create_alert), + stringResource(CoreRString.create_alert), ) } } @@ -234,7 +240,7 @@ private fun PriceOrPercent( Modifier .padding(6.dp) .weight(1f), - title = stringResource(R.string.by_price), + title = stringResource(CoreRString.by_price), enabled = state.priceOrPercent.isLeft(), ) { onPriceOrPercentChanged(true) @@ -244,7 +250,7 @@ private fun PriceOrPercent( Modifier .padding(6.dp) .weight(1f), - title = stringResource(R.string.by_percent), + title = stringResource(CoreRString.by_percent), enabled = state.priceOrPercent.isRight(), ) { onPriceOrPercentChanged(false) @@ -328,51 +334,7 @@ private fun DropDownBtn( ) Icon( modifier = Modifier.padding(start = 8.dp, end = 15.dp), - painter = painterResource(id = R.drawable.ic_chevron), - contentDescription = "", - tint = ArkColor.FGSecondary, - ) - } -} - -@Composable -fun DropDownWithIcon( - modifier: Modifier, - onClick: () -> Unit, - title: String, - icon: Painter, -) { - Row( - modifier = - modifier - .height(44.dp) - .border( - 1.dp, - ArkColor.Border, - RoundedCornerShape(8.dp), - ) - .clip(RoundedCornerShape(8.dp)) - .clickable { onClick() }, - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - modifier = Modifier.padding(start = 16.dp), - painter = icon, - contentDescription = "", - tint = ArkColor.FGSecondary, - ) - Text( - modifier = - Modifier - .padding(start = 8.dp) - .weight(1f), - text = title, - fontSize = 16.sp, - color = ArkColor.TextPlaceHolder, - ) - Icon( - modifier = Modifier.padding(end = 20.dp), - painter = painterResource(id = R.drawable.ic_chevron), + painter = painterResource(id = CoreRDrawable.ic_chevron), contentDescription = "", tint = ArkColor.FGSecondary, ) @@ -391,12 +353,15 @@ private fun EditCondition( horizontalAlignment = Alignment.CenterHorizontally, ) { Row( - modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()), + modifier = + Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center, ) { Text( - text = stringResource(R.string.when_), + text = stringResource(CoreRString.when_), color = ArkColor.TextTertiary, ) DropDownBtn( @@ -409,7 +374,7 @@ private fun EditCondition( } Text( modifier = Modifier.padding(start = 8.dp), - text = stringResource(R.string.price_is), + text = stringResource(CoreRString.price_is), color = ArkColor.TextTertiary, ) Row( @@ -430,9 +395,9 @@ private fun EditCondition( painterResource( id = if (state.aboveNotBelow) - R.drawable.ic_pair_alert_inc + CoreRDrawable.ic_pair_alert_inc else - R.drawable.ic_pair_alert_dec, + CoreRDrawable.ic_pair_alert_dec, ), contentDescription = "", tint = @@ -445,9 +410,9 @@ private fun EditCondition( modifier = Modifier.padding(start = 4.dp), text = if (state.aboveNotBelow) - ctx.getString(R.string.above) + ctx.getString(CoreRString.above) else - ctx.getString(R.string.below), + ctx.getString(CoreRString.below), color = if (state.aboveNotBelow) ArkColor.PairAlertInc @@ -467,7 +432,7 @@ private fun EditCondition( if (!state.oneTimeNotRecurrent) { Text( modifier = Modifier.align(Alignment.CenterVertically), - text = stringResource(R.string.every), + text = stringResource(CoreRString.every), fontSize = 20.sp, fontWeight = FontWeight.Medium, color = ArkColor.TextPrimary, @@ -505,13 +470,16 @@ private fun EditCondition( } } Row( - modifier = Modifier.padding(top = 24.dp).horizontalScroll(rememberScrollState()), + modifier = + Modifier + .padding(top = 24.dp) + .horizontalScroll(rememberScrollState()), verticalAlignment = Alignment.CenterVertically, ) { Text( text = stringResource( - R.string.alert_current_price, + CoreRString.alert_current_price, CurrUtils.prepareToDisplay(state.currentPrice), ), color = ArkColor.TextTertiary, @@ -547,7 +515,7 @@ private fun OneTimeOrRecurrent( Modifier .padding(6.dp) .weight(1f), - title = stringResource(R.string.one_time), + title = stringResource(CoreRString.one_time), enabled = oneTimeNotRecurrent, ) { onOneTimeChanged(true) @@ -559,9 +527,9 @@ private fun OneTimeOrRecurrent( .weight(1f), title = if (byPrice) - stringResource(R.string.every_c) + stringResource(CoreRString.every_c) else - stringResource(R.string.recurrent), + stringResource(CoreRString.recurrent), enabled = !oneTimeNotRecurrent, ) { onOneTimeChanged(false) diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/AddPairAlertViewModel.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt similarity index 94% rename from app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/AddPairAlertViewModel.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt index 3ce7842aa..1801f9c4c 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/AddPairAlertViewModel.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.pairalert +package dev.arkbuilders.rate.feature.pairalert.presentation.add import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -7,18 +7,18 @@ import arrow.core.Either import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.data.divideArk -import dev.arkbuilders.rate.data.toBigDecimalArk -import dev.arkbuilders.rate.data.toDoubleArk -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.PairAlert -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.CodeUseStatRepo -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import dev.arkbuilders.rate.domain.repo.PairAlertRepo -import dev.arkbuilders.rate.domain.usecase.ConvertWithRateUseCase -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.domain.divideArk +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.CodeUseStatRepo +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.toBigDecimalArk +import dev.arkbuilders.rate.core.domain.toDoubleArk +import dev.arkbuilders.rate.core.domain.usecase.ConvertWithRateUseCase +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.feature.pairalert.domain.model.PairAlert +import dev.arkbuilders.rate.feature.pairalert.domain.repo.PairAlertRepo import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.orbitmvi.orbit.Container diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/PairAlertConditionScreen.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/main/PairAlertConditionScreen.kt similarity index 81% rename from app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/PairAlertConditionScreen.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/main/PairAlertConditionScreen.kt index e5c97151f..633e93bb2 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/PairAlertConditionScreen.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/main/PairAlertConditionScreen.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.pairalert +package dev.arkbuilders.rate.feature.pairalert.presentation.main import android.Manifest import android.content.Context @@ -46,29 +46,30 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.domain.model.PairAlert -import dev.arkbuilders.rate.presentation.destinations.AddPairAlertScreenDestination -import dev.arkbuilders.rate.presentation.theme.ArkColor -import dev.arkbuilders.rate.presentation.ui.AppButton -import dev.arkbuilders.rate.presentation.ui.AppHorDiv16 -import dev.arkbuilders.rate.presentation.ui.AppSwipeToDismiss -import dev.arkbuilders.rate.presentation.ui.AppTopBarCenterTitle -import dev.arkbuilders.rate.presentation.ui.CurrIcon -import dev.arkbuilders.rate.presentation.ui.GroupViewPager -import dev.arkbuilders.rate.presentation.ui.LoadingScreen -import dev.arkbuilders.rate.presentation.ui.NoInternetScreen -import dev.arkbuilders.rate.presentation.ui.NotifyRemovedSnackbarVisuals -import dev.arkbuilders.rate.presentation.ui.RateSnackbarHost -import dev.arkbuilders.rate.presentation.utils.DateFormatUtils +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.ui.AppButton +import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv16 +import dev.arkbuilders.rate.core.presentation.ui.AppSwipeToDismiss +import dev.arkbuilders.rate.core.presentation.ui.AppTopBarCenterTitle +import dev.arkbuilders.rate.core.presentation.ui.CurrIcon +import dev.arkbuilders.rate.core.presentation.ui.GroupViewPager +import dev.arkbuilders.rate.core.presentation.ui.LoadingScreen +import dev.arkbuilders.rate.core.presentation.ui.NoInternetScreen +import dev.arkbuilders.rate.core.presentation.ui.NotifyRemovedSnackbarVisuals +import dev.arkbuilders.rate.core.presentation.ui.RateSnackbarHost +import dev.arkbuilders.rate.core.presentation.utils.DateFormatUtils +import dev.arkbuilders.rate.feature.pairalert.di.PairAlertComponent +import dev.arkbuilders.rate.feature.pairalert.di.PairAlertComponentHolder +import dev.arkbuilders.rate.feature.pairalert.domain.model.PairAlert +import dev.arkbuilders.rate.feature.pairalert.presentation.destinations.AddPairAlertScreenDestination import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect import timber.log.Timber @@ -76,10 +77,14 @@ import timber.log.Timber @Destination @Composable fun PairAlertConditionScreen(navigator: DestinationsNavigator) { + val ctx = LocalContext.current + val component = + remember { + PairAlertComponentHolder.provide(ctx) + } val viewModel: PairAlertViewModel = - viewModel(factory = DIManager.component.pairAlertVMFactory()) + viewModel(factory = component.pairAlertVMFactory()) - val ctx = LocalContext.current val onScreenOpenNotificationPermissionLauncher = rememberNotificationPermissionLauncher(ctx) val onNewPairNotificationPermissionLauncher = rememberNotificationPermissionLauncher( @@ -119,12 +124,12 @@ fun PairAlertConditionScreen(navigator: DestinationsNavigator) { NotifyRemovedSnackbarVisuals( title = ctx.getString( - R.string.alert_snackbar_removed_title, + CoreRString.alert_snackbar_removed_title, effect.pair.targetCode, ), description = ctx.getString( - R.string.alert_snackbar_removed_desc, + CoreRString.alert_snackbar_removed_desc, effect.pair.targetCode, effect.pair.baseCode, ), @@ -156,7 +161,7 @@ fun PairAlertConditionScreen(navigator: DestinationsNavigator) { }, topBar = { if (isEmpty) return@Scaffold - AppTopBarCenterTitle(title = stringResource(R.string.alerts)) + AppTopBarCenterTitle(title = stringResource(CoreRString.alerts)) }, snackbarHost = { RateSnackbarHost(snackState) @@ -166,10 +171,11 @@ fun PairAlertConditionScreen(navigator: DestinationsNavigator) { when { state.noInternet -> NoInternetScreen(viewModel::onRefreshClick) state.initialized.not() -> LoadingScreen() - isEmpty -> Empty(navigator, onNewPair = viewModel::onNewPair) + isEmpty -> Empty(onNewPair = viewModel::onNewPair) else -> Content( - state, + component = component, + state = state, onDelete = viewModel::onDelete, onClick = { pair -> viewModel.onNewPair(pair.id) @@ -183,6 +189,7 @@ fun PairAlertConditionScreen(navigator: DestinationsNavigator) { @Composable private fun Content( + component: PairAlertComponent, state: PairAlertScreenState, onDelete: (PairAlert) -> Unit, onClick: (PairAlert) -> Unit, @@ -191,6 +198,7 @@ private fun Content( Column { if (state.pages.size == 1) { GroupPage( + component = component, page = state.pages.first(), onDelete = { onDelete(it) }, onClick = onClick, @@ -202,6 +210,7 @@ private fun Content( groups = state.pages.map { it.group }, ) { index -> GroupPage( + component = component, page = state.pages[index], onDelete = { onDelete(it) }, onClick = onClick, @@ -212,18 +221,13 @@ private fun Content( } } -@Preview @Composable private fun GroupPage( - page: PairAlertScreenPage = - PairAlertScreenPage( - group = "Group 1", - created = listOf(previewPairAlert), - oneTimeTriggered = listOf(previewPairAlert), - ), - onDelete: (PairAlert) -> Unit = {}, - onClick: (PairAlert) -> Unit = {}, - onEnableToggle: (PairAlert, Boolean) -> Unit = { _, _ -> }, + component: PairAlertComponent, + page: PairAlertScreenPage, + onDelete: (PairAlert) -> Unit, + onClick: (PairAlert) -> Unit, + onEnableToggle: (PairAlert, Boolean) -> Unit, ) { LazyColumn(modifier = Modifier.fillMaxSize()) { if (page.created.isNotEmpty()) { @@ -239,6 +243,7 @@ private fun GroupPage( AppSwipeToDismiss( content = { PairAlertItem( + component = component, pairAlert = it, oneTimeTriggered = false, onClick = onClick, @@ -263,6 +268,7 @@ private fun GroupPage( AppSwipeToDismiss( content = { PairAlertItem( + component = component, pairAlert = it, oneTimeTriggered = true, onClick = onClick, @@ -279,6 +285,7 @@ private fun GroupPage( @Composable private fun PairAlertItem( + component: PairAlertComponent, pairAlert: PairAlert, oneTimeTriggered: Boolean, onClick: (PairAlert) -> Unit, @@ -287,7 +294,7 @@ private fun PairAlertItem( var currencyName by remember { mutableStateOf("") } - val currencyRepo = DIManager.component.generalCurrencyRepo() + val currencyRepo = component.currencyRepo() LaunchedEffect(Unit) { currencyName = currencyRepo.nameByCodeUnsafe(pairAlert.targetCode).name } @@ -323,9 +330,9 @@ private fun PairAlertItem( append( "${ if (pairAlert.above()) - stringResource(R.string.above_c) + stringResource(CoreRString.above_c) else - stringResource(R.string.below_c) + stringResource(CoreRString.below_c) } ", ) append("${CurrUtils.prepareToDisplay(pairAlert.targetPrice)} ") @@ -341,7 +348,7 @@ private fun PairAlertItem( Text( text = stringResource( - R.string.alert_notified_on, + CoreRString.alert_notified_on, DateFormatUtils.notifiedOn(date), ), color = ArkColor.TextTertiary, @@ -367,30 +374,27 @@ private fun PairAlertItem( } @Composable -private fun Empty( - navigator: DestinationsNavigator, - onNewPair: () -> Unit, -) { +private fun Empty(onNewPair: () -> Unit) { Box(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier.align(Alignment.Center), horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( - painter = painterResource(id = R.drawable.ic_empty_pair), + painter = painterResource(id = CoreRDrawable.ic_empty_pair), contentDescription = "", tint = Color.Unspecified, ) Text( modifier = Modifier.padding(top = 16.dp), - text = stringResource(R.string.alert_empty_title), + text = stringResource(CoreRString.alert_empty_title), fontWeight = FontWeight.SemiBold, fontSize = 20.sp, color = ArkColor.TextPrimary, ) Text( modifier = Modifier.padding(top = 6.dp, start = 24.dp, end = 24.dp), - text = stringResource(R.string.alert_empty_desc), + text = stringResource(CoreRString.alert_empty_desc), fontSize = 14.sp, lineHeight = 20.sp, color = ArkColor.TextTertiary, @@ -401,12 +405,12 @@ private fun Empty( onClick = { onNewPair() }, ) { Icon( - painter = painterResource(id = R.drawable.ic_add), + painter = painterResource(id = CoreRDrawable.ic_add), contentDescription = "", ) Text( modifier = Modifier.padding(start = 8.dp), - text = stringResource(R.string.new_alert), + text = stringResource(CoreRString.new_alert), ) } } @@ -425,7 +429,7 @@ private fun rememberNotificationPermissionLauncher( } else { Toast.makeText( ctx, - ctx.getString(R.string.alert_post_notification_permission_explanation), + ctx.getString(CoreRString.alert_post_notification_permission_explanation), Toast.LENGTH_SHORT, ).show() } diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/PairAlertViewModel.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/main/PairAlertViewModel.kt similarity index 89% rename from app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/PairAlertViewModel.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/main/PairAlertViewModel.kt index 8457dbb94..ebf93c3cd 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/pairalert/PairAlertViewModel.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/main/PairAlertViewModel.kt @@ -1,15 +1,16 @@ -package dev.arkbuilders.rate.presentation.pairalert +package dev.arkbuilders.rate.feature.pairalert.presentation.main import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope -import dev.arkbuilders.rate.data.permission.NotificationPermissionHelper -import dev.arkbuilders.rate.domain.model.PairAlert -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import dev.arkbuilders.rate.domain.repo.PairAlertRepo -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow -import dev.arkbuilders.rate.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.core.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.feature.pairalert.data.permission.NotificationPermissionHelper +import dev.arkbuilders.rate.feature.pairalert.di.PairAlertScope +import dev.arkbuilders.rate.feature.pairalert.domain.model.PairAlert +import dev.arkbuilders.rate.feature.pairalert.domain.repo.PairAlertRepo import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.orbitmvi.orbit.Container @@ -19,7 +20,6 @@ import org.orbitmvi.orbit.syntax.simple.postSideEffect import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container import javax.inject.Inject -import javax.inject.Singleton data class PairAlertScreenPage( val group: String?, @@ -159,7 +159,7 @@ class PairAlertViewModel( } } -@Singleton +@PairAlertScope class PairAlertViewModelFactory @Inject constructor( private val pairAlertRepo: PairAlertRepo, private val currencyRepo: CurrencyRepo, diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/utils/NotificationUtils.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/utils/NotificationUtils.kt similarity index 87% rename from app/src/main/java/dev/arkbuilders/rate/presentation/utils/NotificationUtils.kt rename to feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/utils/NotificationUtils.kt index 533c55de4..127cc1ba4 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/utils/NotificationUtils.kt +++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/utils/NotificationUtils.kt @@ -1,6 +1,7 @@ -package dev.arkbuilders.rate.presentation.utils +package dev.arkbuilders.rate.feature.pairalert.presentation.utils import android.Manifest +import android.annotation.SuppressLint import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent @@ -12,12 +13,12 @@ import androidx.core.app.ActivityCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.domain.model.PairAlert -import dev.arkbuilders.rate.presentation.MainActivity +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.feature.pairalert.domain.model.PairAlert object NotificationUtils { + @SuppressLint("MissingPermission") fun showPairAlert( pairAlert: PairAlert, ctx: Context, @@ -61,7 +62,8 @@ object NotificationUtils { private fun appIntent(ctx: Context): PendingIntent { val intent = - Intent(ctx, MainActivity::class.java).apply { + Intent().apply { + setClassName("dev.arkbuilders.rate.presentation", "MainActivity") flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } diff --git a/feature/pairalert/src/test/java/dev/arkbuilders/rate/feature/pairalert/ExampleUnitTest.kt b/feature/pairalert/src/test/java/dev/arkbuilders/rate/feature/pairalert/ExampleUnitTest.kt new file mode 100644 index 000000000..41815a709 --- /dev/null +++ b/feature/pairalert/src/test/java/dev/arkbuilders/rate/feature/pairalert/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package dev.arkbuilders.rate.feature.pairalert + +import org.junit.Assert.assertEquals +import org.junit.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) + } +} diff --git a/feature/portfolio/.gitignore b/feature/portfolio/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/feature/portfolio/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/portfolio/build.gradle.kts b/feature/portfolio/build.gradle.kts new file mode 100644 index 000000000..414ac99da --- /dev/null +++ b/feature/portfolio/build.gradle.kts @@ -0,0 +1,83 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) +} + +android { + namespace = "dev.arkbuilders.rate.feature.portfolio" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + ksp { + arg("compose-destinations.mode", "destinations") + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(project(":core:di")) + implementation(project(":core:db")) + implementation(project(":core:domain")) + implementation(project(":core:presentation")) + implementation(project(":feature:search")) + + implementation(libs.androidx.core.ktx) + + implementation(libs.androidx.ui) + implementation(libs.navigation.compose) + implementation(libs.material3) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(libs.constraintlayout.compose) + + implementation(libs.timber) + + implementation(libs.dagger) + ksp(libs.dagger.compiler) + + implementation(libs.orbit.compose) + implementation(libs.orbit.viewmodel) + + implementation(libs.arrow.core) + implementation(libs.arrow.fx.coroutines) + + implementation(libs.compose.destinations.animations) + ksp(libs.compose.destinations.compiler) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":feature:portfolio:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":feature:portfolio:preBuild").dependsOn("ktlintFormat") diff --git a/feature/portfolio/consumer-rules.pro b/feature/portfolio/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/feature/portfolio/proguard-rules.pro b/feature/portfolio/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/feature/portfolio/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/feature/portfolio/src/androidTest/java/dev/arkbuilders/rate/feature/portfolio/ExampleInstrumentedTest.kt b/feature/portfolio/src/androidTest/java/dev/arkbuilders/rate/feature/portfolio/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..488c1283a --- /dev/null +++ b/feature/portfolio/src/androidTest/java/dev/arkbuilders/rate/feature/portfolio/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package dev.arkbuilders.rate.feature.portfolio + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * 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("dev.arkbuilders.rate.feature.portfolio.test", appContext.packageName) + } +} diff --git a/feature/portfolio/src/main/AndroidManifest.xml b/feature/portfolio/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/feature/portfolio/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/PortfolioRepoImpl.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/data/repo/PortfolioRepoImpl.kt similarity index 75% rename from app/src/main/java/dev/arkbuilders/rate/data/repo/PortfolioRepoImpl.kt rename to feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/data/repo/PortfolioRepoImpl.kt index b1b2fe3c9..e53b1723e 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/PortfolioRepoImpl.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/data/repo/PortfolioRepoImpl.kt @@ -1,9 +1,9 @@ -package dev.arkbuilders.rate.data.repo +package dev.arkbuilders.rate.feature.portfolio.data.repo -import dev.arkbuilders.rate.data.db.dao.PortfolioDao -import dev.arkbuilders.rate.data.db.entity.RoomAsset -import dev.arkbuilders.rate.domain.model.Asset -import dev.arkbuilders.rate.domain.repo.PortfolioRepo +import dev.arkbuilders.rate.core.db.dao.PortfolioDao +import dev.arkbuilders.rate.core.db.entity.RoomAsset +import dev.arkbuilders.rate.feature.portfolio.domain.model.Asset +import dev.arkbuilders.rate.feature.portfolio.domain.repo.PortfolioRepo import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioComponent.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioComponent.kt new file mode 100644 index 000000000..2ebc2bff5 --- /dev/null +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioComponent.kt @@ -0,0 +1,23 @@ +package dev.arkbuilders.rate.feature.portfolio.di + +import dagger.Component +import dev.arkbuilders.rate.core.db.dao.PortfolioDao +import dev.arkbuilders.rate.core.di.CoreComponent +import dev.arkbuilders.rate.feature.portfolio.domain.repo.PortfolioRepo +import dev.arkbuilders.rate.feature.portfolio.presentation.add.AddAssetViewModelFactory +import dev.arkbuilders.rate.feature.portfolio.presentation.edit.EditAssetViewModelFactory +import dev.arkbuilders.rate.feature.portfolio.presentation.main.PortfolioViewModelFactory + +@PortfolioScope +@Component(dependencies = [CoreComponent::class], modules = [PortfolioModule::class]) +interface PortfolioComponent { + fun assetsVMFactory(): PortfolioViewModelFactory + + fun addCurrencyVMFactory(): AddAssetViewModelFactory + + fun editAssetVMFactory(): EditAssetViewModelFactory.Factory + + fun assetsRepo(): PortfolioRepo + + fun portfolioDao(): PortfolioDao +} diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioComponentHolder.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioComponentHolder.kt new file mode 100644 index 000000000..729632131 --- /dev/null +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioComponentHolder.kt @@ -0,0 +1,19 @@ +package dev.arkbuilders.rate.feature.portfolio.di + +import android.content.Context +import dev.arkbuilders.rate.core.di.CoreComponentProvider + +object PortfolioComponentHolder { + private var component: PortfolioComponent? = null + + fun provide(ctx: Context): PortfolioComponent { + component ?: let { + val app = ctx.applicationContext + val coreComponent = (app as CoreComponentProvider).provideCoreComponent() + component = + DaggerPortfolioComponent.builder().coreComponent(coreComponent) + .portfolioModule(PortfolioModule()).build() + } + return component!! + } +} diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioModule.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioModule.kt new file mode 100644 index 000000000..1a108b5f4 --- /dev/null +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioModule.kt @@ -0,0 +1,14 @@ +package dev.arkbuilders.rate.feature.portfolio.di + +import dagger.Module +import dagger.Provides +import dev.arkbuilders.rate.core.db.dao.PortfolioDao +import dev.arkbuilders.rate.feature.portfolio.data.repo.PortfolioRepoImpl +import dev.arkbuilders.rate.feature.portfolio.domain.repo.PortfolioRepo + +@Module +class PortfolioModule { + @PortfolioScope + @Provides + fun portfolioRepo(portfolioDao: PortfolioDao): PortfolioRepo = PortfolioRepoImpl(portfolioDao) +} diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioScope.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioScope.kt new file mode 100644 index 000000000..33951609a --- /dev/null +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/di/PortfolioScope.kt @@ -0,0 +1,7 @@ +package dev.arkbuilders.rate.feature.portfolio.di + +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.SOURCE) +annotation class PortfolioScope diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/model/Asset.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/domain/model/Asset.kt similarity index 63% rename from app/src/main/java/dev/arkbuilders/rate/domain/model/Asset.kt rename to feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/domain/model/Asset.kt index 003d2b240..5aafbcdcd 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/model/Asset.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/domain/model/Asset.kt @@ -1,16 +1,14 @@ -package dev.arkbuilders.rate.domain.model +package dev.arkbuilders.rate.feature.portfolio.domain.model -import android.os.Parcelable -import kotlinx.parcelize.Parcelize +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import java.math.BigDecimal -@Parcelize data class Asset( val id: Long = 0, val code: CurrencyCode, var value: BigDecimal, val group: String? = null, -) : Parcelable { +) { companion object { val EMPTY = Asset(0, "", BigDecimal.ZERO) } diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/domain/repo/PortfolioRepo.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/domain/repo/PortfolioRepo.kt new file mode 100644 index 000000000..4872c4c78 --- /dev/null +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/domain/repo/PortfolioRepo.kt @@ -0,0 +1,17 @@ +package dev.arkbuilders.rate.feature.portfolio.domain.repo + +import kotlinx.coroutines.flow.Flow + +interface PortfolioRepo { + suspend fun allAssets(): List + + fun allAssetsFlow(): Flow> + + suspend fun getById(id: Long): dev.arkbuilders.rate.feature.portfolio.domain.model.Asset? + + suspend fun setAsset(asset: dev.arkbuilders.rate.feature.portfolio.domain.model.Asset) + + suspend fun setAssetsList(list: List) + + suspend fun removeAsset(id: Long): Boolean +} diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/AddAssetScreen.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt similarity index 81% rename from app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/AddAssetScreen.kt rename to feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt index f91084148..ddaabc676 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/AddAssetScreen.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.portfolio +package dev.arkbuilders.rate.feature.portfolio.presentation.add import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.border @@ -48,31 +48,36 @@ import androidx.compose.ui.window.PopupProperties import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.domain.model.AmountStr -import dev.arkbuilders.rate.presentation.destinations.SearchCurrencyScreenDestination -import dev.arkbuilders.rate.presentation.pairalert.DropDownWithIcon -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow -import dev.arkbuilders.rate.presentation.shared.AppSharedFlowKey -import dev.arkbuilders.rate.presentation.theme.ArkColor -import dev.arkbuilders.rate.presentation.ui.AppButton -import dev.arkbuilders.rate.presentation.ui.AppTopBarBack -import dev.arkbuilders.rate.presentation.ui.ArkBasicTextField -import dev.arkbuilders.rate.presentation.ui.GroupCreateDialog -import dev.arkbuilders.rate.presentation.ui.GroupSelectPopup -import dev.arkbuilders.rate.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.domain.model.AmountStr +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.core.presentation.AppSharedFlowKey +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.ui.AppButton +import dev.arkbuilders.rate.core.presentation.ui.AppTopBarBack +import dev.arkbuilders.rate.core.presentation.ui.ArkBasicTextField +import dev.arkbuilders.rate.core.presentation.ui.DropDownWithIcon +import dev.arkbuilders.rate.core.presentation.ui.GroupCreateDialog +import dev.arkbuilders.rate.core.presentation.ui.GroupSelectPopup +import dev.arkbuilders.rate.core.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.feature.portfolio.di.PortfolioComponentHolder +import dev.arkbuilders.rate.feature.search.presentation.destinations.SearchCurrencyScreenDestination import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect +import dev.arkbuilders.rate.core.presentation.R as CoreR @Destination @Composable fun AddAssetScreen(navigator: DestinationsNavigator) { val ctx = LocalContext.current + val component = + remember { + PortfolioComponentHolder.provide(ctx) + } val viewModel: AddAssetViewModel = - viewModel(factory = DIManager.component.addCurrencyVMFactory()) + viewModel(factory = component.addCurrencyVMFactory()) val state by viewModel.collectAsState() @@ -87,9 +92,9 @@ fun AddAssetScreen(navigator: DestinationsNavigator) { } AppSharedFlow.ShowAddedSnackbarPortfolio.flow.emit( NotifyAddedSnackbarVisuals( - ctx.getString(R.string.portfolio_snackbar_new_title), + ctx.getString(CoreRString.portfolio_snackbar_new_title), ctx.getString( - R.string.portfolio_snackbar_new_desc, + CoreRString.portfolio_snackbar_new_desc, added, ), ), @@ -101,31 +106,25 @@ fun AddAssetScreen(navigator: DestinationsNavigator) { Scaffold( topBar = { AppTopBarBack( - title = stringResource(R.string.add_new_asset), - navigator = navigator, + title = stringResource(CoreRString.add_new_asset), + onBackClick = { navigator.popBackStack() }, ) }, ) { Box(modifier = Modifier.padding(it)) { Content( state = state, - navigator = navigator, onAssetValueChanged = viewModel::onAssetValueChange, onNewCurrencyClick = { navigator.navigate( - SearchCurrencyScreenDestination( - AppSharedFlowKey.AddAsset.toString(), - ), + SearchCurrencyScreenDestination(AppSharedFlowKey.AddAsset.toString()), ) }, onAssetRemove = viewModel::onAssetRemove, onGroupSelect = viewModel::onGroupSelect, onCodeChange = { navigator.navigate( - SearchCurrencyScreenDestination( - AppSharedFlowKey.SetAssetCode.name, - it, - ), + SearchCurrencyScreenDestination(AppSharedFlowKey.SetAssetCode.name, it), ) }, onAddAsset = viewModel::onAddAsset, @@ -138,7 +137,6 @@ fun AddAssetScreen(navigator: DestinationsNavigator) { @Composable private fun Content( state: AddAssetState = AddAssetState(emptyList(), group = "Hello"), - navigator: DestinationsNavigator = EmptyDestinationsNavigator, onAssetValueChanged: (Int, String) -> Unit = { _, _ -> }, onNewCurrencyClick: () -> Unit = {}, onAssetRemove: (Int) -> Unit = {}, @@ -178,7 +176,7 @@ private fun Content( ) { Icon( modifier = Modifier.padding(start = 20.dp), - painter = painterResource(id = R.drawable.ic_add), + painter = painterResource(id = CoreRDrawable.ic_add), contentDescription = "", ) Text( @@ -189,7 +187,7 @@ private fun Content( bottom = 10.dp, end = 18.dp, ), - text = stringResource(R.string.new_currency), + text = stringResource(CoreRString.new_currency), fontWeight = FontWeight.SemiBold, fontSize = 16.sp, ) @@ -205,8 +203,8 @@ private fun Content( onClick = { showGroupsPopup = true }, title = state.group?.let { state.group } - ?: stringResource(R.string.add_group), - icon = painterResource(id = R.drawable.ic_group), + ?: stringResource(CoreRString.add_group), + icon = painterResource(id = CoreR.drawable.ic_group), ) if (showGroupsPopup) { Box( @@ -243,7 +241,7 @@ private fun Content( onAddAsset() }, ) { - Text(text = stringResource(R.string.add_new_asset)) + Text(text = stringResource(CoreRString.add_new_asset)) } } } @@ -305,13 +303,16 @@ fun InputCurrency( ) Icon( modifier = Modifier.padding(start = 9.dp, end = 5.dp), - painter = painterResource(R.drawable.ic_chevron), + painter = painterResource(CoreRDrawable.ic_chevron), contentDescription = "", tint = ArkColor.FGQuinary, ) } ArkBasicTextField( - modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp), + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp), value = amount.value, onValueChange = { onAssetValueChanged(pos, it) }, textStyle = @@ -324,7 +325,7 @@ fun InputCurrency( .copy(keyboardType = KeyboardType.Number), placeholder = { Text( - text = stringResource(R.string.input_value), + text = stringResource(CoreRString.input_value), color = ArkColor.TextPlaceHolder, fontSize = 16.sp, ) @@ -347,7 +348,7 @@ fun InputCurrency( contentAlignment = Alignment.Center, ) { Icon( - painter = painterResource(id = R.drawable.ic_delete), + painter = painterResource(id = CoreR.drawable.ic_delete), contentDescription = "", tint = ArkColor.FGSecondary, ) diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/AddAssetViewModel.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt similarity index 86% rename from app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/AddAssetViewModel.kt rename to feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt index f26bb122b..20f63d642 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/AddAssetViewModel.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt @@ -1,17 +1,18 @@ -package dev.arkbuilders.rate.presentation.portfolio +package dev.arkbuilders.rate.feature.portfolio.presentation.add import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.data.toBigDecimalArk -import dev.arkbuilders.rate.domain.model.AmountStr -import dev.arkbuilders.rate.domain.model.Asset -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.CodeUseStatRepo -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import dev.arkbuilders.rate.domain.repo.PortfolioRepo -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.domain.model.AmountStr +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.CodeUseStatRepo +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.toBigDecimalArk +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.feature.portfolio.di.PortfolioScope +import dev.arkbuilders.rate.feature.portfolio.domain.model.Asset +import dev.arkbuilders.rate.feature.portfolio.domain.repo.PortfolioRepo import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.orbitmvi.orbit.Container @@ -22,7 +23,6 @@ import org.orbitmvi.orbit.syntax.simple.postSideEffect import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container import javax.inject.Inject -import javax.inject.Singleton data class AddAssetState( val currencies: List = listOf(AmountStr("USD", "")), @@ -130,7 +130,7 @@ class AddAssetViewModel( } } -@Singleton +@PortfolioScope class AddAssetViewModelFactory @Inject constructor( private val assetsRepo: PortfolioRepo, private val currencyRepo: CurrencyRepo, diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/EditAssetScreen.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt similarity index 75% rename from app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/EditAssetScreen.kt rename to feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt index b9ccf1369..1409eb603 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/EditAssetScreen.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.portfolio +package dev.arkbuilders.rate.feature.portfolio.presentation.edit import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -25,29 +25,29 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.presentation.destinations.SearchCurrencyScreenDestination -import dev.arkbuilders.rate.presentation.shared.AppSharedFlowKey -import dev.arkbuilders.rate.presentation.theme.ArkColor -import dev.arkbuilders.rate.presentation.ui.AppHorDiv -import dev.arkbuilders.rate.presentation.ui.AppTopBarBack -import dev.arkbuilders.rate.presentation.ui.ArkLargeTextField -import dev.arkbuilders.rate.presentation.ui.InfoMarketCapitalizationDialog -import dev.arkbuilders.rate.presentation.ui.InfoValueOfCirculatingDialog -import dev.arkbuilders.rate.presentation.ui.LoadingScreen +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.presentation.AppSharedFlowKey +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv +import dev.arkbuilders.rate.core.presentation.ui.AppTopBarBack +import dev.arkbuilders.rate.core.presentation.ui.ArkLargeTextField +import dev.arkbuilders.rate.core.presentation.ui.InfoMarketCapitalizationDialog +import dev.arkbuilders.rate.core.presentation.ui.InfoValueOfCirculatingDialog +import dev.arkbuilders.rate.core.presentation.ui.LoadingScreen +import dev.arkbuilders.rate.feature.portfolio.di.PortfolioComponentHolder +import dev.arkbuilders.rate.feature.search.presentation.destinations.SearchCurrencyScreenDestination import org.orbitmvi.orbit.compose.collectAsState @Destination @@ -56,17 +56,23 @@ fun EditAssetScreen( assetId: Long, navigator: DestinationsNavigator, ) { + val ctx = LocalContext.current + val component = + remember { + PortfolioComponentHolder.provide(ctx) + } + val viewModel: EditAssetViewModel = viewModel( - factory = DIManager.component.editAssetVMFactory().create(assetId), + factory = component.editAssetVMFactory().create(assetId), ) val state by viewModel.collectAsState() Scaffold( topBar = { AppTopBarBack( - title = stringResource(R.string.asset_detail), - navigator = navigator, + title = stringResource(CoreRString.asset_detail), + onBackClick = { navigator.popBackStack() }, ) }, ) { @@ -85,13 +91,12 @@ fun EditAssetScreen( } } -@Preview(showBackground = true) @Composable private fun Content( - navigator: DestinationsNavigator = EmptyDestinationsNavigator, - name: CurrencyName = CurrencyName("USD", "United States dollar"), - value: String = "1000.02", - onValueChange: (String) -> Unit = {}, + navigator: DestinationsNavigator, + name: CurrencyName, + value: String, + onValueChange: (String) -> Unit, ) { var showMarketCapitalizationDialog by remember { mutableStateOf(false) } var showValueOfCirculatingDialog by remember { mutableStateOf(false) } @@ -155,20 +160,18 @@ private fun Content( colors = ButtonDefaults.textButtonColors(contentColor = ArkColor.BrandUtility), onClick = { navigator.navigate( - SearchCurrencyScreenDestination( - AppSharedFlowKey.PickBaseCurrency.toString(), - ), + SearchCurrencyScreenDestination(AppSharedFlowKey.PickBaseCurrency.toString()), ) }, contentPadding = PaddingValues(2.dp), ) { Icon( - painter = painterResource(id = R.drawable.ic_edit), + painter = painterResource(id = CoreRDrawable.ic_edit), contentDescription = "", ) Text( modifier = Modifier.padding(start = 6.dp), - text = stringResource(R.string.change_base_currency), + text = stringResource(CoreRString.change_base_currency), fontWeight = FontWeight.SemiBold, ) } @@ -179,7 +182,7 @@ private fun Content( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = stringResource(R.string.market_capitalization), + text = stringResource(CoreRString.market_capitalization), fontWeight = FontWeight.Medium, color = ArkColor.TextTertiary, ) @@ -191,7 +194,7 @@ private fun Content( onClick = { showMarketCapitalizationDialog = true }, ) { Icon( - painter = painterResource(id = R.drawable.ic_info), + painter = painterResource(id = CoreRDrawable.ic_info), contentDescription = "", tint = ArkColor.Primary, ) @@ -199,7 +202,7 @@ private fun Content( } Text( modifier = Modifier.padding(top = 8.dp), - text = stringResource(R.string.n_a), + text = stringResource(CoreRString.n_a), fontWeight = FontWeight.SemiBold, color = ArkColor.TextPrimary, ) @@ -209,7 +212,7 @@ private fun Content( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = stringResource(R.string.value_of_circulating_currency), + text = stringResource(CoreRString.value_of_circulating_currency), fontWeight = FontWeight.Medium, color = ArkColor.TextTertiary, ) @@ -221,7 +224,7 @@ private fun Content( onClick = { showValueOfCirculatingDialog = true }, ) { Icon( - painter = painterResource(id = R.drawable.ic_info), + painter = painterResource(id = CoreRDrawable.ic_info), contentDescription = "", tint = ArkColor.Primary, ) @@ -229,7 +232,7 @@ private fun Content( } Text( modifier = Modifier.padding(top = 8.dp, bottom = 16.dp), - text = stringResource(R.string.n_a), + text = stringResource(CoreRString.n_a), fontWeight = FontWeight.SemiBold, color = ArkColor.TextPrimary, ) diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/EditAssetViewModel.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetViewModel.kt similarity index 82% rename from app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/EditAssetViewModel.kt rename to feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetViewModel.kt index 7a70511c6..256568207 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/EditAssetViewModel.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetViewModel.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.portfolio +package dev.arkbuilders.rate.feature.portfolio.presentation.edit import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -6,16 +6,16 @@ import androidx.lifecycle.viewModelScope import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.data.toBigDecimalArk -import dev.arkbuilders.rate.domain.model.Asset -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import dev.arkbuilders.rate.domain.repo.PortfolioRepo -import dev.arkbuilders.rate.domain.repo.PreferenceKey -import dev.arkbuilders.rate.domain.repo.Prefs -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.repo.PreferenceKey +import dev.arkbuilders.rate.core.domain.repo.Prefs +import dev.arkbuilders.rate.core.domain.toBigDecimalArk +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.feature.portfolio.domain.model.Asset +import dev.arkbuilders.rate.feature.portfolio.domain.repo.PortfolioRepo import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.launchIn diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/PortfolioScreen.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/main/PortfolioScreen.kt similarity index 84% rename from app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/PortfolioScreen.kt rename to feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/main/PortfolioScreen.kt index 9570a571f..12d5bfc18 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/PortfolioScreen.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/main/PortfolioScreen.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.portfolio +package dev.arkbuilders.rate.feature.portfolio.presentation.main import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -41,28 +41,29 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.domain.model.Amount -import dev.arkbuilders.rate.domain.model.Asset -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.presentation.destinations.AddAssetScreenDestination -import dev.arkbuilders.rate.presentation.destinations.EditAssetScreenDestination -import dev.arkbuilders.rate.presentation.theme.ArkColor -import dev.arkbuilders.rate.presentation.ui.AppButton -import dev.arkbuilders.rate.presentation.ui.AppHorDiv16 -import dev.arkbuilders.rate.presentation.ui.AppSwipeToDismiss -import dev.arkbuilders.rate.presentation.ui.CurrIcon -import dev.arkbuilders.rate.presentation.ui.GroupViewPager -import dev.arkbuilders.rate.presentation.ui.LargeNumberText -import dev.arkbuilders.rate.presentation.ui.LargeNumberTooltipBox -import dev.arkbuilders.rate.presentation.ui.LoadingScreen -import dev.arkbuilders.rate.presentation.ui.NoInternetScreen -import dev.arkbuilders.rate.presentation.ui.NoResult -import dev.arkbuilders.rate.presentation.ui.NotifyRemovedSnackbarVisuals -import dev.arkbuilders.rate.presentation.ui.RateSnackbarHost -import dev.arkbuilders.rate.presentation.ui.SearchTextField +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.domain.model.Amount +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.ui.AppButton +import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv16 +import dev.arkbuilders.rate.core.presentation.ui.AppSwipeToDismiss +import dev.arkbuilders.rate.core.presentation.ui.CurrIcon +import dev.arkbuilders.rate.core.presentation.ui.GroupViewPager +import dev.arkbuilders.rate.core.presentation.ui.LargeNumberText +import dev.arkbuilders.rate.core.presentation.ui.LargeNumberTooltipBox +import dev.arkbuilders.rate.core.presentation.ui.LoadingScreen +import dev.arkbuilders.rate.core.presentation.ui.NoInternetScreen +import dev.arkbuilders.rate.core.presentation.ui.NoResult +import dev.arkbuilders.rate.core.presentation.ui.NotifyRemovedSnackbarVisuals +import dev.arkbuilders.rate.core.presentation.ui.RateSnackbarHost +import dev.arkbuilders.rate.core.presentation.ui.SearchTextField +import dev.arkbuilders.rate.feature.portfolio.di.PortfolioComponentHolder +import dev.arkbuilders.rate.feature.portfolio.domain.model.Asset +import dev.arkbuilders.rate.feature.portfolio.presentation.destinations.AddAssetScreenDestination +import dev.arkbuilders.rate.feature.portfolio.presentation.destinations.EditAssetScreenDestination import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect import java.math.BigDecimal @@ -70,12 +71,17 @@ import java.math.BigDecimal @Destination @Composable fun PortfolioScreen(navigator: DestinationsNavigator) { + val ctx = LocalContext.current + val component = + remember { + PortfolioComponentHolder.provide(ctx) + } + val viewModel: PortfolioViewModel = - viewModel(factory = DIManager.component.assetsVMFactory()) + viewModel(factory = component.assetsVMFactory()) val state by viewModel.collectAsState() val snackState = remember { SnackbarHostState() } - val ctx = LocalContext.current viewModel.collectSideEffect { effect -> when (effect) { @@ -88,10 +94,10 @@ fun PortfolioScreen(navigator: DestinationsNavigator) { " ${effect.asset.code}" val visuals = NotifyRemovedSnackbarVisuals( - title = ctx.getString(R.string.portfolio_snackbar_removed_title), + title = ctx.getString(CoreRString.portfolio_snackbar_removed_title), description = ctx.getString( - R.string.portfolio_snackbar_removed_desc, + CoreRString.portfolio_snackbar_removed_desc, removed, ), onUndo = { @@ -137,7 +143,8 @@ fun PortfolioScreen(navigator: DestinationsNavigator) { Content( state, onClick = { display -> - navigator.navigate(EditAssetScreenDestination(display.asset.id)) + navigator + .navigate(EditAssetScreenDestination(display.asset.id)) }, onFilterChange = viewModel::onFilterChange, onDelete = viewModel::onAssetRemove, @@ -238,7 +245,7 @@ private fun GroupPage( item { Text( modifier = Modifier.padding(top = 32.dp), - text = stringResource(R.string.portfolio_total_assets), + text = stringResource(CoreRString.portfolio_total_assets), color = ArkColor.TextTertiary, fontWeight = FontWeight.Medium, ) @@ -367,20 +374,20 @@ private fun PortfolioEmpty(navigator: DestinationsNavigator) { horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( - painter = painterResource(id = R.drawable.ic_empty_portfolio), + painter = painterResource(id = CoreRDrawable.ic_empty_portfolio), contentDescription = "", tint = Color.Unspecified, ) Text( modifier = Modifier.padding(top = 16.dp), - text = stringResource(R.string.portfolio_empty_title), + text = stringResource(CoreRString.portfolio_empty_title), fontWeight = FontWeight.SemiBold, fontSize = 20.sp, color = ArkColor.TextPrimary, ) Text( modifier = Modifier.padding(top = 6.dp, start = 24.dp, end = 24.dp), - text = stringResource(R.string.portfolio_empty_desc), + text = stringResource(CoreRString.portfolio_empty_desc), fontSize = 14.sp, lineHeight = 20.sp, color = ArkColor.TextTertiary, @@ -393,12 +400,12 @@ private fun PortfolioEmpty(navigator: DestinationsNavigator) { }, ) { Icon( - painter = painterResource(id = R.drawable.ic_add), + painter = painterResource(id = CoreRDrawable.ic_add), contentDescription = "", ) Text( modifier = Modifier.padding(start = 8.dp), - text = stringResource(R.string.new_asset), + text = stringResource(CoreRString.new_asset), ) } } diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/PortfolioViewModel.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/main/PortfolioViewModel.kt similarity index 86% rename from app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/PortfolioViewModel.kt rename to feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/main/PortfolioViewModel.kt index 6d60b473e..0bca5fc45 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/portfolio/PortfolioViewModel.kt +++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/main/PortfolioViewModel.kt @@ -1,19 +1,20 @@ -package dev.arkbuilders.rate.presentation.portfolio +package dev.arkbuilders.rate.feature.portfolio.presentation.main import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope -import dev.arkbuilders.rate.domain.model.Amount -import dev.arkbuilders.rate.domain.model.Asset -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import dev.arkbuilders.rate.domain.repo.PortfolioRepo -import dev.arkbuilders.rate.domain.repo.PreferenceKey -import dev.arkbuilders.rate.domain.repo.Prefs -import dev.arkbuilders.rate.domain.usecase.ConvertWithRateUseCase -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow -import dev.arkbuilders.rate.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.core.domain.model.Amount +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.repo.PreferenceKey +import dev.arkbuilders.rate.core.domain.repo.Prefs +import dev.arkbuilders.rate.core.domain.usecase.ConvertWithRateUseCase +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.core.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.feature.portfolio.di.PortfolioScope +import dev.arkbuilders.rate.feature.portfolio.domain.model.Asset +import dev.arkbuilders.rate.feature.portfolio.domain.repo.PortfolioRepo import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -26,7 +27,6 @@ import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container import java.math.BigDecimal import javax.inject.Inject -import javax.inject.Singleton data class PortfolioScreenState( val filter: String = "", @@ -162,7 +162,7 @@ class PortfolioViewModel( } } -@Singleton +@PortfolioScope class PortfolioViewModelFactory @Inject constructor( private val assetsRepo: PortfolioRepo, private val currencyRepo: CurrencyRepo, diff --git a/feature/portfolio/src/test/java/dev/arkbuilders/rate/feature/portfolio/ExampleUnitTest.kt b/feature/portfolio/src/test/java/dev/arkbuilders/rate/feature/portfolio/ExampleUnitTest.kt new file mode 100644 index 000000000..a1bf31b65 --- /dev/null +++ b/feature/portfolio/src/test/java/dev/arkbuilders/rate/feature/portfolio/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package dev.arkbuilders.rate.feature.portfolio + +import org.junit.Assert.assertEquals +import org.junit.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) + } +} diff --git a/feature/quick/.gitignore b/feature/quick/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/feature/quick/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/quick/build.gradle.kts b/feature/quick/build.gradle.kts new file mode 100644 index 000000000..35017441b --- /dev/null +++ b/feature/quick/build.gradle.kts @@ -0,0 +1,84 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) +} + +android { + namespace = "dev.arkbuilders.rate.feature.quick" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + ksp { + arg("compose-destinations.mode", "destinations") + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(project(":core:di")) + implementation(project(":core:db")) + implementation(project(":core:domain")) + implementation(project(":core:presentation")) + implementation(project(":feature:search")) + + implementation(libs.androidx.ui) + implementation(libs.navigation.compose) + implementation(libs.material3) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(libs.constraintlayout.compose) + + implementation(libs.timber) + + implementation(libs.dagger) + ksp(libs.dagger.compiler) + + implementation(libs.orbit.compose) + implementation(libs.orbit.viewmodel) + + implementation(libs.arrow.core) + implementation(libs.arrow.fx.coroutines) + + implementation(libs.compose.destinations.animations) + ksp(libs.compose.destinations.compiler) + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":feature:quick:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":feature:quick:preBuild").dependsOn("ktlintFormat") diff --git a/feature/quick/consumer-rules.pro b/feature/quick/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/feature/quick/proguard-rules.pro b/feature/quick/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/feature/quick/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/feature/quick/src/androidTest/java/dev/arkbuilders/rate/feature/quick/ExampleInstrumentedTest.kt b/feature/quick/src/androidTest/java/dev/arkbuilders/rate/feature/quick/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..3152c453e --- /dev/null +++ b/feature/quick/src/androidTest/java/dev/arkbuilders/rate/feature/quick/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package dev.arkbuilders.rate.feature.quick + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * 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("dev.arkbuilders.rate.feature.quick.test", appContext.packageName) + } +} diff --git a/feature/quick/src/main/AndroidManifest.xml b/feature/quick/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/feature/quick/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/java/dev/arkbuilders/rate/data/repo/QuickRepoImpl.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/data/QuickRepoImpl.kt similarity index 63% rename from app/src/main/java/dev/arkbuilders/rate/data/repo/QuickRepoImpl.kt rename to feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/data/QuickRepoImpl.kt index 77ed62ff6..f6b59fea6 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/repo/QuickRepoImpl.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/data/QuickRepoImpl.kt @@ -1,9 +1,9 @@ -package dev.arkbuilders.rate.data.repo +package dev.arkbuilders.rate.feature.quick.data -import dev.arkbuilders.rate.data.db.dao.QuickPairDao -import dev.arkbuilders.rate.data.db.entity.RoomQuickPair -import dev.arkbuilders.rate.domain.model.QuickPair -import dev.arkbuilders.rate.domain.repo.QuickRepo +import dev.arkbuilders.rate.core.db.dao.QuickPairDao +import dev.arkbuilders.rate.core.db.entity.RoomQuickPair +import dev.arkbuilders.rate.feature.quick.domain.model.QuickPair +import dev.arkbuilders.rate.feature.quick.domain.repo.QuickRepo import kotlinx.coroutines.flow.map import javax.inject.Inject import javax.inject.Singleton @@ -22,7 +22,15 @@ class QuickRepoImpl @Inject constructor(val dao: QuickPairDao) : QuickRepo { } private fun QuickPair.toRoom() = - RoomQuickPair(id, from, amount, to, calculatedDate, pinnedDate, group) + RoomQuickPair( + id, + from, + amount, + to, + calculatedDate, + pinnedDate, + group, + ) private fun RoomQuickPair.toQuickPair() = QuickPair(id, from, amount, to, calculatedDate, pinnedDate, group) diff --git a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickComponent.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickComponent.kt new file mode 100644 index 000000000..470e9d2a8 --- /dev/null +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickComponent.kt @@ -0,0 +1,20 @@ +package dev.arkbuilders.rate.feature.quick.di + +import dagger.Component +import dev.arkbuilders.rate.core.di.CoreComponent +import dev.arkbuilders.rate.feature.quick.domain.repo.QuickRepo +import dev.arkbuilders.rate.feature.quick.domain.usecase.GetSortedPinnedQuickPairsUseCase +import dev.arkbuilders.rate.feature.quick.presentation.add.AddQuickViewModelFactory +import dev.arkbuilders.rate.feature.quick.presentation.main.QuickViewModelFactory + +@QuickScope +@Component(dependencies = [CoreComponent::class], modules = [QuickModule::class]) +interface QuickComponent { + fun addQuickVMFactory(): AddQuickViewModelFactory.Factory + + fun quickVMFactory(): QuickViewModelFactory.Factory + + fun getPinnedQuickPairUseCase(): GetSortedPinnedQuickPairsUseCase + + fun quickRepo(): QuickRepo +} diff --git a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickComponentHolder.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickComponentHolder.kt new file mode 100644 index 000000000..a40513e4b --- /dev/null +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickComponentHolder.kt @@ -0,0 +1,19 @@ +package dev.arkbuilders.rate.feature.quick.di + +import android.content.Context +import dev.arkbuilders.rate.core.di.CoreComponentProvider + +object QuickComponentHolder { + private var component: QuickComponent? = null + + fun provide(ctx: Context): QuickComponent { + component ?: let { + val app = ctx.applicationContext + val coreComponent = (app as CoreComponentProvider).provideCoreComponent() + component = + DaggerQuickComponent.builder().coreComponent(coreComponent) + .quickModule(QuickModule()).build() + } + return component!! + } +} diff --git a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickModule.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickModule.kt new file mode 100644 index 000000000..290b270aa --- /dev/null +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickModule.kt @@ -0,0 +1,26 @@ +package dev.arkbuilders.rate.feature.quick.di + +import dagger.Module +import dagger.Provides +import dev.arkbuilders.rate.core.db.dao.QuickPairDao +import dev.arkbuilders.rate.core.domain.usecase.ConvertWithRateUseCase +import dev.arkbuilders.rate.feature.quick.data.QuickRepoImpl +import dev.arkbuilders.rate.feature.quick.domain.repo.QuickRepo +import dev.arkbuilders.rate.feature.quick.domain.usecase.GetSortedPinnedQuickPairsUseCase + +@Module +class QuickModule { + @QuickScope + @Provides + fun quickRepo(quickPairDao: QuickPairDao): QuickRepo = QuickRepoImpl(quickPairDao) + + @QuickScope + @Provides + fun getSortedPinnedQuickPairsUseCase( + quickRepo: QuickRepo, + convertWithRateUseCase: ConvertWithRateUseCase, + ) = GetSortedPinnedQuickPairsUseCase( + quickRepo, + convertWithRateUseCase, + ) +} diff --git a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickScope.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickScope.kt new file mode 100644 index 000000000..ab4dfa2cb --- /dev/null +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/di/QuickScope.kt @@ -0,0 +1,7 @@ +package dev.arkbuilders.rate.feature.quick.di + +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.SOURCE) +annotation class QuickScope diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/model/QuickPair.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/domain/model/QuickPair.kt similarity index 73% rename from app/src/main/java/dev/arkbuilders/rate/domain/model/QuickPair.kt rename to feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/domain/model/QuickPair.kt index 3b49191ab..3b4b8c37e 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/model/QuickPair.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/domain/model/QuickPair.kt @@ -1,5 +1,7 @@ -package dev.arkbuilders.rate.domain.model +package dev.arkbuilders.rate.feature.quick.domain.model +import dev.arkbuilders.rate.core.domain.model.Amount +import dev.arkbuilders.rate.core.domain.model.CurrencyCode import java.math.BigDecimal import java.time.OffsetDateTime diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/repo/QuickRepo.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/domain/repo/QuickRepo.kt similarity index 75% rename from app/src/main/java/dev/arkbuilders/rate/domain/repo/QuickRepo.kt rename to feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/domain/repo/QuickRepo.kt index c271d7f04..bec35f2b9 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/repo/QuickRepo.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/domain/repo/QuickRepo.kt @@ -1,6 +1,6 @@ -package dev.arkbuilders.rate.domain.repo +package dev.arkbuilders.rate.feature.quick.domain.repo -import dev.arkbuilders.rate.domain.model.QuickPair +import dev.arkbuilders.rate.feature.quick.domain.model.QuickPair import kotlinx.coroutines.flow.Flow interface QuickRepo { diff --git a/app/src/main/java/dev/arkbuilders/rate/domain/usecase/GetSortedPinnedQuickPairsUseCase.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/domain/usecase/GetSortedPinnedQuickPairsUseCase.kt similarity index 56% rename from app/src/main/java/dev/arkbuilders/rate/domain/usecase/GetSortedPinnedQuickPairsUseCase.kt rename to feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/domain/usecase/GetSortedPinnedQuickPairsUseCase.kt index d516d73a3..f04076e81 100644 --- a/app/src/main/java/dev/arkbuilders/rate/domain/usecase/GetSortedPinnedQuickPairsUseCase.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/domain/usecase/GetSortedPinnedQuickPairsUseCase.kt @@ -1,14 +1,12 @@ -package dev.arkbuilders.rate.domain.usecase +package dev.arkbuilders.rate.feature.quick.domain.usecase -import dev.arkbuilders.rate.domain.model.PinnedQuickPair -import dev.arkbuilders.rate.domain.model.QuickPair -import dev.arkbuilders.rate.domain.repo.QuickRepo +import dev.arkbuilders.rate.core.domain.usecase.ConvertWithRateUseCase +import dev.arkbuilders.rate.feature.quick.domain.model.PinnedQuickPair +import dev.arkbuilders.rate.feature.quick.domain.model.QuickPair +import dev.arkbuilders.rate.feature.quick.domain.repo.QuickRepo import java.time.OffsetDateTime -import javax.inject.Inject -import javax.inject.Singleton -@Singleton -class GetSortedPinnedQuickPairsUseCase @Inject constructor( +class GetSortedPinnedQuickPairsUseCase( private val quickRepo: QuickRepo, private val convertUseCase: ConvertWithRateUseCase, ) { @@ -26,6 +24,10 @@ class GetSortedPinnedQuickPairsUseCase @Inject constructor( val (amount, _) = convertUseCase.invoke(pair.from, pair.amount, to.code) amount } - return PinnedQuickPair(pair, actualTo, OffsetDateTime.now()) + return PinnedQuickPair( + pair, + actualTo, + OffsetDateTime.now(), + ) } } diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/AddQuickScreen.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt similarity index 90% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/AddQuickScreen.kt rename to feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt index 9c0ceb612..d00822d35 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/AddQuickScreen.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.quick +package dev.arkbuilders.rate.feature.quick.presentation.add import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.border @@ -52,24 +52,23 @@ import androidx.compose.ui.window.PopupProperties import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.data.toBigDecimalArk -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.presentation.destinations.SearchCurrencyScreenDestination -import dev.arkbuilders.rate.presentation.pairalert.DropDownWithIcon -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow -import dev.arkbuilders.rate.presentation.shared.AppSharedFlowKey -import dev.arkbuilders.rate.presentation.theme.ArkColor -import dev.arkbuilders.rate.presentation.ui.AppButton -import dev.arkbuilders.rate.presentation.ui.AppHorDiv16 -import dev.arkbuilders.rate.presentation.ui.AppTopBarBack -import dev.arkbuilders.rate.presentation.ui.ArkBasicTextField -import dev.arkbuilders.rate.presentation.ui.GroupCreateDialog -import dev.arkbuilders.rate.presentation.ui.GroupSelectPopup -import dev.arkbuilders.rate.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.toBigDecimalArk +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.core.presentation.AppSharedFlowKey +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.ui.AppButton +import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv16 +import dev.arkbuilders.rate.core.presentation.ui.AppTopBarBack +import dev.arkbuilders.rate.core.presentation.ui.ArkBasicTextField +import dev.arkbuilders.rate.core.presentation.ui.DropDownWithIcon +import dev.arkbuilders.rate.core.presentation.ui.GroupCreateDialog +import dev.arkbuilders.rate.core.presentation.ui.GroupSelectPopup +import dev.arkbuilders.rate.core.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.feature.quick.di.QuickComponentHolder +import dev.arkbuilders.rate.feature.search.presentation.destinations.SearchCurrencyScreenDestination import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @@ -83,10 +82,14 @@ fun AddQuickScreen( navigator: DestinationsNavigator, ) { val ctx = LocalContext.current + val quickComponent = + remember { + QuickComponentHolder.provide(ctx) + } val viewModel: AddQuickViewModel = viewModel( factory = - DIManager.component.addQuickVMFactory() + quickComponent.addQuickVMFactory() .create(quickPairId, newCode, reuseNotEdit, group), ) @@ -124,20 +127,17 @@ fun AddQuickScreen( R.string.quick_edit_pair AppTopBarBack( title = stringResource(title), - navigator = navigator, + onBackClick = { navigator.popBackStack() }, ) }, ) { Box(modifier = Modifier.padding(it)) { Content( state = state, - navigator = navigator, onAmountChanged = viewModel::onAssetAmountChange, onNewCurrencyClick = { navigator.navigate( - SearchCurrencyScreenDestination( - AppSharedFlowKey.AddQuickCode.toString(), - ), + SearchCurrencyScreenDestination(AppSharedFlowKey.AddQuickCode.toString()), ) }, onCurrencyRemove = viewModel::onCurrencyRemove, @@ -145,8 +145,8 @@ fun AddQuickScreen( onCodeChange = { index -> navigator.navigate( SearchCurrencyScreenDestination( - AppSharedFlowKey.SetQuickCode.name, - pos = index, + AppSharedFlowKey.SetQuickCode.toString(), + index, ), ) }, @@ -160,7 +160,6 @@ fun AddQuickScreen( @Composable private fun Content( state: AddQuickScreenState = AddQuickScreenState(), - navigator: DestinationsNavigator = EmptyDestinationsNavigator, onAmountChanged: (String) -> Unit = {}, onNewCurrencyClick: () -> Unit = {}, onCurrencyRemove: (Int) -> Unit = {}, @@ -358,7 +357,10 @@ private fun FromInput( ) } ArkBasicTextField( - modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp), + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp), value = amount, onValueChange = { onAmountChanged(it) }, keyboardOptions = diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/AddQuickViewModel.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt similarity index 90% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/AddQuickViewModel.kt rename to feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt index 6c9188f9c..3c28d4f54 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/AddQuickViewModel.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.quick +package dev.arkbuilders.rate.feature.quick.presentation.add import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -6,19 +6,19 @@ import androidx.lifecycle.viewModelScope import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.data.toBigDecimalArk -import dev.arkbuilders.rate.data.toDoubleArk -import dev.arkbuilders.rate.domain.model.AmountStr -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.QuickPair -import dev.arkbuilders.rate.domain.model.toAmount -import dev.arkbuilders.rate.domain.model.toStrAmount -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.CodeUseStatRepo -import dev.arkbuilders.rate.domain.repo.QuickRepo -import dev.arkbuilders.rate.domain.usecase.ConvertWithRateUseCase -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.domain.model.AmountStr +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.toAmount +import dev.arkbuilders.rate.core.domain.model.toStrAmount +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.CodeUseStatRepo +import dev.arkbuilders.rate.core.domain.toBigDecimalArk +import dev.arkbuilders.rate.core.domain.toDoubleArk +import dev.arkbuilders.rate.core.domain.usecase.ConvertWithRateUseCase +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.feature.quick.domain.model.QuickPair +import dev.arkbuilders.rate.feature.quick.domain.repo.QuickRepo import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.orbitmvi.orbit.Container diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickScreen.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/main/QuickScreen.kt similarity index 84% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickScreen.kt rename to feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/main/QuickScreen.kt index 09ed436a2..066225362 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickScreen.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/main/QuickScreen.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.quick +package dev.arkbuilders.rate.feature.quick.presentation.main import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -46,51 +46,55 @@ import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.domain.model.Amount -import dev.arkbuilders.rate.domain.model.CurrencyCode -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.domain.model.PinnedQuickPair -import dev.arkbuilders.rate.domain.model.QuickPair -import dev.arkbuilders.rate.presentation.destinations.AddQuickScreenDestination -import dev.arkbuilders.rate.presentation.theme.ArkColor -import dev.arkbuilders.rate.presentation.ui.AppButton -import dev.arkbuilders.rate.presentation.ui.AppHorDiv16 -import dev.arkbuilders.rate.presentation.ui.CurrIcon -import dev.arkbuilders.rate.presentation.ui.CurrencyInfoItem -import dev.arkbuilders.rate.presentation.ui.GroupViewPager -import dev.arkbuilders.rate.presentation.ui.ListHeader -import dev.arkbuilders.rate.presentation.ui.LoadingScreen -import dev.arkbuilders.rate.presentation.ui.NoInternetScreen -import dev.arkbuilders.rate.presentation.ui.NoResult -import dev.arkbuilders.rate.presentation.ui.NotifyRemovedSnackbarVisuals -import dev.arkbuilders.rate.presentation.ui.PinnedQuickSwipeItem -import dev.arkbuilders.rate.presentation.ui.QuickSwipeItem -import dev.arkbuilders.rate.presentation.ui.RateSnackbarHost -import dev.arkbuilders.rate.presentation.ui.SearchTextField -import dev.arkbuilders.rate.presentation.utils.DateFormatUtils +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.domain.model.Amount +import dev.arkbuilders.rate.core.domain.model.CurrencyCode +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.ui.AppButton +import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv16 +import dev.arkbuilders.rate.core.presentation.ui.CurrIcon +import dev.arkbuilders.rate.core.presentation.ui.CurrencyInfoItem +import dev.arkbuilders.rate.core.presentation.ui.GroupViewPager +import dev.arkbuilders.rate.core.presentation.ui.ListHeader +import dev.arkbuilders.rate.core.presentation.ui.LoadingScreen +import dev.arkbuilders.rate.core.presentation.ui.NoInternetScreen +import dev.arkbuilders.rate.core.presentation.ui.NoResult +import dev.arkbuilders.rate.core.presentation.ui.NotifyRemovedSnackbarVisuals +import dev.arkbuilders.rate.core.presentation.ui.RateSnackbarHost +import dev.arkbuilders.rate.core.presentation.ui.SearchTextField +import dev.arkbuilders.rate.core.presentation.utils.DateFormatUtils +import dev.arkbuilders.rate.feature.quick.di.QuickComponentHolder +import dev.arkbuilders.rate.feature.quick.domain.model.PinnedQuickPair +import dev.arkbuilders.rate.feature.quick.domain.model.QuickPair +import dev.arkbuilders.rate.feature.quick.presentation.destinations.AddQuickScreenDestination +import dev.arkbuilders.rate.feature.quick.presentation.ui.PinnedQuickSwipeItem +import dev.arkbuilders.rate.feature.quick.presentation.ui.QuickOptionsBottomSheet +import dev.arkbuilders.rate.feature.quick.presentation.ui.QuickSwipeItem import kotlinx.coroutines.launch import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect import java.time.OffsetDateTime @OptIn(ExperimentalMaterial3Api::class) -@RootNavGraph(start = true) @Destination @Composable fun QuickScreen(navigator: DestinationsNavigator) { + val ctx = LocalContext.current + val component = + remember { + QuickComponentHolder.provide(ctx) + } val viewModel: QuickViewModel = viewModel( - factory = DIManager.component.quickVMFactory().create(), + factory = component.quickVMFactory().create(), ) val state by viewModel.collectAsState() val snackState = remember { SnackbarHostState() } - val ctx = LocalContext.current viewModel.collectSideEffect { effect -> when (effect) { is QuickScreenEffect.ShowSnackbarAdded -> @@ -99,16 +103,16 @@ fun QuickScreen(navigator: DestinationsNavigator) { is QuickScreenEffect.ShowRemovedSnackbar -> { val removed = ctx.getString( - R.string.quick_snackbar_new_added_to, + CoreRString.quick_snackbar_new_added_to, effect.pair.from, effect.pair.to.joinToString { it.code }, ) val visuals = NotifyRemovedSnackbarVisuals( - title = ctx.getString(R.string.quick_snackbar_removed_title), + title = ctx.getString(CoreRString.quick_snackbar_removed_title), description = ctx.getString( - R.string.quick_snackbar_removed_desc, + CoreRString.quick_snackbar_removed_desc, removed, ), onUndo = { @@ -155,7 +159,7 @@ fun QuickScreen(navigator: DestinationsNavigator) { when { state.noInternet -> NoInternetScreen(viewModel::onRefreshClick) state.initialized.not() -> LoadingScreen() - isEmpty -> QuickEmpty(navigator = navigator) + isEmpty -> QuickEmpty(navigator) else -> Content( state = state, @@ -167,7 +171,8 @@ fun QuickScreen(navigator: DestinationsNavigator) { onPin = viewModel::onPin, onUnpin = viewModel::onUnpin, onNewCode = { - navigator.navigate(AddQuickScreenDestination(newCode = it)) + navigator + .navigate(AddQuickScreenDestination(newCode = it)) }, ) } @@ -284,7 +289,7 @@ private fun GroupPage( LazyColumn(modifier = Modifier.fillMaxSize()) { if (pinned.isNotEmpty()) { item { - ListHeader(text = stringResource(R.string.quick_pinned_pairs)) + ListHeader(text = stringResource(CoreRString.quick_pinned_pairs)) } items(pinned, key = { it.pair.id }) { PinnedQuickSwipeItem( @@ -294,7 +299,7 @@ private fun GroupPage( to = it.actualTo, dateText = stringResource( - R.string.quick_last_refreshed, + CoreRString.quick_last_refreshed, DateFormatUtils.latestCheckElapsedTime( ctx, OffsetDateTime.now(), @@ -313,7 +318,7 @@ private fun GroupPage( } if (notPinned.isNotEmpty()) { item { - ListHeader(text = stringResource(R.string.quick_calculations)) + ListHeader(text = stringResource(CoreRString.quick_calculations)) } items(notPinned, key = { it.id }) { QuickSwipeItem( @@ -323,7 +328,7 @@ private fun GroupPage( to = it.to, dateText = stringResource( - R.string.quick_calculated_on, + CoreRString.quick_calculated_on, DateFormatUtils.calculatedOn(it.calculatedDate), ), onClick = { onClick(it) }, @@ -338,14 +343,14 @@ private fun GroupPage( } if (frequent.isNotEmpty()) { item { - ListHeader(text = stringResource(R.string.frequent_currencies)) + ListHeader(text = stringResource(CoreRString.frequent_currencies)) } items(frequent) { name -> CurrencyInfoItem(name) { onNewCode(it.code) } } } item { - ListHeader(text = stringResource(R.string.all_currencies)) + ListHeader(text = stringResource(CoreRString.all_currencies)) } items(currencies, key = { it.code }) { name -> CurrencyInfoItem(name) { onNewCode(it.code) } @@ -367,7 +372,7 @@ private fun SearchPage( if (filtered.isNotEmpty()) { LazyColumn(modifier = Modifier.fillMaxSize()) { item { - ListHeader(text = stringResource(R.string.top_results)) + ListHeader(text = stringResource(CoreRString.top_results)) } items(filtered) { name -> CurrencyInfoItem(name) { onNewCode(it.code) } @@ -525,14 +530,14 @@ private fun QuickItem( if (expanded) { Icon( modifier = Modifier, - painter = painterResource(R.drawable.ic_chevron_up), + painter = painterResource(CoreRDrawable.ic_chevron_up), contentDescription = "", tint = ArkColor.FGSecondary, ) } else { Icon( modifier = Modifier, - painter = painterResource(R.drawable.ic_chevron), + painter = painterResource(CoreRDrawable.ic_chevron), contentDescription = "", tint = ArkColor.FGSecondary, ) @@ -561,20 +566,20 @@ private fun QuickEmpty(navigator: DestinationsNavigator) { horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( - painter = painterResource(id = R.drawable.ic_empty_quick), + painter = painterResource(id = CoreRDrawable.ic_empty_quick), contentDescription = "", tint = Color.Unspecified, ) Text( modifier = Modifier.padding(top = 16.dp), - text = stringResource(R.string.quick_empty_title), + text = stringResource(CoreRString.quick_empty_title), fontWeight = FontWeight.SemiBold, fontSize = 20.sp, color = ArkColor.TextPrimary, ) Text( modifier = Modifier.padding(top = 6.dp, start = 24.dp, end = 24.dp), - text = stringResource(R.string.quick_empty_desc), + text = stringResource(CoreRString.quick_empty_desc), fontSize = 14.sp, lineHeight = 20.sp, color = ArkColor.TextTertiary, @@ -587,12 +592,12 @@ private fun QuickEmpty(navigator: DestinationsNavigator) { }, ) { Icon( - painter = painterResource(id = R.drawable.ic_add), + painter = painterResource(id = CoreRDrawable.ic_add), contentDescription = "", ) Text( modifier = Modifier.padding(start = 8.dp), - text = stringResource(R.string.calculate), + text = stringResource(CoreRString.calculate), ) } } diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickViewModel.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/main/QuickViewModel.kt similarity index 89% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickViewModel.kt rename to feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/main/QuickViewModel.kt index 4dad9b499..90611a25f 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickViewModel.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/main/QuickViewModel.kt @@ -1,23 +1,23 @@ -package dev.arkbuilders.rate.presentation.quick +package dev.arkbuilders.rate.feature.quick.presentation.main import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.domain.model.PinnedQuickPair -import dev.arkbuilders.rate.domain.model.QuickPair -import dev.arkbuilders.rate.domain.model.TimestampType -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import dev.arkbuilders.rate.domain.repo.QuickRepo -import dev.arkbuilders.rate.domain.repo.TimestampRepo -import dev.arkbuilders.rate.domain.usecase.CalcFrequentCurrUseCase -import dev.arkbuilders.rate.domain.usecase.ConvertWithRateUseCase -import dev.arkbuilders.rate.domain.usecase.GetTopResultUseCase -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow -import dev.arkbuilders.rate.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.domain.model.TimestampType +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.repo.TimestampRepo +import dev.arkbuilders.rate.core.domain.usecase.CalcFrequentCurrUseCase +import dev.arkbuilders.rate.core.domain.usecase.ConvertWithRateUseCase +import dev.arkbuilders.rate.core.domain.usecase.GetTopResultUseCase +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.core.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.feature.quick.domain.model.PinnedQuickPair +import dev.arkbuilders.rate.feature.quick.domain.model.QuickPair +import dev.arkbuilders.rate.feature.quick.domain.repo.QuickRepo import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickOptionsBottomSheet.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/ui/QuickOptionsBottomSheet.kt similarity index 86% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickOptionsBottomSheet.kt rename to feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/ui/QuickOptionsBottomSheet.kt index dbe74cb27..96232d624 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickOptionsBottomSheet.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/ui/QuickOptionsBottomSheet.kt @@ -1,7 +1,8 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.quick +package dev.arkbuilders.rate.feature.quick.presentation.ui +import android.annotation.SuppressLint import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -28,9 +29,10 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.domain.model.QuickPair -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.feature.quick.domain.model.QuickPair import kotlin.math.abs @Composable @@ -80,7 +82,7 @@ private fun Content( Modifier .padding(start = 16.dp, top = 24.dp) .align(Alignment.TopStart), - text = stringResource(R.string.options), + text = stringResource(CoreRString.options), color = ArkColor.TextPrimary, fontWeight = FontWeight.SemiBold, fontSize = 18.sp, @@ -95,7 +97,7 @@ private fun Content( ) { Icon( modifier = Modifier, - painter = painterResource(id = R.drawable.ic_close), + painter = painterResource(id = CoreRDrawable.ic_close), contentDescription = "", tint = ArkColor.FGQuinary, ) @@ -121,7 +123,7 @@ private fun Content( ) { Icon( modifier = Modifier, - painter = painterResource(id = R.drawable.ic_pin), + painter = painterResource(id = CoreRDrawable.ic_pin), contentDescription = "", tint = ArkColor.TextSecondary, ) @@ -129,9 +131,9 @@ private fun Content( modifier = Modifier.padding(start = 6.dp), text = if (pair.isPinned()) - stringResource(R.string.unpin) + stringResource(CoreRString.unpin) else - stringResource(R.string.pin), + stringResource(CoreRString.pin), color = ArkColor.TextSecondary, fontWeight = FontWeight.SemiBold, fontSize = 16.sp, @@ -148,13 +150,13 @@ private fun Content( ) { Icon( modifier = Modifier, - painter = painterResource(id = R.drawable.ic_edit), + painter = painterResource(id = CoreRDrawable.ic_edit), contentDescription = "", tint = ArkColor.TextSecondary, ) Text( modifier = Modifier.padding(start = 6.dp), - text = stringResource(R.string.edit), + text = stringResource(CoreRString.edit), color = ArkColor.TextSecondary, fontWeight = FontWeight.SemiBold, fontSize = 16.sp, @@ -171,13 +173,13 @@ private fun Content( ) { Icon( modifier = Modifier, - painter = painterResource(id = R.drawable.ic_reuse), + painter = painterResource(id = CoreRDrawable.ic_reuse), contentDescription = "", tint = ArkColor.TextSecondary, ) Text( modifier = Modifier.padding(start = 6.dp), - text = stringResource(R.string.re_use), + text = stringResource(CoreRString.re_use), color = ArkColor.TextSecondary, fontWeight = FontWeight.SemiBold, fontSize = 16.sp, @@ -194,13 +196,13 @@ private fun Content( ) { Icon( modifier = Modifier, - painter = painterResource(id = R.drawable.ic_delete), + painter = painterResource(id = CoreRDrawable.ic_delete), contentDescription = "", tint = ArkColor.TextError, ) Text( modifier = Modifier.padding(start = 6.dp), - text = stringResource(R.string.delete), + text = stringResource(CoreRString.delete), color = ArkColor.TextError, fontWeight = FontWeight.SemiBold, fontSize = 16.sp, @@ -210,6 +212,7 @@ private fun Content( } } +@SuppressLint("SuspiciousModifierThen") fun Modifier.verticalScrollDisabled() = then( pointerInput(Unit) { diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/QuickSwipeItem.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/ui/QuickSwipeItem.kt similarity index 95% rename from app/src/main/java/dev/arkbuilders/rate/presentation/ui/QuickSwipeItem.kt rename to feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/ui/QuickSwipeItem.kt index b06ac279f..95765c467 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/ui/QuickSwipeItem.kt +++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/ui/QuickSwipeItem.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.ui +package dev.arkbuilders.rate.feature.quick.presentation.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -23,9 +23,9 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.domain.model.QuickPair -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.R +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.feature.quick.domain.model.QuickPair // bug: callbacks from swipe called multiply times @Composable diff --git a/feature/quick/src/test/java/dev/arkbuilders/rate/feature/quick/ExampleUnitTest.kt b/feature/quick/src/test/java/dev/arkbuilders/rate/feature/quick/ExampleUnitTest.kt new file mode 100644 index 000000000..697394b23 --- /dev/null +++ b/feature/quick/src/test/java/dev/arkbuilders/rate/feature/quick/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package dev.arkbuilders.rate.feature.quick + +import org.junit.Assert.assertEquals +import org.junit.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) + } +} diff --git a/feature/quickwidget/.gitignore b/feature/quickwidget/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/feature/quickwidget/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/quickwidget/build.gradle.kts b/feature/quickwidget/build.gradle.kts new file mode 100644 index 000000000..ff6c009cb --- /dev/null +++ b/feature/quickwidget/build.gradle.kts @@ -0,0 +1,67 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) +} + +android { + namespace = "dev.arkbuilders.rate.feature.quickwidget" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(project(":core:di")) + implementation(project(":core:db")) + implementation(project(":core:domain")) + implementation(project(":core:data")) + implementation(project(":core:presentation")) + implementation(project(":feature:quick")) + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.glance.appwidget) + + implementation(libs.androidx.work.runtime.ktx) + + implementation(libs.dagger) + ksp(libs.dagger.compiler) + + implementation(libs.timber) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":feature:quickwidget:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":feature:quickwidget:preBuild").dependsOn("ktlintFormat") diff --git a/feature/quickwidget/consumer-rules.pro b/feature/quickwidget/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/feature/quickwidget/proguard-rules.pro b/feature/quickwidget/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/feature/quickwidget/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/feature/quickwidget/src/androidTest/java/dev/arkbuilders/rate/feature/quickwidget/ExampleInstrumentedTest.kt b/feature/quickwidget/src/androidTest/java/dev/arkbuilders/rate/feature/quickwidget/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..1b44ef2f9 --- /dev/null +++ b/feature/quickwidget/src/androidTest/java/dev/arkbuilders/rate/feature/quickwidget/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package dev.arkbuilders.rate.feature.quickwidget + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * 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("dev.arkbuilders.rate.feature.quickwidget.test", appContext.packageName) + } +} diff --git a/feature/quickwidget/src/main/AndroidManifest.xml b/feature/quickwidget/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/feature/quickwidget/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/di/QuickWidgetComponent.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/di/QuickWidgetComponent.kt new file mode 100644 index 000000000..1f9f2d4f3 --- /dev/null +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/di/QuickWidgetComponent.kt @@ -0,0 +1,14 @@ +package dev.arkbuilders.rate.feature.quickwidget.di + +import dagger.Component +import dev.arkbuilders.rate.feature.quick.di.QuickComponent +import dev.arkbuilders.rate.feature.quick.domain.repo.QuickRepo +import dev.arkbuilders.rate.feature.quick.domain.usecase.GetSortedPinnedQuickPairsUseCase + +@QuickWidgetScope +@Component(dependencies = [QuickComponent::class]) +interface QuickWidgetComponent { + fun quickRepo(): QuickRepo + + fun getPinnedQuickPairUseCase(): GetSortedPinnedQuickPairsUseCase +} diff --git a/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/di/QuickWidgetComponentHolder.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/di/QuickWidgetComponentHolder.kt new file mode 100644 index 000000000..f416d4531 --- /dev/null +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/di/QuickWidgetComponentHolder.kt @@ -0,0 +1,17 @@ +package dev.arkbuilders.rate.feature.quickwidget.di + +import android.content.Context +import dev.arkbuilders.rate.feature.quick.di.QuickComponentHolder + +object QuickWidgetComponentHolder { + private var component: QuickWidgetComponent? = null + + fun provide(ctx: Context): QuickWidgetComponent { + component ?: let { + val app = ctx.applicationContext + val quickComponent = QuickComponentHolder.provide(app) + component = DaggerQuickWidgetComponent.builder().quickComponent(quickComponent).build() + } + return component!! + } +} diff --git a/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/di/QuickWidgetScope.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/di/QuickWidgetScope.kt new file mode 100644 index 000000000..e28a32d5b --- /dev/null +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/di/QuickWidgetScope.kt @@ -0,0 +1,7 @@ +package dev.arkbuilders.rate.feature.quickwidget.di + +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.SOURCE) +annotation class QuickWidgetScope diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairItem.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/QuickPairItem.kt similarity index 90% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairItem.kt rename to feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/QuickPairItem.kt index 99f7e7d53..271f2f4d8 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairItem.kt +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/QuickPairItem.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.quick.glancewidget +package dev.arkbuilders.rate.feature.quickwidget.presentation import android.content.Context import androidx.compose.runtime.Composable @@ -18,10 +18,10 @@ import androidx.glance.text.FontWeight import androidx.glance.text.Text import androidx.glance.text.TextStyle import androidx.glance.unit.ColorProvider -import dev.arkbuilders.rate.data.CurrUtils -import dev.arkbuilders.rate.domain.model.PinnedQuickPair -import dev.arkbuilders.rate.presentation.theme.ArkColor -import dev.arkbuilders.rate.presentation.utils.IconUtils +import dev.arkbuilders.rate.core.domain.CurrUtils +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.utils.IconUtils +import dev.arkbuilders.rate.feature.quick.domain.model.PinnedQuickPair @Composable fun QuickPairItem( diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidget.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/QuickPairsWidget.kt similarity index 77% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidget.kt rename to feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/QuickPairsWidget.kt index 98bac6547..22ca2315f 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidget.kt +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/QuickPairsWidget.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.quick.glancewidget +package dev.arkbuilders.rate.feature.quickwidget.presentation import android.content.Context import androidx.compose.ui.graphics.Color @@ -30,27 +30,28 @@ import androidx.glance.text.FontWeight import androidx.glance.text.Text import androidx.glance.text.TextStyle import androidx.glance.unit.ColorProvider -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.presentation.quick.glancewidget.action.AddNewPairAction -import dev.arkbuilders.rate.presentation.quick.glancewidget.action.NextPageAction -import dev.arkbuilders.rate.presentation.quick.glancewidget.action.OpenAppAction -import dev.arkbuilders.rate.presentation.quick.glancewidget.action.PreviousPageAction -import dev.arkbuilders.rate.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.feature.quickwidget.di.QuickWidgetComponentHolder +import dev.arkbuilders.rate.feature.quickwidget.presentation.action.AddNewPairAction +import dev.arkbuilders.rate.feature.quickwidget.presentation.action.NextPageAction +import dev.arkbuilders.rate.feature.quickwidget.presentation.action.OpenAppAction +import dev.arkbuilders.rate.feature.quickwidget.presentation.action.PreviousPageAction class QuickPairsWidget : GlanceAppWidget() { - private val getPinnedUseCase = DIManager.component.getSortedPinnedQuickPairsUseCase() - override suspend fun provideGlance( context: Context, id: GlanceId, ) { + val getPinnedUseCase = + QuickWidgetComponentHolder.provide(context).getPinnedQuickPairUseCase() val pinned = getPinnedUseCase.invoke() provideContent { val prefs = currentState() val group = prefs[QuickPairsWidgetReceiver.currentGroupKey] val quickPairsList = pinned.filter { it.pair.group == group } - val displayGroup = group ?: context.getString(R.string.group_default_name) + val displayGroup = group ?: context.getString(CoreRString.group_default_name) Column( modifier = GlanceModifier.fillMaxSize().background(Color.White) @@ -64,12 +65,12 @@ class QuickPairsWidget : GlanceAppWidget() { modifier = GlanceModifier.size(24.dp).padding(4.dp) .clickable(actionRunCallback()), - provider = ImageProvider(R.drawable.ic_about_logo), + provider = ImageProvider(CoreRDrawable.ic_about_logo), contentDescription = null, ) Text( modifier = GlanceModifier.defaultWeight(), - text = context.getString(R.string.quick_pinned_pairs), + text = context.getString(CoreRString.quick_pinned_pairs), style = TextStyle( color = ColorProvider(ArkColor.TextTertiary), @@ -89,7 +90,7 @@ class QuickPairsWidget : GlanceAppWidget() { modifier = GlanceModifier.size(24.dp).padding(4.dp) .clickable(actionRunCallback()), - provider = ImageProvider(R.drawable.ic_chevron_left), + provider = ImageProvider(CoreRDrawable.ic_chevron_left), contentDescription = null, ) @@ -97,7 +98,7 @@ class QuickPairsWidget : GlanceAppWidget() { modifier = GlanceModifier.size(24.dp).padding(4.dp) .clickable(actionRunCallback()), - provider = ImageProvider(R.drawable.ic_chevron_right), + provider = ImageProvider(CoreRDrawable.ic_chevron_right), contentDescription = null, ) Image( @@ -106,7 +107,7 @@ class QuickPairsWidget : GlanceAppWidget() { .clickable(actionRunCallback()), provider = ImageProvider( - R.drawable.ic_add_circle, + CoreRDrawable.ic_add_circle, ), contentDescription = null, ) @@ -114,10 +115,13 @@ class QuickPairsWidget : GlanceAppWidget() { modifier = GlanceModifier .clickable(actionRunCallback()), - text = context.getString(R.string.quick_open_app), + text = context.getString(CoreRString.quick_open_app), style = TextStyle( - color = ColorProvider(ArkColor.Primary), + color = + ColorProvider( + ArkColor.Primary, + ), fontWeight = FontWeight.Medium, ), ) diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidgetReceiver.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/QuickPairsWidgetReceiver.kt similarity index 92% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidgetReceiver.kt rename to feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/QuickPairsWidgetReceiver.kt index 2edeed8ab..0a60bf56e 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidgetReceiver.kt +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/QuickPairsWidgetReceiver.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.quick.glancewidget +package dev.arkbuilders.rate.feature.quickwidget.presentation import android.appwidget.AppWidgetManager import android.content.Context @@ -9,7 +9,7 @@ import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.GlanceAppWidgetManager import androidx.glance.appwidget.GlanceAppWidgetReceiver import androidx.glance.appwidget.state.updateAppWidgetState -import dev.arkbuilders.rate.di.DIManager +import dev.arkbuilders.rate.feature.quickwidget.di.QuickWidgetComponentHolder import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import timber.log.Timber @@ -60,7 +60,7 @@ class QuickPairsWidgetReceiver : GlanceAppWidgetReceiver() { glanceId: GlanceId, findNewIndex: (currentIndex: Int?, lastIndex: Int) -> Int, ) { - val quickRepo = DIManager.component.quickRepo() + val quickRepo = QuickWidgetComponentHolder.provide(context).quickRepo() val allGroups = quickRepo.getAllGroups() updateAppWidgetState(context, glanceId) { prefs -> var currentIndex: Int? = allGroups.indexOf(prefs[currentGroupKey]) diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/AddNewPairAction.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/AddNewPairAction.kt similarity index 78% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/AddNewPairAction.kt rename to feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/AddNewPairAction.kt index 3b3bf3b7c..4e1dfdc74 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/AddNewPairAction.kt +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/AddNewPairAction.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.quick.glancewidget.action +package dev.arkbuilders.rate.feature.quickwidget.presentation.action import android.content.Context import android.content.Intent @@ -6,8 +6,7 @@ import androidx.glance.GlanceId import androidx.glance.action.ActionParameters import androidx.glance.appwidget.action.ActionCallback import androidx.glance.appwidget.state.updateAppWidgetState -import dev.arkbuilders.rate.presentation.MainActivity -import dev.arkbuilders.rate.presentation.quick.glancewidget.QuickPairsWidgetReceiver +import dev.arkbuilders.rate.feature.quickwidget.presentation.QuickPairsWidgetReceiver class AddNewPairAction : ActionCallback { override suspend fun onAction( @@ -21,7 +20,8 @@ class AddNewPairAction : ActionCallback { group = pref[QuickPairsWidgetReceiver.currentGroupKey] } context.startActivity( - Intent(context, MainActivity::class.java).apply { + Intent().apply { + setClassName(context, "dev.arkbuilders.rate.presentation.MainActivity") putExtra(ADD_NEW_PAIR, "ADD_NEW_PAIR") putExtra(ADD_NEW_PAIR_GROUP_KEY, group) setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/NextPageAction.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/NextPageAction.kt similarity index 82% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/NextPageAction.kt rename to feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/NextPageAction.kt index 2e8a2d800..649072d0e 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/NextPageAction.kt +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/NextPageAction.kt @@ -1,10 +1,10 @@ -package dev.arkbuilders.rate.presentation.quick.glancewidget.action +package dev.arkbuilders.rate.feature.quickwidget.presentation.action import android.content.Context import androidx.glance.GlanceId import androidx.glance.action.ActionParameters import androidx.glance.appwidget.action.ActionCallback -import dev.arkbuilders.rate.presentation.quick.glancewidget.QuickPairsWidgetReceiver +import dev.arkbuilders.rate.feature.quickwidget.presentation.QuickPairsWidgetReceiver class NextPageAction : ActionCallback { override suspend fun onAction( diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/OpenAppAction.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/OpenAppAction.kt similarity index 72% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/OpenAppAction.kt rename to feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/OpenAppAction.kt index 629c5d8b2..82b332472 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/OpenAppAction.kt +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/OpenAppAction.kt @@ -1,11 +1,10 @@ -package dev.arkbuilders.rate.presentation.quick.glancewidget.action +package dev.arkbuilders.rate.feature.quickwidget.presentation.action import android.content.Context import android.content.Intent import androidx.glance.GlanceId import androidx.glance.action.ActionParameters import androidx.glance.appwidget.action.ActionCallback -import dev.arkbuilders.rate.presentation.MainActivity class OpenAppAction : ActionCallback { override suspend fun onAction( @@ -14,7 +13,8 @@ class OpenAppAction : ActionCallback { parameters: ActionParameters, ) { context.startActivity( - Intent(context, MainActivity::class.java).apply { + Intent().apply { + setClassName(context, "dev.arkbuilders.rate.presentation.MainActivity") setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }, ) diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/PreviousPageAction.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/PreviousPageAction.kt similarity index 82% rename from app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/PreviousPageAction.kt rename to feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/PreviousPageAction.kt index 3f4281b7c..2024e340a 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/PreviousPageAction.kt +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/presentation/action/PreviousPageAction.kt @@ -1,10 +1,10 @@ -package dev.arkbuilders.rate.presentation.quick.glancewidget.action +package dev.arkbuilders.rate.feature.quickwidget.presentation.action import android.content.Context import androidx.glance.GlanceId import androidx.glance.action.ActionParameters import androidx.glance.appwidget.action.ActionCallback -import dev.arkbuilders.rate.presentation.quick.glancewidget.QuickPairsWidgetReceiver +import dev.arkbuilders.rate.feature.quickwidget.presentation.QuickPairsWidgetReceiver class PreviousPageAction : ActionCallback { override suspend fun onAction( diff --git a/app/src/main/java/dev/arkbuilders/rate/data/worker/QuickPairsWidgetRefreshWorker.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/worker/QuickPairsWidgetRefreshWorker.kt similarity index 77% rename from app/src/main/java/dev/arkbuilders/rate/data/worker/QuickPairsWidgetRefreshWorker.kt rename to feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/worker/QuickPairsWidgetRefreshWorker.kt index 50828c98c..fa3062869 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/worker/QuickPairsWidgetRefreshWorker.kt +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/worker/QuickPairsWidgetRefreshWorker.kt @@ -1,18 +1,16 @@ -package dev.arkbuilders.rate.data.worker +package dev.arkbuilders.rate.feature.quickwidget.worker import android.content.Context import android.content.Intent import androidx.work.CoroutineWorker import androidx.work.WorkerParameters -import dev.arkbuilders.rate.presentation.quick.glancewidget.QuickPairsWidgetReceiver -import timber.log.Timber +import dev.arkbuilders.rate.feature.quickwidget.presentation.QuickPairsWidgetReceiver class QuickPairsWidgetRefreshWorker( params: WorkerParameters, private val context: Context, ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { - Timber.d("Refresh rates work executed") val intent = Intent(applicationContext, QuickPairsWidgetReceiver::class.java).apply { action = QuickPairsWidgetReceiver.PINNED_PAIRS_REFRESH diff --git a/app/src/main/java/dev/arkbuilders/rate/data/worker/QuickPairsWidgetRefreshWorkerFactory.kt b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/worker/QuickPairsWidgetRefreshWorkerFactory.kt similarity index 92% rename from app/src/main/java/dev/arkbuilders/rate/data/worker/QuickPairsWidgetRefreshWorkerFactory.kt rename to feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/worker/QuickPairsWidgetRefreshWorkerFactory.kt index eb15dc18e..d647342be 100644 --- a/app/src/main/java/dev/arkbuilders/rate/data/worker/QuickPairsWidgetRefreshWorkerFactory.kt +++ b/feature/quickwidget/src/main/java/dev/arkbuilders/rate/feature/quickwidget/worker/QuickPairsWidgetRefreshWorkerFactory.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.data.worker +package dev.arkbuilders.rate.feature.quickwidget.worker import android.content.Context import androidx.work.ListenableWorker diff --git a/feature/quickwidget/src/test/java/dev/arkbuilders/rate/feature/quickwidget/ExampleUnitTest.kt b/feature/quickwidget/src/test/java/dev/arkbuilders/rate/feature/quickwidget/ExampleUnitTest.kt new file mode 100644 index 000000000..b0bbd8315 --- /dev/null +++ b/feature/quickwidget/src/test/java/dev/arkbuilders/rate/feature/quickwidget/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package dev.arkbuilders.rate.feature.quickwidget + +import org.junit.Assert.assertEquals +import org.junit.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) + } +} diff --git a/feature/search/.gitignore b/feature/search/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/feature/search/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/search/build.gradle.kts b/feature/search/build.gradle.kts new file mode 100644 index 000000000..3eb8237dd --- /dev/null +++ b/feature/search/build.gradle.kts @@ -0,0 +1,77 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) +} + +android { + namespace = "dev.arkbuilders.rate.feature.search" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + ksp { + arg("compose-destinations.mode", "destinations") + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(project(":core:di")) + implementation(project(":core:domain")) + implementation(project(":core:presentation")) + implementation(project(":core:db")) + + implementation(libs.androidx.core.ktx) + + implementation(libs.androidx.ui) + implementation(libs.navigation.compose) + implementation(libs.material3) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(libs.constraintlayout.compose) + + implementation(libs.orbit.compose) + implementation(libs.orbit.viewmodel) + + implementation(libs.dagger) + ksp(libs.dagger.compiler) + + implementation(libs.compose.destinations.animations) + ksp(libs.compose.destinations.compiler) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":feature:search:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":feature:search:preBuild").dependsOn("ktlintFormat") diff --git a/feature/search/consumer-rules.pro b/feature/search/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/feature/search/proguard-rules.pro b/feature/search/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/feature/search/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/feature/search/src/androidTest/java/dev/arkbuilders/rate/feature/search/ExampleInstrumentedTest.kt b/feature/search/src/androidTest/java/dev/arkbuilders/rate/feature/search/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..8b6b49e5a --- /dev/null +++ b/feature/search/src/androidTest/java/dev/arkbuilders/rate/feature/search/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package dev.arkbuilders.rate.feature.search + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * 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("dev.arkbuilders.rate.feature.search.test", appContext.packageName) + } +} diff --git a/feature/search/src/main/AndroidManifest.xml b/feature/search/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/feature/search/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/di/SearchComponent.kt b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/di/SearchComponent.kt new file mode 100644 index 000000000..76e990643 --- /dev/null +++ b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/di/SearchComponent.kt @@ -0,0 +1,17 @@ +package dev.arkbuilders.rate.feature.search.di + +import dagger.Component +import dev.arkbuilders.rate.core.di.CoreComponent +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.usecase.GetTopResultUseCase +import dev.arkbuilders.rate.feature.search.presentation.SearchViewModelFactory + +@SearchScope +@Component(dependencies = [CoreComponent::class]) +interface SearchComponent { + fun getTopResultUseCase(): GetTopResultUseCase + + fun analyticsManager(): AnalyticsManager + + fun searchVMFactory(): SearchViewModelFactory.Factory +} diff --git a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/di/SearchComponentHolder.kt b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/di/SearchComponentHolder.kt new file mode 100644 index 000000000..96d42ff97 --- /dev/null +++ b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/di/SearchComponentHolder.kt @@ -0,0 +1,17 @@ +package dev.arkbuilders.rate.feature.search.di + +import android.content.Context +import dev.arkbuilders.rate.core.di.CoreComponentProvider + +object SearchComponentHolder { + private var component: SearchComponent? = null + + fun provide(ctx: Context): SearchComponent { + component ?: let { + val app = ctx.applicationContext + val coreComponent = (app as CoreComponentProvider).provideCoreComponent() + component = DaggerSearchComponent.builder().coreComponent(coreComponent).build() + } + return component!! + } +} diff --git a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/di/SearchScope.kt b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/di/SearchScope.kt new file mode 100644 index 000000000..be787099c --- /dev/null +++ b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/di/SearchScope.kt @@ -0,0 +1,7 @@ +package dev.arkbuilders.rate.feature.search.di + +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.SOURCE) +annotation class SearchScope diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/search/SearchCurrencyScreen.kt b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt similarity index 71% rename from app/src/main/java/dev/arkbuilders/rate/presentation/search/SearchCurrencyScreen.kt rename to feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt index 3663e2081..e1e0864da 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/search/SearchCurrencyScreen.kt +++ b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalMaterial3Api::class) -package dev.arkbuilders.rate.presentation.search +package dev.arkbuilders.rate.feature.search.presentation import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -11,22 +11,24 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.presentation.ui.AppHorDiv -import dev.arkbuilders.rate.presentation.ui.AppTopBarBack -import dev.arkbuilders.rate.presentation.ui.CurrencyInfoItem -import dev.arkbuilders.rate.presentation.ui.ListHeader -import dev.arkbuilders.rate.presentation.ui.LoadingScreen -import dev.arkbuilders.rate.presentation.ui.NoResult -import dev.arkbuilders.rate.presentation.ui.SearchTextField +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv +import dev.arkbuilders.rate.core.presentation.ui.AppTopBarBack +import dev.arkbuilders.rate.core.presentation.ui.CurrencyInfoItem +import dev.arkbuilders.rate.core.presentation.ui.ListHeader +import dev.arkbuilders.rate.core.presentation.ui.LoadingScreen +import dev.arkbuilders.rate.core.presentation.ui.NoResult +import dev.arkbuilders.rate.core.presentation.ui.SearchTextField +import dev.arkbuilders.rate.feature.search.di.SearchComponentHolder import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect @@ -37,10 +39,15 @@ fun SearchCurrencyScreen( pos: Int? = null, navigator: DestinationsNavigator, ) { + val ctx = LocalContext.current + val component = + remember { + SearchComponentHolder.provide(ctx) + } val viewModel: SearchViewModel = viewModel( factory = - DIManager.component.searchVMFactory() + component.searchVMFactory() .create(appSharedFlowKeyString, pos), ) val state by viewModel.collectAsState() @@ -54,8 +61,8 @@ fun SearchCurrencyScreen( Scaffold( topBar = { AppTopBarBack( - title = stringResource(R.string.search_currency), - navigator = navigator, + title = stringResource(CoreRString.search_currency), + onBackClick = { navigator.popBackStack() }, ) }, ) { @@ -104,7 +111,7 @@ private fun Results( filter.isNotEmpty() -> { if (topResultsFiltered.isNotEmpty()) { LazyColumn { - item { ListHeader(stringResource(R.string.top_results)) } + item { ListHeader(stringResource(CoreRString.top_results)) } items(topResultsFiltered) { name -> CurrencyInfoItem(name) { onClick(it) } } @@ -117,12 +124,12 @@ private fun Results( else -> { LazyColumn { if (frequent.isNotEmpty()) { - item { ListHeader(stringResource(R.string.frequent_currencies)) } + item { ListHeader(stringResource(CoreRString.frequent_currencies)) } items(frequent) { name -> CurrencyInfoItem(name) { onClick(it) } } } - item { ListHeader(stringResource(R.string.all_currencies)) } + item { ListHeader(stringResource(CoreRString.all_currencies)) } items(all) { name -> CurrencyInfoItem(name) { onClick(it) } } diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/search/SearchViewModel.kt b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt similarity index 88% rename from app/src/main/java/dev/arkbuilders/rate/presentation/search/SearchViewModel.kt rename to feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt index c877f88cf..c6c5bd020 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/search/SearchViewModel.kt +++ b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt @@ -1,17 +1,18 @@ -package dev.arkbuilders.rate.presentation.search +package dev.arkbuilders.rate.feature.search.presentation import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import dev.arkbuilders.rate.domain.model.CurrencyName -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.CurrencyRepo -import dev.arkbuilders.rate.domain.usecase.CalcFrequentCurrUseCase -import dev.arkbuilders.rate.domain.usecase.GetTopResultUseCase -import dev.arkbuilders.rate.presentation.shared.AppSharedFlow -import dev.arkbuilders.rate.presentation.shared.AppSharedFlowKey +import dev.arkbuilders.rate.core.domain.model.CurrencyName +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo +import dev.arkbuilders.rate.core.domain.usecase.CalcFrequentCurrUseCase +import dev.arkbuilders.rate.core.domain.usecase.GetTopResultUseCase +import dev.arkbuilders.rate.core.presentation.AppSharedFlow +import dev.arkbuilders.rate.core.presentation.AppSharedFlowKey +import dev.arkbuilders.rate.feature.search.di.SearchScope import org.orbitmvi.orbit.Container import org.orbitmvi.orbit.ContainerHost import org.orbitmvi.orbit.syntax.simple.blockingIntent @@ -128,6 +129,7 @@ class SearchViewModelFactory @AssistedInject constructor( ) as T } + @SearchScope @AssistedFactory interface Factory { fun create( diff --git a/feature/search/src/test/java/dev/arkbuilders/rate/feature/search/ExampleUnitTest.kt b/feature/search/src/test/java/dev/arkbuilders/rate/feature/search/ExampleUnitTest.kt new file mode 100644 index 000000000..26a526c8c --- /dev/null +++ b/feature/search/src/test/java/dev/arkbuilders/rate/feature/search/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package dev.arkbuilders.rate.feature.search + +import org.junit.Assert.assertEquals +import org.junit.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) + } +} diff --git a/feature/settings/.gitignore b/feature/settings/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/feature/settings/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts new file mode 100644 index 000000000..266e518d6 --- /dev/null +++ b/feature/settings/build.gradle.kts @@ -0,0 +1,83 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.ksp) +} + +android { + namespace = "dev.arkbuilders.rate.feature.settings" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + ksp { + arg("compose-destinations.mode", "destinations") + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get() + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + } +} + +dependencies { + implementation(project(":core:di")) + implementation(project(":core:domain")) + implementation(project(":core:presentation")) + implementation(project(":core:db")) + + implementation(libs.androidx.core.ktx) + + implementation(libs.ark.about) + + implementation(libs.androidx.ui) + implementation(libs.navigation.compose) + implementation(libs.material3) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(libs.constraintlayout.compose) + + implementation(libs.orbit.compose) + implementation(libs.orbit.viewmodel) + + implementation(libs.dagger) + ksp(libs.dagger.compiler) + + implementation(libs.compose.destinations.animations) + ksp(libs.compose.destinations.compiler) + + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.analytics) + implementation(libs.firebase.crashlytics) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) +} + +tasks.getByPath(":feature:settings:preBuild").dependsOn("ktlintCheck") + +tasks.getByPath(":feature:settings:preBuild").dependsOn("ktlintFormat") diff --git a/feature/settings/consumer-rules.pro b/feature/settings/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/feature/settings/proguard-rules.pro b/feature/settings/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/feature/settings/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/feature/settings/src/androidTest/java/dev/arkbuilders/rate/feature/settings/ExampleInstrumentedTest.kt b/feature/settings/src/androidTest/java/dev/arkbuilders/rate/feature/settings/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..cd9d3dd32 --- /dev/null +++ b/feature/settings/src/androidTest/java/dev/arkbuilders/rate/feature/settings/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package dev.arkbuilders.rate.feature.settings + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * 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("dev.arkbuilders.rate.feature.settings.test", appContext.packageName) + } +} diff --git a/feature/settings/src/main/AndroidManifest.xml b/feature/settings/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/feature/settings/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/di/SettingsComponent.kt b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/di/SettingsComponent.kt new file mode 100644 index 000000000..34c86118a --- /dev/null +++ b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/di/SettingsComponent.kt @@ -0,0 +1,17 @@ +package dev.arkbuilders.rate.feature.settings.di + +import dagger.Component +import dev.arkbuilders.rate.core.di.CoreComponent +import dev.arkbuilders.rate.core.domain.BuildConfigFieldsProvider +import dev.arkbuilders.rate.core.domain.repo.TimestampRepo +import dev.arkbuilders.rate.feature.settings.presentation.SettingsViewModelFactory + +@SettingsScope +@Component(dependencies = [CoreComponent::class]) +interface SettingsComponent { + fun settingsVMFactory(): SettingsViewModelFactory + + fun buildConfigFieldsProvider(): BuildConfigFieldsProvider + + fun timestampRepo(): TimestampRepo +} diff --git a/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/di/SettingsComponentHolder.kt b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/di/SettingsComponentHolder.kt new file mode 100644 index 000000000..79b17a62e --- /dev/null +++ b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/di/SettingsComponentHolder.kt @@ -0,0 +1,17 @@ +package dev.arkbuilders.rate.feature.settings.di + +import android.content.Context +import dev.arkbuilders.rate.core.di.CoreComponentProvider + +object SettingsComponentHolder { + private var component: SettingsComponent? = null + + fun provide(ctx: Context): SettingsComponent { + component ?: let { + val app = ctx.applicationContext + val coreComponent = (app as CoreComponentProvider).provideCoreComponent() + component = DaggerSettingsComponent.builder().coreComponent(coreComponent).build() + } + return component!! + } +} diff --git a/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/di/SettingsScope.kt b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/di/SettingsScope.kt new file mode 100644 index 000000000..2e3b6e877 --- /dev/null +++ b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/di/SettingsScope.kt @@ -0,0 +1,7 @@ +package dev.arkbuilders.rate.feature.settings.di + +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.SOURCE) +annotation class SettingsScope diff --git a/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/AboutScreen.kt b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/AboutScreen.kt new file mode 100644 index 000000000..3de32c27d --- /dev/null +++ b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/AboutScreen.kt @@ -0,0 +1,38 @@ +package dev.arkbuilders.rate.feature.settings.presentation + +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator +import dev.arkbuilders.components.about.presentation.ArkAbout +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.ui.AppTopBarBack +import dev.arkbuilders.rate.feature.settings.di.SettingsComponentHolder + +@Destination +@Composable +fun AboutScreen(navigator: DestinationsNavigator) { + val ctx = LocalContext.current + val component = SettingsComponentHolder.provide(ctx) + Scaffold( + topBar = { + AppTopBarBack( + title = stringResource(CoreRString.about), + onBackClick = { navigator.popBackStack() }, + ) + }, + ) { + ArkAbout( + modifier = Modifier.padding(it), + appName = stringResource(id = CoreRString.app_name), + appLogoResId = CoreRDrawable.ic_about_logo, + versionName = component.buildConfigFieldsProvider().provide().versionName, + privacyPolicyUrl = stringResource(CoreRString.privacy_policy_url), + ) + } +} diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/settings/SettingsScreen.kt b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsScreen.kt similarity index 80% rename from app/src/main/java/dev/arkbuilders/rate/presentation/settings/SettingsScreen.kt rename to feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsScreen.kt index 7f2f6b7bf..81b5c66f8 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/settings/SettingsScreen.kt +++ b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsScreen.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.settings +package dev.arkbuilders.rate.feature.settings.presentation import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -14,6 +14,7 @@ import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -25,12 +26,13 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import dev.arkbuilders.rate.R -import dev.arkbuilders.rate.di.DIManager -import dev.arkbuilders.rate.presentation.destinations.AboutScreenDestination -import dev.arkbuilders.rate.presentation.theme.ArkColor -import dev.arkbuilders.rate.presentation.ui.AppHorDiv16 -import dev.arkbuilders.rate.presentation.utils.DateFormatUtils +import dev.arkbuilders.rate.core.presentation.CoreRDrawable +import dev.arkbuilders.rate.core.presentation.CoreRString +import dev.arkbuilders.rate.core.presentation.theme.ArkColor +import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv16 +import dev.arkbuilders.rate.core.presentation.utils.DateFormatUtils +import dev.arkbuilders.rate.feature.settings.di.SettingsComponentHolder +import dev.arkbuilders.rate.feature.settings.presentation.destinations.AboutScreenDestination import org.orbitmvi.orbit.compose.collectAsState import java.time.Duration import java.time.OffsetDateTime @@ -38,8 +40,13 @@ import java.time.OffsetDateTime @Destination @Composable fun SettingsScreen(navigator: DestinationsNavigator) { + val ctx = LocalContext.current + val component = + remember { + SettingsComponentHolder.provide(ctx) + } val vm: SettingsViewModel = - viewModel(factory = DIManager.component.settingsVMFactory()) + viewModel(factory = component.settingsVMFactory()) val state by vm.collectAsState() @@ -71,7 +78,7 @@ private fun Content( ) { Text( modifier = Modifier.padding(horizontal = 16.dp), - text = stringResource(R.string.settings_quick_portfolio_alerts), + text = stringResource(CoreRString.settings_quick_portfolio_alerts), fontWeight = FontWeight.SemiBold, fontSize = 18.sp, color = ArkColor.TextPrimary, @@ -81,23 +88,23 @@ private fun Content( fun formatTime(date: OffsetDateTime): String { val elapsed = DateFormatUtils.latestCheckElapsedTime(ctx, now, date) val time = DateFormatUtils.latestCheckTime(date) - return ctx.getString(R.string.settings_elapsed_ago, elapsed) + time + return ctx.getString(CoreRString.settings_elapsed_ago, elapsed) + time } val refreshDesc = state.latestRefresh?.let { formatTime(it) - } ?: stringResource(R.string.n_a) + } ?: stringResource(CoreRString.n_a) val pairAlertDesc = state.latestPairAlertCheck?.let { formatTime(it) - } ?: stringResource(R.string.n_a) + } ?: stringResource(CoreRString.n_a) LatestRefresh( - title = stringResource(R.string.settings_latest_rates_refresh), + title = stringResource(CoreRString.settings_latest_rates_refresh), description = refreshDesc, ) LatestRefresh( - title = stringResource(R.string.settings_latest_alerts_check), + title = stringResource(CoreRString.settings_latest_alerts_check), description = pairAlertDesc, ) AppHorDiv16(modifier = Modifier.padding(top = 20.dp)) @@ -108,7 +115,7 @@ private fun Content( ) { Text( modifier = Modifier.weight(1f), - text = stringResource(R.string.crash_reports), + text = stringResource(CoreRString.crash_reports), fontWeight = FontWeight.SemiBold, fontSize = 18.sp, color = ArkColor.TextPrimary, @@ -126,7 +133,7 @@ private fun Content( ) { Text( modifier = Modifier.weight(1f), - text = stringResource(R.string.collect_analytics), + text = stringResource(CoreRString.collect_analytics), fontWeight = FontWeight.SemiBold, fontSize = 18.sp, color = ArkColor.TextPrimary, @@ -146,13 +153,13 @@ private fun Content( verticalAlignment = Alignment.CenterVertically, ) { Icon( - painter = painterResource(R.drawable.ic_info), + painter = painterResource(CoreRDrawable.ic_info), contentDescription = "", tint = ArkColor.TextTertiary, ) Text( modifier = Modifier.padding(start = 6.dp), - text = stringResource(R.string.about), + text = stringResource(CoreRString.about), fontWeight = FontWeight.SemiBold, fontSize = 14.sp, color = ArkColor.TextTertiary, diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/settings/SettingsViewModel.kt b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsViewModel.kt similarity index 75% rename from app/src/main/java/dev/arkbuilders/rate/presentation/settings/SettingsViewModel.kt rename to feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsViewModel.kt index 99f7d0b7c..913245aa4 100644 --- a/app/src/main/java/dev/arkbuilders/rate/presentation/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/java/dev/arkbuilders/rate/feature/settings/presentation/SettingsViewModel.kt @@ -1,4 +1,4 @@ -package dev.arkbuilders.rate.presentation.settings +package dev.arkbuilders.rate.feature.settings.presentation import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider @@ -6,12 +6,14 @@ import androidx.lifecycle.viewModelScope import com.google.firebase.analytics.ktx.analytics import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase -import dev.arkbuilders.rate.BuildConfig -import dev.arkbuilders.rate.data.preferences.PrefsImpl -import dev.arkbuilders.rate.domain.model.TimestampType -import dev.arkbuilders.rate.domain.repo.AnalyticsManager -import dev.arkbuilders.rate.domain.repo.PreferenceKey -import dev.arkbuilders.rate.domain.repo.TimestampRepo +import dev.arkbuilders.rate.core.domain.BuildConfigFields +import dev.arkbuilders.rate.core.domain.BuildConfigFieldsProvider +import dev.arkbuilders.rate.core.domain.model.TimestampType +import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager +import dev.arkbuilders.rate.core.domain.repo.PreferenceKey +import dev.arkbuilders.rate.core.domain.repo.Prefs +import dev.arkbuilders.rate.core.domain.repo.TimestampRepo +import dev.arkbuilders.rate.feature.settings.di.SettingsScope import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -22,12 +24,11 @@ import org.orbitmvi.orbit.syntax.simple.reduce import org.orbitmvi.orbit.viewmodel.container import java.time.OffsetDateTime import javax.inject.Inject -import javax.inject.Singleton data class SettingsScreenState( val latestRefresh: OffsetDateTime? = null, val latestPairAlertCheck: OffsetDateTime? = null, - val showCrashReports: Boolean = BuildConfig.GOOGLE_PLAY_BUILD.not(), + val showCrashReports: Boolean = false, val crashReportsEnabled: Boolean = false, val analyticsEnabled: Boolean = false, ) @@ -35,12 +36,13 @@ data class SettingsScreenState( sealed class SettingsScreenEffect() class SettingsViewModel( - private val prefs: PrefsImpl, + private val prefs: Prefs, private val timestampRepo: TimestampRepo, private val analyticsManager: AnalyticsManager, + private val buildConfigFields: BuildConfigFields, ) : ViewModel(), ContainerHost { override val container: Container = - container(SettingsScreenState()) + container(SettingsScreenState(showCrashReports = buildConfigFields.isGooglePlayBuild.not())) init { analyticsManager.trackScreen("SettingsScreen") @@ -96,13 +98,19 @@ class SettingsViewModel( } } -@Singleton +@SettingsScope class SettingsViewModelFactory @Inject constructor( - private val prefs: PrefsImpl, + private val prefs: Prefs, private val timestampRepo: TimestampRepo, private val analyticsManager: AnalyticsManager, + private val buildConfigFieldsProvider: BuildConfigFieldsProvider, ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return SettingsViewModel(prefs, timestampRepo, analyticsManager) as T + return SettingsViewModel( + prefs, + timestampRepo, + analyticsManager, + buildConfigFieldsProvider.provide(), + ) as T } } diff --git a/feature/settings/src/test/java/dev/arkbuilders/rate/feature/settings/ExampleUnitTest.kt b/feature/settings/src/test/java/dev/arkbuilders/rate/feature/settings/ExampleUnitTest.kt new file mode 100644 index 000000000..67e0e0868 --- /dev/null +++ b/feature/settings/src/test/java/dev/arkbuilders/rate/feature/settings/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package dev.arkbuilders.rate.feature.settings + +import org.junit.Assert.assertEquals +import org.junit.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) + } +} diff --git a/fiaticons/build.gradle.kts b/fiaticons/build.gradle.kts index 75db21f0c..218edbfe2 100644 --- a/fiaticons/build.gradle.kts +++ b/fiaticons/build.gradle.kts @@ -19,16 +19,16 @@ android { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" + "proguard-rules.pro", ) } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "17" } } @@ -40,4 +40,4 @@ dependencies { testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") -} \ No newline at end of file +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c015f3b38..d936546e6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,15 @@ [versions] +kotlin = "1.9.22" +agp = "8.1.3" +coreKtx = "1.13.1" +minSdk = "26" +compileSdk = "34" +composeCompiler = "1.5.10" +ksp = "1.9.22-1.0.17" + arkAbout = "0.1.1" activityCompose = "1.9.2" -composeDestinationsVersion = "1.9.62" +composeDestinationsVersion = "1.11.7" arrowFxCoroutines = "1.2.1" arrowCore = "1.2.1" constraintlayoutCompose = "1.0.1" @@ -15,7 +23,7 @@ junit = "4.13.2" junitVersion = "1.2.1" lifecycleRuntimeKtx = "2.8.6" loggingInterceptor = "4.12.0" -material3 = "1.2.1" +material3 = "1.3.1" navigationComposeVersion = "2.8.2" orbitCompose = "4.6.1" orbitViewmodel = "6.1.0" @@ -24,8 +32,11 @@ retrofit = "2.11.0" roomRuntime = "2.6.1" tagcloud = "1.1.0" timber = "5.0.1" -composeUi = "1.6.8" +composeUi = "1.7.5" workRuntimeKtx = "2.8.1" +appcompat = "1.7.0" +material = "1.12.0" +gson = "2.11.0" [libraries] ark-about = { module = "dev.arkbuilders.components:about", version.ref = "arkAbout" } @@ -55,6 +66,7 @@ constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayou converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" } dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" } dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" } +gson = { module = "com.google.code.gson:gson", version.ref = "gson" } firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } firebase-analytics = { module = "com.google.firebase:firebase-analytics-ktx", version = "" } @@ -70,3 +82,10 @@ qrgenerator = { module = "com.github.androidmads:QRGenerator", version.ref = "qr retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } tagcloud = { module = "io.github.oleksandrbalan:tagcloud", version.ref = "tagcloud" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +[plugins] +android-library = { id = "com.android.library", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp"} diff --git a/settings.gradle b/settings.gradle index 7921fd28b..faefe68e9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,3 +28,14 @@ rootProject.name = "arkrate" include ':app' include ':fiaticons' include ':cryptoicons' +include ':core:domain' +include ':core:data' +include ':core:presentation' +include ':feature:quick' +include ':core:db' +include ':core:di' +include ':feature:portfolio' +include ':feature:pairalert' +include ':feature:quickwidget' +include ':feature:search' +include ':feature:settings'