diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index faef845..72d7cb7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,3 +1,3 @@ ## Why is this important? -## Any review notes? +## Notes diff --git a/.github/workflows/_prepare-all.yml b/.github/workflows/_prepare-all.yml new file mode 100644 index 0000000..e7d6f8f --- /dev/null +++ b/.github/workflows/_prepare-all.yml @@ -0,0 +1,19 @@ +name: Prepare All + +on: + workflow_call: + +jobs: + prepare-all: + runs-on: macos-latest # use [ self-hosted, macOS ] to host on our own mac mini, which is twice as fast and cheaper than hosting on a github runner. See README for more info. + steps: + - uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: "corretto" + java-version: "21" # keep the same as your Android Studio version + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 diff --git a/.github/workflows/_prepare-release.yml b/.github/workflows/_prepare-release.yml new file mode 100644 index 0000000..991ef8f --- /dev/null +++ b/.github/workflows/_prepare-release.yml @@ -0,0 +1,16 @@ +name: Prepare Release + +on: + workflow_call: + +jobs: + prepareRelease: + runs-on: macos-latest # use [ self-hosted, macOS ] to host on our own mac mini, which is twice as fast and cheaper than hosting on a github runner. See README for more info. + steps: + - uses: actions/checkout@v4 + + - name: Update Version Code # we increase the version code with each build + uses: chkfung/android-version-actions@v1.2.3 + with: + gradlePath: app/build.gradle + versionCode: ${{ github.run_number }} diff --git a/.github/workflows/debug.yml b/.github/workflows/debug.yml new file mode 100644 index 0000000..83fa60c --- /dev/null +++ b/.github/workflows/debug.yml @@ -0,0 +1,34 @@ +name: Debug Builds + +on: [ push ] # run on all pushes on any branch + +jobs: + prepare-all: + uses: ./.github/workflows/_prepare-all.yml + build: + runs-on: macos-latest # use [ self-hosted, macOS ] to host on our own mac mini, which is twice as fast and cheaper than hosting on a github runner. See README for more info. + steps: + - uses: actions/checkout@v4 + + - name: Build Debug APK + run: ./gradlew assembleDevDebug --stacktrace + + - name: Run unit tests + run: ./gradlew test --stacktrace + + - name: Upload Dev Debug APK to Artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.repository.name }}-${{ github.run_number }}-dev-debug-apk + path: | + ${{ github.workspace }}/app/build/outputs/apk/dev/debug/app-dev-debug.apk + + - name: Build Prod APK + run: ./gradlew assembleProdDebug --stacktrace + + - name: Upload Prod Debug APK to Artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.repository.name }}_${{ github.run_number }}-prod-debug-apk + path: | + ${{ github.workspace }}/app/build/outputs/apk/prod/debug/app-prod-debug.apk diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml deleted file mode 100644 index 8c49833..0000000 --- a/.github/workflows/pr.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Unit-Test Pull Request - -on: - workflow_dispatch: - pull_request: - -jobs: - build-dev-pr: - runs-on: ubuntu-latest - permissions: - packages: read - contents: read - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 - with: - distribution: "corretto" # See 'Supported distributions' for available options - java-version: "21" - cache: "gradle" - - name: Update version code - uses: chkfung/android-version-actions@v1.2.1 - with: - gradlePath: app/build.gradle - versionCode: ${{ github.run_number }} - - name: Set up ruby env - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.7.2 - bundler-cache: true - - name: Clean - run: bundle exec fastlane android clean - - name: Run tests - run: bundle exec fastlane android test - - name: Build Debug APK - run: bundle exec fastlane android assembleDevDebug - - name: Build Release APK - run: bundle exec fastlane android assembleDevRelease - - name: Upload APKs To Github - uses: actions/upload-artifact@v3 - with: - name: template_android_${{ github.run_number }}_apks - path: | - ${{ github.workspace }}/app/build/outputs/apk/ diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..666a663 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,61 @@ +name: Release Builds + +on: + workflow_dispatch: + pull_request: + branches: [ 'develop' ] + +jobs: + prepare-all: + uses: ./.github/workflows/_prepare-all.yml + prepare-release: + uses: ./.github/workflows/_prepare-release.yml + build: + runs-on: macos-latest # use [ self-hosted, macOS ] to host on our own mac mini, which is twice as fast and cheaper than hosting on a github runner. See README for more info. + steps: + - uses: actions/checkout@v4 + + # This will decode the keystore from base 64 text representation that we have stored in secrets + # and generates and keystore file and gets stored in /android-app path + - name: Decode Keystore + env: + ENCODED_STRING: ${{ secrets.KEYSTORE_BASE_64 }} + shell: bash + run: | + echo $ENCODED_STRING > keystore-b64.txt + base64 -d upload-keystore.jks + + - name: Build Prod Release APK + env: + RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }} + RELEASE_KEYSTORE_ALIAS: ${{ secrets.RELEASE_KEYSTORE_ALIAS }} + RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }} + run: ./gradlew assembleProdRelease --stacktrace + + - name: Upload Release APK to Artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.repository.name }}-${{ github.run_number }}-prod-release-apk + path: | + ${{ github.workspace }}/app/build/outputs/apk/prod/release/app-prod-release.apk + + - name: Upload Prod Release APK to Firebase App Distribution + uses: nickwph/firebase-app-distribution-action@v1 + with: + file: ${{ github.workspace }}/app/build/outputs/apk/prod/release/app-prod-release.apk + app: ${{ secrets.FIREBASE_PROD_APP_ID }} + credentials: ${{ secrets.FIREBASE_CREDENTIALS }} + + - name: Build Prod Release Bundle + env: + RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }} + RELEASE_KEYSTORE_ALIAS: ${{ secrets.RELEASE_KEYSTORE_ALIAS }} + RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }} + run: ./gradlew bundleProdRelease --stacktrace + + - name: Upload Release Bundle to Artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.repository.name }}-${{ github.run_number }}-prod-release-bundle + path: | + ${{ github.workspace }}/app/build/outputs/bundle/prodRelease/app-prod-release.aab diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c98467b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,40 @@ +name: Release Builds + +on: + workflow_dispatch: + pull_request: + branches: [ '*' ] # run on all pull requests + +jobs: + prepare-all: + uses: ./.github/workflows/_prepare-all.yml + prepare-release: + uses: ./.github/workflows/_prepare-release.yml + build: + runs-on: macos-latest # use [ self-hosted, macOS ] to host on our own mac mini, which is twice as fast and cheaper than hosting on a github runner. See README for more info. + steps: + - uses: actions/checkout@v4 + + # This will decode the keystore from base 64 text representation that we have stored in secrets + # and generates and keystore file and gets stored in /android-app path + - name: Decode Keystore + env: + ENCODED_STRING: ${{ secrets.KEYSTORE_BASE_64 }} + shell: bash + run: | + echo $ENCODED_STRING > keystore-b64.txt + base64 -d upload-keystore.jks + + - name: Build Prod Release APK + env: + RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }} + RELEASE_KEYSTORE_ALIAS: ${{ secrets.RELEASE_KEYSTORE_ALIAS }} + RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }} + run: ./gradlew assembleProdRelease --stacktrace + + - name: Upload Release APK to Artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.repository.name }}-${{ github.run_number }}-prod-release-apk + path: | + ${{ github.workspace }}/app/build/outputs/apk/prod/release/app-prod-release.apk diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/other.xml b/.idea/other.xml new file mode 100644 index 0000000..0d3a1fb --- /dev/null +++ b/.idea/other.xml @@ -0,0 +1,263 @@ + + + + + + \ No newline at end of file diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 7a118b4..0000000 --- a/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source "https://rubygems.org" - -gem "fastlane" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index f1f87f0..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,222 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.6) - rexml - addressable (2.8.5) - public_suffix (>= 2.0.2, < 6.0) - artifactory (3.0.15) - atomos (0.1.3) - aws-eventstream (1.2.0) - aws-partitions (1.802.0) - aws-sdk-core (3.180.3) - aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.5) - jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.71.0) - aws-sdk-core (~> 3, >= 3.177.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.132.1) - aws-sdk-core (~> 3, >= 3.179.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.6) - aws-sigv4 (1.6.0) - aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.4) - claide (1.1.0) - colored (1.2) - colored2 (3.1.2) - commander (4.6.0) - highline (~> 2.0.0) - declarative (0.0.20) - digest-crc (0.6.5) - rake (>= 12.0.0, < 14.0.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - dotenv (2.8.1) - emoji_regex (3.2.3) - excon (0.100.0) - faraday (1.10.3) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.7) - faraday (>= 0.8.0) - http-cookie (~> 1.0.0) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-multipart (1.0.4) - multipart-post (~> 2) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) - faraday_middleware (1.2.0) - faraday (~> 1.0) - fastimage (2.2.7) - fastlane (2.214.0) - CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.8, < 3.0.0) - artifactory (~> 3.0) - aws-sdk-s3 (~> 1.0) - babosa (>= 1.0.3, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) - colored - commander (~> 4.6) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 4.0) - excon (>= 0.71.0, < 1.0.0) - faraday (~> 1.0) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 1.0) - fastimage (>= 2.1.0, < 3.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-apis-androidpublisher_v3 (~> 0.3) - google-apis-playcustomapp_v1 (~> 0.1) - google-cloud-storage (~> 1.31) - highline (~> 2.0) - json (< 3.0.0) - jwt (>= 2.1.0, < 3) - mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (>= 2.0.0, < 3.0.0) - naturally (~> 2.2) - optparse (~> 0.1.1) - plist (>= 3.1.0, < 4.0.0) - rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.3) - simctl (~> 1.6.3) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) - gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.47.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.1) - addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.16.2, < 2.a) - httpclient (>= 2.8.1, < 3.a) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.a) - rexml - webrick - google-apis-iamcredentials_v1 (0.17.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-playcustomapp_v1 (0.13.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.19.0) - google-apis-core (>= 0.9.0, < 2.a) - google-cloud-core (1.6.0) - google-cloud-env (~> 1.0) - google-cloud-errors (~> 1.0) - google-cloud-env (1.6.0) - faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.3.1) - google-cloud-storage (1.44.0) - addressable (~> 2.8) - digest-crc (~> 0.4) - google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.19.0) - google-cloud-core (~> 1.6) - googleauth (>= 0.16.2, < 2.a) - mini_mime (~> 1.0) - googleauth (1.7.0) - faraday (>= 0.17.3, < 3.a) - jwt (>= 1.4, < 3.0) - memoist (~> 0.16) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (>= 0.16, < 2.a) - highline (2.0.3) - http-cookie (1.0.5) - domain_name (~> 0.5) - httpclient (2.8.3) - jmespath (1.6.2) - json (2.6.3) - jwt (2.7.1) - memoist (0.16.2) - mini_magick (4.12.0) - mini_mime (1.1.5) - multi_json (1.15.0) - multipart-post (2.3.0) - nanaimo (0.3.0) - naturally (2.2.1) - optparse (0.1.1) - os (1.1.4) - plist (3.7.0) - public_suffix (5.0.3) - rake (13.0.6) - representable (3.2.0) - declarative (< 0.1.0) - trailblazer-option (>= 0.1.1, < 0.2.0) - uber (< 0.2.0) - retriable (3.1.2) - rexml (3.2.8) - strscan (>= 3.0.9) - rouge (2.0.7) - ruby2_keywords (0.0.5) - rubyzip (2.3.2) - security (0.1.3) - signet (0.17.0) - addressable (~> 2.8) - faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - simctl (1.6.10) - CFPropertyList - naturally - strscan (3.1.0) - terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) - trailblazer-option (0.1.2) - tty-cursor (0.7.1) - tty-screen (0.8.1) - tty-spinner (0.9.3) - tty-cursor (~> 0.7) - uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) - unicode-display_width (1.8.0) - webrick (1.8.1) - word_wrap (1.0.0) - xcodeproj (1.22.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (~> 3.2.4) - xcpretty (0.3.0) - rouge (~> 2.0.7) - xcpretty-travis-formatter (1.0.1) - xcpretty (~> 0.2, >= 0.0.7) - -PLATFORMS - arm64-darwin-22 - x86_64-darwin-22 - x86_64-linux - -DEPENDENCIES - fastlane - -BUNDLED WITH - 2.4.10 diff --git a/README.MD b/README.MD index 56d9d89..0e0313a 100644 --- a/README.MD +++ b/README.MD @@ -17,6 +17,11 @@ A template for creating Android projects at Q42. here: [Firebase project](https://console.firebase.google.com/u/0/project/template-android-799ab/overview) Google Services currently in use: - Crashlytics +1. Setup your signing key secrets for usage in automated builds, . Steps are described + in [CI / Automated Builds](#ci--automated-builds) +1. You probably want to enable the self-hosted runner (our own, free and fast, mac-mini), as described + in [self hosted runner](#self-hosted-runner), when using Github Actions. +1. Setup/adjust uploads to [Firebase app distribution](#firebase-app-distribution). 1. Build your awesome project :-) 1. Consider contributing to this template when you find something that could be improved. @@ -31,14 +36,16 @@ note on changes: ## Wishlist for changes / new features +- add this wishlist as issues in Git, so we can self-assign, etc. :) + remove it here. +- on most projects we only use one module for data, and one for domain. We could simplify the + template by removing the data and domain modules and calling them 'data/main' and 'domain/main', + in that way more modules can easily be added, but are not added by default. - Research android screen transition standards + update our animations -- Github Actions only: CI now combines fastlane with Github Actions, with responsibility shared. Use - Github Actions only, that's a much simpler setup. - - Make sure to also demonstrate / implement CI singing keys in this setup. - - Make a "any branche" setup: debug build only - - Make a "develop & main branche" setup: all builds, dev and release - Switch build files to KTS, also build some custom plugins. -- Optionally: Setup proper Typography in AppTheme, using custom (non-material) Typography keys, because that is what we have in 90%+ of our projects. +- Use lifecycle events from Google (we use our own now) +- Use preview light dark from google (we use our own now) +- Optionally: Setup proper Typography in AppTheme, using custom (non-material) Typography keys, + because that is what we have in 90%+ of our projects. - Optionally: Add compose events from VM to View (https://github.com/leonard-palm/compose-state-events) and showcase with snackbar or toast. - Optionally: Switch from retrofit to ktor (because Ktor is multiplatform) @@ -59,23 +66,75 @@ Only basic features that almost all projects use, were added in this template: ## Project Setup -### CI +### Signing Builds -This project uses Github Actions + fastlane for CI. There is a nice upgrade path to automatically -deploy builds to Firebase App Distribution and Play Store. +If you need local signing of release builds, copy the upload keystore into the root of your project, +and call it 'upload-keystore.jks'. Note that you probably don't need to use local signing. The CI +build setup builds and signs your app for +you. -#### Installing fastlane +### CI / Automated Builds -A big advantage of fastlane is that the ci and publishing steps can run locally. -Use `brew install fastlane` and then `fastlane test` to verify this installation. +This project uses Github Actions for CI. We have added these workflows for now: -#### Removing Github Actions + fastlane +- _debug.yml_: any commit on any branch triggers a `devDebug` apk and a `prodDebug` apk build +- _release.yml_: any PR triggers signed release builds (`prodRelease` bundle and `prodRelease` apk) -Using Bitrise instead? Then you can delete: +#### Adding your own keystore's details on Github Actions + +- First create your own keystore and store it in 1pw. _You do not add this keystore to the repo_ ( + like we did in this template as an example). +- add a KEYSTORE_BASE_64 [new github repository secret](./settings/secrets/actions/new) that is the + encoded text + representation of the upload + keystore of your app. CI will later decode it into a keystore file and then make it available for + the system to sign the app. + +Run the below command to generate an encoded text representation +of your keystore. If you don't have one, you +can [generate a new keystore](https://developer.android.com/studio/publish/app-signing#generate-key). + + openssl base64 < upload-keystore.jks | tr -d '\n' | tee keystore.base64.txt + +The above command will generate a new file called `keystore.base64.txt`. Open the file, copy the +contents and save it to your repo's github secrets in the variable KEYSTORE_BASE_64. + +Also [add these repository secrets](./settings/secrets/actions/new) in github, and store them in +your projects 1Password vault as well: + +- RELEASE_KEYSTORE_PASSWORD ('firstpass' for this template) +- RELEASE_KEYSTORE_ALIAS ('template' for this template) +- RELEASE_KEY_PASSWORD ('secondpass' for this template) + +#### Firebase app distribution + +We use a [firebase app github action](https://github.com/nickwph/firebase-app-distribution-action) ( +in our github actions setup) to automatically upload release builds to firebase. + +To change this to your own firebase project, you need to set two secrets: + +- `FIREBASE_PROD_APP_ID` from your firebase project settings. It's in your firebase project + settings (for the app you want to publish), it looks something like `1:xxxxxxxxx:android:yyyyyyyy` +- `FIREBASE_CREDENTIALS` from your firebase project. More info how to obtain it can be found + in [creating a service account](https://github.com/nickwph/firebase-app-distribution-action?tab=readme-ov-file#download-credentials-from-firebase). + +#### Self-hosted runner + +You can run the worlkflows on our self-hosted runner (a mac mini). This is: + +- 2-3 times faster (compared to building with Github hosted runners) +- a lot cheaper + +In a private repo you can enable the use of our self-hosted runner using `[self-hosted, +macOS]` as desribed in the `.github/workflows/\*.yml` files. If you need more help: +[the documentation is in Notion](https://www.notion.so/q42/GitHub-Actions-CI-op-onze-self-hosted-runner-Mac-Mini-7f7d2e6312444b98be521394223db6e2). + +For security reasons, using the mac mini is not possible on a public repo (like this template repo). + +#### Removing Github Actions + +Using Bitrise or another CI tool instead? Then you can skip the above and delete this folder: -- `./fastlane` -- `./GemFile` -- `./Gemfile.lock` - `./.github/workflows` ## Setup decisions diff --git a/app/build.gradle b/app/build.gradle index 56531cf..e2f9763 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { namespace = "nl.q42.template" defaultConfig { - versionCode = 1 + versionCode = 1 // version code is set by CI versionName = "1.0" } @@ -21,6 +21,15 @@ android { buildConfig = true } + signingConfigs { + upload { + storeFile file("../upload-keystore.jks") + storePassword System.getenv("RELEASE_KEYSTORE_PASSWORD") + keyAlias System.getenv("RELEASE_KEYSTORE_ALIAS") + keyPassword System.getenv("RELEASE_KEY_PASSWORD") + } + } + buildTypes { debug { buildConfigField 'boolean', 'config_log_http_calls', "true" @@ -28,6 +37,7 @@ android { } release { minifyEnabled true + signingConfig signingConfigs.upload proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") buildConfigField 'boolean', 'config_log_http_calls', "false" } diff --git a/app/src/main/kotlin/nl/q42/template/navigation/NavGraphs.kt b/app/src/main/kotlin/nl/q42/template/navigation/NavGraphs.kt index ef65f07..4a50c8c 100644 --- a/app/src/main/kotlin/nl/q42/template/navigation/NavGraphs.kt +++ b/app/src/main/kotlin/nl/q42/template/navigation/NavGraphs.kt @@ -2,9 +2,9 @@ package nl.q42.template.navigation import com.ramcosta.composedestinations.spec.DestinationSpec import com.ramcosta.composedestinations.spec.NavGraphSpec -import nl.q42.template.ui.home.destinations.HomeScreenDestination -import nl.q42.template.ui.home.destinations.HomeSecondScreenDestination -import nl.q42.template.ui.onboarding.start.destinations.OnboardingStartScreenDestination +import nl.q42.template.home.destinations.HomeScreenDestination +import nl.q42.template.home.destinations.HomeSecondScreenDestination +import nl.q42.template.onboarding.ui.start.destinations.OnboardingStartScreenDestination /** * @@ -16,7 +16,7 @@ object NavGraphs { val home = object : NavGraphSpec { override val route = AppGraphRoutes.home - + override val startRoute = HomeScreenDestination override val destinationsByRoute = listOf>( @@ -45,4 +45,4 @@ object NavGraphs { home, onboarding ) } -} \ No newline at end of file +} diff --git a/fastlane/Appfile b/fastlane/Appfile deleted file mode 100644 index 3275f34..0000000 --- a/fastlane/Appfile +++ /dev/null @@ -1,2 +0,0 @@ -json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one -package_name("nl.q42.template") # e.g. com.krausefx.app diff --git a/fastlane/Fastfile b/fastlane/Fastfile deleted file mode 100644 index c6d682a..0000000 --- a/fastlane/Fastfile +++ /dev/null @@ -1,47 +0,0 @@ -# This file contains the fastlane.tools configuration -# You can find the documentation at https://docs.fastlane.tools -# -# For a list of all available actions, check out -# -# https://docs.fastlane.tools/actions -# -# For a list of all available plugins, check out -# -# https://docs.fastlane.tools/plugins/available-plugins -# - -# Uncomment the line if you want fastlane to automatically update itself -# update_fastlane - -default_platform(:android) - -platform :android do - desc "Runs all the tests" - lane :clean do - gradle(task: "clean") - end - lane :test do - gradle(task: "testDebugUnitTest") - end - lane :assembleDevDebug do - gradle(task: "assembleDevDebug") - end - lane :assembleDevRelease do - gradle(task: "assembleDevRelease") - end - - desc "Submit a new Beta Build to Crashlytics Beta" - lane :beta do - gradle(task: "clean assembleRelease") - crashlytics - - # sh "your_script.sh" - # You can also use other beta testing services here - end - - desc "Deploy a new version to the Google Play" - lane :deploy do - gradle(task: "clean assembleRelease") - upload_to_play_store - end -end diff --git a/fastlane/README.md b/fastlane/README.md deleted file mode 100644 index 3491dd9..0000000 --- a/fastlane/README.md +++ /dev/null @@ -1,72 +0,0 @@ -fastlane documentation ----- - -# Installation - -Make sure you have the latest version of the Xcode command line tools installed: - -```sh -xcode-select --install -``` - -For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) - -# Available Actions - -## Android - -### android clean - -```sh -[bundle exec] fastlane android clean -``` - -Runs all the tests - -### android test - -```sh -[bundle exec] fastlane android test -``` - - - -### android assembleDevDebug - -```sh -[bundle exec] fastlane android assembleDevDebug -``` - - - -### android assembleDevRelease - -```sh -[bundle exec] fastlane android assembleDevRelease -``` - - - -### android beta - -```sh -[bundle exec] fastlane android beta -``` - -Submit a new Beta Build to Crashlytics Beta - -### android deploy - -```sh -[bundle exec] fastlane android deploy -``` - -Deploy a new version to the Google Play - ----- - -This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. - -More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). - -The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/feature/home/src/main/kotlin/nl/q42/template/presentation/home/HomeViewModel.kt b/feature/home/src/main/kotlin/nl/q42/template/home/main/presentation/HomeViewModel.kt similarity index 95% rename from feature/home/src/main/kotlin/nl/q42/template/presentation/home/HomeViewModel.kt rename to feature/home/src/main/kotlin/nl/q42/template/home/main/presentation/HomeViewModel.kt index e3124cc..06e083b 100644 --- a/feature/home/src/main/kotlin/nl/q42/template/presentation/home/HomeViewModel.kt +++ b/feature/home/src/main/kotlin/nl/q42/template/home/main/presentation/HomeViewModel.kt @@ -1,4 +1,4 @@ -package nl.q42.template.presentation.home +package nl.q42.template.home.main.presentation import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -16,9 +16,9 @@ import nl.q42.template.actionresult.data.handleAction import nl.q42.template.domain.user.usecase.FetchUserUseCase import nl.q42.template.domain.user.usecase.GetUserFlowUseCase import nl.q42.template.feature.home.R +import nl.q42.template.home.destinations.HomeSecondScreenDestination import nl.q42.template.navigation.AppGraphRoutes import nl.q42.template.navigation.viewmodel.RouteNavigator -import nl.q42.template.ui.home.destinations.HomeSecondScreenDestination import nl.q42.template.ui.presentation.ViewStateString import javax.inject.Inject diff --git a/feature/home/src/main/kotlin/nl/q42/template/presentation/home/HomeViewState.kt b/feature/home/src/main/kotlin/nl/q42/template/home/main/presentation/HomeViewState.kt similarity index 81% rename from feature/home/src/main/kotlin/nl/q42/template/presentation/home/HomeViewState.kt rename to feature/home/src/main/kotlin/nl/q42/template/home/main/presentation/HomeViewState.kt index 51da46f..ff319a8 100644 --- a/feature/home/src/main/kotlin/nl/q42/template/presentation/home/HomeViewState.kt +++ b/feature/home/src/main/kotlin/nl/q42/template/home/main/presentation/HomeViewState.kt @@ -1,4 +1,4 @@ -package nl.q42.template.presentation.home +package nl.q42.template.home.main.presentation import nl.q42.template.ui.presentation.ViewStateString diff --git a/feature/home/src/main/kotlin/nl/q42/template/ui/home/HomeContent.kt b/feature/home/src/main/kotlin/nl/q42/template/home/main/ui/HomeContent.kt similarity index 95% rename from feature/home/src/main/kotlin/nl/q42/template/ui/home/HomeContent.kt rename to feature/home/src/main/kotlin/nl/q42/template/home/main/ui/HomeContent.kt index 2bdddb7..b113920 100644 --- a/feature/home/src/main/kotlin/nl/q42/template/ui/home/HomeContent.kt +++ b/feature/home/src/main/kotlin/nl/q42/template/home/main/ui/HomeContent.kt @@ -1,4 +1,4 @@ -package nl.q42.template.ui.home +package nl.q42.template.home.main.ui import androidx.compose.foundation.layout.Arrangement.Center import androidx.compose.foundation.layout.Column @@ -9,7 +9,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier -import nl.q42.template.presentation.home.HomeViewState +import nl.q42.template.home.main.presentation.HomeViewState import nl.q42.template.ui.compose.get import nl.q42.template.ui.presentation.toViewStateString import nl.q42.template.ui.theme.PreviewAppTheme diff --git a/feature/home/src/main/kotlin/nl/q42/template/ui/home/HomeScreen.kt b/feature/home/src/main/kotlin/nl/q42/template/home/main/ui/HomeScreen.kt similarity index 86% rename from feature/home/src/main/kotlin/nl/q42/template/ui/home/HomeScreen.kt rename to feature/home/src/main/kotlin/nl/q42/template/home/main/ui/HomeScreen.kt index e38e228..ad2d588 100644 --- a/feature/home/src/main/kotlin/nl/q42/template/ui/home/HomeScreen.kt +++ b/feature/home/src/main/kotlin/nl/q42/template/home/main/ui/HomeScreen.kt @@ -1,4 +1,4 @@ -package nl.q42.template.ui.home +package nl.q42.template.home.main.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -6,9 +6,8 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator +import nl.q42.template.home.main.presentation.HomeViewModel import nl.q42.template.navigation.viewmodel.InitNavigator -import nl.q42.template.presentation.home.HomeViewModel -import nl.q42.template.presentation.home.HomeViewState import nl.q42.template.ui.compose.OnLifecycleResume @Destination diff --git a/feature/home/src/main/kotlin/nl/q42/template/presentation/home/second/HomeSecondViewModel.kt b/feature/home/src/main/kotlin/nl/q42/template/home/second/presentation/HomeSecondViewModel.kt similarity index 91% rename from feature/home/src/main/kotlin/nl/q42/template/presentation/home/second/HomeSecondViewModel.kt rename to feature/home/src/main/kotlin/nl/q42/template/home/second/presentation/HomeSecondViewModel.kt index b52e824..cbb5c30 100644 --- a/feature/home/src/main/kotlin/nl/q42/template/presentation/home/second/HomeSecondViewModel.kt +++ b/feature/home/src/main/kotlin/nl/q42/template/home/second/presentation/HomeSecondViewModel.kt @@ -1,9 +1,8 @@ -package nl.q42.template.presentation.home.second +package nl.q42.template.home.second.presentation import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow diff --git a/feature/home/src/main/kotlin/nl/q42/template/home/second/presentation/HomeSecondViewState.kt b/feature/home/src/main/kotlin/nl/q42/template/home/second/presentation/HomeSecondViewState.kt new file mode 100644 index 0000000..a6194c4 --- /dev/null +++ b/feature/home/src/main/kotlin/nl/q42/template/home/second/presentation/HomeSecondViewState.kt @@ -0,0 +1,3 @@ +package nl.q42.template.home.second.presentation + +class HomeSecondViewState(val title: String) diff --git a/feature/home/src/main/kotlin/nl/q42/template/ui/home/second/HomeSecondScreen.kt b/feature/home/src/main/kotlin/nl/q42/template/home/second/ui/HomeSecondScreen.kt similarity index 94% rename from feature/home/src/main/kotlin/nl/q42/template/ui/home/second/HomeSecondScreen.kt rename to feature/home/src/main/kotlin/nl/q42/template/home/second/ui/HomeSecondScreen.kt index b08c841..d114769 100644 --- a/feature/home/src/main/kotlin/nl/q42/template/ui/home/second/HomeSecondScreen.kt +++ b/feature/home/src/main/kotlin/nl/q42/template/home/second/ui/HomeSecondScreen.kt @@ -1,4 +1,4 @@ -package nl.q42.template.ui.home.second +package nl.q42.template.home.second.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -14,8 +14,8 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator +import nl.q42.template.home.second.presentation.HomeSecondViewModel import nl.q42.template.navigation.viewmodel.InitNavigator -import nl.q42.template.presentation.home.second.HomeSecondViewModel @Destination @Composable diff --git a/feature/home/src/main/kotlin/nl/q42/template/presentation/home/second/HomeSecondViewState.kt b/feature/home/src/main/kotlin/nl/q42/template/presentation/home/second/HomeSecondViewState.kt deleted file mode 100644 index 1156c83..0000000 --- a/feature/home/src/main/kotlin/nl/q42/template/presentation/home/second/HomeSecondViewState.kt +++ /dev/null @@ -1,3 +0,0 @@ -package nl.q42.template.presentation.home.second - -class HomeSecondViewState(val title: String) \ No newline at end of file diff --git a/feature/home/src/test/kotlin/nl/q42/template/presentation/home/HomeViewModelTest.kt b/feature/home/src/test/kotlin/nl/q42/template/home/main/presentation/HomeViewModelTest.kt similarity index 87% rename from feature/home/src/test/kotlin/nl/q42/template/presentation/home/HomeViewModelTest.kt rename to feature/home/src/test/kotlin/nl/q42/template/home/main/presentation/HomeViewModelTest.kt index 34773db..8c282f4 100644 --- a/feature/home/src/test/kotlin/nl/q42/template/presentation/home/HomeViewModelTest.kt +++ b/feature/home/src/test/kotlin/nl/q42/template/home/main/presentation/HomeViewModelTest.kt @@ -1,4 +1,4 @@ -package nl.q42.template.presentation.home +package nl.q42.template.home.main.presentation import app.cash.turbine.test import io.mockk.coEvery @@ -10,10 +10,15 @@ import kotlinx.coroutines.test.runTest import nl.q42.template.actionresult.domain.ActionResult import nl.q42.template.domain.user.usecase.FetchUserUseCase import nl.q42.template.domain.user.usecase.GetUserFlowUseCase +import nl.q42.template.presentation.home.MainDispatcherRule import org.junit.Rule import org.junit.Test import kotlin.time.Duration.Companion.seconds +/** + * An example ViewModel test. Weather you want to test ViewModels is up to you. On most projects, + * you probably want to focus on unit testing the Domain layer first. + */ class HomeViewModelTest { @get:Rule diff --git a/feature/onboarding/src/main/kotlin/nl/q42/template/presentation/onboarding/start/OnboardingStartViewModel.kt b/feature/onboarding/src/main/kotlin/nl/q42/template/onboarding/presentation/start/OnboardingStartViewModel.kt similarity index 93% rename from feature/onboarding/src/main/kotlin/nl/q42/template/presentation/onboarding/start/OnboardingStartViewModel.kt rename to feature/onboarding/src/main/kotlin/nl/q42/template/onboarding/presentation/start/OnboardingStartViewModel.kt index 4062168..37c5acc 100644 --- a/feature/onboarding/src/main/kotlin/nl/q42/template/presentation/onboarding/start/OnboardingStartViewModel.kt +++ b/feature/onboarding/src/main/kotlin/nl/q42/template/onboarding/presentation/start/OnboardingStartViewModel.kt @@ -1,4 +1,4 @@ -package nl.q42.template.presentation.onboarding.start +package nl.q42.template.onboarding.presentation.start import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel diff --git a/feature/onboarding/src/main/kotlin/nl/q42/template/onboarding/presentation/start/OnboardingStartViewState.kt b/feature/onboarding/src/main/kotlin/nl/q42/template/onboarding/presentation/start/OnboardingStartViewState.kt new file mode 100644 index 0000000..3d90fbc --- /dev/null +++ b/feature/onboarding/src/main/kotlin/nl/q42/template/onboarding/presentation/start/OnboardingStartViewState.kt @@ -0,0 +1,3 @@ +package nl.q42.template.onboarding.presentation.start + +class OnboardingStartViewState(val title: String) diff --git a/feature/onboarding/src/main/kotlin/nl/q42/template/ui/onboarding/start/OnboardingStartScreen.kt b/feature/onboarding/src/main/kotlin/nl/q42/template/onboarding/ui/start/OnboardingStartScreen.kt similarity index 88% rename from feature/onboarding/src/main/kotlin/nl/q42/template/ui/onboarding/start/OnboardingStartScreen.kt rename to feature/onboarding/src/main/kotlin/nl/q42/template/onboarding/ui/start/OnboardingStartScreen.kt index 4051662..6d6b3e5 100644 --- a/feature/onboarding/src/main/kotlin/nl/q42/template/ui/onboarding/start/OnboardingStartScreen.kt +++ b/feature/onboarding/src/main/kotlin/nl/q42/template/onboarding/ui/start/OnboardingStartScreen.kt @@ -1,4 +1,4 @@ -package nl.q42.template.ui.onboarding.start +package nl.q42.template.onboarding.ui.start import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -15,8 +15,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import nl.q42.template.navigation.viewmodel.InitNavigator -import nl.q42.template.presentation.onboarding.start.OnboardingStartViewModel -import nl.q42.template.presentation.onboarding.start.OnboardingStartViewState +import nl.q42.template.onboarding.presentation.start.OnboardingStartViewModel @Destination @Composable diff --git a/feature/onboarding/src/main/kotlin/nl/q42/template/presentation/onboarding/start/OnboardingStartViewState.kt b/feature/onboarding/src/main/kotlin/nl/q42/template/presentation/onboarding/start/OnboardingStartViewState.kt deleted file mode 100644 index f2b682f..0000000 --- a/feature/onboarding/src/main/kotlin/nl/q42/template/presentation/onboarding/start/OnboardingStartViewState.kt +++ /dev/null @@ -1,3 +0,0 @@ -package nl.q42.template.presentation.onboarding.start - -class OnboardingStartViewState(val title: String) \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a9019d3..c5584be 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,8 +8,8 @@ jvmTarget = "21" kotlin = "2.0.0" ksp = "2.0.0-1.0.21" gradlePlugin = "8.4.1" -googleServices = "4.4.1" -crashlyticsPlugin = "3.0.1" +googleServices = "4.4.2" +crashlyticsPlugin = "3.0.2" firebaseBOM = "33.0.0" manesVersions = "0.44.0" littleRobotsCatalogUpdates = "0.8.1" diff --git a/upload-keystore-that-you-in-a-real-project-do-not-commit.jks b/upload-keystore-that-you-in-a-real-project-do-not-commit.jks new file mode 100644 index 0000000..bfc8002 Binary files /dev/null and b/upload-keystore-that-you-in-a-real-project-do-not-commit.jks differ