From 539d36fb0bb2935ab25926b26468fe9a558d161c Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 17 Oct 2024 12:35:34 -0700 Subject: [PATCH] WIP: Move app to use compose multiplatform --- app/build.gradle.kts | 204 +++++++++++------- .../apps/nowinandroid/ui/NavigationTest.kt | 23 +- .../apps/nowinandroid/ui/NiaAppStateTest.kt | 0 .../apps/nowinandroid/ui/UiTestExtensions.kt | 13 +- .../{main => androidMain}/AndroidManifest.xml | 0 .../ic_launcher-playstore.png | Bin .../samples/apps/nowinandroid/MainActivity.kt | 11 +- .../nowinandroid/MainActivityViewModel.kt | 5 +- .../apps/nowinandroid/NiaApplication.kt | 39 ++-- .../apps/nowinandroid/di/JankStatsModule.kt | 16 +- .../nowinandroid/navigation/NiaNavHost.kt | 0 .../navigation/TopLevelDestination.kt | 20 +- .../samples/apps/nowinandroid/ui/NiaApp.kt | 0 .../apps/nowinandroid/ui/NiaAppState.kt | 0 .../interests2pane/Interests2PaneViewModel.kt | 7 +- .../InterestsListDetailScreen.kt | 4 +- .../util/ProfileVerifierLogger.kt | 3 +- .../res/drawable/ic_launcher_background.xml | 0 .../res/drawable/ic_launcher_foreground.xml | 0 .../res/drawable/ic_splash.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../res/mipmap-hdpi/ic_launcher.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../res/mipmap-mdpi/ic_launcher.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../res/mipmap-xxxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../res/values-night/colors.xml | 0 .../res/values-night/themes.xml | 0 .../res/values/colors.xml | 0 .../res/values/strings.xml | 0 .../res/values/themes.xml | 0 app/src/benchmark/res/values/colors.xml | 22 -- .../composeResources/values/strings.xml} | 8 +- build-logic/convention/build.gradle.kts | 4 + .../kotlin/CmpApplicationConventionPlugin.kt | 127 +++++++++++ .../main/kotlin/KmpLibraryConventionPlugin.kt | 2 - .../main/kotlin/SqlDelightConventionPlugin.kt | 13 -- .../apps/nowinandroid/KotlinMultiplatform.kt | 2 - feature/bookmarks/build.gradle.kts | 5 + feature/foryou/build.gradle.kts | 5 + feature/interests/build.gradle.kts | 5 + feature/search/build.gradle.kts | 1 + feature/settings/build.gradle.kts | 2 + feature/topic/build.gradle.kts | 1 + gradle/libs.versions.toml | 3 + 51 files changed, 361 insertions(+), 184 deletions(-) rename app/src/{androidTest => androidInstrumentedTest}/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt (90%) rename app/src/{androidTest => androidInstrumentedTest}/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt (100%) rename app/src/{androidTest => androidInstrumentedTest}/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtensions.kt (74%) rename app/src/{main => androidMain}/AndroidManifest.xml (100%) rename app/src/{main => androidMain}/ic_launcher-playstore.png (100%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt (95%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt (92%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt (50%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/di/JankStatsModule.kt (85%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt (100%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt (77%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt (100%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt (100%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt (90%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt (98%) rename app/src/{main => androidMain}/kotlin/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt (97%) rename app/src/{main => androidMain}/res/drawable/ic_launcher_background.xml (100%) rename app/src/{main => androidMain}/res/drawable/ic_launcher_foreground.xml (100%) rename app/src/{main => androidMain}/res/drawable/ic_splash.xml (100%) rename app/src/{main => androidMain}/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename app/src/{main => androidMain}/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename app/src/{main => androidMain}/res/mipmap-hdpi/ic_launcher.png (100%) rename app/src/{main => androidMain}/res/mipmap-hdpi/ic_launcher_round.png (100%) rename app/src/{main => androidMain}/res/mipmap-mdpi/ic_launcher.png (100%) rename app/src/{main => androidMain}/res/mipmap-mdpi/ic_launcher_round.png (100%) rename app/src/{main => androidMain}/res/mipmap-xhdpi/ic_launcher.png (100%) rename app/src/{main => androidMain}/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename app/src/{main => androidMain}/res/mipmap-xxhdpi/ic_launcher.png (100%) rename app/src/{main => androidMain}/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename app/src/{main => androidMain}/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename app/src/{main => androidMain}/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename app/src/{main => androidMain}/res/values-night/colors.xml (100%) rename app/src/{main => androidMain}/res/values-night/themes.xml (100%) rename app/src/{main => androidMain}/res/values/colors.xml (100%) rename app/src/{main => androidMain}/res/values/strings.xml (100%) rename app/src/{main => androidMain}/res/values/themes.xml (100%) delete mode 100644 app/src/benchmark/res/values/colors.xml rename app/src/{benchmark/res/values-night/colors.xml => commonMain/composeResources/values/strings.xml} (63%) create mode 100644 build-logic/convention/src/main/kotlin/CmpApplicationConventionPlugin.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 07676ce59..fdbae43f9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,16 +14,14 @@ * limitations under the License. */ import com.google.samples.apps.nowinandroid.NiaBuildType +import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree plugins { - alias(libs.plugins.nowinandroid.android.application) - alias(libs.plugins.nowinandroid.android.application.compose) - alias(libs.plugins.nowinandroid.android.application.flavors) - alias(libs.plugins.nowinandroid.android.application.jacoco) - alias(libs.plugins.nowinandroid.android.application.firebase) - alias(libs.plugins.nowinandroid.hilt) - id("com.google.android.gms.oss-licenses-plugin") - alias(libs.plugins.baselineprofile) + alias(libs.plugins.nowinandroid.cmp.application) + alias(libs.plugins.nowinandroid.di.koin) +// alias(libs.plugins.baselineprofile) alias(libs.plugins.roborazzi) alias(libs.plugins.kotlin.serialization) } @@ -34,8 +32,7 @@ android { versionCode = 8 versionName = "0.1.2" // X.Y.Z; X = Major, Y = minor, Z = Patch level - // Custom test runner to set up Hilt dependency graph - testInstrumentationRunner = "com.google.samples.apps.nowinandroid.core.testing.NiaTestRunner" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true } @@ -55,7 +52,7 @@ android { // TODO: Abstract the signing configuration to a separate file to avoid hardcoding this. signingConfig = signingConfigs.named("debug").get() // Ensure Baseline Profile is fresh for release builds. - baselineProfile.automaticGenerationDuringBuild = true +// baselineProfile.automaticGenerationDuringBuild = true } } @@ -72,76 +69,127 @@ android { namespace = "com.google.samples.apps.nowinandroid" } +kotlin { + androidTarget { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) + } + + sourceSets { + commonMain.dependencies { + implementation(projects.feature.interests) + implementation(projects.feature.foryou) + implementation(projects.feature.bookmarks) + implementation(projects.feature.topic) + implementation(projects.feature.search) + implementation(projects.feature.settings) + implementation(projects.core.common) + implementation(projects.core.ui) + implementation(projects.core.designsystem) + implementation(projects.core.data) + implementation(projects.core.model) + implementation(projects.core.analytics) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material) + implementation(compose.ui) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) +// implementation(projects.sync.work) + + implementation(libs.coil.core) + implementation(libs.coil.compose) + implementation(libs.kotlinx.serialization.json) + } + + androidMain.dependencies { + implementation(compose.preview) + implementation(project.dependencies.platform(libs.androidx.compose.bom)) + implementation(libs.androidx.activity.compose) + implementation(libs.androidx.compose.material3.adaptive) + implementation(libs.androidx.compose.material3.adaptive.layout) + implementation(libs.androidx.compose.material3.adaptive.navigation) + implementation(libs.androidx.compose.material3.windowSizeClass) + implementation(libs.androidx.compose.runtime.tracing) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.core.splashscreen) + implementation(libs.androidx.lifecycle.runtimeCompose) + implementation(libs.androidx.navigation.compose) + implementation(libs.androidx.profileinstaller) + implementation(libs.androidx.tracing.ktx) + implementation(libs.androidx.window.core) + implementation(libs.kotlinx.coroutines.guava) + implementation(libs.koin.android) + } + + commonTest.dependencies { + implementation(projects.core.dataTest) + implementation(projects.core.testing) + implementation(projects.core.screenshotTesting) +// implementation(projects.sync.syncTest) + implementation(libs.kotlin.test) + @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) + implementation(compose.uiTest) + } + + androidInstrumentedTest.dependencies { + implementation(projects.core.dataTest) + implementation(projects.core.testing) + implementation(libs.androidx.test.espresso.core) + implementation(libs.androidx.navigation.testing) + implementation(project.dependencies.platform(libs.androidx.compose.bom)) + implementation(libs.androidx.compose.ui.test) + } + + jvmMain.dependencies { + implementation(compose.desktop.currentOs) + implementation(libs.kotlinx.coroutines.swing) + } + + jvmTest.dependencies { + implementation(compose.desktop.uiTestJUnit4) + } + } +} + dependencies { - implementation(projects.feature.interests) - implementation(projects.feature.foryou) - implementation(projects.feature.bookmarks) - implementation(projects.feature.topic) - implementation(projects.feature.search) - implementation(projects.feature.settings) - - implementation(projects.core.common) - implementation(projects.core.ui) - implementation(projects.core.designsystem) - implementation(projects.core.data) - implementation(projects.core.model) - implementation(projects.core.analytics) - implementation(projects.sync.work) - - implementation(libs.androidx.activity.compose) - implementation(libs.androidx.compose.material3.adaptive) - implementation(libs.androidx.compose.material3.adaptive.layout) - implementation(libs.androidx.compose.material3.adaptive.navigation) - implementation(libs.androidx.compose.material3.windowSizeClass) - implementation(libs.androidx.compose.runtime.tracing) - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.core.splashscreen) - implementation(libs.androidx.hilt.navigation.compose) - implementation(libs.androidx.lifecycle.runtimeCompose) - implementation(libs.androidx.navigation.compose) - implementation(libs.androidx.profileinstaller) - implementation(libs.androidx.tracing.ktx) - implementation(libs.androidx.window.core) - implementation(libs.kotlinx.coroutines.guava) - implementation(libs.coil) - implementation(libs.kotlinx.serialization.json) - - ksp(libs.hilt.compiler) - - debugImplementation(libs.androidx.compose.ui.testManifest) - debugImplementation(projects.uiTestHiltManifest) - - kspTest(libs.hilt.compiler) - - testImplementation(projects.core.dataTest) - testImplementation(libs.hilt.android.testing) - testImplementation(projects.sync.syncTest) - testImplementation(libs.kotlin.test) - - testDemoImplementation(libs.robolectric) - testDemoImplementation(libs.roborazzi) - testDemoImplementation(projects.core.screenshotTesting) - - androidTestImplementation(kotlin("test")) - androidTestImplementation(projects.core.testing) - androidTestImplementation(projects.core.dataTest) - androidTestImplementation(libs.androidx.test.espresso.core) - androidTestImplementation(libs.androidx.navigation.testing) - androidTestImplementation(libs.androidx.compose.ui.test) - androidTestImplementation(libs.hilt.android.testing) - - baselineProfile(projects.benchmarks) + debugImplementation(compose.uiTooling) + androidTestImplementation(libs.androidx.compose.ui.test.android) + androidTestImplementation(libs.androidx.compose.ui.testManifest) } -baselineProfile { - // Don't build on every iteration of a full assemble. - // Instead enable generation directly for the release build variant. - automaticGenerationDuringBuild = false +compose.desktop { + application { + mainClass = "com.google.sample.apps.nowinandroid.MainKt" - // Make use of Dex Layout Optimizations via Startup Profiles - dexLayoutOptimization = true + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "com.google.sample.apps.nowinandroid" + packageVersion = "1.0.0" + } + } } -dependencyGuard { - configuration("prodReleaseRuntimeClasspath") -} +//dependencies { +// +// debugImplementation(libs.androidx.compose.ui.testManifest) +// debugImplementation(projects.uiTestHiltManifest) +// +// +// +// testDemoImplementation(libs.robolectric) +// testDemoImplementation(libs.roborazzi) +// testDemoImplementation(projects.core.screenshotTesting) +// +// baselineProfile(projects.benchmarks) +//} +// +//baselineProfile { +// // Don't build on every iteration of a full assemble. +// // Instead enable generation directly for the release build variant. +// automaticGenerationDuringBuild = false +// +// // Make use of Dex Layout Optimizations via Startup Profiles +// dexLayoutOptimization = true +//} +// diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt b/app/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt similarity index 90% rename from app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt rename to app/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt index f421adaeb..76f48e55b 100644 --- a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt +++ b/app/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt @@ -31,27 +31,30 @@ import androidx.compose.ui.test.performScrollToNode import androidx.test.espresso.Espresso import androidx.test.espresso.NoActivityResumedException import com.google.samples.apps.nowinandroid.MainActivity -import com.google.samples.apps.nowinandroid.R import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule -import dagger.hilt.android.testing.HiltAndroidRule -import dagger.hilt.android.testing.HiltAndroidTest import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking +import nowinandroid.app.generated.resources.Res +import nowinandroid.feature.bookmarks.generated.resources.feature_bookmarks_title +import nowinandroid.feature.foryou.generated.resources.feature_foryou_navigate_up +import nowinandroid.feature.foryou.generated.resources.feature_foryou_title +import nowinandroid.feature.search.generated.resources.feature_search_interests +import nowinandroid.feature.settings.generated.resources.feature_settings_brand_android +import nowinandroid.feature.settings.generated.resources.feature_settings_dismiss_dialog_button_text +import nowinandroid.feature.settings.generated.resources.feature_settings_top_app_bar_action_icon_description import org.junit.Before import org.junit.Rule import org.junit.Test import javax.inject.Inject -import com.google.samples.apps.nowinandroid.feature.bookmarks.R as BookmarksR -import com.google.samples.apps.nowinandroid.feature.foryou.R as FeatureForyouR -import com.google.samples.apps.nowinandroid.feature.search.R as FeatureSearchR -import com.google.samples.apps.nowinandroid.feature.settings.R as SettingsR - +import nowinandroid.feature.bookmarks.generated.resources.Res as BookmarksR +import nowinandroid.feature.foryou.generated.resources.Res as FeatureForyouR +import nowinandroid.feature.search.generated.resources.Res as FeatureSearchR +import nowinandroid.feature.settings.generated.resources.Res as SettingsR /** * Tests all the navigation flows that are handled by the navigation library. */ -@HiltAndroidTest class NavigationTest { /** @@ -80,7 +83,7 @@ class NavigationTest { private val forYou by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_title) private val interests by composeTestRule.stringResource(FeatureSearchR.string.feature_search_interests) private val sampleTopic = "Headlines" - private val appName by composeTestRule.stringResource(R.string.app_name) + private val appName by composeTestRule.stringResource(Res.string.app_name) private val saved by composeTestRule.stringResource(BookmarksR.string.feature_bookmarks_title) private val settings by composeTestRule.stringResource(SettingsR.string.feature_settings_top_app_bar_action_icon_description) private val brand by composeTestRule.stringResource(SettingsR.string.feature_settings_brand_android) diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt b/app/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt similarity index 100% rename from app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt rename to app/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtensions.kt b/app/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtensions.kt similarity index 74% rename from app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtensions.kt rename to app/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtensions.kt index bdc09885d..4e45c237a 100644 --- a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtensions.kt +++ b/app/src/androidInstrumentedTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtensions.kt @@ -16,11 +16,18 @@ package com.google.samples.apps.nowinandroid.ui -import androidx.annotation.StringRes import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import kotlinx.coroutines.runBlocking +import org.jetbrains.compose.resources.StringResource import kotlin.properties.ReadOnlyProperty fun AndroidComposeTestRule<*, *>.stringResource( - @StringRes resId: Int, + resId: StringResource, ): ReadOnlyProperty = - ReadOnlyProperty { _, _ -> activity.getString(resId) } + ReadOnlyProperty { _, _ -> + runBlocking { + // TODO remove runBlocking + org.jetbrains.compose.resources.getString(resId) + } + } + diff --git a/app/src/main/AndroidManifest.xml b/app/src/androidMain/AndroidManifest.xml similarity index 100% rename from app/src/main/AndroidManifest.xml rename to app/src/androidMain/AndroidManifest.xml diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/androidMain/ic_launcher-playstore.png similarity index 100% rename from app/src/main/ic_launcher-playstore.png rename to app/src/androidMain/ic_launcher-playstore.png diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt similarity index 95% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt index 0fab38e17..864482504 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt +++ b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt @@ -43,27 +43,22 @@ import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourc import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme -import com.google.samples.apps.nowinandroid.core.di.ApplicationComponent -import com.google.samples.apps.nowinandroid.core.di.ApplicationComponentProvider import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone import com.google.samples.apps.nowinandroid.ui.NiaApp import com.google.samples.apps.nowinandroid.ui.rememberNiaAppState -import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import javax.inject.Inject +import org.koin.core.context.GlobalContext.get -@AndroidEntryPoint -class MainActivity : ComponentActivity(), ApplicationComponentProvider { +class MainActivity : ComponentActivity() { /** * Lazily inject [JankStats], which is used to track jank throughout the app. */ - @Inject - lateinit var lazyStats: dagger.Lazy + private val lazyStats: JankStats = get() @Inject lateinit var networkMonitor: NetworkMonitor diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt similarity index 92% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt index 09f4597a7..5abd082ed 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt +++ b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt @@ -22,15 +22,12 @@ import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading import com.google.samples.apps.nowinandroid.MainActivityUiState.Success import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.model.data.UserData -import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import javax.inject.Inject -@HiltViewModel -class MainActivityViewModel @Inject constructor( +class MainActivityViewModel( userDataRepository: UserDataRepository, ) : ViewModel() { val uiState: StateFlow = userDataRepository.userData.map { diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt similarity index 50% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt index 8e3ad814a..0254a43a1 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt +++ b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/NiaApplication.kt @@ -17,30 +17,45 @@ package com.google.samples.apps.nowinandroid import android.app.Application -import coil.ImageLoader -import coil.ImageLoaderFactory -import com.google.samples.apps.nowinandroid.sync.initializers.Sync +import coil3.ImageLoader +import coil3.PlatformContext +import coil3.SingletonImageLoader +import coil3.request.crossfade +import com.google.samples.apps.nowinandroid.core.analytics.di.analyticsModule +import com.google.samples.apps.nowinandroid.core.di.CommonModule +import com.google.samples.apps.nowinandroid.core.di.commonModule import com.google.samples.apps.nowinandroid.util.ProfileVerifierLogger -import dagger.hilt.android.HiltAndroidApp -import javax.inject.Inject +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.core.context.startKoin +import org.koin.ksp.generated.module /** * [Application] class for NiA */ -@HiltAndroidApp -class NiaApplication : Application(), ImageLoaderFactory { - @Inject - lateinit var imageLoader: dagger.Lazy +class NiaApplication : Application(), SingletonImageLoader.Factory { - @Inject lateinit var profileVerifierLogger: ProfileVerifierLogger override fun onCreate() { super.onCreate() + startKoin { + androidLogger() + androidContext(this@NiaApplication) + val allModules = + modules( + CommonModule().module, + AnalyticModule().module, + ) + } // Initialize Sync; the system responsible for keeping data in the app up to date. - Sync.initialize(context = this) +// Sync.initialize(context = this) profileVerifierLogger() } - override fun newImageLoader(): ImageLoader = imageLoader.get() + override fun newImageLoader(context: PlatformContext): ImageLoader{ + return ImageLoader.Builder(context) + .crossfade(true) + .build() + } } diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/di/JankStatsModule.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/di/JankStatsModule.kt similarity index 85% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/di/JankStatsModule.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/di/JankStatsModule.kt index 56d1b6e24..c4c4786ca 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/di/JankStatsModule.kt +++ b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/di/JankStatsModule.kt @@ -21,15 +21,13 @@ import android.util.Log import android.view.Window import androidx.metrics.performance.JankStats import androidx.metrics.performance.JankStats.OnFrameListener -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityComponent +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Module +import org.koin.core.annotation.Single @Module -@InstallIn(ActivityComponent::class) -object JankStatsModule { - @Provides +class JankStatsModule { + @Factory fun providesOnFrameListener(): OnFrameListener = OnFrameListener { frameData -> // Make sure to only log janky frames. if (frameData.isJank) { @@ -38,10 +36,10 @@ object JankStatsModule { } } - @Provides + @Single fun providesWindow(activity: Activity): Window = activity.window - @Provides + @Factory fun providesJankStats( window: Window, frameListener: OnFrameListener, diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt similarity index 100% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt similarity index 77% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt index 815061273..1b8eafa24 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt +++ b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt @@ -16,17 +16,21 @@ package com.google.samples.apps.nowinandroid.navigation -import androidx.annotation.StringRes + import androidx.compose.ui.graphics.vector.ImageVector -import com.google.samples.apps.nowinandroid.R import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.BookmarksRoute import com.google.samples.apps.nowinandroid.feature.foryou.navigation.ForYouRoute import com.google.samples.apps.nowinandroid.feature.interests.navigation.InterestsRoute +import nowinandroid.app.generated.resources.Res +import nowinandroid.feature.bookmarks.generated.resources.feature_bookmarks_title +import nowinandroid.feature.foryou.generated.resources.feature_foryou_title +import nowinandroid.feature.search.generated.resources.feature_search_interests +import org.jetbrains.compose.resources.StringResource import kotlin.reflect.KClass -import com.google.samples.apps.nowinandroid.feature.bookmarks.R as bookmarksR -import com.google.samples.apps.nowinandroid.feature.foryou.R as forYouR -import com.google.samples.apps.nowinandroid.feature.search.R as searchR +import nowinandroid.feature.bookmarks.generated.resources.Res as bookmarksR +import nowinandroid.feature.foryou.generated.resources.Res as forYouR +import nowinandroid.feature.search.generated.resources.Res as searchR /** * Type for the top level destinations in the application. Each of these destinations @@ -36,15 +40,15 @@ import com.google.samples.apps.nowinandroid.feature.search.R as searchR enum class TopLevelDestination( val selectedIcon: ImageVector, val unselectedIcon: ImageVector, - @StringRes val iconTextId: Int, - @StringRes val titleTextId: Int, + val iconTextId: StringResource, + val titleTextId: StringResource, val route: KClass<*>, ) { FOR_YOU( selectedIcon = NiaIcons.Upcoming, unselectedIcon = NiaIcons.UpcomingBorder, iconTextId = forYouR.string.feature_foryou_title, - titleTextId = R.string.app_name, + titleTextId = Res.string.app_name, route = ForYouRoute::class, ), BOOKMARKS( diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt similarity index 100% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/ui/NiaApp.kt diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt similarity index 100% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt similarity index 90% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt index 3d37f3417..69916a113 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt +++ b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/Interests2PaneViewModel.kt @@ -20,14 +20,13 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.navigation.toRoute import com.google.samples.apps.nowinandroid.feature.interests.navigation.InterestsRoute -import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.StateFlow -import javax.inject.Inject +import org.koin.android.annotation.KoinViewModel const val TOPIC_ID_KEY = "selectedTopicId" -@HiltViewModel -class Interests2PaneViewModel @Inject constructor( +@KoinViewModel +class Interests2PaneViewModel( private val savedStateHandle: SavedStateHandle, ) : ViewModel() { diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt similarity index 98% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt index 669c6300a..34fc642c9 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt +++ b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreen.kt @@ -37,7 +37,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.NavHost @@ -50,6 +49,7 @@ import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicRoute import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen import kotlinx.serialization.Serializable +import org.koin.compose.viewmodel.koinViewModel import java.util.UUID @Serializable internal object TopicPlaceholderRoute @@ -66,7 +66,7 @@ fun NavGraphBuilder.interestsListDetailScreen() { @Composable internal fun InterestsListDetailScreen( - viewModel: Interests2PaneViewModel = hiltViewModel(), + viewModel: Interests2PaneViewModel = koinViewModel(), windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo(), ) { val selectedTopicId by viewModel.selectedTopicId.collectAsStateWithLifecycle() diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt similarity index 97% rename from app/src/main/kotlin/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt rename to app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt index 9ca4b3373..b4bdcd3ac 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt +++ b/app/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/util/ProfileVerifierLogger.kt @@ -22,7 +22,6 @@ import com.google.samples.apps.nowinandroid.core.di.ApplicationScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.guava.await import kotlinx.coroutines.launch -import javax.inject.Inject /** * Logs the app's Baseline Profile Compilation Status using [ProfileVerifier]. @@ -48,7 +47,7 @@ import javax.inject.Inject * * @see androidx.profileinstaller.ProfileVerifier.CompilationStatus.ResultCode */ -class ProfileVerifierLogger @Inject constructor( +class ProfileVerifierLogger( @ApplicationScope private val scope: CoroutineScope, ) { companion object { diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/androidMain/res/drawable/ic_launcher_background.xml similarity index 100% rename from app/src/main/res/drawable/ic_launcher_background.xml rename to app/src/androidMain/res/drawable/ic_launcher_background.xml diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/androidMain/res/drawable/ic_launcher_foreground.xml similarity index 100% rename from app/src/main/res/drawable/ic_launcher_foreground.xml rename to app/src/androidMain/res/drawable/ic_launcher_foreground.xml diff --git a/app/src/main/res/drawable/ic_splash.xml b/app/src/androidMain/res/drawable/ic_splash.xml similarity index 100% rename from app/src/main/res/drawable/ic_splash.xml rename to app/src/androidMain/res/drawable/ic_splash.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to app/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/androidMain/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher.png rename to app/src/androidMain/res/mipmap-hdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to app/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/androidMain/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher.png rename to app/src/androidMain/res/mipmap-mdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to app/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/androidMain/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to app/src/androidMain/res/mipmap-xhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to app/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to app/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to app/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to app/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/values-night/colors.xml b/app/src/androidMain/res/values-night/colors.xml similarity index 100% rename from app/src/main/res/values-night/colors.xml rename to app/src/androidMain/res/values-night/colors.xml diff --git a/app/src/main/res/values-night/themes.xml b/app/src/androidMain/res/values-night/themes.xml similarity index 100% rename from app/src/main/res/values-night/themes.xml rename to app/src/androidMain/res/values-night/themes.xml diff --git a/app/src/main/res/values/colors.xml b/app/src/androidMain/res/values/colors.xml similarity index 100% rename from app/src/main/res/values/colors.xml rename to app/src/androidMain/res/values/colors.xml diff --git a/app/src/main/res/values/strings.xml b/app/src/androidMain/res/values/strings.xml similarity index 100% rename from app/src/main/res/values/strings.xml rename to app/src/androidMain/res/values/strings.xml diff --git a/app/src/main/res/values/themes.xml b/app/src/androidMain/res/values/themes.xml similarity index 100% rename from app/src/main/res/values/themes.xml rename to app/src/androidMain/res/values/themes.xml diff --git a/app/src/benchmark/res/values/colors.xml b/app/src/benchmark/res/values/colors.xml deleted file mode 100644 index a98c6d8f6..000000000 --- a/app/src/benchmark/res/values/colors.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - #000000 - #FF006780 - diff --git a/app/src/benchmark/res/values-night/colors.xml b/app/src/commonMain/composeResources/values/strings.xml similarity index 63% rename from app/src/benchmark/res/values-night/colors.xml rename to app/src/commonMain/composeResources/values/strings.xml index cbf22c766..cd92f3977 100644 --- a/app/src/benchmark/res/values-night/colors.xml +++ b/app/src/commonMain/composeResources/values/strings.xml @@ -1,6 +1,6 @@ - - #FFFFFF - #FF006780 + Now in Android + ⚠️ You aren’t connected to the internet diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index 28614aa19..71ff6bbc0 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -129,6 +129,10 @@ gradlePlugin { id = "nowinandroid.cmp.feature" implementationClass = "CmpFeatureConventionPlugin" } + register("cmpApplication") { + id = "nowinandroid.cmp.application" + implementationClass = "CmpApplicationConventionPlugin" + } register("koin") { id = "nowinandroid.di.koin" implementationClass = "KoinConventionPlugin" diff --git a/build-logic/convention/src/main/kotlin/CmpApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/CmpApplicationConventionPlugin.kt new file mode 100644 index 000000000..62d44c1c3 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/CmpApplicationConventionPlugin.kt @@ -0,0 +1,127 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.api.dsl.ApplicationExtension +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.gradle.BaseExtension +import com.android.build.gradle.LibraryExtension +import com.google.samples.apps.nowinandroid.configureBadgingTasks +import com.google.samples.apps.nowinandroid.configureGradleManagedDevices +import com.google.samples.apps.nowinandroid.configureKotlinAndroid +import com.google.samples.apps.nowinandroid.configureKotlinMultiplatform +import com.google.samples.apps.nowinandroid.configurePrintApksTask +import com.google.samples.apps.nowinandroid.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.getByType +import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +// Convention plugin for the Compose Multiplatform feature module +class CmpApplicationConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply { + apply("com.android.application") + apply("org.jetbrains.kotlin.multiplatform") + apply("org.jetbrains.kotlin.plugin.compose") + apply("org.jetbrains.compose") +// apply("com.dropbox.dependency-guard") + } + configureComposeMultiplatformApp() + extensions.configure { + configureKotlinAndroid(this) + defaultConfig.targetSdk = 34 + @Suppress("UnstableApiUsage") + testOptions.animationsDisabled = true + configureGradleManagedDevices(this) + } + extensions.configure { + configurePrintApksTask(this) + configureBadgingTasks(extensions.getByType(), this) + } + + dependencies { + add("commonMainImplementation", project(":core:ui")) + add("commonMainImplementation", project(":core:designsystem")) + add("commonMainImplementation", libs.findLibrary("jetbrains.compose.viewmodel").get()) + add("commonMainImplementation", libs.findLibrary("jetbrains.compose.navigation").get()) + add("commonMainImplementation", libs.findLibrary("koin.compose").get()) + add("commonMainImplementation", libs.findLibrary("koin.compose.viewmodel").get()) + add("commonMainImplementation", libs.findLibrary("koin.compose.viewmodel.navigation").get()) + + add("androidMainImplementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) + add("androidMainImplementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) + add("androidMainImplementation", libs.findLibrary("androidx.tracing.ktx").get()) + } + } + } +} + +private fun Project.configureComposeMultiplatformApp() { + extensions.configure { + // Enable native group by default + // https://kotlinlang.org/docs/whatsnew1820.html#new-approach-to-source-set-hierarchy + applyDefaultHierarchyTemplate() + + // Configure JVM target for Android + androidTarget { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } + } + + // Add JVM target for desktop + jvm() + + // Configure iOS targets + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).forEach { iosTarget -> + iosTarget.binaries.framework { + baseName = "NowInAndroid" + isStatic = true + } + } + + // Other targets + macosX64() + macosArm64() + + // Suppress 'expect'/'actual' classes are in Beta. + targets.configureEach { + compilations.configureEach { + compilerOptions.configure { + freeCompilerArgs.addAll("-Xexpect-actual-classes") + } + } + } + + // Fixes Cannot locate tasks that match ':core:model:testClasses' as task 'testClasses' + // not found in project ':core:model'. Some candidates are: 'jsTestClasses', 'jvmTestClasses'. + project.tasks.create("testClasses") { + dependsOn("allTests") + } + } + +} diff --git a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt index bb8adccdd..9797df939 100644 --- a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt @@ -15,7 +15,6 @@ */ import com.android.build.gradle.LibraryExtension -import com.google.samples.apps.nowinandroid.configureFlavors import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configureKotlinMultiplatform @@ -34,7 +33,6 @@ class KmpLibraryConventionPlugin: Plugin { extensions.configure { configureKotlinAndroid(this) defaultConfig.targetSdk = 34 - configureFlavors(this) configureGradleManagedDevices(this) // The resource prefix is derived from the module name, // so resources inside ":core:module1" must be prefixed with "core_module1_" diff --git a/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt b/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt index 5d2c1b888..29bc3bf54 100644 --- a/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt @@ -14,26 +14,13 @@ * limitations under the License. */ -import com.android.build.api.variant.LibraryAndroidComponentsExtension -import com.android.build.gradle.LibraryExtension -import com.google.samples.apps.nowinandroid.configureFlavors -import com.google.samples.apps.nowinandroid.configureGradleManagedDevices -import com.google.samples.apps.nowinandroid.configureKotlinAndroid -import com.google.samples.apps.nowinandroid.configurePrintApksTask -import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.kotlin.dsl.configure -import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.kotlin -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension class SqlDelightConventionPlugin: Plugin { override fun apply(target: Project) { with(target) { pluginManager.apply("app.cash.sqldelight") - extensions.configure { - } } } } \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index 9e77a6f91..e440a5360 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -21,7 +21,6 @@ import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.konan.target.HostManager @@ -30,7 +29,6 @@ import org.jetbrains.kotlin.konan.target.HostManager * A plugin that applies the Kotlin Multiplatform plugin and configures it for the project. * https://github.com/cashapp/sqldelight/blob/master/buildLogic/multiplatform-convention/src/main/kotlin/app/cash/sqldelight/multiplatform/MultiplatformConventions.kt */ -@OptIn(ExperimentalWasmDsl::class) internal fun Project.configureKotlinMultiplatform() { extensions.configure { // Enable native group by default diff --git a/feature/bookmarks/build.gradle.kts b/feature/bookmarks/build.gradle.kts index a5d2e8ec5..cf7c68b79 100644 --- a/feature/bookmarks/build.gradle.kts +++ b/feature/bookmarks/build.gradle.kts @@ -46,3 +46,8 @@ kotlin { } } } + +compose.resources { + publicResClass = true + generateResClass = always +} diff --git a/feature/foryou/build.gradle.kts b/feature/foryou/build.gradle.kts index e35140a9d..ee305c5cd 100644 --- a/feature/foryou/build.gradle.kts +++ b/feature/foryou/build.gradle.kts @@ -56,3 +56,8 @@ kotlin { } } } + +compose.resources { + publicResClass = true + generateResClass = always +} diff --git a/feature/interests/build.gradle.kts b/feature/interests/build.gradle.kts index abb195c0d..66c6e6f53 100644 --- a/feature/interests/build.gradle.kts +++ b/feature/interests/build.gradle.kts @@ -51,3 +51,8 @@ kotlin { } } } + +compose.resources { + publicResClass = true + generateResClass = always +} diff --git a/feature/search/build.gradle.kts b/feature/search/build.gradle.kts index b81032e36..c91f4bfd8 100644 --- a/feature/search/build.gradle.kts +++ b/feature/search/build.gradle.kts @@ -53,4 +53,5 @@ kotlin { compose.resources { publicResClass = true + generateResClass = always } diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts index 6c01874b8..17c8e8dd2 100644 --- a/feature/settings/build.gradle.kts +++ b/feature/settings/build.gradle.kts @@ -49,6 +49,8 @@ kotlin { } } + compose.resources { publicResClass = true + generateResClass = always } diff --git a/feature/topic/build.gradle.kts b/feature/topic/build.gradle.kts index 493a3a02c..28dffb475 100644 --- a/feature/topic/build.gradle.kts +++ b/feature/topic/build.gradle.kts @@ -55,4 +55,5 @@ kotlin { compose.resources { publicResClass = true + generateResClass = always } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1c7fa18fa..df8a7cf9b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -97,6 +97,7 @@ androidx-compose-material3-windowSizeClass = { group = "androidx.compose.materia androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime" } androidx-compose-runtime-tracing = { group = "androidx.compose.runtime", name = "runtime-tracing", version.ref = "androidxComposeRuntimeTracing" } androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" } +androidx-compose-ui-test-android = { group = "androidx.compose.ui", name = "ui-test-junit4-android" } androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } @@ -162,6 +163,7 @@ kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.re kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-guava = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-guava", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } kotlinx-serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "kotlinxSerializationJson" } @@ -276,4 +278,5 @@ nowinandroid-kmp-library = { id = "nowinandroid.kmp.library" } nowinandroid-kotlin-inject = { id = "nowinandroid.kmp.inject" } nowinandroid-sqldelight = { id = "nowinandroid.sqldelight" } nowinandroid-cmp-feature = { id = "nowinandroid.cmp.feature" } +nowinandroid-cmp-application = { id = "nowinandroid.cmp.application" } nowinandroid-di-koin = { id = "nowinandroid.di.koin" }