From 15301ff3940d92eadd273c10706da9660320431c Mon Sep 17 00:00:00 2001 From: Ryan Conway Date: Fri, 19 May 2023 12:20:36 +0700 Subject: [PATCH 01/16] [#433] Add force argument --- scripts/new_project.kts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/new_project.kts b/scripts/new_project.kts index 9ae3837c3..20c687415 100644 --- a/scripts/new_project.kts +++ b/scripts/new_project.kts @@ -6,6 +6,7 @@ object NewProject { private const val DELIMITER_ARGUMENT = "=" private const val KEY_APP_NAME = "app-name" + private const val KEY_FORCE = "force" private const val KEY_HELP = "--help" private const val KEY_PACKAGE_NAME = "package-name" private const val KEY_TEMPLATE = "template" @@ -36,10 +37,11 @@ object NewProject { $KEY_PACKAGE_NAME= New package name (i.e., com.example.package) $KEY_APP_NAME= New app name (i.e., MyApp, "My App", "my-app") $KEY_TEMPLATE= Template (i.e., $TEMPLATE_XML, $TEMPLATE_COMPOSE) + $KEY_FORCE= Force project creation even if the script fails (default: false) Examples: kscript new_project.kts $KEY_PACKAGE_NAME=co.myxmlproject.example $KEY_APP_NAME="My XML Project" $KEY_TEMPLATE=$TEMPLATE_XML - kscript scripts/new_project.kts $KEY_PACKAGE_NAME=co.myxmlproject.example $KEY_APP_NAME="My XML Project" $KEY_TEMPLATE=$TEMPLATE_XML + kscript scripts/new_project.kts $KEY_PACKAGE_NAME=co.myxmlproject.example $KEY_APP_NAME="My XML Project" $KEY_TEMPLATE=$TEMPLATE_XML $KEY_FORCE=true """.trimIndent() private val modules = listOf("app", "data", "domain") @@ -58,6 +60,8 @@ object NewProject { } } + private var forceProjectCreation = false + private var packageName = "" private var projectFolderName: String = "" @@ -141,6 +145,10 @@ object NewProject { validateTemplate(value) hasTemplate = true } + arg.startsWith("$KEY_FORCE$DELIMITER_ARGUMENT") -> { + val (key, value) = arg.split(DELIMITER_ARGUMENT) + forceProjectCreation = value.toBoolean() + } else -> { showMessage( message = "ERROR: Invalid argument name: $arg \n$helpMessage", From fccb703b35579346f1029edec3b3bc32b3326e9f Mon Sep 17 00:00:00 2001 From: Ryan Conway Date: Fri, 19 May 2023 12:21:09 +0700 Subject: [PATCH 02/16] [#433] Delete generated project on error if forceProjectCreation is false --- scripts/new_project.kts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/scripts/new_project.kts b/scripts/new_project.kts index 20c687415..d449fb506 100644 --- a/scripts/new_project.kts +++ b/scripts/new_project.kts @@ -383,7 +383,23 @@ object NewProject { isError: Boolean = false, ) { println("\n${if (isError) "❌ " else ""}${message}\n") - if (exitAfterMessage) System.exit(exitValue) + if (exitAfterMessage) { + if (isError) { + exitWithError(exitValue) + } else { + System.exit(exitValue) + } + } + } + + private fun exitWithError(exitValue: Int = 0) { + if (!forceProjectCreation && projectFolderName.isNotBlank()) { + val file = File(projectPath) + if (file.exists()) { + file.deleteRecursively() + } + } + System.exit(exitValue) } private fun String.uppercaseEveryFirstCharacter(): String { From 2939d2e95f46d545edd3fcbf2ef4a8237d8d168d Mon Sep 17 00:00:00 2001 From: Ryan Conway Date: Fri, 19 May 2023 12:26:38 +0700 Subject: [PATCH 03/16] [#433] Update README with force argument --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 821923ab0..1da7506bb 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ A collection of our Android templates: package-name= New package name (i.e., com.example.package) app-name= New app name (i.e., MyApp, "My App", "my-app") template= Template (i.e., xml, compose) + force= Force project creation even if the script fails (default: false) ``` Examples: From d9859f3d8456dd3e44e75966d9a7056a08a40600 Mon Sep 17 00:00:00 2001 From: Luong Vo Date: Fri, 19 May 2023 15:19:54 +0700 Subject: [PATCH 04/16] [#464] Update Kover's exclusion config --- sample-compose/build.gradle.kts | 37 ++++++++----------------------- template-compose/build.gradle.kts | 37 ++++++++----------------------- 2 files changed, 18 insertions(+), 56 deletions(-) diff --git a/sample-compose/build.gradle.kts b/sample-compose/build.gradle.kts index 97eea8d7a..138a17135 100644 --- a/sample-compose/build.gradle.kts +++ b/sample-compose/build.gradle.kts @@ -67,45 +67,26 @@ detekt { koverMerged { enable() - val generatedFiles = setOf( - "*.R.class", - "*.R\$*.class", - "*.*\$ViewBinder*.*", - "*.*\$InjectAdapter*.*", - "*.*Injector*.*", + val excludedFiles = listOf( "*.BuildConfig.*", "*.BuildConfig", - "*.Manifest*.*", - "*.*_ViewBinding*.*", - "*.*Adapter*.*", - "*.*Test*.*", // Enum "*.*\$Creator*", - // Nav Component - "*.*_Factory*", - "*.*FragmentArgs*", - "*.*FragmentDirections*", - "*.FragmentNavArgsLazy.kt", - "*.*Fragment*navArgs*", - "*.*ModuleDeps*.*", - "*.*NavGraphDirections*", + // DI + "*.di.*", // Hilt "*.*_ComponentTreeDeps*", "*.*_HiltComponents*", "*.*_HiltModules*", "*.*_MembersInjector*", - "*.Hilt_*" - ) - - val excludedPackages = setOf( - "com.bumptech.glide.*", - "dagger.hilt.internal.*", - "hilt_aggregated_deps.*", - "co.nimblehq.sample.compose.databinding.*", - "co.nimblehq.sample.compose.di.*" + "*.*_Factory*", + "*.Hilt_*", + // Jetpack Compose + "*.ComposableSingletons*", + "*.*\$*Preview\$*", + "*.ui.preview.*", ) - val excludedFiles = generatedFiles + excludedPackages filters { classes { excludes += excludedFiles diff --git a/template-compose/build.gradle.kts b/template-compose/build.gradle.kts index c7c714ea4..c0411c3d9 100644 --- a/template-compose/build.gradle.kts +++ b/template-compose/build.gradle.kts @@ -65,45 +65,26 @@ tasks.withType().configureEach { koverMerged { enable() - val generatedFiles = setOf( - "*.R.class", - "*.R\$*.class", - "*.*\$ViewBinder*.*", - "*.*\$InjectAdapter*.*", - "*.*Injector*.*", + val excludedFiles = listOf( "*.BuildConfig.*", "*.BuildConfig", - "*.Manifest*.*", - "*.*_ViewBinding*.*", - "*.*Adapter*.*", - "*.*Test*.*", // Enum "*.*\$Creator*", - // Nav Component - "*.*_Factory*", - "*.*FragmentArgs*", - "*.*FragmentDirections*", - "*.FragmentNavArgsLazy.kt", - "*.*Fragment*navArgs*", - "*.*ModuleDeps*.*", - "*.*NavGraphDirections*", + // DI + "*.di.*", // Hilt "*.*_ComponentTreeDeps*", "*.*_HiltComponents*", "*.*_HiltModules*", "*.*_MembersInjector*", - "*.Hilt_*" - ) - - val excludedPackages = setOf( - "com.bumptech.glide.*", - "dagger.hilt.internal.*", - "hilt_aggregated_deps.*", - "co.nimblehq.template.compose.databinding.*", - "co.nimblehq.template.compose.di.*" + "*.*_Factory*", + "*.Hilt_*", + // Jetpack Compose + "*.ComposableSingletons*", + "*.*\$*Preview\$*", + "*.ui.preview.*", ) - val excludedFiles = generatedFiles + excludedPackages filters { classes { excludes += excludedFiles From 1e3c6a92271cd5ce76ff827a46b368ff77d11142 Mon Sep 17 00:00:00 2001 From: Luong Vo Date: Thu, 25 May 2023 11:04:13 +0700 Subject: [PATCH 05/16] [#464] Revert missing exclusion for Hilt --- sample-compose/build.gradle.kts | 2 ++ template-compose/build.gradle.kts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sample-compose/build.gradle.kts b/sample-compose/build.gradle.kts index 138a17135..267fe3af3 100644 --- a/sample-compose/build.gradle.kts +++ b/sample-compose/build.gradle.kts @@ -81,6 +81,8 @@ koverMerged { "*.*_MembersInjector*", "*.*_Factory*", "*.Hilt_*", + "dagger.hilt.internal.*", + "hilt_aggregated_deps.*", // Jetpack Compose "*.ComposableSingletons*", "*.*\$*Preview\$*", diff --git a/template-compose/build.gradle.kts b/template-compose/build.gradle.kts index c0411c3d9..c2d513a97 100644 --- a/template-compose/build.gradle.kts +++ b/template-compose/build.gradle.kts @@ -79,6 +79,8 @@ koverMerged { "*.*_MembersInjector*", "*.*_Factory*", "*.Hilt_*", + "dagger.hilt.internal.*", + "hilt_aggregated_deps.*", // Jetpack Compose "*.ComposableSingletons*", "*.*\$*Preview\$*", From fd2b5db4c4281f13198692860b4880951b2c17e5 Mon Sep 17 00:00:00 2001 From: ryan-conway Date: Thu, 1 Jun 2023 04:02:52 +0000 Subject: [PATCH 06/16] [Chore] Bump version to 3.21.0 --- sample-compose/buildSrc/src/main/java/Versions.kt | 2 +- sample-xml/buildSrc/src/main/java/Versions.kt | 2 +- version.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sample-compose/buildSrc/src/main/java/Versions.kt b/sample-compose/buildSrc/src/main/java/Versions.kt index 7ef68e4d6..f8c2444c3 100644 --- a/sample-compose/buildSrc/src/main/java/Versions.kt +++ b/sample-compose/buildSrc/src/main/java/Versions.kt @@ -6,7 +6,7 @@ object Versions { const val ANDROID_TARGET_SDK_VERSION = 33 const val ANDROID_VERSION_CODE = 1 - const val ANDROID_VERSION_NAME = "3.20.0" + const val ANDROID_VERSION_NAME = "3.21.0" // Dependencies (Alphabet sorted) const val ACCOMPANIST_PERMISSIONS_VERSION = "0.30.1" diff --git a/sample-xml/buildSrc/src/main/java/Versions.kt b/sample-xml/buildSrc/src/main/java/Versions.kt index c13692e9d..8271e0940 100644 --- a/sample-xml/buildSrc/src/main/java/Versions.kt +++ b/sample-xml/buildSrc/src/main/java/Versions.kt @@ -6,7 +6,7 @@ object Versions { const val ANDROID_TARGET_SDK_VERSION = 33 const val ANDROID_VERSION_CODE = 1 - const val ANDROID_VERSION_NAME = "3.20.0" + const val ANDROID_VERSION_NAME = "3.21.0" // Dependencies (Alphabet sorted) const val ANDROID_COMMON_KTX_VERSION = "0.1.1" diff --git a/version.properties b/version.properties index 230cb574e..052c20892 100644 --- a/version.properties +++ b/version.properties @@ -1,3 +1,3 @@ kotlinVersion=1.6.21 kscriptVersion=4.0.3 -templateScriptVersion=3.20.0 +templateScriptVersion=3.21.0 From 6309094949b273e9cc5c9900b3aeb5a773326253 Mon Sep 17 00:00:00 2001 From: hoangnguyen92dn Date: Mon, 5 Jun 2023 15:25:08 +0700 Subject: [PATCH 07/16] [#360] Update config files and README to guide distribute app by using Service Account --- .cicdtemplate/.github/README.md | 2 +- ...taging_and_production_to_firebase_app_distribution.yml | 4 ++-- RxJavaTemplate[DEPRECATED]/fastlane/script/config.rb | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.cicdtemplate/.github/README.md b/.cicdtemplate/.github/README.md index 71d509d88..56f71406e 100644 --- a/.cicdtemplate/.github/README.md +++ b/.cicdtemplate/.github/README.md @@ -6,4 +6,4 @@ The `.github` contains all the required setup for Github Action to trigger. Simp Here are the current Secrets we need to add to the Project Settings - Secret: - `FIREBASE_APP_ID_STAGING` - your application id on Firebase. -- `FIREBASE_TOKEN` - your Firebase access/refresh token. Follow this [Guideline](https://firebase.google.com/docs/cli#cli-ci-systems) to get one for your project. \ No newline at end of file +- `FIREBASE_SERVICE_ACCOUNT_CREDENTIAL_FILE_CONTENT` - your Firebase service account credential file. Follow this [Guideline](https://github.com/wzieba/Firebase-Distribution-Github-Action/wiki/FIREBASE_TOKEN-migration#guide-2---the-same-but-with-screenshots) to get one for your project. \ No newline at end of file diff --git a/.cicdtemplate/.github/workflows/deploy_staging_and_production_to_firebase_app_distribution.yml b/.cicdtemplate/.github/workflows/deploy_staging_and_production_to_firebase_app_distribution.yml index b4a3c15ae..f684c2826 100644 --- a/.cicdtemplate/.github/workflows/deploy_staging_and_production_to_firebase_app_distribution.yml +++ b/.cicdtemplate/.github/workflows/deploy_staging_and_production_to_firebase_app_distribution.yml @@ -63,7 +63,7 @@ jobs: uses: wzieba/Firebase-Distribution-Github-Action@v1 with: appId: ${{secrets.FIREBASE_APP_ID_STAGING}} - token: ${{secrets.FIREBASE_TOKEN}} + serviceCredentialsFileContent: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_CREDENTIAL_FILE_CONTENT }} groups: testers file: app/build/outputs/apk/staging/debug/app-staging-debug.apk @@ -74,6 +74,6 @@ jobs: uses: wzieba/Firebase-Distribution-Github-Action@v1 with: appId: ${{secrets.FIREBASE_APP_ID_PRODUCTION}} - token: ${{secrets.FIREBASE_TOKEN}} + serviceCredentialsFileContent: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_CREDENTIAL_FILE_CONTENT }} groups: testers file: app/build/outputs/apk/production/debug/app-production-debug.apk diff --git a/RxJavaTemplate[DEPRECATED]/fastlane/script/config.rb b/RxJavaTemplate[DEPRECATED]/fastlane/script/config.rb index 3f6281b69..b1141ec36 100644 --- a/RxJavaTemplate[DEPRECATED]/fastlane/script/config.rb +++ b/RxJavaTemplate[DEPRECATED]/fastlane/script/config.rb @@ -39,7 +39,7 @@ def sanity_check errors = [] errors << verify_slack_config unless verify_slack_config.nil? errors << verify_workspace_config unless verify_workspace_config.nil? - errors << verify_firebase_token unless verify_firebase_token.nil? + errors << verify_service_account unless verify_service_account.nil? throw errors unless errors.empty? end @@ -55,9 +55,9 @@ def verify_workspace_config 'Missing env.WORKSPACE, please set it accordingly and retry' end - def verify_firebase_token - return unless ENV['FIREBASE_TOKEN'].nil? + def verify_service_account + return unless ENV['FIREBASE_SERVICE_ACCOUNT_CREDENTIAL_FILE_CONTENT'].nil? - 'Missing env.FIREBASE_TOKEN for Firebase, please set it accordingly and retry' + 'Missing env.FIREBASE_SERVICE_ACCOUNT_CREDENTIAL_FILE_CONTENT for Firebase App Distribution, please set it accordingly and retry' end end From 2e19dbd1b71f99f9ce67055f3129146a41bcab49 Mon Sep 17 00:00:00 2001 From: Wadeewee Date: Thu, 15 Jun 2023 14:06:16 +0700 Subject: [PATCH 08/16] [#428] Add modifier parameter --- .../java/co/nimblehq/sample/compose/ui/screens/AppBar.kt | 7 ++++++- .../co/nimblehq/sample/compose/ui/screens/home/Item.kt | 5 +++-- .../co/nimblehq/sample/compose/ui/screens/home/ItemList.kt | 6 ++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/AppBar.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/AppBar.kt index 880809bd0..ae9289870 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/AppBar.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/AppBar.kt @@ -4,6 +4,7 @@ import androidx.annotation.StringRes import androidx.compose.material.Text import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import co.nimblehq.sample.compose.R @@ -11,8 +12,12 @@ import co.nimblehq.sample.compose.ui.theme.AppTheme.colors import co.nimblehq.sample.compose.ui.theme.ComposeTheme @Composable -fun AppBar(@StringRes title: Int) { +fun AppBar( + @StringRes title: Int, + modifier: Modifier = Modifier, +) { TopAppBar( + modifier = modifier, title = { Text(text = stringResource(title)) }, backgroundColor = colors.topAppBarBackground ) diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/Item.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/Item.kt index 681169755..91fabd676 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/Item.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/Item.kt @@ -17,12 +17,13 @@ import co.nimblehq.sample.compose.ui.theme.AppTheme.dimensions fun Item( uiModel: UiModel, onClick: (UiModel) -> Unit, - onLongClick: (UiModel) -> Unit + onLongClick: (UiModel) -> Unit, + modifier: Modifier = Modifier, ) { var expanded by remember { mutableStateOf(false) } Box( - modifier = Modifier + modifier = modifier .fillMaxWidth() .combinedClickable( onClick = { onClick(uiModel) }, diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/ItemList.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/ItemList.kt index 9ed8b0374..2323ecee9 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/ItemList.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/ItemList.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.Divider import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import co.nimblehq.sample.compose.model.UiModel import co.nimblehq.sample.compose.ui.theme.ComposeTheme @@ -12,9 +13,10 @@ import co.nimblehq.sample.compose.ui.theme.ComposeTheme fun ItemList( uiModels: List, onItemClick: (UiModel) -> Unit, - onItemLongClick: (UiModel) -> Unit + onItemLongClick: (UiModel) -> Unit, + modifier: Modifier = Modifier, ) { - LazyColumn { + LazyColumn(modifier) { items(uiModels) { uiModel -> Item( uiModel = uiModel, From 487ef5a68bd17d2af492d2e0d72d986bc95bbe10 Mon Sep 17 00:00:00 2001 From: Avishek Khan Date: Tue, 20 Jun 2023 11:42:08 +0700 Subject: [PATCH 09/16] [#436] Add an argument to specify output folder location to new_project.kts script --- scripts/new_project.kts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/scripts/new_project.kts b/scripts/new_project.kts index d449fb506..299496412 100644 --- a/scripts/new_project.kts +++ b/scripts/new_project.kts @@ -6,6 +6,7 @@ object NewProject { private const val DELIMITER_ARGUMENT = "=" private const val KEY_APP_NAME = "app-name" + private const val KEY_DESTINATION = "destination" private const val KEY_FORCE = "force" private const val KEY_HELP = "--help" private const val KEY_PACKAGE_NAME = "package-name" @@ -38,10 +39,12 @@ object NewProject { $KEY_APP_NAME= New app name (i.e., MyApp, "My App", "my-app") $KEY_TEMPLATE= Template (i.e., $TEMPLATE_XML, $TEMPLATE_COMPOSE) $KEY_FORCE= Force project creation even if the script fails (default: false) + $KEY_DESTINATION= Set the output location where the project should be generated (i.e., /Users/johndoe/documents/projectfolder) Examples: kscript new_project.kts $KEY_PACKAGE_NAME=co.myxmlproject.example $KEY_APP_NAME="My XML Project" $KEY_TEMPLATE=$TEMPLATE_XML kscript scripts/new_project.kts $KEY_PACKAGE_NAME=co.myxmlproject.example $KEY_APP_NAME="My XML Project" $KEY_TEMPLATE=$TEMPLATE_XML $KEY_FORCE=true + kscript scripts/new_project.kts $KEY_PACKAGE_NAME=co.myxmlproject.example $KEY_APP_NAME="My XML Project" $KEY_TEMPLATE=$TEMPLATE_XML $KEY_FORCE=true $KEY_DESTINATION=/Users/johndoe/documents/projectfolder """.trimIndent() private val modules = listOf("app", "data", "domain") @@ -64,10 +67,12 @@ object NewProject { private var packageName = "" + private var destination = rootPath + private var projectFolderName: String = "" private val projectPath: String - get() = rootPath + projectFolderName + get() = destination + projectFolderName private val rootPath: String get() = System.getProperty("user.dir").let { userDir -> @@ -149,6 +154,10 @@ object NewProject { val (key, value) = arg.split(DELIMITER_ARGUMENT) forceProjectCreation = value.toBoolean() } + arg.startsWith("$KEY_DESTINATION$DELIMITER_ARGUMENT") -> { + val (key, value) = arg.split(DELIMITER_ARGUMENT) + validateDestination(value) + } else -> { showMessage( message = "ERROR: Invalid argument name: $arg \n$helpMessage", @@ -213,6 +222,18 @@ object NewProject { } } + private fun validateDestination(value: String) { + if (value.isNotBlank()) { + destination = "${value.trim()}$SEPARATOR_SLASH" + } else { + showMessage( + message = "Error: Invalid Destination: destination cannot be blank \n$helpMessage", + exitAfterMessage = true, + isError = true, + ) + } + } + private fun initializeNewProjectFolder() { showMessage("=> 🐢 Initializing new project...") copyFiles(fromPath = rootPath + templateFolderName, toPath = projectPath) From 37d9b921e511109bc2380082dc31f0d67e17bb23 Mon Sep 17 00:00:00 2001 From: Avishek Khan Date: Wed, 21 Jun 2023 16:32:32 +0700 Subject: [PATCH 10/16] [#419] [Bug] Prevent the toast for first time app launch from reappearing after navigating back from the second screen --- .../sample/compose/ui/screens/home/HomeScreen.kt | 1 + .../compose/ui/screens/home/HomeViewModel.kt | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt index 2858d05b3..71d61996c 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt @@ -39,6 +39,7 @@ fun HomeScreen( LaunchedEffect(isFirstTimeLaunch) { if (isFirstTimeLaunch) { context.showToast("This is the first time launch") + viewModel.onFirstTimeLaunch() } } diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt index 41174f1f2..f07d5f258 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt @@ -15,10 +15,10 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( - dispatchersProvider: DispatchersProvider, getModelsUseCase: GetModelsUseCase, isFirstTimeLaunchPreferencesUseCase: IsFirstTimeLaunchPreferencesUseCase, - updateFirstTimeLaunchPreferencesUseCase: UpdateFirstTimeLaunchPreferencesUseCase, + private val updateFirstTimeLaunchPreferencesUseCase: UpdateFirstTimeLaunchPreferencesUseCase, + private val dispatchersProvider: DispatchersProvider, ) : BaseViewModel() { private val _uiModels = MutableStateFlow>(emptyList()) @@ -44,9 +44,13 @@ class HomeViewModel @Inject constructor( .first() _isFirstTimeLaunch.emit(isFirstTimeLaunch) - if (isFirstTimeLaunch) { - updateFirstTimeLaunchPreferencesUseCase(false) - } + } + } + + fun onFirstTimeLaunch() { + launch(dispatchersProvider.io) { + updateFirstTimeLaunchPreferencesUseCase(false) + _isFirstTimeLaunch.emit(false) } } From 359e391e4f147295bda0ecff33053c757714bcf0 Mon Sep 17 00:00:00 2001 From: Avishek Khan Date: Wed, 21 Jun 2023 17:45:11 +0700 Subject: [PATCH 11/16] [#419] [Bug] Update tests --- .../compose/ui/screens/home/HomeScreenTest.kt | 4 ++-- .../compose/ui/screens/home/HomeScreenTest.kt | 4 ++-- .../ui/screens/home/HomeViewModelTest.kt | 22 ++++++++++++++----- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/sample-compose/app/src/androidTest/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt b/sample-compose/app/src/androidTest/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt index a5320cd92..a5ce5d6ea 100644 --- a/sample-compose/app/src/androidTest/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt +++ b/sample-compose/app/src/androidTest/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt @@ -46,10 +46,10 @@ class HomeScreenTest { every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(false) viewModel = HomeViewModel( - TestDispatchersProvider, mockGetModelsUseCase, mockIsFirstTimeLaunchPreferencesUseCase, - mockUpdateFirstTimeLaunchPreferencesUseCase + mockUpdateFirstTimeLaunchPreferencesUseCase, + TestDispatchersProvider ) } diff --git a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt index 2b508ba2b..78301ae67 100644 --- a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt +++ b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt @@ -106,10 +106,10 @@ class HomeScreenTest : BaseScreenTest() { private fun initViewModel() { viewModel = HomeViewModel( - coroutinesRule.testDispatcherProvider, mockGetModelsUseCase, mockIsFirstTimeLaunchPreferencesUseCase, - mockUpdateFirstTimeLaunchPreferencesUseCase + mockUpdateFirstTimeLaunchPreferencesUseCase, + coroutinesRule.testDispatcherProvider ) } } diff --git a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt index 117acc397..e9961a119 100644 --- a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt +++ b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt @@ -8,11 +8,9 @@ import co.nimblehq.sample.compose.test.CoroutineTestRule import co.nimblehq.sample.compose.ui.AppDestination import co.nimblehq.sample.compose.util.DispatchersProvider import io.kotest.matchers.shouldBe -import io.mockk.every -import io.mockk.mockk +import io.mockk.* import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.* import kotlinx.coroutines.test.* import org.junit.* @@ -34,6 +32,7 @@ class HomeViewModelTest { fun setUp() { every { mockGetModelsUseCase() } returns flowOf(models) every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(false) + coEvery { mockUpdateFirstTimeLaunchPreferencesUseCase(any()) } just Runs initViewModel() } @@ -78,12 +77,23 @@ class HomeViewModelTest { } } + @Test + fun `When launching the app for the first time, it executes the use case and emits value accordingly`() = + runTest { + viewModel.onFirstTimeLaunch() + + coVerify(exactly = 1) { + mockUpdateFirstTimeLaunchPreferencesUseCase(false) + } + viewModel.isFirstTimeLaunch.first() shouldBe false + } + private fun initViewModel(dispatchers: DispatchersProvider = coroutinesRule.testDispatcherProvider) { viewModel = HomeViewModel( - dispatchers, mockGetModelsUseCase, mockIsFirstTimeLaunchPreferencesUseCase, - mockUpdateFirstTimeLaunchPreferencesUseCase + mockUpdateFirstTimeLaunchPreferencesUseCase, + dispatchers ) } } From d8c44c06ca24799ca04afcc1b59913dd1c4725d9 Mon Sep 17 00:00:00 2001 From: Avishek Khan Date: Thu, 22 Jun 2023 16:31:17 +0700 Subject: [PATCH 12/16] [#419] [Bug] Refactor flow collection and add test for the first time launch when the VM is initialized --- .../compose/ui/screens/home/HomeViewModel.kt | 14 +++++++------- .../compose/ui/screens/home/HomeViewModelTest.kt | 9 ++++++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt index f07d5f258..889254ecc 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModel.kt @@ -38,13 +38,13 @@ class HomeViewModel @Inject constructor( .catch { e -> _error.emit(e) } .launchIn(viewModelScope) - launch(dispatchersProvider.io) { - val isFirstTimeLaunch = isFirstTimeLaunchPreferencesUseCase() - .catch { e -> _error.emit(e) } - .first() - - _isFirstTimeLaunch.emit(isFirstTimeLaunch) - } + isFirstTimeLaunchPreferencesUseCase() + .onEach { isFirstTimeLaunch -> + _isFirstTimeLaunch.emit(isFirstTimeLaunch) + } + .flowOn(dispatchersProvider.io) + .catch { e -> _error.emit(e) } + .launchIn(viewModelScope) } fun onFirstTimeLaunch() { diff --git a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt index e9961a119..cb204971d 100644 --- a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt +++ b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt @@ -27,11 +27,12 @@ class HomeViewModelTest { private lateinit var viewModel: HomeViewModel private val models = listOf(Model(1), Model(2), Model(3)) + private val isFirstTimeLaunch = false @Before fun setUp() { every { mockGetModelsUseCase() } returns flowOf(models) - every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(false) + every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(isFirstTimeLaunch) coEvery { mockUpdateFirstTimeLaunchPreferencesUseCase(any()) } just Runs initViewModel() @@ -77,6 +78,12 @@ class HomeViewModelTest { } } + @Test + fun `When initializing the ViewModel, it emits whether the app is launched for the first time accordingly`() = + runTest { + viewModel.isFirstTimeLaunch.first() shouldBe isFirstTimeLaunch + } + @Test fun `When launching the app for the first time, it executes the use case and emits value accordingly`() = runTest { From 05f52fe0d8deeb3b13376880f4401623907e2f72 Mon Sep 17 00:00:00 2001 From: Avishek Khan Date: Thu, 22 Jun 2023 16:39:42 +0700 Subject: [PATCH 13/16] [#436] Update README to include the example for the destination argument --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1da7506bb..fbb382d14 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,13 @@ A collection of our Android templates: app-name= New app name (i.e., MyApp, "My App", "my-app") template= Template (i.e., xml, compose) force= Force project creation even if the script fails (default: false) + destination= Set the output location where the project should be generated (i.e., /Users/johndoe/documents/projectfolder) ``` Examples: `kscript new_project.kts package-name=co.myxmlproject.example app-name="My XML Project" template=xml` `kscript scripts/new_project.kts package-name=co.myxmlproject.example app-name="My XML Project" template=xml` + `kscript new_project.kts package-name=co.nimblehq.suvkmmsurveys.android app-name=Surveys template=compose destination=/Users/johndoe/documents/projectfolder` 4. Update `android_version_code` and `android_version_name` in `template/build.gradle` From 29ccca967a3952f212823388c49c57053600e98a Mon Sep 17 00:00:00 2001 From: Avishek Khan Date: Thu, 22 Jun 2023 16:46:20 +0700 Subject: [PATCH 14/16] [#419] [Bug] Add test case for error when executing isFirstTimeLaunchPreferencesUseCase --- .../compose/ui/screens/home/HomeViewModelTest.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt index cb204971d..91cea39b6 100644 --- a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt +++ b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeViewModelTest.kt @@ -84,6 +84,21 @@ class HomeViewModelTest { viewModel.isFirstTimeLaunch.first() shouldBe isFirstTimeLaunch } + @Test + fun `When initializing the ViewModel and isFirstTimeLaunchPreferencesUseCase returns error, it shows the corresponding error`() = + runTest { + val error = Exception() + every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flow { throw error } + + initViewModel(dispatchers = CoroutineTestRule(StandardTestDispatcher()).testDispatcherProvider) + + viewModel.error.test { + advanceUntilIdle() + + expectMostRecentItem() shouldBe error + } + } + @Test fun `When launching the app for the first time, it executes the use case and emits value accordingly`() = runTest { From 758969883bcc40bf20322ab7a5aa0ba8ec4cc9bc Mon Sep 17 00:00:00 2001 From: Avishek Khan Date: Fri, 23 Jun 2023 10:56:06 +0700 Subject: [PATCH 15/16] [#419] [Bug] Add UI tests --- .../compose/ui/screens/home/HomeScreen.kt | 2 +- .../app/src/main/res/values/strings.xml | 2 ++ .../compose/ui/screens/home/HomeScreenTest.kt | 26 +++++++++++++++++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt index 71d61996c..b2b521d73 100644 --- a/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt +++ b/sample-compose/app/src/main/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreen.kt @@ -38,7 +38,7 @@ fun HomeScreen( LaunchedEffect(isFirstTimeLaunch) { if (isFirstTimeLaunch) { - context.showToast("This is the first time launch") + context.showToast(context.getString(R.string.message_first_time_launch)) viewModel.onFirstTimeLaunch() } } diff --git a/sample-compose/app/src/main/res/values/strings.xml b/sample-compose/app/src/main/res/values/strings.xml index 1f735069e..06743934e 100644 --- a/sample-compose/app/src/main/res/values/strings.xml +++ b/sample-compose/app/src/main/res/values/strings.xml @@ -11,4 +11,6 @@ Third Data: %s Edit + + This is the first time launch diff --git a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt index 78301ae67..cb67695aa 100644 --- a/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt +++ b/sample-compose/app/src/test/java/co/nimblehq/sample/compose/ui/screens/home/HomeScreenTest.kt @@ -14,8 +14,7 @@ import co.nimblehq.sample.compose.ui.screens.BaseScreenTest import co.nimblehq.sample.compose.ui.screens.MainActivity import co.nimblehq.sample.compose.ui.theme.ComposeTheme import io.kotest.matchers.shouldBe -import io.mockk.every -import io.mockk.mockk +import io.mockk.* import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.* @@ -54,6 +53,29 @@ class HomeScreenTest : BaseScreenTest() { listOf(Model(1), Model(2), Model(3)) ) every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(false) + coEvery { mockUpdateFirstTimeLaunchPreferencesUseCase(any()) } just Runs + } + + @Test + fun `When entering the Home screen for the first time, it shows a toast confirming that`() { + every { mockIsFirstTimeLaunchPreferencesUseCase() } returns flowOf(true) + + initComposable { + composeRule.waitForIdle() + advanceUntilIdle() + + ShadowToast.showedToast(activity.getString(R.string.message_first_time_launch)) shouldBe true + } + } + + @Test + fun `When entering the Home screen NOT for the first time, it doesn't show the toast confirming that`() { + initComposable { + composeRule.waitForIdle() + advanceUntilIdle() + + ShadowToast.showedToast(activity.getString(R.string.message_first_time_launch)) shouldBe false + } } @Test From 1eeb2103166dbe93e9f25f2013b5c34fbe6a5e1a Mon Sep 17 00:00:00 2001 From: Avishek Khan Date: Thu, 29 Jun 2023 15:48:08 +0700 Subject: [PATCH 16/16] [#436] Update the example command in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fbb382d14..40a882607 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ A collection of our Android templates: Examples: `kscript new_project.kts package-name=co.myxmlproject.example app-name="My XML Project" template=xml` `kscript scripts/new_project.kts package-name=co.myxmlproject.example app-name="My XML Project" template=xml` - `kscript new_project.kts package-name=co.nimblehq.suvkmmsurveys.android app-name=Surveys template=compose destination=/Users/johndoe/documents/projectfolder` + `kscript new_project.kts package-name=co.myxmlproject.example app-name="My XML Project" template=xml destination=/Users/johndoe/documents/projectfolder` 4. Update `android_version_code` and `android_version_name` in `template/build.gradle`