diff --git a/.github/workflows/build-and-deploy-site.yml b/.github/workflows/build-and-deploy-site.yml
new file mode 100644
index 000000000..280e58d83
--- /dev/null
+++ b/.github/workflows/build-and-deploy-site.yml
@@ -0,0 +1,65 @@
+# This workflow is designed to automate the process of building and deploying a Kotlin/JS web application to GitHub Pages.
+# It ensures that whenever changes are merged into the dev branch or when manually triggered, the web application is built,
+# packaged, and deployed to the GitHub Pages environment, making it accessible online.
+
+# Key Features:
+# - Automated web application build using Kotlin/JS
+# - Deployment to GitHub Pages
+# - Supports configurable web project module name
+# - Manages deployment concurrency and environment settings
+# - Provides secure deployment with proper permissions
+
+# Prerequisites:
+# - Kotlin Multiplatform/JS project configured with Gradle
+# - Web module set up for browser distribution
+# - Java 17 or compatible version
+# - GitHub Pages enabled in repository settings
+
+# Workflow Configuration:
+# - Requires input of `web_package_name` to specify the web project module
+# - Uses Windows runner for build process
+# - Leverages GitHub Actions for build, pages configuration, and deployment
+
+# Workflow Triggers:
+# - Can be manually called from other workflows
+# - Supports workflow_call for reusability across projects
+
+# Deployment Process:
+# 1. Checkout repository code
+# 2. Set up Java development environment
+# 3. Build Kotlin/JS web application
+# 4. Configure GitHub Pages
+# 5. Upload built artifacts
+# 6. Deploy to GitHub Pages
+
+# https://github.com/openMF/mifos-x-actionhub/blob/main/.github/workflows/build-and-deploy-site.yaml
+
+# ##############################################################################
+# DON'T EDIT THIS FILE UNLESS NECESSARY #
+# ##############################################################################
+
+name: Build And Deploy Web App
+
+# Trigger conditions for the workflow
+on:
+ workflow_dispatch:
+
+# Concurrency settings to manage multiple workflow runs
+# This ensures orderly deployment to production environment
+concurrency:
+ group: "web-pages"
+ cancel-in-progress: false
+
+permissions:
+ contents: read # Read repository contents
+ pages: write # Write to GitHub Pages
+ id-token: write # Write authentication tokens
+ pull-requests: write # Write to pull requests
+
+jobs:
+ build_and_deploy_web:
+ name: Build And Deploy Web App
+ uses: openMF/mifos-x-actionhub/.github/workflows/build-and-deploy-site.yaml@v1.0.2
+ secrets: inherit
+ with:
+ web_package_name: 'cmp-web' # <-- Change with your web package name
diff --git a/.github/workflows/cache-cleanup.yaml b/.github/workflows/cache-cleanup.yaml
new file mode 100644
index 000000000..02685f363
--- /dev/null
+++ b/.github/workflows/cache-cleanup.yaml
@@ -0,0 +1,15 @@
+name: Cleanup Cache
+
+on:
+ pull_request:
+ types: [ closed ]
+ workflow_dispatch:
+
+jobs:
+ cleanup:
+ uses: openMF/mifos-x-actionhub/.github/workflows/cache-cleanup.yaml@v1.0.2
+ with:
+ cleanup_pr: ${{ github.event_name == 'pull_request' && github.event.repository.private == true }}
+ cleanup_all: ${{ github.event_name == 'workflow_dispatch' }}
+ secrets:
+ token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/monthly-version-tag.yml b/.github/workflows/monthly-version-tag.yml
new file mode 100644
index 000000000..e96c13615
--- /dev/null
+++ b/.github/workflows/monthly-version-tag.yml
@@ -0,0 +1,66 @@
+# Automated Monthly Release Versioning Workflow
+# ============================================
+
+# Purpose:
+# - Automatically create consistent monthly version tags
+# - Implement a calendar-based versioning strategy
+# - Facilitate easy tracking of monthly releases
+
+# Versioning Strategy:
+# - Tag format: YYYY.MM.0 (e.g., 2024.01.0 for January 2024)
+# - First digit: Full year
+# - Second digit: Month (01-12)
+# - Third digit: Patch version (starts at 0, allows for potential updates)
+
+# Key Features:
+# - Runs automatically on the first day of each month at 3:30 AM UTC
+# - Can be manually triggered via workflow_dispatch
+# - Uses GitHub Actions to generate tags programmatically
+# - Provides a predictable and systematic versioning approach
+
+# Prerequisites:
+# - Repository configured with GitHub Actions
+# - Permissions to create tags
+# - Access to actions/checkout and tag creation actions
+
+# Workflow Triggers:
+# - Scheduled monthly run
+# - Manual workflow dispatch
+# - Callable from other workflows
+
+# Actions Used:
+# 1. actions/checkout@v4 - Checks out repository code
+# 2. josStorer/get-current-time - Retrieves current timestamp
+# 3. rickstaa/action-create-tag - Creates Git tags
+
+# Example Generated Tags:
+# - 2024.01.0 (January 2024 initial release)
+# - 2024.02.0 (February 2024 initial release)
+# - 2024.02.1 (Potential patch for February 2024)
+
+# https://github.com/openMF/mifos-x-actionhub/blob/main/.github/workflows/monthly-version-tag.yaml
+
+# ##############################################################################
+# DON'T EDIT THIS FILE UNLESS NECESSARY #
+# ##############################################################################
+
+name: Tag Monthly Release
+
+on:
+ # Allow manual triggering of the workflow
+ workflow_dispatch:
+ # Schedule the workflow to run monthly
+ schedule:
+ # Runs at 03:30 UTC on the first day of every month
+ # Cron syntax: minute hour day-of-month month day-of-week
+ - cron: '30 3 1 * *'
+
+concurrency:
+ group: "monthly-release"
+ cancel-in-progress: false
+
+jobs:
+ monthly_release:
+ name: Tag Monthly Release
+ uses: openMF/mifos-x-actionhub/.github/workflows/monthly-version-tag.yaml@v1.0.2
+ secrets: inherit
diff --git a/.github/workflows/multi-platform-build-and-publish.yml b/.github/workflows/multi-platform-build-and-publish.yml
index 9d256dd3e..95eb380b9 100644
--- a/.github/workflows/multi-platform-build-and-publish.yml
+++ b/.github/workflows/multi-platform-build-and-publish.yml
@@ -71,7 +71,7 @@ on:
target_branch:
type: string
- default: 'development'
+ default: 'dev'
description: 'Target branch for release'
distribute_ios_firebase:
@@ -89,16 +89,6 @@ on:
default: false
description: Distribute iOS App to Appstore
- distribute_macos_testflight:
- type: boolean
- default: false
- description: Distribute macOS App via TestFlight (App Store Connect)
-
- distribute_macos_appstore:
- type: boolean
- default: false
- description: Distribute macOS App to Appstore
-
permissions:
contents: write
id-token: write
@@ -111,32 +101,27 @@ concurrency:
jobs:
multi_platform_build_and_publish:
name: Multi-Platform Build and Publish
- uses: openMF/mifos-x-actionhub/.github/workflows/multi-platform-build-and-publish.yaml@v1.0.7
+ uses: openMF/mifos-x-actionhub/.github/workflows/multi-platform-build-and-publish.yaml@v1.0.3
with:
- java-version: 21
release_type: ${{ inputs.release_type }}
target_branch: ${{ inputs.target_branch }}
- android_package_name: 'cmp-android'
- ios_package_name: 'cmp-ios'
- desktop_package_name: 'cmp-desktop'
- web_package_name: 'cmp-web'
- tester_groups: 'mifos-mobile-apps'
- app_identifier: 'org.mifos.mobile'
+ android_package_name: 'cmp-android' # <-- Change this to your android package name
+ ios_package_name: 'cmp-ios' # <-- Change this to your ios package name
+ desktop_package_name: 'cmp-desktop' # <-- Change this to your desktop package name
+ web_package_name: 'cmp-web' # <-- Change this to your web package name
+ tester_groups: 'mifos-mobile-apps' # <-- Change this to your Firebase tester group
+ app_identifier: 'org.mifos.kmp.template'
git_url: 'git@github.com:openMF/ios-provisioning-profile.git'
- git_branch: 'mifos-mobile'
+ git_branch: 'master'
match_type: 'adhoc'
- provisioning_profile_name: 'match AdHoc org.mifos.mobile'
- firebase_app_id: '1:728434912738:ios:ee2e0815a6915b351a1dbb'
- metadata_path: './fastlane/metadata/ios'
+ provisioning_profile_name: 'match AdHoc org.mifos.kmp.template'
+ firebase_app_id: '1:728434912738:ios:1d81f8e53ca7a6f31a1dbb'
+ metadata_path: './fastlane/metadata'
use_cocoapods: true # <-- Set to true if using CocoaPods integration for KMP
shared_module: ':cmp-shared' # <-- Gradle path to your shared KMP module (e.g., :shared)
- cmp_desktop_dir: 'cmp-desktop'
- keychain_name: signing.keychain-db # optional
distribute_ios_firebase: ${{ inputs.distribute_ios_firebase }}
distribute_ios_testflight: ${{ inputs.distribute_ios_testflight }}
distribute_ios_appstore: ${{ inputs.distribute_ios_appstore }}
- distribute_macos_testflight: ${{ inputs.distribute_macos_testflight }}
- distribute_macos_appstore: ${{ inputs.distribute_macos_appstore }}
secrets:
original_keystore_file: ${{ secrets.ORIGINAL_KEYSTORE_FILE }}
original_keystore_file_password: ${{ secrets.ORIGINAL_KEYSTORE_FILE_PASSWORD }}
@@ -151,12 +136,6 @@ jobs:
notarization_apple_id: ${{ secrets.NOTARIZATION_APPLE_ID }}
notarization_password: ${{ secrets.NOTARIZATION_PASSWORD }}
notarization_team_id: ${{ secrets.NOTARIZATION_TEAM_ID }}
- keychain_password: ${{ secrets.KEYCHAIN_PASSWORD }}
- certificates_password: ${{ secrets.CERTIFICATES_PASSWORD }}
- mac_app_distribution_certificate_b64: ${{ secrets.MAC_APP_DISTRIBUTION_CERTIFICATE_B64 }}
- mac_installer_distribution_certificate_b64: ${{ secrets.MAC_INSTALLER_DISTRIBUTION_CERTIFICATE_B64 }}
- mac_embedded_provision_b64: ${{ secrets.MAC_EMBEDDED_PROVISION_B64 }}
- mac_runtime_provision_b64: ${{ secrets.MAC_RUNTIME_PROVISION_B64 }}
appstore_key_id: ${{ secrets.APPSTORE_KEY_ID }}
appstore_issuer_id: ${{ secrets.APPSTORE_ISSUER_ID }}
appstore_auth_key: ${{ secrets.APPSTORE_AUTH_KEY }}
diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml
index 0452cca62..42c3a6665 100644
--- a/.github/workflows/pr-check.yml
+++ b/.github/workflows/pr-check.yml
@@ -13,7 +13,7 @@
### Workflow Jobs
# 1. **Setup**: Prepares the build environment
# - Checks out repository code
-# - Sets up Java (configurable; defaults to 17)
+# - Sets up Java 17
# - Configures Gradle
# - Manages dependency caching
#
@@ -36,7 +36,7 @@
# - Generates platform-specific executables and packages
#
### Prerequisites
-# - Java (configurable; default 17)
+# - Java 17
# - Gradle
# - Configured build scripts for:
# - Android module
@@ -49,16 +49,10 @@
### Configuration Parameters
# The workflow requires two input parameters:
#
-# | Parameter | Description | Type | Required |
-# |------------------------|------------------------------------|--------|-----------|
-# | `android_package_name` | Name of the Android project module | String | Yes |
-# | `desktop_package_name` | Name of the Desktop project module | String | Yes |
-# |`web_package_name` | Name of the Web (Kotlin/JS) project/module | String | No|
-# |`ios_package_name` | Name of the iOS project/module | String | No |
-# |`build_ios` | Build iOS targets as part of PR checks | Boolean | No |
-# |`use_cocoapods` | Use CocoaPods for iOS integration | Boolean | No |
-# |`shared_module | Path of the shared KMP module | String | (required when build_ios=true) |
-# |`java-version | Java version to use (configurable; defaults to 17)| No |
+# | Parameter | Description | Type | Required |
+# |------------------------|------------------------------------|--------|----------|
+# | `android_package_name` | Name of the Android project module | String | Yes |
+# | `desktop_package_name` | Name of the Desktop project module | String | Yes |
#
# https://github.com/openMF/mifos-x-actionhub/blob/main/.github/workflows/pr-check.yaml
@@ -68,18 +62,18 @@
# ##############################################################################
-name: PR Checks for KMP
+name: PR Checks
# Trigger conditions for the workflow
on:
push:
- branches: [ development ] # Runs on pushes to dev branch
+ branches: [ dev ] # Runs on pushes to dev branch
pull_request:
- branches: [ development ] # Runs on pushes to dev branch
+ branches: [ dev ] # Runs on pushes to dev branch
# Concurrency settings to prevent multiple simultaneous workflow runs
concurrency:
- group: pr-kmp-${{ github.ref }}
+ group: pr-${{ github.ref }}
cancel-in-progress: true # Cancels previous runs if a new one is triggered
permissions:
@@ -87,9 +81,8 @@ permissions:
jobs:
pr_checks:
- name: PR Checks KMP
- uses: openMF/mifos-x-actionhub/.github/workflows/pr-check.yaml@v1.0.7
- secrets: inherit
+ name: PR Checks
+ uses: openMF/mifos-x-actionhub/.github/workflows/pr-check.yaml@v1.0.3
with:
android_package_name: 'cmp-android' # <-- Change Your Android Package Name
desktop_package_name: 'cmp-desktop' # <-- Change Your Desktop Package Name
@@ -98,4 +91,3 @@ jobs:
build_ios: true # <-- Change to 'false' if you don't want to build iOS
use_cocoapods: true
shared_module: ':cmp-shared'
- java-version: '21'
diff --git a/.github/workflows/promote-to-production.yml b/.github/workflows/promote-to-production.yml
index 8099c4ffa..081e3d629 100644
--- a/.github/workflows/promote-to-production.yml
+++ b/.github/workflows/promote-to-production.yml
@@ -43,7 +43,7 @@
# end
# ```
-# https://github.com/openMF/mifos-mobile-github-actions/blob/main/.github/workflows/promote-to-production.yaml
+# https://github.com/openMF/mifos-x-actionhub/blob/main/.github/workflows/promote-to-production.yaml
# ##############################################################################
# DON'T EDIT THIS FILE UNLESS NECESSARY #
@@ -70,6 +70,6 @@ jobs:
# Job to promote app from beta to production in Play Store
play_promote_production:
name: Promote Beta to Production Play Store
- uses: openMF/mifos-x-actionhub/.github/workflows/promote-to-production.yaml@v1.0.7
+ uses: openMF/mifos-x-actionhub/.github/workflows/promote-to-production.yaml@v1.0.2
secrets:
playstore_creds: ${{ secrets.PLAYSTORECREDS }}
diff --git a/.github/workflows/sync-dirs.yaml b/.github/workflows/sync-dirs.yaml
index 6a947f649..d8ef43626 100644
--- a/.github/workflows/sync-dirs.yaml
+++ b/.github/workflows/sync-dirs.yaml
@@ -23,7 +23,7 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
- ref: development
+ ref: dev
- name: Setup Git config
run: |
@@ -32,12 +32,14 @@ jobs:
- name: Add upstream remote and fetch
run: |
+ set -euo pipefail
UPSTREAM="${{ inputs.upstream || 'https://github.com/openMF/kmp-project-template.git' }}"
git remote add upstream "$UPSTREAM" || true
git fetch upstream || exit 1
- name: Check upstream/dev exists
run: |
+ set -euo pipefail
if ! git rev-parse --verify upstream/dev >/dev/null 2>&1; then
echo "Error: upstream/dev branch does not exist"
exit 1
@@ -45,12 +47,16 @@ jobs:
- name: Create and checkout temporary branch
run: |
+ set -euo pipefail
TEMP_BRANCH="temp-sync-branch-${{ github.run_number }}"
git checkout -b "$TEMP_BRANCH" upstream/dev || exit 1
echo "TEMP_BRANCH=$TEMP_BRANCH" >> $GITHUB_ENV
- name: Sync directories and files
+ shell: bash
run: |
+ set -euo pipefail
+
# Declare directories and files to sync
DIRS=(
"cmp-android"
@@ -66,14 +72,14 @@ jobs:
".github"
".run"
)
-
+
FILES=(
"Gemfile"
"Gemfile.lock"
"ci-prepush.bat"
"ci-prepush.sh"
)
-
+
# Define exclusions
declare -A EXCLUSIONS=(
["cmp-android"]="src/main/res dependencies src/main/ic_launcher-playstore.png google-services.json"
@@ -82,25 +88,27 @@ jobs:
["cmp-ios"]="iosApp/Assets.xcassets"
["root"]="secrets.env"
)
-
+
# Function to check if path should be excluded
should_exclude() {
local dir=$1
local path=$2
-
- # Check for root exclusions
- if [[ "$dir" == "." && -n "${EXCLUSIONS["root"]}" ]]; then
- local root_excluded_paths=(${EXCLUSIONS["root"]})
+
+ # Check for root exclusions (when dir is "." or "root")
+ if [[ "$dir" == "." || "$dir" == "root" ]] && [[ -v "EXCLUSIONS[root]" ]]; then
+ local root_excluded_paths
+ IFS=' ' read -ra root_excluded_paths <<< "${EXCLUSIONS[root]}"
for excluded in "${root_excluded_paths[@]}"; do
if [[ "$path" == *"$excluded"* ]]; then
return 0
fi
done
fi
-
+
# Check directory-specific exclusions
- if [[ -n "${EXCLUSIONS[$dir]}" ]]; then
- local excluded_paths=(${EXCLUSIONS[$dir]})
+ if [[ -v "EXCLUSIONS[$dir]" ]]; then
+ local excluded_paths
+ IFS=' ' read -ra excluded_paths <<< "${EXCLUSIONS[$dir]}"
for excluded in "${excluded_paths[@]}"; do
if [[ "$path" == *"$excluded"* ]]; then
return 0
@@ -109,29 +117,31 @@ jobs:
fi
return 1
}
-
+
# Function to preserve excluded paths
preserve_excluded() {
local dir=$1
- if [[ -n "${EXCLUSIONS[$dir]}" ]]; then
- local excluded_paths=(${EXCLUSIONS[$dir]})
+ if [[ -v "EXCLUSIONS[$dir]" ]]; then
+ local excluded_paths
+ IFS=' ' read -ra excluded_paths <<< "${EXCLUSIONS[$dir]}"
for excluded in "${excluded_paths[@]}"; do
local full_path="$dir/$excluded"
if [[ -e "$full_path" ]]; then
echo "Preserving excluded path: $full_path"
local temp_path="temp_excluded/$full_path"
mkdir -p "$(dirname "$temp_path")"
- cp -r "$full_path" "$(dirname "$temp_path")"
+ cp -r "$full_path" "$(dirname "$temp_path")/"
fi
done
fi
}
-
+
# Function to restore excluded paths
restore_excluded() {
local dir=$1
- if [[ -n "${EXCLUSIONS[$dir]}" ]]; then
- local excluded_paths=(${EXCLUSIONS[$dir]})
+ if [[ -v "EXCLUSIONS[$dir]" ]]; then
+ local excluded_paths
+ IFS=' ' read -ra excluded_paths <<< "${EXCLUSIONS[$dir]}"
for excluded in "${excluded_paths[@]}"; do
local full_path="$dir/$excluded"
local temp_path="temp_excluded/$full_path"
@@ -139,16 +149,17 @@ jobs:
echo "Restoring excluded path: $full_path"
mkdir -p "$(dirname "$full_path")"
rm -rf "$full_path"
- cp -r "$temp_path" "$(dirname "$full_path")"
+ cp -r "$temp_path" "$(dirname "$full_path")/"
fi
done
fi
}
-
+
# Function to preserve root-level excluded files
preserve_root_files() {
- if [[ -n "${EXCLUSIONS["root"]}" ]]; then
- local excluded_paths=(${EXCLUSIONS["root"]})
+ if [[ -v "EXCLUSIONS[root]" ]]; then
+ local excluded_paths
+ IFS=' ' read -ra excluded_paths <<< "${EXCLUSIONS[root]}"
for excluded in "${excluded_paths[@]}"; do
if [[ -e "$excluded" ]]; then
echo "Preserving root-level excluded file: $excluded"
@@ -158,11 +169,12 @@ jobs:
done
fi
}
-
+
# Function to restore root-level excluded files
restore_root_files() {
- if [[ -n "${EXCLUSIONS["root"]}" ]]; then
- local excluded_paths=(${EXCLUSIONS["root"]})
+ if [[ -v "EXCLUSIONS[root]" ]]; then
+ local excluded_paths
+ IFS=' ' read -ra excluded_paths <<< "${EXCLUSIONS[root]}"
for excluded in "${excluded_paths[@]}"; do
if [[ -e "temp_excluded/root/$excluded" ]]; then
echo "Restoring root-level excluded file: $excluded"
@@ -171,63 +183,81 @@ jobs:
done
fi
}
-
+
# Create temp directory for exclusions
mkdir -p temp_excluded
-
+
# Preserve root-level exclusions before sync
preserve_root_files
-
- # Switch to development branch
- git checkout development
-
+
+ # Switch to dev branch
+ git checkout dev
+
# Sync directories
for dir in "${DIRS[@]}"; do
- if [ ! -d "$dir" ]; then
+ if [[ ! -d "$dir" ]]; then
echo "Creating $dir..."
mkdir -p "$dir"
fi
-
+
# Preserve excluded paths before sync
if [[ -d "$dir" ]]; then
preserve_excluded "$dir"
fi
-
+
echo "Syncing $dir..."
- git checkout "${{ env.TEMP_BRANCH }}" -- "$dir" || exit 1
-
+ if ! git checkout "${{ env.TEMP_BRANCH }}" -- "$dir" 2>/dev/null; then
+ echo "Warning: Could not sync directory $dir (may not exist in upstream)"
+ fi
+
# Restore excluded paths after sync
restore_excluded "$dir"
done
-
+
# Sync files
for file in "${FILES[@]}"; do
- dir=$(dirname "$file")
- if ! should_exclude "$dir" "$file"; then
- echo "Syncing $file..."
- git checkout "${{ env.TEMP_BRANCH }}" -- "$file" || true
- else
+ file_dir=$(dirname "$file")
+ file_name=$(basename "$file")
+
+ # Check root exclusions for root-level files
+ if [[ "$file_dir" == "." ]]; then
+ if should_exclude "root" "$file_name"; then
+ echo "Skipping excluded file: $file"
+ continue
+ fi
+ elif should_exclude "$file_dir" "$file"; then
echo "Skipping excluded file: $file"
+ continue
+ fi
+
+ echo "Syncing $file..."
+ if ! git checkout "${{ env.TEMP_BRANCH }}" -- "$file" 2>/dev/null; then
+ echo "Warning: Could not sync file $file (may not exist in upstream)"
fi
done
-
+
# Restore root-level excluded files
restore_root_files
-
+
# Cleanup temp directory
rm -rf temp_excluded
+ echo "Sync completed successfully!"
+
- name: Clean up temporary branch
if: always()
- run: git branch -D "${{ env.TEMP_BRANCH }}" || true
+ run: git branch -D "${{ env.TEMP_BRANCH }}" 2>/dev/null || true
- name: Check for changes
id: check_changes
run: |
if [[ -n "$(git status --porcelain)" ]]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
+ echo "Changes detected:"
+ git status --short
else
echo "has_changes=false" >> $GITHUB_OUTPUT
+ echo "No changes detected"
fi
- name: Create Pull Request
@@ -239,35 +269,37 @@ jobs:
title: "chore: Sync directories and files from upstream"
body: |
Automated sync of directories and files from upstream repository.
-
+
Changes included in this sync:
-
- Directories:
+
+ **Directories:**
- cmp-android (excluding src/main/res, dependencies, ic_launcher-playstore.png, google-services.json)
- cmp-desktop (excluding icons)
- cmp-ios (excluding iosApp/Assets.xcassets)
- cmp-web (excluding src/jsMain/resources, src/wasmJsMain/resources)
- cmp-shared
+ - core-base
- build-logic
- fastlane
- scripts
- config
- .github
- .run
-
- Files:
+
+ **Files:**
- Gemfile
- Gemfile.lock
- ci-prepush.bat
- ci-prepush.sh
-
- Root-level exclusions:
+
+ **Root-level exclusions:**
- secrets.env
-
+
+ ---
Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
branch: sync-dirs-${{ github.run_number }}
delete-branch: true
labels: |
sync
automated pr
- base: development
\ No newline at end of file
+ base: dev
diff --git a/.github/workflows/tag-weekly-release.yml b/.github/workflows/tag-weekly-release.yml
index cd90ab15f..9607410db 100644
--- a/.github/workflows/tag-weekly-release.yml
+++ b/.github/workflows/tag-weekly-release.yml
@@ -1,29 +1,104 @@
+# Weekly Release Tagging and Beta Deployment Workflow
+# ===================================================
+
+# Purpose:
+# - Automate weekly version tagging for consistent software versioning
+# - Trigger automated beta releases across multiple platforms
+# - Maintain a predictable release cycle
+
+# Workflow Overview:
+# - Runs automatically every Sunday at 4:00 AM UTC
+# - Supports manual triggering via workflow_dispatch
+# - Utilizes Gradle Reckon plugin for intelligent versioning
+# - Triggers multi-platform build and publish workflow
+
+# Key Features:
+# - Automatic semantic versioning
+# - Cross-platform release automation
+# - Configurable target branch for releases
+# - Full repository history checkout for accurate versioning
+
+# Versioning Strategy:
+# - Uses Reckon Gradle plugin for semantic versioning
+# - Generates production-ready (final) version tags
+# - Provides consistent and predictable version incrementation
+
+# Release Process:
+# 1. Checkout repository with full commit history
+# 2. Setup Java 17 development environment
+# 3. Create and push new version tag
+# 4. Trigger multi-platform build and publish workflow
+
+# Prerequisites:
+# - Gradle project configured with Reckon plugin
+# - Java 17 development environment
+# - Configured multi-platform build workflow
+# - GitHub Actions permissions for workflow dispatch
+
+# Workflow Inputs:
+# - target_branch: Branch to use for releases (default: 'dev')
+# Allows flexible release targeting across different branches
+
+# Security Considerations:
+# - Uses GitHub's native GITHUB_TOKEN for authentication
+# - Controlled workflow dispatch with specific inputs
+# - Limited to authorized repository members
+
+# Potential Use Cases:
+# - Regular software release cycles
+# - Automated beta testing distributions
+# - Consistent multi-platform deployment
+
+# Workflow Triggers:
+# - Scheduled weekly run (Sunday 4:00 AM UTC)
+# - Manual workflow dispatch
+# - Callable from other workflows
+
+# ##############################################################################
+# DON'T EDIT THIS FILE UNLESS NECESSARY #
+# ##############################################################################
+
name: Tag Weekly Release
on:
+ # Allow manual triggering of the workflow
workflow_dispatch:
+ # Schedule the workflow to run weekly
schedule:
+ # Runs at 04:00 UTC every Sunday
+ # Cron syntax: minute hour day-of-month month day-of-week
- cron: '0 4 * * 0'
+
+concurrency:
+ group: "weekly-release"
+ cancel-in-progress: false
+
jobs:
tag:
name: Tag Weekly Release
runs-on: ubuntu-latest
steps:
+ # Checkout the repository with full history for proper versioning
- uses: actions/checkout@v4
with:
fetch-depth: 0
- - name: Set up JDK 21
+ # Setup Java environment for Gradle operations
+ - name: Set up JDK 17
uses: actions/setup-java@v4.2.2
with:
distribution: 'temurin'
- java-version: '21'
+ java-version: '17'
+ # Create and push a new version tag using Reckon
+ # This uses the 'final' stage for production-ready releases
- name: Tag Weekly Release
env:
- GITHUB_TOKEN: ${{ secrets.TAG_PUSH_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew :reckonTagPush -Preckon.stage=final
+ # Trigger the build and publish workflow for beta release
+ # This starts the process of building and deploying the app to various platforms
- name: Trigger Workflow
uses: actions/github-script@v7
with:
@@ -31,9 +106,10 @@ jobs:
github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
- workflow_id: 'android-release.yml',
- ref: 'development',
+ workflow_id: 'multi-platform-build-and-publish.yml',
+ ref: 'dev',
inputs: {
"release_type": "beta",
},
- })
\ No newline at end of file
+ })
+
diff --git a/.run/cmp-android.run.xml b/.run/cmp-android.run.xml
new file mode 100644
index 000000000..3750e7d07
--- /dev/null
+++ b/.run/cmp-android.run.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/cmp-desktop.run.xml b/.run/cmp-desktop.run.xml
index 88186e065..32bd0b3af 100644
--- a/.run/cmp-desktop.run.xml
+++ b/.run/cmp-desktop.run.xml
@@ -1,11 +1,6 @@
-
diff --git a/.run/cmp-web-js.run.xml b/.run/cmp-web-js.run.xml
index 89a8b9653..ae0413e3c 100644
--- a/.run/cmp-web-js.run.xml
+++ b/.run/cmp-web-js.run.xml
@@ -10,7 +10,7 @@
diff --git a/Gemfile b/Gemfile
index 54cbd00f0..090749838 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,14 @@
source "https://rubygems.org"
+ruby '3.3.6'
+
+# Add compatibility gems for Ruby 3.3+
+gem "abbrev"
+gem "base64"
+gem "mutex_m"
+gem "bigdecimal"
+
gem "fastlane"
-gem "cocoapods"
+
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
diff --git a/Gemfile.lock b/Gemfile.lock
index 2d23059c6..fc32bdf26 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -5,101 +5,44 @@ GEM
base64
nkf
rexml
- activesupport (7.2.2.1)
- base64
- benchmark (>= 0.3)
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.3.1)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- logger (>= 1.4.2)
- minitest (>= 5.1)
- securerandom (>= 0.3)
- tzinfo (~> 2.0, >= 2.0.5)
+ abbrev (0.1.2)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
- algoliasearch (1.27.5)
- httpclient (~> 2.8, >= 2.8.3)
- json (>= 1.5.1)
artifactory (3.0.17)
atomos (0.1.3)
- aws-eventstream (1.3.0)
- aws-partitions (1.1048.0)
- aws-sdk-core (3.218.1)
+ aws-eventstream (1.4.0)
+ aws-partitions (1.1174.0)
+ aws-sdk-core (3.233.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
+ bigdecimal
jmespath (~> 1, >= 1.6.1)
- aws-sdk-kms (1.98.0)
- aws-sdk-core (~> 3, >= 3.216.0)
+ logger
+ aws-sdk-kms (1.114.0)
+ aws-sdk-core (~> 3, >= 3.231.0)
aws-sigv4 (~> 1.5)
- aws-sdk-s3 (1.180.0)
- aws-sdk-core (~> 3, >= 3.216.0)
+ aws-sdk-s3 (1.200.0)
+ aws-sdk-core (~> 3, >= 3.231.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
- aws-sigv4 (1.11.0)
+ aws-sigv4 (1.12.1)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
- base64 (0.2.0)
- benchmark (0.3.0)
- bigdecimal (3.2.2)
+ base64 (0.3.0)
+ bigdecimal (3.3.1)
claide (1.1.0)
- cocoapods (1.16.2)
- addressable (~> 2.8)
- claide (>= 1.0.2, < 2.0)
- cocoapods-core (= 1.16.2)
- cocoapods-deintegrate (>= 1.0.3, < 2.0)
- cocoapods-downloader (>= 2.1, < 3.0)
- cocoapods-plugins (>= 1.0.0, < 2.0)
- cocoapods-search (>= 1.0.0, < 2.0)
- cocoapods-trunk (>= 1.6.0, < 2.0)
- cocoapods-try (>= 1.1.0, < 2.0)
- colored2 (~> 3.1)
- escape (~> 0.0.4)
- fourflusher (>= 2.3.0, < 3.0)
- gh_inspector (~> 1.0)
- molinillo (~> 0.8.0)
- nap (~> 1.0)
- ruby-macho (>= 2.3.0, < 3.0)
- xcodeproj (>= 1.27.0, < 2.0)
- cocoapods-core (1.16.2)
- activesupport (>= 5.0, < 8)
- addressable (~> 2.8)
- algoliasearch (~> 1.0)
- concurrent-ruby (~> 1.1)
- fuzzy_match (~> 2.0.4)
- nap (~> 1.0)
- netrc (~> 0.11)
- public_suffix (~> 4.0)
- typhoeus (~> 1.0)
- cocoapods-deintegrate (1.0.5)
- cocoapods-downloader (2.1)
- cocoapods-plugins (1.0.0)
- nap
- cocoapods-search (1.0.1)
- cocoapods-trunk (1.6.0)
- nap (>= 0.8, < 2.0)
- netrc (~> 0.11)
- cocoapods-try (1.2.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
- concurrent-ruby (1.3.5)
- connection_pool (2.5.0)
declarative (0.0.20)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20240107)
dotenv (2.8.1)
- drb (2.2.0)
- ruby2_keywords
emoji_regex (3.2.3)
- escape (0.0.4)
- ethon (0.16.0)
- ffi (>= 1.15.0)
excon (0.112.0)
faraday (1.10.4)
faraday-em_http (~> 1.0)
@@ -117,10 +60,10 @@ GEM
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
- faraday-em_synchrony (1.0.0)
+ faraday-em_synchrony (1.0.1)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
- faraday-multipart (1.1.0)
+ faraday-multipart (1.1.1)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
@@ -130,7 +73,7 @@ GEM
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.4.0)
- fastlane (2.226.0)
+ fastlane (2.228.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -170,17 +113,14 @@ GEM
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
- xcpretty (~> 0.4.0)
+ xcpretty (~> 0.4.1)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
- fastlane-plugin-firebase_app_distribution (0.10.0)
+ fastlane-plugin-firebase_app_distribution (0.10.1)
google-apis-firebaseappdistribution_v1 (~> 0.3.0)
google-apis-firebaseappdistribution_v1alpha (~> 0.2.0)
fastlane-plugin-increment_build_number (0.0.4)
fastlane-sirp (1.0.0)
sysrandom (~> 1.0)
- ffi (1.17.1-arm64-darwin)
- fourflusher (2.3.1)
- fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.54.0)
google-apis-core (>= 0.11.0, < 2.a)
@@ -202,12 +142,12 @@ GEM
google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.31.0)
google-apis-core (>= 0.11.0, < 2.a)
- google-cloud-core (1.7.1)
+ google-cloud-core (1.8.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
- google-cloud-errors (1.4.0)
+ google-cloud-errors (1.5.0)
google-cloud-storage (1.47.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
@@ -225,46 +165,40 @@ GEM
highline (2.0.3)
http-cookie (1.0.8)
domain_name (~> 0.5)
- httpclient (2.8.3)
- i18n (1.14.7)
- concurrent-ruby (~> 1.0)
+ httpclient (2.9.0)
+ mutex_m
jmespath (1.6.2)
- json (2.9.1)
- jwt (2.10.1)
+ json (2.15.1)
+ jwt (2.10.2)
base64
logger (1.7.0)
mini_magick (4.13.2)
mini_mime (1.1.5)
- minitest (5.20.0)
- molinillo (0.8.0)
- multi_json (1.15.0)
+ multi_json (1.17.0)
multipart-post (2.4.1)
+ mutex_m (0.3.0)
nanaimo (0.4.0)
- nap (1.1.0)
- naturally (2.2.1)
- netrc (0.11.0)
+ naturally (2.3.0)
nkf (0.2.0)
optparse (0.6.0)
os (1.1.4)
plist (3.7.2)
- public_suffix (4.0.7)
- rake (13.2.1)
+ public_suffix (6.0.2)
+ rake (13.3.0)
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.4.0)
+ rexml (3.4.4)
rouge (3.28.0)
- ruby-macho (2.5.1)
ruby2_keywords (0.0.5)
rubyzip (2.4.1)
- securerandom (0.3.1)
security (0.1.5)
- signet (0.19.0)
+ signet (0.21.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
- jwt (>= 1.5, < 3.0)
+ jwt (>= 1.5, < 4.0)
multi_json (~> 1.10)
simctl (1.6.10)
CFPropertyList
@@ -278,10 +212,6 @@ GEM
tty-screen (0.8.2)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
- typhoeus (1.4.1)
- ethon (>= 0.9.0)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
uber (0.1.0)
unicode-display_width (2.6.0)
word_wrap (1.0.0)
@@ -292,20 +222,26 @@ GEM
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
- xcpretty (0.4.0)
+ xcpretty (0.4.1)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
- arm64-darwin-24
+ arm64-darwin-25
ruby
DEPENDENCIES
- cocoapods
+ abbrev
+ base64
+ bigdecimal
fastlane
fastlane-plugin-firebase_app_distribution
fastlane-plugin-increment_build_number
+ mutex_m
+
+RUBY VERSION
+ ruby 3.3.6p108
BUNDLED WITH
- 2.5.18
+ 2.7.2
diff --git a/build-logic/README.md b/build-logic/README.md
index f0ea8ede9..3852831e7 100644
--- a/build-logic/README.md
+++ b/build-logic/README.md
@@ -29,10 +29,10 @@ setup.
Current list of convention plugins:
-- [`mifos.android.application`](convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt),
- [`mifos.android.library`](convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt),
- [`mifos.android.test`](convention/src/main/kotlin/AndroidTestConventionPlugin.kt):
+- [`android.application`](convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt),
+ [`android.library`](convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt),
+ [`android.test`](convention/src/main/kotlin/AndroidTestConventionPlugin.kt):
Configures common Android and Kotlin options.
-- [`mifos.android.application.compose`](convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt),
- [`mifos.android.library.compose`](convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt):
+- [`android.application.compose`](convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt),
+ [`android.library.compose`](convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt):
Configures Jetpack Compose options
diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts
index e0168aa06..e696e0505 100644
--- a/build-logic/convention/build.gradle.kts
+++ b/build-logic/convention/build.gradle.kts
@@ -1,20 +1,21 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
`kotlin-dsl`
}
-group = "org.mifos.mobile.buildlogic"
+group = "org.convention.buildlogic"
-// Configure the build-logic plugins to target JDK 17
+// Configure the build-logic plugins to target JDK 19
// This matches the JDK used to build the project, and is not related to what is running on device.
java {
- sourceCompatibility = JavaVersion.VERSION_21
- targetCompatibility = JavaVersion.VERSION_21
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
}
-tasks.withType().configureEach {
- kotlinOptions {
- jvmTarget = JavaVersion.VERSION_21.toString()
+
+kotlin {
+ compilerOptions {
+ jvmTarget = JvmTarget.JVM_17
}
}
@@ -24,14 +25,27 @@ dependencies {
compileOnly(libs.compose.gradlePlugin)
compileOnly(libs.kotlin.gradlePlugin)
compileOnly(libs.ksp.gradlePlugin)
- compileOnly(libs.room.gradlePlugin)
compileOnly(libs.detekt.gradlePlugin)
compileOnly(libs.ktlint.gradlePlugin)
- compileOnly(libs.spotless.gradlePlugin)
+ compileOnly(libs.spotless.gradle)
implementation(libs.truth)
-
+ compileOnly(libs.androidx.room.gradle.plugin)
compileOnly(libs.firebase.crashlytics.gradlePlugin)
compileOnly(libs.firebase.performance.gradlePlugin)
+
+ // Keystore management dependencies
+ implementation(libs.github.api)
+ implementation(libs.okhttp)
+ implementation(libs.jackson.core)
+ implementation(libs.jackson.databind)
+ implementation(libs.jackson.module.kotlin)
+ implementation(libs.commons.codec)
+
+ // Test dependencies for keystore management
+ testImplementation(libs.junit.jupiter.api)
+ testImplementation(libs.junit.jupiter.engine)
+ testImplementation(libs.junit.jupiter.params)
+ testRuntimeOnly(libs.platform.junit.platform.launcher)
}
tasks {
@@ -39,21 +53,30 @@ tasks {
enableStricterValidation = true
failOnWarning = true
}
+
+ // Configure JUnit 5 for testing keystore management functionality
+ test {
+ useJUnitPlatform()
+ testLogging {
+ events("passed", "skipped", "failed")
+ }
+ }
}
gradlePlugin {
plugins {
- register("androidApplication") {
- id = "mifos.android.application"
- implementationClass = "AndroidApplicationConventionPlugin"
- }
+ // Android Plugins
register("androidApplicationCompose") {
- id = "mifos.android.application.compose"
+ id = "org.convention.android.application.compose"
implementationClass = "AndroidApplicationComposeConventionPlugin"
}
+ register("androidApplication") {
+ id = "org.convention.android.application"
+ implementationClass = "AndroidApplicationConventionPlugin"
+ }
register("androidFlavors") {
- id = "mifos.android.application.flavors"
+ id = "org.convention.android.application.flavors"
implementationClass = "AndroidApplicationFlavorsConventionPlugin"
}
@@ -67,45 +90,6 @@ gradlePlugin {
implementationClass = "AndroidLintConventionPlugin"
}
- // This can removed after migration
- register("androidLibrary") {
- id = "mifos.android.library"
- implementationClass = "AndroidLibraryConventionPlugin"
- }
-
- register("androidLibraryCompose") {
- id = "mifos.android.library.compose"
- implementationClass = "AndroidLibraryComposeConventionPlugin"
- }
-
- register("androidFeature") {
- id = "mifos.android.feature"
- implementationClass = "AndroidFeatureConventionPlugin"
- }
-
- // Room Plugin
- register("kmpRoom") {
- id = "mifos.kmp.room"
- implementationClass = "KMPRoomConventionPlugin"
- }
-
- // Utility Plugins
- register("detekt") {
- id = "mifos.detekt.plugin"
- implementationClass = "MifosDetektConventionPlugin"
- description = "Configures detekt for the project"
- }
- register("spotless") {
- id = "mifos.spotless.plugin"
- implementationClass = "MifosSpotlessConventionPlugin"
- description = "Configures spotless for the project"
- }
- register("gitHooks") {
- id = "mifos.git.hooks"
- implementationClass = "MifosGitHooksConventionPlugin"
- description = "Installs git hooks for the project"
- }
-
// KMP & CMP Plugins
register("cmpFeature") {
id = "org.convention.cmp.feature"
@@ -116,10 +100,47 @@ gradlePlugin {
id = "org.convention.kmp.koin"
implementationClass = "KMPKoinConventionPlugin"
}
-
register("kmpLibrary") {
id = "org.convention.kmp.library"
implementationClass = "KMPLibraryConventionPlugin"
}
+
+ // Static Analysis & Formatting Plugins
+ register("detekt") {
+ id = "org.convention.detekt.plugin"
+ implementationClass = "DetektConventionPlugin"
+ description = "Configures detekt for the project"
+ }
+ register("spotless") {
+ id = "org.convention.spotless.plugin"
+ implementationClass = "SpotlessConventionPlugin"
+ description = "Configures spotless for the project"
+ }
+ register("ktlint") {
+ id = "org.convention.ktlint.plugin"
+ implementationClass = "KtlintConventionPlugin"
+ description = "Configures kotlinter for the project"
+ }
+ register("gitHooks") {
+ id = "org.convention.git.hooks"
+ implementationClass = "GitHooksConventionPlugin"
+ description = "Installs git hooks for the project"
+ }
+
+// Room Plugin
+ register("KMPRoom"){
+ id = "mifos.kmp.room"
+ implementationClass = "KMPRoomConventionPlugin"
+ description = "Configures Room for the project"
+ }
+
+ // NEW ===============================
+
+ register("keystoreManagement") {
+ id = "org.convention.keystore.management"
+ implementationClass = "KeystoreManagementConventionPlugin"
+ description = "Configures keystore management tasks for the project"
+ }
+
}
}
diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt
index 2ec83e383..7b0ab73ef 100644
--- a/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt
@@ -1,10 +1,13 @@
import com.android.build.api.dsl.ApplicationExtension
+import org.convention.configureAndroidCompose
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
-import org.mifos.mobile.configureAndroidCompose
+/**
+ * Plugin that applies the Android application and Compose plugins and configures them.
+ */
class AndroidApplicationComposeConventionPlugin : Plugin {
override fun apply(target: Project) {
with(target) {
diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt
index 8f19540fa..6aa8c9474 100644
--- a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt
@@ -1,37 +1,34 @@
+
import com.android.build.api.dsl.ApplicationExtension
-import com.android.build.api.variant.ApplicationAndroidComponentsExtension
-import com.android.build.gradle.BaseExtension
+import org.convention.configureGradleManagedDevices
+import org.convention.configureKotlinAndroid
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
-import org.gradle.kotlin.dsl.getByType
-import org.mifos.mobile.configureBadgingTasks
-import org.mifos.mobile.configureKotlinAndroid
-import org.mifos.mobile.configurePrintApksTask
+/**
+ * Plugin that applies the Android application plugin and configures it.
+ */
class AndroidApplicationConventionPlugin : Plugin {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.application")
apply("org.jetbrains.kotlin.android")
-
apply("com.dropbox.dependency-guard")
- apply("mifos.detekt.plugin")
- apply("mifos.spotless.plugin")
- apply("mifos.git.hooks")
+ apply("org.convention.detekt.plugin")
+ apply("org.convention.spotless.plugin")
+ apply("org.convention.git.hooks")
apply("org.convention.android.application.lint")
apply("org.convention.android.application.firebase")
-
}
extensions.configure {
configureKotlinAndroid(this)
- defaultConfig.targetSdk = 34
- }
- extensions.configure {
- configurePrintApksTask(this)
- configureBadgingTasks(extensions.getByType(), this)
+ defaultConfig.targetSdk = 36
+ @Suppress("UnstableApiUsage")
+ testOptions.animationsDisabled = true
+ configureGradleManagedDevices(this)
}
}
}
diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt
index 818904484..f9acb889c 100644
--- a/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidApplicationFirebaseConventionPlugin.kt
@@ -1,13 +1,27 @@
-
+/*
+ * Copyright 2022 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.google.firebase.crashlytics.buildtools.gradle.CrashlyticsExtension
+import org.convention.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
-import org.mifos.mobile.libs
class AndroidApplicationFirebaseConventionPlugin : Plugin {
override fun apply(target: Project) {
@@ -34,4 +48,4 @@ class AndroidApplicationFirebaseConventionPlugin : Plugin {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationFlavorsConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationFlavorsConventionPlugin.kt
index 3a7394b66..83759a99a 100644
--- a/build-logic/convention/src/main/kotlin/AndroidApplicationFlavorsConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidApplicationFlavorsConventionPlugin.kt
@@ -2,8 +2,11 @@ import com.android.build.api.dsl.ApplicationExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
-import org.mifos.mobile.configureFlavors
+import org.convention.configureFlavors
+/**
+ * Plugin that applies the Android application flavors plugin and configures it.
+ */
class AndroidApplicationFlavorsConventionPlugin : Plugin {
override fun apply(target: Project) {
with(target) {
diff --git a/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt
index 09fdb72cc..884d6f076 100644
--- a/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2022 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.dsl.LibraryExtension
@@ -31,4 +46,4 @@ private fun Lint.configure() {
sarifReport = true
checkDependencies = true
disable += "GradleDependency"
-}
\ No newline at end of file
+}
diff --git a/build-logic/convention/src/main/kotlin/CMPFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/CMPFeatureConventionPlugin.kt
index ce47af4bf..c2531dce4 100644
--- a/build-logic/convention/src/main/kotlin/CMPFeatureConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/CMPFeatureConventionPlugin.kt
@@ -1,41 +1,59 @@
+import org.convention.libs
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
-import org.mifos.mobile.libs
+/**
+ * Plugin that applies the CMP feature plugin and configures it.
+ * This plugin applies the following plugins:
+ * - org.mifos.kmp.library - Kotlin Multiplatform Library
+ * - org.mifos.kmp.koin - Koin for Kotlin Multiplatform
+ * - org.jetbrains.kotlin.plugin.compose - Kotlin Compose
+ * - org.jetbrains.compose - Compose Multiplatform
+ * - org.mifos.detekt.plugin - Detekt Plugin
+ * - org.mifos.spotless.plugin - Spotless Plugin
+ *
+ */
class CMPFeatureConventionPlugin : Plugin {
-
override fun apply(target: Project) {
with(target) {
- with(pluginManager) {
+ pluginManager.apply {
apply("org.convention.kmp.library")
apply("org.convention.kmp.koin")
apply("org.jetbrains.kotlin.plugin.compose")
apply("org.jetbrains.compose")
+ apply("org.convention.detekt.plugin")
+ apply("org.convention.spotless.plugin")
}
dependencies {
add("commonMainImplementation", project(":core:ui"))
+ add("commonMainImplementation", project(":core-base:ui"))
add("commonMainImplementation", project(":core:designsystem"))
-// add("commonMainImplementation", project(":core:testing"))
+ add("commonMainImplementation", project(":core-base:designsystem"))
add("commonMainImplementation", project(":core:data"))
+ add("commonMainImplementation", project(":core-base:designsystem"))
+ add("commonMainImplementation", project(":core:analytics"))
add("commonMainImplementation", libs.findLibrary("koin.compose").get())
add("commonMainImplementation", libs.findLibrary("koin.compose.viewmodel").get())
add("commonMainImplementation", libs.findLibrary("jb.composeRuntime").get())
- add("commonMainImplementation", libs.findLibrary("jb.lifecycle.compose").get())
add("commonMainImplementation", libs.findLibrary("jb.composeViewmodel").get())
add("commonMainImplementation", libs.findLibrary("jb.lifecycleViewmodel").get())
- add("commonMainImplementation", libs.findLibrary("jb.lifecycleViewmodelSavedState").get())
+ add("commonMainImplementation", libs.findLibrary("jb.lifecycle.compose").get())
+
+ add(
+ "commonMainImplementation",
+ libs.findLibrary("jb.lifecycleViewmodelSavedState").get(),
+ )
add("commonMainImplementation", libs.findLibrary("jb.savedstate").get())
add("commonMainImplementation", libs.findLibrary("jb.bundle").get())
add("commonMainImplementation", libs.findLibrary("jb.composeNavigation").get())
- add("commonMainImplementation", libs.findLibrary("kotlinx.collections.immutable").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())
+ add(
+ "commonMainImplementation",
+ libs.findLibrary("kotlinx.collections.immutable").get(),
+ )
add("androidMainImplementation", platform(libs.findLibrary("koin-bom").get()))
add("androidMainImplementation", libs.findLibrary("koin-android").get())
@@ -46,12 +64,7 @@ class CMPFeatureConventionPlugin : Plugin {
add("androidMainImplementation", libs.findLibrary("koin.androidx.compose").get())
add("androidMainImplementation", libs.findLibrary("koin.core.viewmodel").get())
- add("androidTestImplementation", libs.findLibrary("koin.test.junit4").get())
-
- add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.navigation.testing").get())
- add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.compose.ui.test").get())
- add("androidInstrumentedTestImplementation", libs.findLibrary("androidx.lifecycle.runtimeTesting").get())
}
}
}
-}
\ No newline at end of file
+}
diff --git a/build-logic/convention/src/main/kotlin/DetektConventionPlugin.kt b/build-logic/convention/src/main/kotlin/DetektConventionPlugin.kt
new file mode 100644
index 000000000..363f9df50
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/DetektConventionPlugin.kt
@@ -0,0 +1,26 @@
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.convention.configureDetekt
+import org.convention.detektGradle
+
+/**
+ * Plugin that applies the Detekt plugin and configures it.
+ */
+class DetektConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ applyPlugins()
+
+ detektGradle {
+ configureDetekt(this)
+ }
+ }
+ }
+
+ private fun Project.applyPlugins() {
+ pluginManager.apply {
+ apply("io.gitlab.arturbosch.detekt")
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/FieldSkippingClassVisitor.kt b/build-logic/convention/src/main/kotlin/FieldSkippingClassVisitor.kt
index b632c04fb..8074f3422 100644
--- a/build-logic/convention/src/main/kotlin/FieldSkippingClassVisitor.kt
+++ b/build-logic/convention/src/main/kotlin/FieldSkippingClassVisitor.kt
@@ -31,8 +31,8 @@ class FieldSkippingClassVisitor(
override fun createClassVisitor(
classContext: ClassContext,
- nextClassVisitor: ClassVisitor,
- ): ClassVisitor {
+ nextClassVisitor: org.objectweb.asm.ClassVisitor,
+ ): org.objectweb.asm.ClassVisitor {
return FieldSkippingClassVisitor(
apiVersion = instrumentationContext.apiVersion.get(),
nextClassVisitor = nextClassVisitor,
diff --git a/build-logic/convention/src/main/kotlin/GitHooksConventionPlugin.kt b/build-logic/convention/src/main/kotlin/GitHooksConventionPlugin.kt
new file mode 100644
index 000000000..226e8d2d2
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/GitHooksConventionPlugin.kt
@@ -0,0 +1,57 @@
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.Exec
+import org.gradle.kotlin.dsl.register
+import java.util.Locale
+
+/**
+ * Plugin that installs the pre-commit git hooks from the scripts directory.
+ */
+class GitHooksConventionPlugin : Plugin {
+ override fun apply(project: Project) {
+ // Define a function to check if the OS is Linux or MacOS
+ fun isLinuxOrMacOs(): Boolean {
+ val osName = System.getProperty("os.name").lowercase(Locale.getDefault())
+ return osName.contains("linux") || osName.contains("mac os") || osName.contains("macos")
+ }
+
+ // Define the copyGitHooks task
+ project.tasks.register("copyGitHooks") {
+ description = "Copies the git hooks from /scripts to the .git/hooks folder."
+ from("${project.rootDir}/scripts/") {
+ include("**/*.sh")
+ rename { it.removeSuffix(".sh") }
+ }
+ into("${project.rootDir}/.git/hooks")
+ }
+
+ // Define the installGitHooks task
+ project.tasks.register("installGitHooks") {
+ description = "Installs the pre-commit git hooks from the scripts directory."
+ group = "git hooks"
+ workingDir = project.rootDir
+
+ if (isLinuxOrMacOs()) {
+ commandLine("chmod", "-R", "+x", ".git/hooks/")
+ }else {
+ commandLine("cmd", "/c", "attrib", "-R", "+X", ".git/hooks/*.*")
+ }
+ dependsOn(project.tasks.named("copyGitHooks"))
+
+ doLast {
+ println("Git hooks installed successfully.")
+ }
+ }
+
+ // Configure task dependencies after evaluation
+ project.afterEvaluate {
+ project.tasks.matching {
+ it.name in listOf("preBuild", "build", "assembleDebug", "assembleRelease", "installDebug", "installRelease", "clean")
+ }.configureEach {
+ dependsOn(project.tasks.named("installGitHooks"))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/KMPKoinConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPKoinConventionPlugin.kt
index 27c413da7..39149ccc4 100644
--- a/build-logic/convention/src/main/kotlin/KMPKoinConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/KMPKoinConventionPlugin.kt
@@ -1,15 +1,16 @@
-import com.google.devtools.ksp.gradle.KspExtension
+
+import org.convention.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.mifos.mobile.libs
+/**
+ * Plugin that applies the Koin plugin and configures it.
+ */
class KMPKoinConventionPlugin : Plugin {
-
override fun apply(target: Project) {
- with(target){
- with(pluginManager){
+ with(target) {
+ with(pluginManager) {
apply("com.google.devtools.ksp")
}
@@ -18,14 +19,9 @@ class KMPKoinConventionPlugin : Plugin {
add("commonMainImplementation", platform(bom))
add("commonMainImplementation", libs.findLibrary("koin.core").get())
add("commonMainImplementation", libs.findLibrary("koin.annotations").get())
- add("kspCommonMainMetadata", libs.findLibrary("koin.ksp.compiler").get())
- add("commonTestImplementation", libs.findLibrary("koin.test").get())
- }
- extensions.configure {
- arg("KOIN_CONFIG_CHECK","true")
+ add("commonTestImplementation", libs.findLibrary("koin.test").get())
}
}
}
-
-}
\ No newline at end of file
+}
diff --git a/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt
index 33aa493e6..8131dead7 100644
--- a/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt
@@ -1,35 +1,38 @@
+
import com.android.build.gradle.LibraryExtension
+import org.convention.configureFlavors
+import org.convention.configureKotlinAndroid
+import org.convention.configureKotlinMultiplatform
+import org.convention.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.mifos.mobile.configureFlavors
-import org.mifos.mobile.configureKotlinAndroid
-import org.mifos.mobile.configureKotlinMultiplatform
-import org.mifos.mobile.libs
-
-class KMPLibraryConventionPlugin : Plugin {
+/**
+ * Plugin that applies the Android library and Kotlin multiplatform plugins and configures them.
+ */
+class KMPLibraryConventionPlugin: Plugin {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.library")
apply("org.jetbrains.kotlin.multiplatform")
apply("org.convention.kmp.koin")
- apply("mifos.detekt.plugin")
- apply("mifos.spotless.plugin")
+ apply("org.convention.detekt.plugin")
+ apply("org.convention.spotless.plugin")
+ apply("org.jetbrains.kotlin.plugin.serialization")
+ apply("org.jetbrains.kotlin.plugin.parcelize")
}
configureKotlinMultiplatform()
extensions.configure {
configureKotlinAndroid(this)
- defaultConfig.targetSdk = 34
+ defaultConfig.targetSdk = 36
configureFlavors(this)
- /**
- * The resource prefix is derived from the module name,
- * so resources inside ":core:module1" must be prefixed with "core_module1_"
- */
+ // The resource prefix is derived from the module name,
+ // so resources inside ":core:module1" must be prefixed with "core_module1_"
resourcePrefix = path
.split("""\W""".toRegex())
.drop(1).distinct()
@@ -38,6 +41,7 @@ class KMPLibraryConventionPlugin : Plugin {
}
dependencies {
+ add("commonMainImplementation", libs.findLibrary("kotlinx.serialization.json").get())
add("commonTestImplementation", libs.findLibrary("kotlin.test").get())
add("commonTestImplementation", libs.findLibrary("kotlinx.coroutines.test").get())
}
diff --git a/build-logic/convention/src/main/kotlin/KMPRoomConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPRoomConventionPlugin.kt
index 6edd9041b..2a2de3ae9 100644
--- a/build-logic/convention/src/main/kotlin/KMPRoomConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/KMPRoomConventionPlugin.kt
@@ -1,10 +1,10 @@
import androidx.room.gradle.RoomExtension
import com.google.devtools.ksp.gradle.KspExtension
+import org.convention.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.mifos.mobile.libs
class KMPRoomConventionPlugin : Plugin {
override fun apply(target: Project) {
@@ -24,9 +24,8 @@ class KMPRoomConventionPlugin : Plugin {
}
dependencies {
- "implementation"(libs.findLibrary("androidx.room.runtime").get())
- "implementation"(libs.findLibrary("androidx.room.ktx").get())
// Adding ksp dependencies for multiple platforms
+ "implementation"(libs.findLibrary("androidx.room.ktx").get())
listOf(
"kspDesktop",
"kspAndroid",
@@ -36,8 +35,10 @@ class KMPRoomConventionPlugin : Plugin {
// Add any other platform you may support
).forEach { platform ->
add(platform, libs.findLibrary("androidx.room.compiler").get())
+// Kotlin Extensions and Coroutines support for Room
+// add(platform, libs.findLibrary("androidx.room.ktx").get())
}
}
}
}
-}
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/KeystoreManagementConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KeystoreManagementConventionPlugin.kt
new file mode 100644
index 000000000..381e41e77
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/KeystoreManagementConventionPlugin.kt
@@ -0,0 +1,153 @@
+import org.convention.keystore.ConfigurationFileUpdatesTask
+import org.convention.keystore.KeystoreConfig
+import org.convention.keystore.KeystoreGenerationTask
+import org.convention.keystore.SecretsConfig
+import org.convention.keystore.SecretsEnvUpdateTask
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+/**
+ * Convention plugin for keystore management following your existing patterns
+ */
+class KeystoreManagementConventionPlugin : Plugin {
+
+ override fun apply(target: Project) {
+ with(target) {
+ // Create extension for configuration
+ val keystoreExtension = extensions.create("keystoreManagement", KeystoreManagementExtension::class.java)
+
+ // Set default configurations
+ keystoreExtension.keystoreConfig.convention(KeystoreConfig())
+ keystoreExtension.secretsConfig.convention(SecretsConfig())
+
+ // Register the keystore generation task
+ val generateKeystoresTask = tasks.register("generateKeystores", KeystoreGenerationTask::class.java) {
+ // Configure task with extension values
+ keystoreConfig.set(keystoreExtension.keystoreConfig)
+ secretsConfig.set(keystoreExtension.secretsConfig)
+
+ // Load configuration from secrets.env if it exists
+ KeystoreGenerationTask.createWithSecretsConfig(this, keystoreExtension.secretsConfig.get())
+ }
+
+ // Register configuration file updates task
+ val updateConfigFilesTask = tasks.register("updateConfigurationFiles", ConfigurationFileUpdatesTask::class.java) {
+ // Load configuration from secrets.env if it exists
+ ConfigurationFileUpdatesTask.createWithSecretsConfig(this, keystoreExtension.secretsConfig.get())
+ }
+
+ // Register secrets.env update task (KMPPT-57)
+ val updateSecretsEnvTask = tasks.register("updateSecretsEnv", SecretsEnvUpdateTask::class.java) {
+ // Configure to use keystores from generation task
+ SecretsEnvUpdateTask.createFromKeystoreGeneration(this, generateKeystoresTask.get(), keystoreExtension.secretsConfig.get())
+ }
+
+ // Register combined task that generates keystores and updates config files
+ tasks.register("generateKeystoresAndUpdateConfigs", KeystoreGenerationTask::class.java) {
+ keystoreConfig.set(keystoreExtension.keystoreConfig)
+ secretsConfig.set(keystoreExtension.secretsConfig)
+
+ KeystoreGenerationTask.createWithSecretsConfig(this, keystoreExtension.secretsConfig.get())
+
+ // Configure the update tasks to run after this task
+ finalizedBy(updateConfigFilesTask)
+ finalizedBy(updateSecretsEnvTask)
+ }
+
+ // Configure the update task to use generated keystores
+ updateConfigFilesTask.configure {
+ // Set dependency on keystore generation
+ dependsOn(generateKeystoresTask)
+
+ // Configure to use upload keystore from generation task
+ ConfigurationFileUpdatesTask.createForUploadKeystore(this, generateKeystoresTask.get())
+ }
+
+ // Register convenience tasks for individual keystore types
+ tasks.register("generateOriginalKeystore", KeystoreGenerationTask::class.java) {
+ keystoreConfig.set(keystoreExtension.keystoreConfig)
+ secretsConfig.set(keystoreExtension.secretsConfig)
+ generateOriginal.set(true)
+ generateUpload.set(false)
+
+ KeystoreGenerationTask.createWithSecretsConfig(this, keystoreExtension.secretsConfig.get())
+ }
+
+ tasks.register("generateUploadKeystore", KeystoreGenerationTask::class.java) {
+ keystoreConfig.set(keystoreExtension.keystoreConfig)
+ secretsConfig.set(keystoreExtension.secretsConfig)
+ generateOriginal.set(false)
+ generateUpload.set(true)
+
+ KeystoreGenerationTask.createWithSecretsConfig(this, keystoreExtension.secretsConfig.get())
+ }
+
+ // Add task group description
+ tasks.register("keystoreHelp") {
+ group = "keystore"
+ description = "Shows available keystore management commands"
+ doLast {
+ logger.lifecycle("""
+ |Keystore Management Plugin - Available Tasks:
+ |
+ |Generation Tasks:
+ | - generateKeystores: Generate both ORIGINAL and UPLOAD keystores
+ | - generateOriginalKeystore: Generate only the ORIGINAL (debug) keystore
+ | - generateUploadKeystore: Generate only the UPLOAD (release) keystore
+ | - generateKeystoresAndUpdateConfigs: Generate keystores and update config files
+ |
+ |Configuration Update Tasks:
+ | - updateConfigurationFiles: Update fastlane and gradle config files with keystore info
+ | - updateSecretsEnv: Update secrets.env with base64-encoded keystores (KMPPT-57)
+ |
+ |Help Tasks:
+ | - keystoreHelp: Shows this help message
+ |
+ |Configuration:
+ | The plugin automatically loads configuration from 'secrets.env' if it exists.
+ | You can also configure manually in build.gradle.kts:
+ |
+ | keystoreManagement {
+ | keystoreConfig {
+ | companyName = "Your Company Name"
+ | department = "Your Department"
+ | organization = "Your Organization"
+ | city = "Your City"
+ | state = "Your State"
+ | country = "US"
+ | keyAlgorithm = "RSA"
+ | keySize = 2048
+ | validity = 25
+ | overwriteExisting = false
+ | }
+ | secretsConfig {
+ | secretsEnvFile = file("secrets.env")
+ | preserveComments = true
+ | createBackup = true
+ | }
+ | }
+ |
+ |Usage Examples:
+ | ./gradlew generateKeystores # Generate both keystores
+ | ./gradlew generateOriginalKeystore # Generate debug keystore only
+ | ./gradlew generateUploadKeystore # Generate release keystore only
+ | ./gradlew generateKeystoresAndUpdateConfigs # Generate keystores and update configs
+ | ./gradlew updateConfigurationFiles # Update config files only
+ | ./gradlew updateSecretsEnv # Update secrets.env with base64 keystores
+ |
+ |Note: This task replicates the functionality of keystore-manager.sh
+ | with better cross-platform compatibility and Gradle integration.
+ """.trimMargin())
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Extension class for keystore management configuration
+ */
+abstract class KeystoreManagementExtension {
+ abstract val keystoreConfig: org.gradle.api.provider.Property
+ abstract val secretsConfig: org.gradle.api.provider.Property
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/KtlintConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KtlintConventionPlugin.kt
new file mode 100644
index 000000000..da7e3c06c
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/KtlintConventionPlugin.kt
@@ -0,0 +1,19 @@
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+/**
+ * Plugin that applies the Ktlint plugin and configures it.
+ */
+class KtlintConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ applyPlugins()
+ }
+ }
+
+ private fun Project.applyPlugins() {
+ pluginManager.apply {
+ apply("org.jlleitschuh.gradle.ktlint")
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/SpotlessConventionPlugin.kt b/build-logic/convention/src/main/kotlin/SpotlessConventionPlugin.kt
new file mode 100644
index 000000000..771e67a20
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/SpotlessConventionPlugin.kt
@@ -0,0 +1,25 @@
+import org.convention.configureSpotless
+import org.convention.spotlessGradle
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+/**
+ * Plugin that applies the Spotless plugin and configures it.
+ */
+class SpotlessConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ applyPlugins()
+
+ spotlessGradle {
+ configureSpotless(this)
+ }
+ }
+ }
+
+ private fun Project.applyPlugins() {
+ pluginManager.apply {
+ apply("com.diffplug.spotless")
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/convention/AndroidCompose.kt b/build-logic/convention/src/main/kotlin/org/convention/AndroidCompose.kt
new file mode 100644
index 000000000..ed68bb27b
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/AndroidCompose.kt
@@ -0,0 +1,63 @@
+package org.convention
+
+import com.android.build.api.dsl.CommonExtension
+import org.gradle.api.Project
+import org.gradle.api.provider.Provider
+import org.gradle.kotlin.dsl.assign
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.dependencies
+import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension
+
+/**
+ * Configure Compose-specific options
+ */
+internal fun Project.configureAndroidCompose(
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
+) {
+ commonExtension.apply {
+ buildFeatures {
+ compose = true
+ }
+
+ dependencies {
+ val bom = libs.findLibrary("androidx-compose-bom").get()
+ add("implementation", platform(bom))
+ add("androidTestImplementation", platform(bom))
+
+ add("implementation", libs.findLibrary("androidx-compose-ui-tooling-preview").get())
+ add("debugImplementation", libs.findLibrary("androidx-compose-ui-tooling").get())
+ }
+
+ testOptions {
+ unitTests {
+ // For Robolectric
+ isIncludeAndroidResources = true
+
+ isReturnDefaultValues = true
+
+ all {
+ it.systemProperties["robolectric.pixelCopyRenderMode"] = "hardware"
+ }
+ }
+ }
+
+ }
+
+ extensions.configure {
+ fun Provider.onlyIfTrue() = flatMap { provider { it.takeIf(String::toBoolean) } }
+ fun Provider<*>.relativeToRootProject(dir: String) = flatMap {
+ rootProject.layout.buildDirectory.dir(projectDir.toRelativeString(rootDir))
+ }.map { it.dir(dir) }
+
+ project.providers.gradleProperty("enableComposeCompilerMetrics").onlyIfTrue()
+ .relativeToRootProject("compose-metrics")
+ .let(metricsDestination::set)
+
+ project.providers.gradleProperty("enableComposeCompilerReports").onlyIfTrue()
+ .relativeToRootProject("compose-reports")
+ .let(reportsDestination::set)
+
+ stabilityConfigurationFiles
+ .add(isolated.rootProject.projectDirectory.file("compose_compiler_config.conf"))
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/convention/AndroidInstrumentedTests.kt b/build-logic/convention/src/main/kotlin/org/convention/AndroidInstrumentedTests.kt
new file mode 100644
index 000000000..e6688d580
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/AndroidInstrumentedTests.kt
@@ -0,0 +1,19 @@
+package org.convention
+
+import com.android.build.api.variant.LibraryAndroidComponentsExtension
+import org.gradle.api.Project
+
+/**
+ * Disable unnecessary Android instrumented tests for the [project] if there is no `androidTest` folder.
+ * Otherwise, these projects would be compiled, packaged, installed and ran only to end-up with the following message:
+ *
+ * > Starting 0 tests on AVD
+ *
+ * Note: this could be improved by checking other potential sourceSets based on buildTypes and flavors.
+ */
+internal fun LibraryAndroidComponentsExtension.disableUnnecessaryAndroidTests(
+ project: Project,
+) = beforeVariants {
+ it.enableAndroidTest = it.enableAndroidTest
+ && project.projectDir.resolve("src/androidTest").exists()
+}
diff --git a/build-logic/convention/src/main/kotlin/org/convention/AppBuildType.kt b/build-logic/convention/src/main/kotlin/org/convention/AppBuildType.kt
new file mode 100644
index 000000000..a663be520
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/AppBuildType.kt
@@ -0,0 +1,9 @@
+package org.convention
+
+/**
+ * This is shared between :app and :benchmarks module to provide configurations type safety.
+ */
+enum class AppBuildType(val applicationIdSuffix: String? = null) {
+ DEBUG(".debug"),
+ RELEASE,
+}
diff --git a/build-logic/convention/src/main/kotlin/org/convention/AppFlavor.kt b/build-logic/convention/src/main/kotlin/org/convention/AppFlavor.kt
new file mode 100644
index 000000000..095a42f78
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/AppFlavor.kt
@@ -0,0 +1,48 @@
+package org.convention
+
+import com.android.build.api.dsl.ApplicationExtension
+import com.android.build.api.dsl.ApplicationProductFlavor
+import com.android.build.api.dsl.CommonExtension
+import com.android.build.api.dsl.ProductFlavor
+
+@Suppress("EnumEntryName")
+enum class FlavorDimension {
+ contentType
+}
+
+// The content for the app can either come from local static data which is useful for demo
+// purposes, or from a production backend server which supplies up-to-date, real content.
+// These two product flavors reflect this behaviour.
+@Suppress("EnumEntryName")
+enum class AppFlavor(val dimension: FlavorDimension, val applicationIdSuffix: String? = null) {
+ demo(FlavorDimension.contentType, applicationIdSuffix = ".demo"),
+ prod(FlavorDimension.contentType)
+}
+
+/**
+ * Configure product flavors for the app module
+ * @param commonExtension the common extension for the app module
+ * @param flavorConfigurationBlock the configuration block for each flavor
+ * @see AppFlavor
+ */
+fun configureFlavors(
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
+ flavorConfigurationBlock: ProductFlavor.(flavor: AppFlavor) -> Unit = {},
+) {
+ commonExtension.apply {
+ flavorDimensions += FlavorDimension.contentType.name
+ productFlavors {
+ AppFlavor.values().forEach {
+ create(it.name) {
+ dimension = it.dimension.name
+ flavorConfigurationBlock(this, it)
+ if (this@apply is ApplicationExtension && this is ApplicationProductFlavor) {
+ if (it.applicationIdSuffix != null) {
+ applicationIdSuffix = it.applicationIdSuffix
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/kotlin/org/convention/Badging.kt b/build-logic/convention/src/main/kotlin/org/convention/Badging.kt
new file mode 100644
index 000000000..bc5c5a0a9
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/Badging.kt
@@ -0,0 +1,156 @@
+package org.convention
+
+import com.android.build.api.artifact.SingleArtifact
+import com.android.build.api.variant.ApplicationAndroidComponentsExtension
+import com.android.build.gradle.BaseExtension
+import com.android.SdkConstants
+import com.google.common.truth.Truth.assertWithMessage
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Copy
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import org.gradle.configurationcache.extensions.capitalized
+import org.gradle.kotlin.dsl.register
+import org.gradle.language.base.plugins.LifecycleBasePlugin
+import org.gradle.process.ExecOperations
+import java.io.File
+import java.util.Locale
+import javax.inject.Inject
+
+/**
+ * Generates the badging information of the APK.
+ * This task is cacheable, meaning that if the inputs and outputs have not changed,
+ * the task will be considered up-to-date and will not run.
+ * This task is also incremental, meaning that if the inputs have not changed,
+ *
+ */
+@CacheableTask
+abstract class GenerateBadgingTask : DefaultTask() {
+
+ @get:OutputFile
+ abstract val badging: RegularFileProperty
+
+ @get:PathSensitive(PathSensitivity.NONE)
+ @get:InputFile
+ abstract val apk: RegularFileProperty
+
+ @get:PathSensitive(PathSensitivity.NONE)
+ @get:InputFile
+ abstract val aapt2Executable: RegularFileProperty
+
+ @get:Inject
+ abstract val execOperations: ExecOperations
+
+ @TaskAction
+ fun taskAction() {
+ execOperations.exec {
+ commandLine(
+ aapt2Executable.get().asFile.absolutePath,
+ "dump",
+ "badging",
+ apk.get().asFile.absolutePath,
+ )
+ standardOutput = badging.asFile.get().outputStream()
+ }
+ }
+}
+
+@CacheableTask
+abstract class CheckBadgingTask : DefaultTask() {
+
+ // In order for the task to be up-to-date when the inputs have not changed,
+ // the task must declare an output, even if it's not used. Tasks with no
+ // output are always run regardless of whether the inputs changed
+ @get:OutputDirectory
+ abstract val output: DirectoryProperty
+
+ @get:PathSensitive(PathSensitivity.NONE)
+ @get:InputFile
+ abstract val goldenBadging: RegularFileProperty
+
+ @get:PathSensitive(PathSensitivity.NONE)
+ @get:InputFile
+ abstract val generatedBadging: RegularFileProperty
+
+ @get:Input
+ abstract val updateBadgingTaskName: Property
+
+ override fun getGroup(): String = LifecycleBasePlugin.VERIFICATION_GROUP
+
+ @TaskAction
+ fun taskAction() {
+ assertWithMessage(
+ "Generated badging is different from golden badging! " +
+ "If this change is intended, run ./gradlew ${updateBadgingTaskName.get()}",
+ )
+ .that(generatedBadging.get().asFile.readText())
+ .isEqualTo(goldenBadging.get().asFile.readText())
+ }
+}
+
+fun Project.configureBadgingTasks(
+ baseExtension: BaseExtension,
+ componentsExtension: ApplicationAndroidComponentsExtension,
+) {
+ // Registers a callback to be called, when a new variant is configured
+ componentsExtension.onVariants { variant ->
+ // Registers a new task to verify the app bundle.
+ val capitalizedVariantName = variant.name.let {
+ if (it.isEmpty()) it else it[0].titlecase(
+ Locale.getDefault(),
+ ) + it.substring(1)
+ }
+ val generateBadgingTaskName = "generate${capitalizedVariantName}Badging"
+ val generateBadging =
+ tasks.register(generateBadgingTaskName) {
+ apk.set(
+ variant.artifacts.get(SingleArtifact.APK_FROM_BUNDLE),
+ )
+ aapt2Executable.set(
+ File(
+ baseExtension.sdkDirectory,
+ "${SdkConstants.FD_BUILD_TOOLS}/" +
+ "${baseExtension.buildToolsVersion}/" +
+ SdkConstants.FN_AAPT2,
+ ),
+ )
+
+ badging.set(
+ project.layout.buildDirectory.file(
+ "outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt",
+ ),
+ )
+ }
+
+ val updateBadgingTaskName = "update${capitalizedVariantName}Badging"
+ tasks.register(updateBadgingTaskName) {
+ from(generateBadging.get().badging)
+ into(project.layout.projectDirectory)
+ }
+
+ val checkBadgingTaskName = "check${capitalizedVariantName}Badging"
+ tasks.register(checkBadgingTaskName) {
+ goldenBadging.set(
+ project.layout.projectDirectory.file("${variant.name}-badging.txt"),
+ )
+ generatedBadging.set(
+ generateBadging.get().badging,
+ )
+ this.updateBadgingTaskName.set(updateBadgingTaskName)
+
+ output.set(
+ project.layout.buildDirectory.dir("intermediates/$checkBadgingTaskName"),
+ )
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/kotlin/org/convention/Detekt.kt b/build-logic/convention/src/main/kotlin/org/convention/Detekt.kt
new file mode 100644
index 000000000..0844153b4
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/Detekt.kt
@@ -0,0 +1,43 @@
+package org.convention
+
+import io.gitlab.arturbosch.detekt.Detekt
+import io.gitlab.arturbosch.detekt.extensions.DetektExtension
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.dependencies
+import org.gradle.kotlin.dsl.named
+
+/**
+ * Configures the Detekt plugin with the [extension] configuration.
+ * This includes setting the JVM target to 17 and enabling all reports.
+ * Additionally, it adds the `detekt-formatting` and `twitter-detekt-compose` plugins.
+ * @see DetektExtension
+ * @see Detekt
+ */
+internal fun Project.configureDetekt(extension: DetektExtension) = extension.apply {
+ tasks.named("detekt") {
+ mustRunAfter(":cmp-android:dependencyGuard")
+ jvmTarget = "17"
+ source(files(rootDir))
+ include("**/*.kt")
+ exclude("**/*.kts")
+ exclude("**/resources/**")
+ exclude("**/build/**")
+ exclude("**/generated/**")
+ exclude("**/build-logic/**")
+ exclude("**/spotless/**")
+ // TODO:: Remove this exclusion
+ exclude("core-base/designsystem/**")
+ exclude("feature/home/**")
+ reports {
+ xml.required.set(true)
+ html.required.set(true)
+ txt.required.set(true)
+ sarif.required.set(true)
+ md.required.set(true)
+ }
+ }
+ dependencies {
+ "detektPlugins"(libs.findLibrary("detekt-formatting").get())
+ "detektPlugins"(libs.findLibrary("twitter-detekt-compose").get())
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/convention/GradleManagedDevices.kt b/build-logic/convention/src/main/kotlin/org/convention/GradleManagedDevices.kt
new file mode 100644
index 000000000..202838ec6
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/GradleManagedDevices.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 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.
+ */
+
+package org.convention
+
+import com.android.build.api.dsl.CommonExtension
+import com.android.build.api.dsl.ManagedVirtualDevice
+import org.gradle.kotlin.dsl.get
+import org.gradle.kotlin.dsl.invoke
+
+/**
+ * Configure project for Gradle managed devices
+ */
+internal fun configureGradleManagedDevices(
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
+) {
+ val pixel4 = DeviceConfig("Pixel 4", 30, "aosp-atd")
+ val pixel6 = DeviceConfig("Pixel 6", 31, "aosp")
+ val pixelC = DeviceConfig("Pixel C", 30, "aosp-atd")
+
+ val localDevices = listOf(pixel4, pixel6, pixelC)
+ val ciDevices = listOf(pixel4, pixelC)
+
+ commonExtension.testOptions {
+ managedDevices {
+ allDevices {
+ localDevices.forEach { deviceConfig ->
+ maybeCreate(deviceConfig.taskName, ManagedVirtualDevice::class.java).apply {
+ device = deviceConfig.device
+ apiLevel = deviceConfig.apiLevel
+ systemImageSource = deviceConfig.systemImageSource
+ }
+ }
+ }
+ groups {
+ maybeCreate("ci").apply {
+ ciDevices.forEach { deviceConfig ->
+ targetDevices.add(allDevices[deviceConfig.taskName])
+ }
+ }
+ }
+ }
+ }
+}
+
+private data class DeviceConfig(
+ val device: String,
+ val apiLevel: Int,
+ val systemImageSource: String,
+) {
+ val taskName = buildString {
+ append(device.lowercase().replace(" ", ""))
+ append("api")
+ append(apiLevel.toString())
+ append(systemImageSource.replace("-", ""))
+ }
+}
diff --git a/build-logic/convention/src/main/kotlin/org/convention/HierarchyTemplate.kt b/build-logic/convention/src/main/kotlin/org/convention/HierarchyTemplate.kt
new file mode 100644
index 000000000..db72fb947
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/HierarchyTemplate.kt
@@ -0,0 +1,179 @@
+/**
+ * Kotlin Multiplatform project hierarchy template configuration.
+ *
+ * This file defines a structured hierarchy for organizing source sets in Kotlin Multiplatform
+ * projects. It establishes a logical grouping of platform targets that enables efficient code
+ * sharing across platforms with similar characteristics.
+ *
+ * The hierarchy template creates the following logical groupings:
+ * - `common`: Base shared code for all platforms
+ * - `nonAndroid`: Code shared between JVM, JS, and native platforms, excluding Android
+ * - `jsCommon`: Code shared between JavaScript and WebAssembly JavaScript targets
+ * - `nonJsCommon`: Code shared between JVM and native platforms, excluding JS platforms
+ * - `jvmCommon`: Code shared between Android and JVM targets
+ * - `nonJvmCommon`: Code shared between JS and native platforms, excluding JVM platforms
+ * - `native`: Code shared across all native platforms
+ * - `apple`: Code shared across Apple platforms (iOS, macOS)
+ * - `ios`: iOS-specific code
+ * - `macos`: macOS-specific code
+ * - `nonNative`: Code shared between JS and JVM platforms
+ *
+ * This template applies to both main and test source sets, establishing a consistent
+ * structure throughout the project.
+ *
+ * Note: This implementation uses experimental Kotlin Gradle plugin APIs and may be subject
+ * to change in future Kotlin releases.
+ */
+@file:OptIn(ExperimentalKotlinGradlePluginApi::class)
+
+package org.convention
+
+import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyBuilder
+import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyTemplate
+import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree
+
+/**
+ * Defines the hierarchical structure for source set organization.
+ *
+ * This template establishes the relationships between different platform targets,
+ * creating logical groupings based on platform similarities to facilitate code sharing.
+ */
+private val hierarchyTemplate = KotlinHierarchyTemplate {
+ withSourceSetTree(
+ KotlinSourceSetTree.main,
+ KotlinSourceSetTree.test,
+ )
+
+ common {
+ withCompilations { true }
+
+ groupNonAndroid()
+ groupJsCommon()
+ groupNonJsCommon()
+ groupJvmCommon()
+ groupNonJvmCommon()
+ groupNative()
+ groupNonNative()
+ groupJvmJsCommon()
+ groupMobile()
+ }
+}
+
+/**
+ * Creates a group of non-Android platforms (JVM, JS, and native).
+ */
+private fun KotlinHierarchyBuilder.groupNonAndroid() {
+ group("nonAndroid") {
+ withJvm()
+ groupJsCommon()
+ groupNative()
+ }
+}
+
+/**
+ * Creates a group of JavaScript-related platforms (JS and WebAssembly JS).
+ */
+private fun KotlinHierarchyBuilder.groupJsCommon() {
+ group("jsCommon") {
+ withJs()
+ withWasmJs()
+ }
+}
+
+/**
+ * Creates a group of non-JavaScript platforms (JVM-based and native).
+ */
+private fun KotlinHierarchyBuilder.groupNonJsCommon() {
+ group("nonJsCommon") {
+ groupJvmCommon()
+ groupNative()
+ }
+}
+
+/**
+ * Creates a group of JVM-based platforms (Android and JVM).
+ */
+private fun KotlinHierarchyBuilder.groupJvmCommon() {
+ group("jvmCommon") {
+ withAndroidTarget()
+ withJvm()
+ }
+}
+
+/**
+ * Creates a group of non-JVM platforms (JavaScript and native).
+ */
+private fun KotlinHierarchyBuilder.groupNonJvmCommon() {
+ group("nonJvmCommon") {
+ groupJsCommon()
+ groupNative()
+ }
+}
+
+/**
+ * Creates a group of JVM, JS platforms (JavaScript and JVM).
+ */
+private fun KotlinHierarchyBuilder.groupJvmJsCommon() {
+ group("jvmJsCommon") {
+ groupJsCommon()
+ withJvm()
+ }
+}
+
+/**
+ * Creates a hierarchical group of native platforms with subgroups for Apple platforms.
+ */
+private fun KotlinHierarchyBuilder.groupNative() {
+ group("native") {
+ withNative()
+
+ group("apple") {
+ withApple()
+
+ group("ios") {
+ withIos()
+ }
+
+ group("macos") {
+ withMacos()
+ }
+ }
+ }
+}
+
+/**
+ * Creates a group of non-native platforms (JavaScript and JVM-based).
+ */
+private fun KotlinHierarchyBuilder.groupNonNative() {
+ group("nonNative") {
+ groupJsCommon()
+ groupJvmCommon()
+ }
+}
+
+private fun KotlinHierarchyBuilder.groupMobile() {
+ group("mobile") {
+ withAndroidTarget()
+ withApple()
+ }
+}
+
+/**
+ * Applies the predefined hierarchy template to a Kotlin Multiplatform project.
+ *
+ * This extension function should be called within the `kotlin` block of a Multiplatform
+ * project's build script to establish the source set hierarchy defined in this file.
+ *
+ * Example usage:
+ * ```
+ * kotlin {
+ * applyProjectHierarchyTemplate()
+ * // Configure targets...
+ * }
+ * ```
+ */
+fun KotlinMultiplatformExtension.applyProjectHierarchyTemplate() {
+ applyHierarchyTemplate(hierarchyTemplate)
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/convention/Jacoco.kt b/build-logic/convention/src/main/kotlin/org/convention/Jacoco.kt
new file mode 100644
index 000000000..6f4dbd383
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/Jacoco.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+package org.convention
+
+import com.android.build.api.artifact.ScopedArtifact
+import com.android.build.api.variant.AndroidComponentsExtension
+import com.android.build.api.variant.ScopedArtifacts
+import com.android.build.api.variant.SourceDirectories
+import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.file.RegularFile
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Provider
+import org.gradle.api.tasks.testing.Test
+import org.gradle.kotlin.dsl.assign
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.register
+import org.gradle.kotlin.dsl.withType
+import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
+import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
+import org.gradle.testing.jacoco.tasks.JacocoReport
+import java.util.Locale
+
+private val coverageExclusions = listOf(
+ // Android
+ "**/R.class",
+ "**/R\$*.class",
+ "**/BuildConfig.*",
+ "**/Manifest*.*",
+ "**/*_Hilt*.class",
+ "**/Hilt_*.class",
+)
+
+private fun String.capitalize() = replaceFirstChar {
+ if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
+}
+
+/**
+ * Creates a new task that generates a combined coverage report with data from local and
+ * instrumented tests.
+ *
+ * `create{variant}CombinedCoverageReport`
+ *
+ * Note that coverage data must exist before running the task. This allows us to run device
+ * tests on CI using a different Github Action or an external device farm.
+ */
+internal fun Project.configureJacoco(
+ androidComponentsExtension: AndroidComponentsExtension<*, *, *>,
+) {
+ configure {
+ toolVersion = libs.findVersion("jacoco").get().toString()
+ }
+
+ androidComponentsExtension.onVariants { variant ->
+ val myObjFactory = project.objects
+ val buildDir = layout.buildDirectory.get().asFile
+ val allJars: ListProperty = myObjFactory.listProperty(RegularFile::class.java)
+ val allDirectories: ListProperty =
+ myObjFactory.listProperty(Directory::class.java)
+ val reportTask =
+ tasks.register(
+ "create${variant.name.capitalize()}CombinedCoverageReport",
+ JacocoReport::class,
+ ) {
+
+ classDirectories.setFrom(
+ allJars,
+ allDirectories.map { dirs ->
+ dirs.map { dir ->
+ myObjFactory.fileTree().setDir(dir).exclude(coverageExclusions)
+ }
+ },
+ )
+ reports {
+ xml.required = true
+ html.required = true
+ }
+
+ fun SourceDirectories.Flat?.toFilePaths(): Provider> = this
+ ?.all
+ ?.map { directories -> directories.map { it.asFile.path } }
+ ?: provider { emptyList() }
+ sourceDirectories.setFrom(
+ files(
+ variant.sources.java.toFilePaths(),
+ variant.sources.kotlin.toFilePaths()
+ ),
+ )
+
+ executionData.setFrom(
+ project.fileTree("$buildDir/outputs/unit_test_code_coverage/${variant.name}UnitTest")
+ .matching { include("**/*.exec") },
+
+ project.fileTree("$buildDir/outputs/code_coverage/${variant.name}AndroidTest")
+ .matching { include("**/*.ec") },
+ )
+ }
+
+
+ variant.artifacts.forScope(ScopedArtifacts.Scope.PROJECT)
+ .use(reportTask)
+ .toGet(
+ ScopedArtifact.CLASSES,
+ { _ -> allJars },
+ { _ -> allDirectories },
+ )
+ }
+
+ tasks.withType().configureEach {
+ configure {
+ // Required for JaCoCo + Robolectric
+ // https://github.com/robolectric/robolectric/issues/2230
+ isIncludeNoLocationClasses = true
+
+ // Required for JDK 11 with the above
+ // https://github.com/gradle/gradle/issues/5184#issuecomment-391982009
+ excludes = listOf("jdk.internal.*")
+ }
+ }
+}
diff --git a/build-logic/convention/src/main/kotlin/org/convention/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/org/convention/KotlinAndroid.kt
new file mode 100644
index 000000000..fe40731b7
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/KotlinAndroid.kt
@@ -0,0 +1,77 @@
+package org.convention
+
+import com.android.build.api.dsl.CommonExtension
+import org.gradle.api.JavaVersion
+import org.gradle.api.Project
+import org.gradle.api.plugins.JavaPluginExtension
+import org.gradle.kotlin.dsl.assign
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.dependencies
+import org.gradle.kotlin.dsl.provideDelegate
+import org.gradle.kotlin.dsl.withType
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+/**
+ * Configure base Kotlin with Android options
+ */
+internal fun Project.configureKotlinAndroid(
+ commonExtension: CommonExtension<*, *, *, *, *, *>,
+) {
+ commonExtension.apply {
+ compileSdk = 36
+
+ defaultConfig {
+ minSdk = 26
+ }
+
+ compileOptions {
+ // Up to Java 11 APIs are available through desugaring
+ // https://developer.android.com/studio/write/java11-minimal-support-table
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ isCoreLibraryDesugaringEnabled = true
+ }
+ }
+
+ configureKotlin()
+
+ dependencies {
+ add("coreLibraryDesugaring", libs.findLibrary("android.desugarJdkLibs").get())
+ }
+}
+
+/**
+ * Configure base Kotlin options for JVM (non-Android)
+ */
+internal fun Project.configureKotlinJvm() {
+ extensions.configure {
+ // Up to Java 11 APIs are available through desugaring
+ // https://developer.android.com/studio/write/java11-minimal-support-table
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
+ configureKotlin()
+}
+
+/**
+ * Configure base Kotlin options
+ */
+private fun Project.configureKotlin() {
+ // Use withType to workaround https://youtrack.jetbrains.com/issue/KT-55947
+ tasks.withType().configureEach {
+ compilerOptions {
+ // Set JVM target to 17
+ jvmTarget = JvmTarget.JVM_17
+ // Treat all Kotlin warnings as errors (disabled by default)
+ // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties
+ val warningsAsErrors: String? by project
+ allWarningsAsErrors = warningsAsErrors.toBoolean()
+ freeCompilerArgs.add(
+ // Enable experimental coroutines APIs, including Flow
+ "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/convention/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/org/convention/KotlinMultiplatform.kt
new file mode 100644
index 000000000..4ff2e48d4
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/KotlinMultiplatform.kt
@@ -0,0 +1,40 @@
+package org.convention
+
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.configure
+import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+
+/**
+ * Configure the Kotlin Multiplatform plugin with the default hierarchy template and additional targets.
+ * This includes JVM, Android, iOS, JS and WASM targets.
+ * @see KotlinMultiplatformExtension
+ * @see configure
+ */
+@OptIn(ExperimentalWasmDsl::class, ExperimentalKotlinGradlePluginApi::class)
+internal fun Project.configureKotlinMultiplatform() {
+ extensions.configure {
+ applyProjectHierarchyTemplate()
+
+ jvm("desktop")
+ androidTarget()
+ iosSimulatorArm64()
+ iosX64()
+ iosArm64()
+ js(IR) {
+ this.nodejs()
+ binaries.executable()
+ }
+ wasmJs() {
+ browser()
+ nodejs()
+ }
+
+ compilerOptions {
+ freeCompilerArgs.add("-Xexpect-actual-classes")
+ freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn")
+ freeCompilerArgs.add("-opt-in=kotlin.time.ExperimentalTime")
+ }
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/convention/PrintTestApks.kt b/build-logic/convention/src/main/kotlin/org/convention/PrintTestApks.kt
new file mode 100644
index 000000000..d95410bb8
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/PrintTestApks.kt
@@ -0,0 +1,95 @@
+package org.convention
+
+import com.android.build.api.artifact.SingleArtifact
+import com.android.build.api.variant.AndroidComponentsExtension
+import com.android.build.api.variant.BuiltArtifactsLoader
+import com.android.build.api.variant.HasAndroidTest
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.file.Directory
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import org.gradle.work.DisableCachingByDefault
+import java.io.File
+
+/**
+ * Configures the `printTestApks` task for the [project].
+ * This task will print the location of the androidTest APKs.
+ * This is useful when running tests on a device or emulator.
+ * The task will only be created if there are androidTest sources.
+ * @see PrintApkLocationTask
+ * @see HasAndroidTest
+ */
+internal fun Project.configurePrintApksTask(extension: AndroidComponentsExtension<*, *, *>) {
+ extension.onVariants { variant ->
+ if (variant is HasAndroidTest) {
+ val loader = variant.artifacts.getBuiltArtifactsLoader()
+ val artifact = variant.androidTest?.artifacts?.get(SingleArtifact.APK)
+ val javaSources = variant.androidTest?.sources?.java?.all
+ val kotlinSources = variant.androidTest?.sources?.kotlin?.all
+
+ val testSources = if (javaSources != null && kotlinSources != null) {
+ javaSources.zip(kotlinSources) { javaDirs, kotlinDirs ->
+ javaDirs + kotlinDirs
+ }
+ } else javaSources ?: kotlinSources
+
+ if (artifact != null && testSources != null) {
+ tasks.register(
+ "${variant.name}PrintTestApk",
+ PrintApkLocationTask::class.java,
+ ) {
+ apkFolder.set(artifact)
+ builtArtifactsLoader.set(loader)
+ variantName.set(variant.name)
+ sources.set(testSources)
+ }
+ }
+ }
+ }
+}
+
+@DisableCachingByDefault(because = "Prints output")
+internal abstract class PrintApkLocationTask : DefaultTask() {
+
+ @get:PathSensitive(PathSensitivity.RELATIVE)
+ @get:InputDirectory
+ abstract val apkFolder: DirectoryProperty
+
+ @get:PathSensitive(PathSensitivity.RELATIVE)
+ @get:InputFiles
+ abstract val sources: ListProperty
+
+ @get:Internal
+ abstract val builtArtifactsLoader: Property
+
+ @get:Input
+ abstract val variantName: Property
+
+ @TaskAction
+ fun taskAction() {
+ val hasFiles = sources.orNull?.any { directory ->
+ directory.asFileTree.files.any {
+ it.isFile && "build${File.separator}generated" !in it.parentFile.path
+ }
+ } ?: throw RuntimeException("Cannot check androidTest sources")
+
+ // Don't print APK location if there are no androidTest source files
+ if (!hasFiles) return
+
+ val builtArtifacts = builtArtifactsLoader.get().load(apkFolder.get())
+ ?: throw RuntimeException("Cannot load APKs")
+ if (builtArtifacts.elements.size != 1)
+ throw RuntimeException("Expected one APK !")
+ val apk = File(builtArtifacts.elements.single().outputFile).toPath()
+ println(apk)
+ }
+}
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/convention/ProjectExtensions.kt b/build-logic/convention/src/main/kotlin/org/convention/ProjectExtensions.kt
new file mode 100644
index 000000000..8b05d6ee0
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/ProjectExtensions.kt
@@ -0,0 +1,39 @@
+package org.convention
+
+import com.diffplug.gradle.spotless.SpotlessExtension
+import io.gitlab.arturbosch.detekt.extensions.DetektExtension
+import org.gradle.api.Project
+import org.gradle.api.artifacts.VersionCatalog
+import org.gradle.api.artifacts.VersionCatalogsExtension
+import org.gradle.kotlin.dsl.configure
+import org.gradle.kotlin.dsl.getByType
+import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
+import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyBuilder
+
+/**
+ * Get the `libs` version catalog.
+ */
+val Project.libs
+ get(): VersionCatalog = extensions.getByType().named("libs")
+
+/**
+ * Get the dynamic version of the project.
+ */
+val Project.dynamicVersion
+ get() = project.version.toString().split('+')[0]
+
+/**
+ * Configures the `detekt` plugin with the [configure] lambda.
+ */
+inline fun Project.detektGradle(crossinline configure: DetektExtension.() -> Unit) =
+ extensions.configure {
+ configure()
+ }
+
+/**
+ * Configures the `spotless` plugin with the [configure] lambda.
+ */
+inline fun Project.spotlessGradle(crossinline configure: SpotlessExtension.() -> Unit) =
+ extensions.configure {
+ configure()
+ }
\ No newline at end of file
diff --git a/build-logic/convention/src/main/kotlin/org/convention/Spotless.kt b/build-logic/convention/src/main/kotlin/org/convention/Spotless.kt
new file mode 100644
index 000000000..8a29e1d7f
--- /dev/null
+++ b/build-logic/convention/src/main/kotlin/org/convention/Spotless.kt
@@ -0,0 +1,36 @@
+package org.convention
+
+import com.diffplug.gradle.spotless.SpotlessExtension
+import org.gradle.api.Project
+
+const val ktlintVersion = "1.0.1"
+
+/**
+ * Configures the Spotless plugin with the [extension] configuration.
+ * This includes setting up the `ktlint` formatter and the license header.
+ * @see SpotlessExtension
+ */
+internal fun Project.configureSpotless(extension: SpotlessExtension) = extension.apply {
+ kotlin {
+ target("**/*.kt")
+ targetExclude("**/build/**/*.kt")
+ ktlint(ktlintVersion).editorConfigOverride(
+ mapOf("android" to "true"),
+ )
+ licenseHeaderFile(rootProject.file("spotless/copyright.kt"))
+ }
+
+ format("kts") {
+ target("**/*.kts")
+ targetExclude("**/build/**/*.kts")
+ // Look for the first line that doesn't have a block comment (assumed to be the license)
+ licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)")
+ }
+
+ format("xml") {
+ target("**/*.xml")
+ targetExclude("**/build/**/*.xml")
+ // Look for the first XML tag that isn't a comment (
diff --git a/cmp-android/prodRelease-badging.txt b/cmp-android/prodRelease-badging.txt
index 0314444d1..c3c948380 100644
--- a/cmp-android/prodRelease-badging.txt
+++ b/cmp-android/prodRelease-badging.txt
@@ -1,123 +1,115 @@
-package: name='org.mifos.mobile' versionCode='1' versionName='2024.12.4-beta.0.4' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
-sdkVersion:'26'
-targetSdkVersion:'34'
+package: name='cmp.android.app' versionCode='1' versionName='2025.7.3-beta.0.0' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
+minSdkVersion:'26'
+targetSdkVersion:'36'
uses-permission: name='android.permission.INTERNET'
-uses-permission: name='android.permission.CAMERA'
-uses-permission: name='android.permission.READ_EXTERNAL_STORAGE' maxSdkVersion='32'
-uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE' maxSdkVersion='32'
-uses-permission: name='android.permission.VIBRATE'
-uses-permission: name='android.permission.FLASHLIGHT'
+uses-permission: name='android.permission.POST_NOTIFICATIONS'
uses-permission: name='android.permission.ACCESS_NETWORK_STATE'
uses-permission: name='android.permission.WAKE_LOCK'
uses-permission: name='com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE'
uses-permission: name='android.permission.ACCESS_ADSERVICES_ATTRIBUTION'
uses-permission: name='android.permission.ACCESS_ADSERVICES_AD_ID'
-uses-permission: name='org.mifos.mobile.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION'
-application-label:'Mifos Mobile'
-application-label-af:'Mifos Mobile'
-application-label-am:'Mifos Mobile'
-application-label-ar:'Mifos Mobile'
-application-label-as:'Mifos Mobile'
-application-label-az:'Mifos Mobile'
-application-label-be:'Mifos Mobile'
-application-label-bg:'Mifos Mobile'
-application-label-bn:'Mifos Mobile'
-application-label-bs:'Mifos Mobile'
-application-label-ca:'Mifos Mobile'
-application-label-cs:'Mifos Mobile'
-application-label-da:'Mifos Mobile'
-application-label-de:'Mifos Mobile'
-application-label-el:'Mifos Mobile'
-application-label-en-AU:'Mifos Mobile'
-application-label-en-CA:'Mifos Mobile'
-application-label-en-GB:'Mifos Mobile'
-application-label-en-IN:'Mifos Mobile'
-application-label-en-XC:'Mifos Mobile'
-application-label-es:'Mifos Mobile'
-application-label-es-US:'Mifos Mobile'
-application-label-et:'Mifos Mobile'
-application-label-eu:'Mifos Mobile'
-application-label-fa:'Mifos Mobile'
-application-label-fi:'Mifos Mobile'
-application-label-fr:'Mifos Mobile'
-application-label-fr-CA:'Mifos Mobile'
-application-label-gl:'Mifos Mobile'
-application-label-gu:'Mifos Mobile'
-application-label-hi:'Mifos Mobile'
-application-label-hr:'Mifos Mobile'
-application-label-hu:'Mifos Mobile'
-application-label-hy:'Mifos Mobile'
-application-label-in:'Mifos Mobile'
-application-label-is:'Mifos Mobile'
-application-label-it:'Mifos Mobile'
-application-label-iw:'Mifos Mobile'
-application-label-ja:'Mifos Mobile'
-application-label-ka:'Mifos Mobile'
-application-label-kk:'Mifos Mobile'
-application-label-km:'Mifos Mobile'
-application-label-kn:'Mifos Mobile'
-application-label-ko:'Mifos Mobile'
-application-label-ky:'Mifos Mobile'
-application-label-lo:'Mifos Mobile'
-application-label-lt:'Mifos Mobile'
-application-label-lv:'Mifos Mobile'
-application-label-mk:'Mifos Mobile'
-application-label-ml:'Mifos Mobile'
-application-label-mn:'Mifos Mobile'
-application-label-mr:'Mifos Mobile'
-application-label-ms:'Mifos Mobile'
-application-label-my:'Mifos Mobile'
-application-label-nb:'Mifos Mobile'
-application-label-ne:'Mifos Mobile'
-application-label-nl:'Mifos Mobile'
-application-label-or:'Mifos Mobile'
-application-label-pa:'Mifos Mobile'
-application-label-pl:'Mifos Mobile'
-application-label-pt:'Mifos Mobile'
-application-label-pt-BR:'Mifos Mobile'
-application-label-pt-PT:'Mifos Mobile'
-application-label-ro:'Mifos Mobile'
-application-label-ru:'Mifos Mobile'
-application-label-si:'Mifos Mobile'
-application-label-sk:'Mifos Mobile'
-application-label-sl:'Mifos Mobile'
-application-label-sq:'Mifos Mobile'
-application-label-sr:'Mifos Mobile'
-application-label-sr-Latn:'Mifos Mobile'
-application-label-sv:'Mifos Mobile'
-application-label-sw:'Mifos Mobile'
-application-label-ta:'Mifos Mobile'
-application-label-te:'Mifos Mobile'
-application-label-th:'Mifos Mobile'
-application-label-tl:'Mifos Mobile'
-application-label-tr:'Mifos Mobile'
-application-label-uk:'Mifos Mobile'
-application-label-ur:'Mifos Mobile'
-application-label-uz:'Mifos Mobile'
-application-label-vi:'Mifos Mobile'
-application-label-zh-CN:'Mifos Mobile'
-application-label-zh-HK:'Mifos Mobile'
-application-label-zh-TW:'Mifos Mobile'
-application-label-zu:'Mifos Mobile'
+uses-permission: name='cmp.android.app.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION'
+application-label:'AndroidApp'
+application-label-af:'AndroidApp'
+application-label-am:'AndroidApp'
+application-label-ar:'AndroidApp'
+application-label-as:'AndroidApp'
+application-label-az:'AndroidApp'
+application-label-be:'AndroidApp'
+application-label-bg:'AndroidApp'
+application-label-bn:'AndroidApp'
+application-label-bs:'AndroidApp'
+application-label-ca:'AndroidApp'
+application-label-cs:'AndroidApp'
+application-label-da:'AndroidApp'
+application-label-de:'AndroidApp'
+application-label-el:'AndroidApp'
+application-label-en-AU:'AndroidApp'
+application-label-en-CA:'AndroidApp'
+application-label-en-GB:'AndroidApp'
+application-label-en-IN:'AndroidApp'
+application-label-en-XC:'AndroidApp'
+application-label-es:'AndroidApp'
+application-label-es-US:'AndroidApp'
+application-label-et:'AndroidApp'
+application-label-eu:'AndroidApp'
+application-label-fa:'AndroidApp'
+application-label-fi:'AndroidApp'
+application-label-fr:'AndroidApp'
+application-label-fr-CA:'AndroidApp'
+application-label-gl:'AndroidApp'
+application-label-gu:'AndroidApp'
+application-label-hi:'AndroidApp'
+application-label-hr:'AndroidApp'
+application-label-hu:'AndroidApp'
+application-label-hy:'AndroidApp'
+application-label-in:'AndroidApp'
+application-label-is:'AndroidApp'
+application-label-it:'AndroidApp'
+application-label-iw:'AndroidApp'
+application-label-ja:'AndroidApp'
+application-label-ka:'AndroidApp'
+application-label-kk:'AndroidApp'
+application-label-km:'AndroidApp'
+application-label-kn:'AndroidApp'
+application-label-ko:'AndroidApp'
+application-label-ky:'AndroidApp'
+application-label-lo:'AndroidApp'
+application-label-lt:'AndroidApp'
+application-label-lv:'AndroidApp'
+application-label-mk:'AndroidApp'
+application-label-ml:'AndroidApp'
+application-label-mn:'AndroidApp'
+application-label-mr:'AndroidApp'
+application-label-ms:'AndroidApp'
+application-label-my:'AndroidApp'
+application-label-nb:'AndroidApp'
+application-label-ne:'AndroidApp'
+application-label-nl:'AndroidApp'
+application-label-or:'AndroidApp'
+application-label-pa:'AndroidApp'
+application-label-pl:'AndroidApp'
+application-label-pt:'AndroidApp'
+application-label-pt-BR:'AndroidApp'
+application-label-pt-PT:'AndroidApp'
+application-label-ro:'AndroidApp'
+application-label-ru:'AndroidApp'
+application-label-si:'AndroidApp'
+application-label-sk:'AndroidApp'
+application-label-sl:'AndroidApp'
+application-label-sq:'AndroidApp'
+application-label-sr:'AndroidApp'
+application-label-sr-Latn:'AndroidApp'
+application-label-sv:'AndroidApp'
+application-label-sw:'AndroidApp'
+application-label-ta:'AndroidApp'
+application-label-te:'AndroidApp'
+application-label-th:'AndroidApp'
+application-label-tl:'AndroidApp'
+application-label-tr:'AndroidApp'
+application-label-uk:'AndroidApp'
+application-label-ur:'AndroidApp'
+application-label-uz:'AndroidApp'
+application-label-vi:'AndroidApp'
+application-label-zh-CN:'AndroidApp'
+application-label-zh-HK:'AndroidApp'
+application-label-zh-TW:'AndroidApp'
+application-label-zu:'AndroidApp'
application-icon-160:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-240:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-320:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-480:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-640:'res/mipmap-anydpi-v26/ic_launcher.xml'
application-icon-65534:'res/mipmap-anydpi-v26/ic_launcher.xml'
-application: label='Mifos Mobile' icon='res/mipmap-anydpi-v26/ic_launcher.xml'
-launchable-activity: name='org.mifospay.MainActivity' label='' icon=''
-property: name='android.adservices.AD_SERVICES_CONFIG' resource='res/xml/ga_ad_services_config.xml'
+application: label='AndroidApp' icon='res/mipmap-anydpi-v26/ic_launcher.xml'
+launchable-activity: name='cmp.android.app.MainActivity' label='' icon=''
uses-library-not-required:'androidx.window.extensions'
uses-library-not-required:'androidx.window.sidecar'
uses-library-not-required:'android.ext.adservices'
feature-group: label=''
- uses-feature: name='android.hardware.camera'
- uses-feature: name='android.hardware.camera.autofocus'
+ uses-feature-not-required: name='android.hardware.camera'
uses-feature: name='android.hardware.faketouch'
uses-implied-feature: name='android.hardware.faketouch' reason='default feature for all apps'
- uses-feature: name='android.hardware.screen.portrait'
- uses-implied-feature: name='android.hardware.screen.portrait' reason='one or more activities have specified a portrait orientation'
main
other-activities
other-receivers
diff --git a/cmp-android/proguard-rules.pro b/cmp-android/proguard-rules.pro
index fe8b865e0..962d929df 100644
--- a/cmp-android/proguard-rules.pro
+++ b/cmp-android/proguard-rules.pro
@@ -6,6 +6,9 @@
-keep class com.yalantis.ucrop** { *; }
-keep interface com.yalantis.ucrop** { *; }
+-keepattributes SourceFile,LineNumberTable # Keep file names and line numbers.
+-keep public class * extends java.lang.Exception # Optional: Keep custom exceptions.
+
# Proguard Kotlin Example https://github.com/Guardsquare/proguard/blob/master/examples/application-kotlin/proguard.pro
-keepattributes *Annotation*
@@ -129,7 +132,6 @@
-dontwarn io.ktor.client.network.sockets.SocketTimeoutException
-dontwarn java.lang.management.RuntimeMXBean
--keep class org.mifospay.core.network.services.* { *;}
-keep class de.jensklingenberg.ktorfit.converter.** { *; }
-keep class de.jensklingenberg.ktorfit.** { *; }
-keeppackagenames de.jensklingenberg.ktorfit.*
\ No newline at end of file
diff --git a/cmp-android/src/main/AndroidManifest.xml b/cmp-android/src/main/AndroidManifest.xml
index da6c973cb..714717c8e 100644
--- a/cmp-android/src/main/AndroidManifest.xml
+++ b/cmp-android/src/main/AndroidManifest.xml
@@ -6,7 +6,7 @@
If a copy of the MPL was not distributed with this file,
You can obtain one at https://mozilla.org/MPL/2.0/.
- See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
-->
@@ -16,13 +16,7 @@
android:required="false" />
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/cmp-android/src/main/kotlin/cmp/android/app/AndroidApp.kt b/cmp-android/src/main/kotlin/cmp/android/app/AndroidApp.kt
index cbbff9b09..bb5b40bea 100644
--- a/cmp-android/src/main/kotlin/cmp/android/app/AndroidApp.kt
+++ b/cmp-android/src/main/kotlin/cmp/android/app/AndroidApp.kt
@@ -5,15 +5,21 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package cmp.android.app
import android.app.Application
import cmp.shared.utils.initKoin
+import coil3.ImageLoader
+import coil3.PlatformContext
+import coil3.SingletonImageLoader
+import coil3.disk.DiskCache
+import coil3.disk.directory
+import coil3.request.CachePolicy
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
-import org.koin.core.logger.Level
+import template.core.base.ui.getDefaultImageLoader
/**
* Android application class.
@@ -23,12 +29,24 @@ import org.koin.core.logger.Level
* @constructor Create empty Android app
* @see Application
*/
-class AndroidApp : Application() {
+class AndroidApp : Application(), SingletonImageLoader.Factory {
override fun onCreate() {
super.onCreate()
initKoin {
- androidContext(this@AndroidApp) // Provides the Android app context
- androidLogger(Level.DEBUG) // Enables Koin's logging for debugging
+ androidContext(this@AndroidApp)
+ androidLogger()
}
}
+
+ override fun newImageLoader(context: PlatformContext): ImageLoader =
+ getDefaultImageLoader(context)
+ .newBuilder()
+ .diskCachePolicy(CachePolicy.ENABLED)
+ .diskCache {
+ DiskCache.Builder()
+ .directory(context.cacheDir.resolve("image_cache"))
+ .maxSizePercent(0.25)
+ .build()
+ }
+ .build()
}
diff --git a/cmp-android/src/main/kotlin/cmp/android/app/AppThemeExtensions.kt b/cmp-android/src/main/kotlin/cmp/android/app/AppThemeExtensions.kt
new file mode 100644
index 000000000..0068bf6df
--- /dev/null
+++ b/cmp-android/src/main/kotlin/cmp/android/app/AppThemeExtensions.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package cmp.android.app
+
+import org.mifos.core.model.DarkThemeConfig
+
+fun DarkThemeConfig.isDarkMode(
+ isSystemDarkMode: Boolean,
+): Boolean =
+ when (this) {
+ DarkThemeConfig.FOLLOW_SYSTEM -> isSystemDarkMode
+ DarkThemeConfig.DARK -> true
+ DarkThemeConfig.LIGHT -> false
+ }
diff --git a/cmp-android/src/main/kotlin/cmp/android/app/BuildConfigUtils.kt b/cmp-android/src/main/kotlin/cmp/android/app/BuildConfigUtils.kt
new file mode 100644
index 000000000..a96b9151c
--- /dev/null
+++ b/cmp-android/src/main/kotlin/cmp/android/app/BuildConfigUtils.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package cmp.android.app
+
+import android.os.Build
+
+/**
+ * A boolean property that indicates whether the current build is a dev build.
+ */
+val isDevBuild: Boolean
+ get() = BuildConfig.BUILD_TYPE == "debug"
+
+/**
+ * A string that represents a displayable app version.
+ */
+val versionData: String
+ get() = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
+
+/**
+ * A string that represents device data.
+ */
+val deviceData: String get() = "$deviceBrandModel $osInfo $buildInfo"
+
+/**
+ * A string representing the build flavor or blank if it is the standard configuration.
+ */
+private val buildFlavorName: String
+ get() = when (BuildConfig.FLAVOR) {
+ "demo" -> ""
+ else -> "-${BuildConfig.FLAVOR}"
+ }
+
+/**
+ * A string representing the build type.
+ */
+private val buildTypeName: String
+ get() = when (BuildConfig.BUILD_TYPE) {
+ "debug" -> "dev"
+ "release" -> "prod"
+ else -> BuildConfig.BUILD_TYPE
+ }
+
+/**
+ * A string representing the device brand and model.
+ */
+private val deviceBrandModel: String get() = "\uD83D\uDCF1 ${Build.BRAND} ${Build.MODEL}"
+
+/**
+ * A string representing the operating system information.
+ */
+private val osInfo: String get() = "\uD83E\uDD16 ${Build.VERSION.RELEASE}@${Build.VERSION.SDK_INT}"
+
+/**
+ * A string representing the build information.
+ */
+private val buildInfo: String
+ get() = "\uD83D\uDCE6 $buildTypeName" +
+ buildFlavorName.takeUnless { it.isBlank() }?.let { " $it" }.orEmpty()
diff --git a/cmp-android/src/main/kotlin/cmp/android/app/ComponentActivityExtensions.kt b/cmp-android/src/main/kotlin/cmp/android/app/ComponentActivityExtensions.kt
index 2c0118991..7d4af98bc 100644
--- a/cmp-android/src/main/kotlin/cmp/android/app/ComponentActivityExtensions.kt
+++ b/cmp-android/src/main/kotlin/cmp/android/app/ComponentActivityExtensions.kt
@@ -5,29 +5,32 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package cmp.android.app
+import android.content.res.Configuration
+import android.graphics.Color
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatDelegate
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
+import androidx.core.util.Consumer
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import cmp.android.app.util.isSystemInDarkModeFlow
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
-import org.mifos.mobile.core.model.MifosThemeConfig
+import org.mifos.core.model.DarkThemeConfig
@ColorInt
-private val SCRIM_COLOR: Int = Color.Transparent.toArgb()
+private val SCRIM_COLOR: Int = Color.TRANSPARENT
/**
* Helper method to handle edge-to-edge logic for dark mode.
@@ -37,7 +40,7 @@ private val SCRIM_COLOR: Int = Color.Transparent.toArgb()
*/
@Suppress("MaxLineLength")
fun ComponentActivity.setupEdgeToEdge(
- appThemeFlow: Flow,
+ appThemeFlow: Flow,
) {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(state = Lifecycle.State.STARTED) {
@@ -45,18 +48,8 @@ fun ComponentActivity.setupEdgeToEdge(
isSystemInDarkModeFlow(),
appThemeFlow,
) { isSystemDarkMode, appTheme ->
-
- val currentNightMode = AppCompatDelegate.getDefaultNightMode()
- if (currentNightMode != appTheme.osValue) {
- AppCompatDelegate.setDefaultNightMode(appTheme.osValue)
- }
-
- when (appTheme.osValue) {
- AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> isSystemDarkMode
- AppCompatDelegate.MODE_NIGHT_YES -> true
- AppCompatDelegate.MODE_NIGHT_NO -> false
- else -> isSystemDarkMode
- }
+ AppCompatDelegate.setDefaultNightMode(appTheme.osValue)
+ appTheme.isDarkMode(isSystemDarkMode = isSystemDarkMode)
}
.distinctUntilChanged()
.collect { isDarkMode ->
@@ -66,10 +59,28 @@ fun ComponentActivity.setupEdgeToEdge(
val style = SystemBarStyle.auto(
darkScrim = SCRIM_COLOR,
lightScrim = SCRIM_COLOR,
- detectDarkMode = { isDarkMode },
+ // Disabling Dark Mode for this app
+ detectDarkMode = { false },
)
enableEdgeToEdge(statusBarStyle = style, navigationBarStyle = style)
}
}
}
}
+
+/**
+ * Adds a configuration change listener to retrieve whether system is in
+ * dark theme or not. This will emit current status immediately and then
+ * will emit changes as needed.
+ */
+private fun ComponentActivity.isSystemInDarkModeFlow(): Flow =
+ callbackFlow {
+ channel.trySend(element = resources.configuration.isSystemInDarkMode)
+ val listener = Consumer {
+ channel.trySend(element = it.isSystemInDarkMode)
+ }
+ addOnConfigurationChangedListener(listener = listener)
+ awaitClose { removeOnConfigurationChangedListener(listener = listener) }
+ }
+ .distinctUntilChanged()
+ .conflate()
diff --git a/cmp-android/src/main/kotlin/cmp/android/app/ConfigurationExtension.kt b/cmp-android/src/main/kotlin/cmp/android/app/ConfigurationExtension.kt
new file mode 100644
index 000000000..cf4c540f4
--- /dev/null
+++ b/cmp-android/src/main/kotlin/cmp/android/app/ConfigurationExtension.kt
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package cmp.android.app
+
+import android.content.res.Configuration
+
+val Configuration.isSystemInDarkMode
+ get() = (uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
diff --git a/cmp-android/src/main/kotlin/cmp/android/app/MainActivity.kt b/cmp-android/src/main/kotlin/cmp/android/app/MainActivity.kt
index 831a2cf0f..7738e2c93 100644
--- a/cmp-android/src/main/kotlin/cmp/android/app/MainActivity.kt
+++ b/cmp-android/src/main/kotlin/cmp/android/app/MainActivity.kt
@@ -5,83 +5,117 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package cmp.android.app
import android.os.Bundle
+import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatDelegate
+import androidx.compose.runtime.getValue
import androidx.core.os.LocaleListCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
-import androidx.core.view.WindowCompat
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cmp.shared.SharedApp
import io.github.vinceglb.filekit.FileKit
import io.github.vinceglb.filekit.dialogs.init
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.runBlocking
import org.koin.android.ext.android.inject
-import org.mifos.mobile.core.datastore.UserPreferencesRepository
-import org.mifos.mobile.core.ui.utils.ShareUtils
-import template.core.base.platform.LocalManagerProvider
+import org.mifos.core.data.repository.NetworkMonitor
+import org.mifos.core.data.repository.UserDataRepository
+import template.core.base.analytics.AnalyticsHelper
+import template.core.base.analytics.lifecycleTracker
+import template.core.base.platform.update.AppUpdateManager
+import template.core.base.platform.update.AppUpdateManagerImpl
+import template.core.base.ui.ShareUtils
import java.util.Locale
-import kotlin.getValue
/**
- * Main activity class.
- * This class is used to set the content view of the activity.
+ * Main activity class. This class is used to set the content view of the
+ * activity.
*
* @constructor Create empty Main activity
* @see ComponentActivity
*/
+@Suppress("UnusedPrivateProperty")
class MainActivity : ComponentActivity() {
- /**
- * Called when the activity is starting.
- * This is where most initialization should go: calling [setContentView(int)] to inflate the activity's UI,
- */
- private val userPreferencesRepository: UserPreferencesRepository by inject()
+ private lateinit var appUpdateManager: AppUpdateManager
+
+ private val userPreferencesRepository: UserDataRepository by inject()
+
+ private val networkMonitor: NetworkMonitor by inject()
+
+ private val analyticsHelper: AnalyticsHelper by inject()
+ private val lifecycleTracker by lazy { analyticsHelper.lifecycleTracker() }
override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- runBlocking {
- val userThemeConfig = userPreferencesRepository.observeDarkThemeConfig.first()
- AppCompatDelegate.setDefaultNightMode(userThemeConfig.osValue)
- }
-
var shouldShowSplashScreen = true
installSplashScreen().setKeepOnScreenCondition { shouldShowSplashScreen }
+ super.onCreate(savedInstanceState)
+ appUpdateManager = AppUpdateManagerImpl(this)
+
val darkThemeConfigFlow = userPreferencesRepository.observeDarkThemeConfig
- WindowCompat.setDecorFitsSystemWindows(window, false)
setupEdgeToEdge(darkThemeConfigFlow)
+
ShareUtils.setActivityProvider { return@setActivityProvider this }
FileKit.init(this)
- /**
- * Set the content view of the activity.
- * @see setContent
- */
+
+ analyticsHelper.setUserId(deviceData)
+
setContent {
- LocalManagerProvider(context = this) {
- SharedApp(
- handleThemeMode = {
- AppCompatDelegate.setDefaultNightMode(it)
- },
- handleAppLocale = {
- it?.let {
- AppCompatDelegate.setApplicationLocales(
- LocaleListCompat.forLanguageTags(it),
- )
- Locale.setDefault(Locale(it))
- }
- },
- onSplashScreenRemoved = {
- shouldShowSplashScreen = false
- },
- )
+ val status by networkMonitor.isOnline.collectAsStateWithLifecycle(false)
+
+ if (status) {
+ appUpdateManager.checkForAppUpdate()
}
+
+ lifecycleTracker.markAppLaunchComplete()
+
+ SharedApp(
+ updateScreenCapture = ::updateScreenCapture,
+ handleRecreate = ::handleRecreate,
+ handleThemeMode = {
+ AppCompatDelegate.setDefaultNightMode(it)
+ },
+ handleAppLocale = {
+ it?.let {
+ AppCompatDelegate.setApplicationLocales(
+ LocaleListCompat.forLanguageTags(it),
+ )
+ Locale.setDefault(Locale(it))
+ }
+ },
+ onSplashScreenRemoved = {
+ shouldShowSplashScreen = false
+ },
+ )
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ appUpdateManager.checkForResumeUpdateState()
+ lifecycleTracker.markAppBackground()
+ }
+
+ override fun onStart() {
+ super.onStart()
+ lifecycleTracker.markAppLaunchStart()
+ }
+
+ private fun handleRecreate() {
+ recreate()
+ }
+
+ private fun updateScreenCapture(isScreenCaptureAllowed: Boolean) {
+ if (isScreenCaptureAllowed) {
+ window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
+ } else {
+ window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
}
}
diff --git a/cmp-android/src/prod/AndroidManifest.xml b/cmp-android/src/prod/AndroidManifest.xml
new file mode 100644
index 000000000..0d211bbf9
--- /dev/null
+++ b/cmp-android/src/prod/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cmp-desktop/build.gradle.kts b/cmp-desktop/build.gradle.kts
index 9ae1d463d..1dd0cc5bf 100644
--- a/cmp-desktop/build.gradle.kts
+++ b/cmp-desktop/build.gradle.kts
@@ -17,11 +17,9 @@ plugins {
}
kotlin {
- jvm {
- withJava()
- }
+ jvm()
- jvmToolchain(21)
+ jvmToolchain(17)
sourceSets {
jvmMain.dependencies {
@@ -33,32 +31,19 @@ kotlin {
implementation(libs.kotlin.reflect)
implementation(libs.koin.core)
-
- implementation(compose.components.resources)
}
}
}
-val appName: String = libs.versions.packageName.get()
-val packageNameSpace: String = libs.versions.packageNamespace.get()
-val appVersion: String = libs.versions.packageVersion.get()
+val appName: String = libs.versions.desktopPackageName.get()
+val packageNameSpace: String = libs.versions.desktopPackageNamespace.get()
+val appVersion: String = libs.versions.desktopPackageVersion.get()
compose.desktop {
application {
mainClass = "MainKt"
-
- val buildNumber: String = (project.findProperty("buildNumber") as String?) ?: "1"
- val isAppStoreRelease: Boolean =
- (project.findProperty("macOsAppStoreRelease") as String?)?.toBoolean() ?: false
-
nativeDistributions {
- targetFormats(
- TargetFormat.Pkg,
- TargetFormat.Dmg,
- TargetFormat.Msi,
- TargetFormat.Exe,
- TargetFormat.Deb
- )
+ targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Exe, TargetFormat.Deb)
packageName = appName
packageVersion = appVersion
description = "Desktop Application"
@@ -66,39 +51,16 @@ compose.desktop {
vendor = "Mifos Initiative"
licenseFile.set(project.file("../LICENSE"))
includeAllModules = true
- outputBaseDir.set(project.layout.buildDirectory.dir("release"))
macOS {
bundleID = packageNameSpace
dockName = appName
iconFile.set(project.file("icons/ic_launcher.icns"))
- minimumSystemVersion = "12.0"
- appStore = isAppStoreRelease
-
- infoPlist {
- packageBuildVersion = buildNumber
- extraKeysRawXml = """
- ITSAppUsesNonExemptEncryption
-
- """.trimIndent()
- }
-
- if (isAppStoreRelease) {
- signing {
- sign.set(true)
- identity.set("The Mifos Initiative")
- }
- provisioningProfile.set(project.file("embedded.provisionprofile"))
- runtimeProvisioningProfile.set(project.file("runtime.provisionprofile"))
- entitlementsFile.set(project.file("entitlements.plist"))
- runtimeEntitlementsFile.set(project.file("runtime-entitlements.plist"))
- } else {
- notarization {
- val providers = project.providers
- appleID.set(providers.environmentVariable("NOTARIZATION_APPLE_ID"))
- password.set(providers.environmentVariable("NOTARIZATION_PASSWORD"))
- teamID.set(providers.environmentVariable("NOTARIZATION_TEAM_ID"))
- }
+ notarization {
+ val providers = project.providers
+ appleID.set(providers.environmentVariable("NOTARIZATION_APPLE_ID"))
+ password.set(providers.environmentVariable("NOTARIZATION_PASSWORD"))
+ teamID.set(providers.environmentVariable("NOTARIZATION_TEAM_ID"))
}
}
@@ -116,36 +78,9 @@ compose.desktop {
}
}
buildTypes.release.proguard {
- isEnabled = false
-// configurationFiles.from(file("compose-desktop.pro"))
-// obfuscate.set(true)
-// optimize.set(true)
+ configurationFiles.from(file("compose-desktop.pro"))
+ obfuscate.set(true)
+ optimize.set(true)
}
}
}
-
-/**
- * Removes the `com.apple.quarantine` extended attribute from the built `.app`.
- *
- * Why:
- * Gatekeeper may mark files from the Internet with `com.apple.quarantine`.
- * If any such file ends up inside the `.app`, App Store validation can fail.
- */
-val unquarantineApp = tasks.register("unquarantineMacApp") {
- group = "macOS"
- description = "Remove com.apple.quarantine from the built .app before signing"
- onlyIf { org.gradle.internal.os.OperatingSystem.current().isMacOsX }
-
- dependsOn("createReleaseDistributable")
-
- val appName = "$appName.app" // set to your final .app name
- val appPath = layout.buildDirectory
- .dir("release/main-release/app/$appName")
- .map { it.asFile.absolutePath }
-
- commandLine("xattr", "-dr", "com.apple.quarantine", appPath.get())
-}
-
-tasks.matching { it.name == "packageReleasePkg" }.configureEach {
- dependsOn(unquarantineApp)
-}
diff --git a/cmp-desktop/src/jvmMain/kotlin/main.kt b/cmp-desktop/src/jvmMain/kotlin/main.kt
index 8d9a6853c..3553fa7cc 100644
--- a/cmp-desktop/src/jvmMain/kotlin/main.kt
+++ b/cmp-desktop/src/jvmMain/kotlin/main.kt
@@ -12,10 +12,7 @@ import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import cmp.shared.SharedApp
-import cmp.shared.generated.resources.Res
-import cmp.shared.generated.resources.application_title
import cmp.shared.utils.initKoin
-import org.jetbrains.compose.resources.stringResource
/**
* Main function.
@@ -43,13 +40,15 @@ fun main() {
Window(
onCloseRequest = ::exitApplication,
state = windowState,
- title = stringResource(Res.string.application_title),
+ title = "DesktopApp",
) {
// Sets the content of the window.
SharedApp(
+ updateScreenCapture = {},
+ handleRecreate = {},
handleThemeMode = {},
handleAppLocale = {},
- onSplashScreenRemoved = {},
+ onSplashScreenRemoved = {}
)
}
}
diff --git a/cmp-ios/Configuration/Config.xcconfig b/cmp-ios/Configuration/Config.xcconfig
index ce3c249ae..a23634af6 100644
--- a/cmp-ios/Configuration/Config.xcconfig
+++ b/cmp-ios/Configuration/Config.xcconfig
@@ -1,3 +1,3 @@
TEAM_ID=L432S2FZP5
-BUNDLE_ID=org.mifos.mobile
-APP_NAME=Mifos Mobile
+BUNDLE_ID=cmp.ios
+APP_NAME=LiteDo
\ No newline at end of file
diff --git a/cmp-ios/Podfile b/cmp-ios/Podfile
index 0f05686a7..fd91d3575 100644
--- a/cmp-ios/Podfile
+++ b/cmp-ios/Podfile
@@ -1,21 +1,13 @@
-deployment_target = '16.0'
+source 'https://cdn.cocoapods.org'
+platform :ios, '16.0'
+use_frameworks!
target 'iosApp' do
- use_frameworks!
- platform :ios, deployment_target
- # Pods for iosApp
+ project 'iosApp.xcodeproj'
pod 'cmp_shared', :path => '../cmp-shared'
-end
-post_install do |installer|
- installer.generated_projects.each do |project|
- project.targets.each do |target|
- target.build_configurations.each do |config|
- config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = deployment_target
- end
- end
- project.build_configurations.each do |config|
- config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = deployment_target
- end
- end
+ # Native Firebase iOS Pods required by firebase-kotlin-sdk
+ pod 'FirebaseCore'
+ pod 'FirebaseAnalytics'
+ pod 'FirebaseCrashlytics'
end
\ No newline at end of file
diff --git a/cmp-ios/iosApp.xcodeproj/project.pbxproj b/cmp-ios/iosApp.xcodeproj/project.pbxproj
index 1cbac6655..04c6ae538 100644
--- a/cmp-ios/iosApp.xcodeproj/project.pbxproj
+++ b/cmp-ios/iosApp.xcodeproj/project.pbxproj
@@ -9,22 +9,22 @@
/* Begin PBXBuildFile section */
058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
- 1C3FE2006C77932769810076 /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6DF4DB1CF5E68C5614135A56 /* Pods_iosApp.framework */; };
2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
+ 73867D86E599B875F7561EBD /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8B63CDEE6A83A279F370FDF /* Pods_iosApp.framework */; };
7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
- 1EB8E354CA5D35F960D11D5D /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.release.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig"; sourceTree = ""; };
2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; };
- 6DF4DB1CF5E68C5614135A56 /* Pods_iosApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- 7555FF7B242A565900829871 /* Mifos Mobile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mifos Mobile.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7555FF7B242A565900829871 /* LiteDo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LiteDo.app; sourceTree = BUILT_PRODUCTS_DIR; };
7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- 8DADAFB5E75F5E24CA4F0EB4 /* Pods-iosApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.debug.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig"; sourceTree = ""; };
AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; };
+ B5AB1CF32380D00920DC66AD /* Pods-iosApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.debug.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig"; sourceTree = ""; };
+ F8B63CDEE6A83A279F370FDF /* Pods_iosApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ FCFBF765CF1AE0CE66538ADF /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.release.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -32,7 +32,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 1C3FE2006C77932769810076 /* Pods_iosApp.framework in Frameworks */,
+ 73867D86E599B875F7561EBD /* Pods_iosApp.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -47,19 +47,10 @@
path = "Preview Content";
sourceTree = "";
};
- 1826950F674A72E12E219090 /* Pods */ = {
- isa = PBXGroup;
- children = (
- 8DADAFB5E75F5E24CA4F0EB4 /* Pods-iosApp.debug.xcconfig */,
- 1EB8E354CA5D35F960D11D5D /* Pods-iosApp.release.xcconfig */,
- );
- path = Pods;
- sourceTree = "";
- };
42799AB246E5F90AF97AA0EF /* Frameworks */ = {
isa = PBXGroup;
children = (
- 6DF4DB1CF5E68C5614135A56 /* Pods_iosApp.framework */,
+ F8B63CDEE6A83A279F370FDF /* Pods_iosApp.framework */,
);
name = Frameworks;
sourceTree = "";
@@ -71,14 +62,14 @@
7555FF7D242A565900829871 /* iosApp */,
7555FF7C242A565900829871 /* Products */,
42799AB246E5F90AF97AA0EF /* Frameworks */,
- 1826950F674A72E12E219090 /* Pods */,
+ AF5B1102F079B671D1EB1074 /* Pods */,
);
sourceTree = "";
};
7555FF7C242A565900829871 /* Products */ = {
isa = PBXGroup;
children = (
- 7555FF7B242A565900829871 /* Mifos Mobile.app */,
+ 7555FF7B242A565900829871 /* LiteDo.app */,
);
name = Products;
sourceTree = "";
@@ -103,6 +94,15 @@
path = Configuration;
sourceTree = "";
};
+ AF5B1102F079B671D1EB1074 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ B5AB1CF32380D00920DC66AD /* Pods-iosApp.debug.xcconfig */,
+ FCFBF765CF1AE0CE66538ADF /* Pods-iosApp.release.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -110,11 +110,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
buildPhases = (
- D005710AF42AFBD3373CBB90 /* [CP] Check Pods Manifest.lock */,
+ C0BDCDFA5D651BAABC1D1A96 /* [CP] Check Pods Manifest.lock */,
7555FF77242A565900829871 /* Sources */,
B92378962B6B1156000C7307 /* Frameworks */,
7555FF79242A565900829871 /* Resources */,
- 3843B476B28208558ACE8C15 /* [CP] Copy Pods Resources */,
+ F3CE6A35EE97CC26402CBE37 /* [CP] Embed Pods Frameworks */,
+ 087C63914A01F15B2F3377F1 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -122,7 +123,7 @@
);
name = iosApp;
productName = iosApp;
- productReference = 7555FF7B242A565900829871 /* Mifos Mobile.app */;
+ productReference = 7555FF7B242A565900829871 /* LiteDo.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@@ -172,7 +173,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 3843B476B28208558ACE8C15 /* [CP] Copy Pods Resources */ = {
+ 087C63914A01F15B2F3377F1 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -189,7 +190,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources.sh\"\n";
showEnvVarsInLog = 0;
};
- D005710AF42AFBD3373CBB90 /* [CP] Check Pods Manifest.lock */ = {
+ C0BDCDFA5D651BAABC1D1A96 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -211,6 +212,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
+ F3CE6A35EE97CC26402CBE37 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -350,7 +368,7 @@
};
7555FFA6242A565B00829871 /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 8DADAFB5E75F5E24CA4F0EB4 /* Pods-iosApp.debug.xcconfig */;
+ baseConfigurationReference = B5AB1CF32380D00920DC66AD /* Pods-iosApp.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Development";
@@ -363,8 +381,8 @@
"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
);
INFOPLIST_FILE = iosApp/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = "Mifos Mobile";
- INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
+ INFOPLIST_KEY_CFBundleDisplayName = LiteDo;
+ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -372,6 +390,7 @@
);
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
+ "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.mifos.kmp.template;
PRODUCT_NAME = "${APP_NAME}";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
@@ -381,21 +400,22 @@
};
7555FFA7242A565B00829871 /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 1EB8E354CA5D35F960D11D5D /* Pods-iosApp.release.xcconfig */;
+ baseConfigurationReference = FCFBF765CF1AE0CE66538ADF /* Pods-iosApp.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
- DEVELOPMENT_TEAM = "${TEAM_ID}";
+ DEVELOPMENT_TEAM = L432S2FZP5;
ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
- "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
+ "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
+ "$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
);
INFOPLIST_FILE = iosApp/Info.plist;
- INFOPLIST_KEY_CFBundleDisplayName = "Mifos Mobile";
- INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.finance";
+ INFOPLIST_KEY_CFBundleDisplayName = LiteDo;
+ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 15.3;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -403,6 +423,7 @@
);
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
+ "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.mifos.kmp.template;
PRODUCT_NAME = "${APP_NAME}";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
diff --git a/cmp-ios/iosApp.xcodeproj/project.xcworkspace/xcuserdata/hekmatullahamin.xcuserdatad/UserInterfaceState.xcuserstate b/cmp-ios/iosApp.xcodeproj/project.xcworkspace/xcuserdata/hekmatullahamin.xcuserdatad/UserInterfaceState.xcuserstate
index 86c5772eb..d70f71e4c 100644
Binary files a/cmp-ios/iosApp.xcodeproj/project.xcworkspace/xcuserdata/hekmatullahamin.xcuserdatad/UserInterfaceState.xcuserstate and b/cmp-ios/iosApp.xcodeproj/project.xcworkspace/xcuserdata/hekmatullahamin.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/cmp-ios/iosApp.xcodeproj/xcuserdata/hekmatullahamin.xcuserdatad/xcschemes/xcschememanagement.plist b/cmp-ios/iosApp.xcodeproj/xcuserdata/hekmatullahamin.xcuserdatad/xcschemes/xcschememanagement.plist
index aede5d13b..56b5955f8 100644
--- a/cmp-ios/iosApp.xcodeproj/xcuserdata/hekmatullahamin.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/cmp-ios/iosApp.xcodeproj/xcuserdata/hekmatullahamin.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -4,7 +4,7 @@
SchemeUserState
- cmp-ios.xcscheme_^#shared#^_
+ iosApp.xcscheme_^#shared#^_
orderHint
0
diff --git a/cmp-ios/iosApp.xcodeproj/xcuserdata/skniyajali.xcuserdatad/xcschemes/iosApp.xcscheme b/cmp-ios/iosApp.xcodeproj/xcuserdata/skniyajali.xcuserdatad/xcschemes/iosApp.xcscheme
new file mode 100644
index 000000000..d9679030b
--- /dev/null
+++ b/cmp-ios/iosApp.xcodeproj/xcuserdata/skniyajali.xcuserdatad/xcschemes/iosApp.xcscheme
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cmp-ios/iosApp.xcodeproj/xcuserdata/skniyajali.xcuserdatad/xcschemes/xcschememanagement.plist b/cmp-ios/iosApp.xcodeproj/xcuserdata/skniyajali.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 000000000..fa59f97d5
--- /dev/null
+++ b/cmp-ios/iosApp.xcodeproj/xcuserdata/skniyajali.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,14 @@
+
+
+
+
+ SchemeUserState
+
+ iosApp.xcscheme
+
+ orderHint
+ 0
+
+
+
+
diff --git a/cmp-ios/iosApp.xcworkspace/xcuserdata/skniyajali.xcuserdatad/xcschemes/xcschememanagement.plist b/cmp-ios/iosApp.xcworkspace/xcuserdata/skniyajali.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 000000000..ee3458dd7
--- /dev/null
+++ b/cmp-ios/iosApp.xcworkspace/xcuserdata/skniyajali.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/cmp-ios/iosApp/ContentView.swift b/cmp-ios/iosApp/ContentView.swift
index e130b8a2e..63b9da7e0 100644
--- a/cmp-ios/iosApp/ContentView.swift
+++ b/cmp-ios/iosApp/ContentView.swift
@@ -13,7 +13,8 @@ struct ComposeView: UIViewControllerRepresentable {
struct ContentView: View {
var body: some View {
ComposeView()
- .ignoresSafeArea(.keyboard) // Compose has own keyboard handler
+ .ignoresSafeArea(edges: .all)
+ .ignoresSafeArea(.keyboard) // .ignoresSafeArea(.keyboard) // Compose has own keyboard handler
}
}
diff --git a/cmp-ios/iosApp/Info.plist b/cmp-ios/iosApp/Info.plist
index 597a03ea9..dfcda749a 100644
--- a/cmp-ios/iosApp/Info.plist
+++ b/cmp-ios/iosApp/Info.plist
@@ -17,19 +17,13 @@
CFBundlePackageType
$(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
- 1.0.0
+ 1.0
CFBundleVersion
- 4
- ITSAppUsesNonExemptEncryption
-
+ 11
LSRequiresIPhoneOS
- NSCameraUsageDescription
- We use the camera to scan QR codes for payments and to add beneficiaries. No images or video are stored.
NSPhotoLibraryAddUsageDescription
- Allow access to choose a photo or document you decide to upload (e.g., profile photo or ID).
- NSPhotoLibraryUsageDescription
- Allow access to choose a photo or document you decide to upload (e.g., profile photo or ID).
+ Allow access to add photos to your library so you can save artworks directly to your device and view them offline.
UIApplicationSceneManifest
UIApplicationSupportsMultipleScenes
diff --git a/cmp-shared/build.gradle.kts b/cmp-shared/build.gradle.kts
index d548fabb5..c78a117d2 100644
--- a/cmp-shared/build.gradle.kts
+++ b/cmp-shared/build.gradle.kts
@@ -5,30 +5,37 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
plugins {
+ alias(libs.plugins.kmp.library.convention)
alias(libs.plugins.cmp.feature.convention)
- alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.kotlinCocoapods)
}
kotlin {
- iosX64()
- iosArm64()
- iosSimulatorArm64()
+ listOf(
+ iosX64(),
+ iosArm64(),
+ iosSimulatorArm64()
+ ).forEach { iosTarget ->
+ iosTarget.binaries.framework {
+ baseName = "ComposeApp"
+ isStatic = true
+ optimized = true
+ }
+ }
sourceSets {
commonMain.dependencies {
// Navigation Modules
implementation(projects.cmpNavigation)
implementation(compose.components.resources)
- api(projects.core.data)
- api(projects.core.network)
- //put your multiplatform dependencies here
- implementation(compose.material)
- implementation(compose.material3)
+ implementation(projects.coreBase.platform)
+ implementation(projects.coreBase.ui)
+
+ implementation(libs.coil.kt.compose)
}
desktopMain.dependencies {
@@ -40,7 +47,7 @@ kotlin {
cocoapods {
summary = "KMP Shared Module"
- homepage = "https://github.com/openMF/mifos-mobile"
+ homepage = "https://github.com/openMF/kmp-project-template"
version = "1.0"
ios.deploymentTarget = "16.0"
podfile = project.file("../cmp-ios/Podfile")
diff --git a/cmp-shared/cmp_shared.podspec b/cmp-shared/cmp_shared.podspec
index 7094d449a..a81166694 100644
--- a/cmp-shared/cmp_shared.podspec
+++ b/cmp-shared/cmp_shared.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |spec|
spec.name = 'cmp_shared'
spec.version = '1.0'
- spec.homepage = 'https://github.com/openMF/mifos-mobile'
+ spec.homepage = 'https://github.com/openMF/kmp-project-template'
spec.source = { :http=> ''}
spec.authors = ''
spec.license = ''
diff --git a/cmp-shared/src/commonMain/kotlin/cmp/shared/SharedApp.kt b/cmp-shared/src/commonMain/kotlin/cmp/shared/SharedApp.kt
index 91045f391..f28717e10 100644
--- a/cmp-shared/src/commonMain/kotlin/cmp/shared/SharedApp.kt
+++ b/cmp-shared/src/commonMain/kotlin/cmp/shared/SharedApp.kt
@@ -5,25 +5,38 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package cmp.shared
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import cmp.navigation.ComposeApp
+import coil3.compose.LocalPlatformContext
+import template.core.base.platform.LocalManagerProvider
+import template.core.base.platform.context.LocalContext
+import template.core.base.ui.LocalImageLoaderProvider
+import template.core.base.ui.getDefaultImageLoader
@Composable
fun SharedApp(
+ updateScreenCapture: (isScreenCaptureAllowed: Boolean) -> Unit,
+ handleRecreate: () -> Unit,
handleThemeMode: (osValue: Int) -> Unit,
handleAppLocale: (locale: String?) -> Unit,
- onSplashScreenRemoved: () -> Unit,
modifier: Modifier = Modifier,
+ onSplashScreenRemoved: () -> Unit,
) {
- ComposeApp(
- handleThemeMode = handleThemeMode,
- handleAppLocale = handleAppLocale,
- onSplashScreenRemoved = onSplashScreenRemoved,
- modifier = modifier,
- )
+ LocalManagerProvider(LocalContext.current) {
+ LocalImageLoaderProvider(getDefaultImageLoader(LocalPlatformContext.current)) {
+ ComposeApp(
+ updateScreenCapture = updateScreenCapture,
+ handleRecreate = handleRecreate,
+ handleThemeMode = handleThemeMode,
+ handleAppLocale = handleAppLocale,
+ onSplashScreenRemoved = onSplashScreenRemoved,
+ modifier = modifier,
+ )
+ }
+ }
}
diff --git a/cmp-shared/src/commonMain/kotlin/cmp/shared/utils/KoinExt.kt b/cmp-shared/src/commonMain/kotlin/cmp/shared/utils/KoinExt.kt
index d5a794a77..55f64f0b1 100644
--- a/cmp-shared/src/commonMain/kotlin/cmp/shared/utils/KoinExt.kt
+++ b/cmp-shared/src/commonMain/kotlin/cmp/shared/utils/KoinExt.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package cmp.shared.utils
diff --git a/cmp-shared/src/nativeMain/kotlin/org/mifos/shared/ViewController.kt b/cmp-shared/src/nativeMain/kotlin/org/mifos/shared/ViewController.kt
index bcca05304..0f9114c9f 100644
--- a/cmp-shared/src/nativeMain/kotlin/org/mifos/shared/ViewController.kt
+++ b/cmp-shared/src/nativeMain/kotlin/org/mifos/shared/ViewController.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package org.mifos.shared
@@ -19,6 +19,8 @@ fun viewController() = ComposeUIViewController(
},
) {
SharedApp(
+ updateScreenCapture = {},
+ handleRecreate = {},
handleThemeMode = {},
handleAppLocale = {},
onSplashScreenRemoved = {},
diff --git a/cmp-web/build.gradle.kts b/cmp-web/build.gradle.kts
index 5dec055ff..f07ed52b8 100644
--- a/cmp-web/build.gradle.kts
+++ b/cmp-web/build.gradle.kts
@@ -8,7 +8,7 @@ plugins {
kotlin {
js(IR) {
- moduleName = "cmp-web"
+ outputModuleName = "cmp-web"
browser {
commonWebpackConfig {
outputFileName = "cmp-web.js"
@@ -19,7 +19,7 @@ kotlin {
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
- moduleName = "cmp-wasm"
+ outputModuleName = "cmp-wasm"
browser {
commonWebpackConfig {
outputFileName = "cmp-wasm.js"
@@ -51,6 +51,7 @@ kotlin {
implementation(libs.multiplatform.settings.coroutines)
implementation(libs.koin.core)
+ implementation(libs.ktor.client.js)
}
}
@@ -59,10 +60,6 @@ kotlin {
}
}
-tasks.register("jsBrowserRun") {
- dependsOn("jsBrowserDevelopmentRun")
-}
-
compose.resources {
publicResClass = true
generateResClass = always
diff --git a/cmp-web/src/jsMain/kotlin/Application.kt b/cmp-web/src/jsMain/kotlin/Application.kt
index 68bdefc70..f39b86177 100644
--- a/cmp-web/src/jsMain/kotlin/Application.kt
+++ b/cmp-web/src/jsMain/kotlin/Application.kt
@@ -21,9 +21,11 @@ fun main() {
onWasmReady {
ComposeViewport(document.body!!) {
SharedApp(
+ updateScreenCapture = {},
+ handleRecreate = {},
handleThemeMode = {},
handleAppLocale = {},
- onSplashScreenRemoved = {},
+ onSplashScreenRemoved = {}
)
}
}
diff --git a/cmp-web/src/wasmJsMain/kotlin/Main.kt b/cmp-web/src/wasmJsMain/kotlin/Main.kt
index e91410a38..19df36e83 100644
--- a/cmp-web/src/wasmJsMain/kotlin/Main.kt
+++ b/cmp-web/src/wasmJsMain/kotlin/Main.kt
@@ -37,7 +37,7 @@ fun main() {
* This window uses the canvas element with the ID "ComposeTarget" and has the title "WebApp".
*/
CanvasBasedWindow(
- title = "Mifos Mobile", // Window title
+ title = "WebApp", // Window title
canvasElementId = "ComposeTarget", // The canvas element where the Compose UI will be rendered
) {
/*
@@ -45,9 +45,11 @@ fun main() {
* This function is responsible for setting up the entire UI structure of the app.
*/
SharedApp(
+ updateScreenCapture = {},
+ handleRecreate = {},
handleThemeMode = {},
handleAppLocale = {},
- onSplashScreenRemoved = {},
+ onSplashScreenRemoved = {}
)
}
}
diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml
index 8bcd830b7..811d8cb04 100644
--- a/config/detekt/detekt.yml
+++ b/config/detekt/detekt.yml
@@ -137,7 +137,7 @@ complexity:
threshold: 15
ComplexCondition:
active: true
- threshold: 5
+ threshold: 4
ComplexInterface:
active: false
threshold: 10
@@ -146,7 +146,7 @@ complexity:
ignoreOverloaded: false
CyclomaticComplexMethod:
active: true
- threshold: 25
+ threshold: 15
ignoreSingleWhenExpression: false
ignoreSimpleWhenEntries: false
ignoreNestingFunctions: false
@@ -168,7 +168,7 @@ complexity:
threshold: 600
LongMethod:
active: true
- threshold: 180 #60
+ threshold: 150 #60
LongParameterList:
active: true
# Updating Common values based on current scenario
@@ -186,7 +186,7 @@ complexity:
ignoreArgumentsMatchingNames: false
NestedBlockDepth:
active: true
- threshold: 6
+ threshold: 4
NestedScopeFunctions:
active: false
threshold: 1
@@ -232,7 +232,7 @@ complexity:
thresholdInFiles: 20
thresholdInClasses: 20
thresholdInInterfaces: 20
- thresholdInObjects: 22
+ thresholdInObjects: 20
thresholdInEnums: 20
ignoreDeprecated: false
ignorePrivate: false
@@ -432,6 +432,7 @@ naming:
"**/androidInstrumentedTest/**",
"**/jsTest/**",
"**/iosTest/**",
+ "**/generated/**",
]
functionPattern: "[a-z][a-zA-Z0-9]*"
excludeClassPattern: "$^"
@@ -689,7 +690,7 @@ style:
active: false
DestructuringDeclarationWithTooManyEntries:
active: true
- maxDestructuringEntries: 4
+ maxDestructuringEntries: 3
DoubleNegativeLambda:
active: false
negativeFunctions:
diff --git a/core-base/analytics/build.gradle.kts b/core-base/analytics/build.gradle.kts
index 2b7111a99..a4214c766 100644
--- a/core-base/analytics/build.gradle.kts
+++ b/core-base/analytics/build.gradle.kts
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
plugins {
alias(libs.plugins.kmp.library.convention)
diff --git a/core-base/analytics/src/androidDemo/kotlin/template/core/base/analytics/di/AnalyticsModule.kt b/core-base/analytics/src/androidDemo/kotlin/template/core/base/analytics/di/AnalyticsModule.kt
index 90f9f515d..45bcfaff3 100644
--- a/core-base/analytics/src/androidDemo/kotlin/template/core/base/analytics/di/AnalyticsModule.kt
+++ b/core-base/analytics/src/androidDemo/kotlin/template/core/base/analytics/di/AnalyticsModule.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics.di
diff --git a/core-base/analytics/src/androidMain/AndroidManifest.xml b/core-base/analytics/src/androidMain/AndroidManifest.xml
index 37d31716e..afcf313e3 100644
--- a/core-base/analytics/src/androidMain/AndroidManifest.xml
+++ b/core-base/analytics/src/androidMain/AndroidManifest.xml
@@ -6,6 +6,6 @@
If a copy of the MPL was not distributed with this file,
You can obtain one at https://mozilla.org/MPL/2.0/.
- See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
-->
\ No newline at end of file
diff --git a/core-base/analytics/src/androidProd/kotlin/template.core.base.analytics/di/AnalyticsModule.kt b/core-base/analytics/src/androidProd/kotlin/template.core.base.analytics/di/AnalyticsModule.kt
index 3f398c109..1e09b2cb2 100644
--- a/core-base/analytics/src/androidProd/kotlin/template.core.base.analytics/di/AnalyticsModule.kt
+++ b/core-base/analytics/src/androidProd/kotlin/template.core.base.analytics/di/AnalyticsModule.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
@file:Suppress("InvalidPackageDeclaration")
diff --git a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsEvent.kt b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsEvent.kt
index d98030a9e..12ff75c86 100644
--- a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsEvent.kt
+++ b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsEvent.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics
@@ -255,11 +255,26 @@ object Types {
*
* @since 1.0.0
*/
-data class Param(val key: String, val value: String) {
- init {
- require(key.isNotBlank()) { "Parameter key cannot be blank" }
- require(key.length <= 40) { "Parameter key cannot exceed 40 characters" }
- require(value.length <= 100) { "Parameter value cannot exceed 100 characters" }
+@ConsistentCopyVisibility
+data class Param private constructor(
+ val key: String,
+ val value: String,
+) {
+ companion object {
+ private const val MAX_VALUE_LENGTH = 100
+ private const val MAX_KEY_LENGTH = 40
+ private const val FALLBACK_KEY = "unknown_param"
+
+ operator fun invoke(key: String, value: String): Param {
+ val safeKey = key
+ .takeIf { it.isNotBlank() }
+ ?.take(MAX_KEY_LENGTH)
+ ?: FALLBACK_KEY
+
+ val safeValue = value.take(MAX_VALUE_LENGTH)
+
+ return Param(safeKey, safeValue)
+ }
}
}
diff --git a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsExtensions.kt b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsExtensions.kt
index 848f7c7b1..cd521e79f 100644
--- a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsExtensions.kt
+++ b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsExtensions.kt
@@ -5,11 +5,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics
-import kotlinx.datetime.Clock
+import kotlin.time.Clock
import kotlin.time.Duration
import kotlin.time.DurationUnit
diff --git a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsHelper.kt b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsHelper.kt
index 00b6c997e..e4561bc06 100644
--- a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsHelper.kt
+++ b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/AnalyticsHelper.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics
diff --git a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/NoOpAnalyticsHelper.kt b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/NoOpAnalyticsHelper.kt
index b515ef192..4cd5a8225 100644
--- a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/NoOpAnalyticsHelper.kt
+++ b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/NoOpAnalyticsHelper.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics
diff --git a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/PerformanceTracker.kt b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/PerformanceTracker.kt
index d8fa77fa8..696a0643a 100644
--- a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/PerformanceTracker.kt
+++ b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/PerformanceTracker.kt
@@ -5,11 +5,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics
-import kotlinx.datetime.Clock
+import kotlin.time.Clock
/** Performance tracking utilities for analytics */
diff --git a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/StubAnalyticsHelper.kt b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/StubAnalyticsHelper.kt
index cb8f26024..7b092612d 100644
--- a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/StubAnalyticsHelper.kt
+++ b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/StubAnalyticsHelper.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics
diff --git a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/TestingUtils.kt b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/TestingUtils.kt
index 4e48ab6a9..38b245938 100644
--- a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/TestingUtils.kt
+++ b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/TestingUtils.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics
diff --git a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/UiHelpers.kt b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/UiHelpers.kt
index 57bc2845d..6a7d71dba 100644
--- a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/UiHelpers.kt
+++ b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/UiHelpers.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics
diff --git a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/ValidationUtils.kt b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/ValidationUtils.kt
index 0f71384ef..bfb1440c0 100644
--- a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/ValidationUtils.kt
+++ b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/ValidationUtils.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics
diff --git a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/di/AnalyticsModule.kt b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/di/AnalyticsModule.kt
index c424346dd..ff4919044 100644
--- a/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/di/AnalyticsModule.kt
+++ b/core-base/analytics/src/commonMain/kotlin/template/core/base/analytics/di/AnalyticsModule.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics.di
diff --git a/core-base/analytics/src/desktopMain/kotlin/template/core/base/analytics/di/AnalyticsModule.desktop.kt b/core-base/analytics/src/desktopMain/kotlin/template/core/base/analytics/di/AnalyticsModule.desktop.kt
index 84b754df6..c82b116dc 100644
--- a/core-base/analytics/src/desktopMain/kotlin/template/core/base/analytics/di/AnalyticsModule.desktop.kt
+++ b/core-base/analytics/src/desktopMain/kotlin/template/core/base/analytics/di/AnalyticsModule.desktop.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics.di
diff --git a/core-base/analytics/src/jsMain/kotlin/template.core.base.analytics/di/AnalyticsModule.js.kt b/core-base/analytics/src/jsMain/kotlin/template.core.base.analytics/di/AnalyticsModule.js.kt
index 6f9b6f75e..1b3db40e5 100644
--- a/core-base/analytics/src/jsMain/kotlin/template.core.base.analytics/di/AnalyticsModule.js.kt
+++ b/core-base/analytics/src/jsMain/kotlin/template.core.base.analytics/di/AnalyticsModule.js.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
@file:Suppress("InvalidPackageDeclaration")
diff --git a/core-base/analytics/src/nativeMain/kotlin/template.core.base.analytics.di/AnalyticsModule.native.kt b/core-base/analytics/src/nativeMain/kotlin/template.core.base.analytics.di/AnalyticsModule.native.kt
index 530bacb71..369336f23 100644
--- a/core-base/analytics/src/nativeMain/kotlin/template.core.base.analytics.di/AnalyticsModule.native.kt
+++ b/core-base/analytics/src/nativeMain/kotlin/template.core.base.analytics.di/AnalyticsModule.native.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
@file:Suppress("InvalidPackageDeclaration")
diff --git a/core-base/analytics/src/nonJsCommonMain/kotlin/template/core/base/analytics/FirebaseAnalyticsHelper.kt b/core-base/analytics/src/nonJsCommonMain/kotlin/template/core/base/analytics/FirebaseAnalyticsHelper.kt
index a3a29eae8..bfa9814ba 100644
--- a/core-base/analytics/src/nonJsCommonMain/kotlin/template/core/base/analytics/FirebaseAnalyticsHelper.kt
+++ b/core-base/analytics/src/nonJsCommonMain/kotlin/template/core/base/analytics/FirebaseAnalyticsHelper.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics
diff --git a/core-base/analytics/src/wasmJsMain/kotlin/template/core/base/analytics/di/AnalyticsModule.wasmJs.kt b/core-base/analytics/src/wasmJsMain/kotlin/template/core/base/analytics/di/AnalyticsModule.wasmJs.kt
index 84b754df6..c82b116dc 100644
--- a/core-base/analytics/src/wasmJsMain/kotlin/template/core/base/analytics/di/AnalyticsModule.wasmJs.kt
+++ b/core-base/analytics/src/wasmJsMain/kotlin/template/core/base/analytics/di/AnalyticsModule.wasmJs.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.analytics.di
diff --git a/core-base/common/build.gradle.kts b/core-base/common/build.gradle.kts
index 1e1456274..246ece295 100644
--- a/core-base/common/build.gradle.kts
+++ b/core-base/common/build.gradle.kts
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
plugins {
alias(libs.plugins.kmp.library.convention)
diff --git a/core-base/common/src/androidMain/AndroidManifest.xml b/core-base/common/src/androidMain/AndroidManifest.xml
index e60d7f745..8e3b49f55 100644
--- a/core-base/common/src/androidMain/AndroidManifest.xml
+++ b/core-base/common/src/androidMain/AndroidManifest.xml
@@ -6,7 +6,7 @@
If a copy of the MPL was not distributed with this file,
You can obtain one at https://mozilla.org/MPL/2.0/.
- See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
-->
diff --git a/core-base/common/src/androidMain/kotlin/template/core/base/common/Parcelize.android.kt b/core-base/common/src/androidMain/kotlin/template/core/base/common/Parcelize.android.kt
index 4f9a1c7ba..e7d0f5e83 100644
--- a/core-base/common/src/androidMain/kotlin/template/core/base/common/Parcelize.android.kt
+++ b/core-base/common/src/androidMain/kotlin/template/core/base/common/Parcelize.android.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.common
diff --git a/core-base/common/src/androidMain/kotlin/template/core/base/common/di/CommonModule.android.kt b/core-base/common/src/androidMain/kotlin/template/core/base/common/di/CommonModule.android.kt
index c293e2999..3b59da829 100644
--- a/core-base/common/src/androidMain/kotlin/template/core/base/common/di/CommonModule.android.kt
+++ b/core-base/common/src/androidMain/kotlin/template/core/base/common/di/CommonModule.android.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.common.di
diff --git a/core-base/common/src/androidMain/kotlin/template/core/base/common/manager/DispatchManagerImpl.kt b/core-base/common/src/androidMain/kotlin/template/core/base/common/manager/DispatchManagerImpl.kt
index 73a934420..8268588f6 100644
--- a/core-base/common/src/androidMain/kotlin/template/core/base/common/manager/DispatchManagerImpl.kt
+++ b/core-base/common/src/androidMain/kotlin/template/core/base/common/manager/DispatchManagerImpl.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
@file:Suppress("ktlint:standard:filename", "MatchingDeclarationName")
diff --git a/core-base/common/src/commonMain/kotlin/template/core/base/common/DataState.kt b/core-base/common/src/commonMain/kotlin/template/core/base/common/DataState.kt
index b2d736a85..c10083246 100644
--- a/core-base/common/src/commonMain/kotlin/template/core/base/common/DataState.kt
+++ b/core-base/common/src/commonMain/kotlin/template/core/base/common/DataState.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.common
diff --git a/core-base/common/src/commonMain/kotlin/template/core/base/common/DataStateExtensions.kt b/core-base/common/src/commonMain/kotlin/template/core/base/common/DataStateExtensions.kt
index 407862176..b33afa7ab 100644
--- a/core-base/common/src/commonMain/kotlin/template/core/base/common/DataStateExtensions.kt
+++ b/core-base/common/src/commonMain/kotlin/template/core/base/common/DataStateExtensions.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.common
diff --git a/core-base/common/src/commonMain/kotlin/template/core/base/common/ImageExtension.kt b/core-base/common/src/commonMain/kotlin/template/core/base/common/ImageExtension.kt
index 39fa764dc..d58bad17b 100644
--- a/core-base/common/src/commonMain/kotlin/template/core/base/common/ImageExtension.kt
+++ b/core-base/common/src/commonMain/kotlin/template/core/base/common/ImageExtension.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.common
diff --git a/core-base/common/src/commonMain/kotlin/template/core/base/common/Parcelize.kt b/core-base/common/src/commonMain/kotlin/template/core/base/common/Parcelize.kt
index fbd1b2e4a..dcbb90490 100644
--- a/core-base/common/src/commonMain/kotlin/template/core/base/common/Parcelize.kt
+++ b/core-base/common/src/commonMain/kotlin/template/core/base/common/Parcelize.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.common
diff --git a/core-base/common/src/commonMain/kotlin/template/core/base/common/di/CommonModule.kt b/core-base/common/src/commonMain/kotlin/template/core/base/common/di/CommonModule.kt
index e78b98fd4..d1b0ce6db 100644
--- a/core-base/common/src/commonMain/kotlin/template/core/base/common/di/CommonModule.kt
+++ b/core-base/common/src/commonMain/kotlin/template/core/base/common/di/CommonModule.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.common.di
diff --git a/core-base/common/src/commonMain/kotlin/template/core/base/common/manager/DispatcherManager.kt b/core-base/common/src/commonMain/kotlin/template/core/base/common/manager/DispatcherManager.kt
index f8bf3102c..5c686d4ad 100644
--- a/core-base/common/src/commonMain/kotlin/template/core/base/common/manager/DispatcherManager.kt
+++ b/core-base/common/src/commonMain/kotlin/template/core/base/common/manager/DispatcherManager.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.common.manager
diff --git a/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/Parcelize.nonAndroid.kt b/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/Parcelize.nonAndroid.kt
index 54e898c35..c4070fb95 100644
--- a/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/Parcelize.nonAndroid.kt
+++ b/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/Parcelize.nonAndroid.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.common
diff --git a/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/di/CommonModule.nonAndroid.kt b/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/di/CommonModule.nonAndroid.kt
index c293e2999..3b59da829 100644
--- a/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/di/CommonModule.nonAndroid.kt
+++ b/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/di/CommonModule.nonAndroid.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.common.di
diff --git a/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/manager/DispatchManagerImpl.kt b/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/manager/DispatchManagerImpl.kt
index c04acce84..3dbee647c 100644
--- a/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/manager/DispatchManagerImpl.kt
+++ b/core-base/common/src/nonAndroidMain/kotlin/template/core/base/common/manager/DispatchManagerImpl.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
@file:Suppress("ktlint:standard:filename", "MatchingDeclarationName")
diff --git a/core-base/database/build.gradle.kts b/core-base/database/build.gradle.kts
index bb9928297..d458eade7 100644
--- a/core-base/database/build.gradle.kts
+++ b/core-base/database/build.gradle.kts
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
import org.jetbrains.compose.compose
diff --git a/core-base/database/src/androidMain/kotlin/template/core/base/database/AppDatabaseFactory.kt b/core-base/database/src/androidMain/kotlin/template/core/base/database/AppDatabaseFactory.kt
index 8fc9b4c48..302d3bff4 100644
--- a/core-base/database/src/androidMain/kotlin/template/core/base/database/AppDatabaseFactory.kt
+++ b/core-base/database/src/androidMain/kotlin/template/core/base/database/AppDatabaseFactory.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.database
diff --git a/core-base/database/src/commonMain/kotlin/template/core/base/database/Room.kt b/core-base/database/src/commonMain/kotlin/template/core/base/database/Room.kt
index e935dd5f7..1fd6c12eb 100644
--- a/core-base/database/src/commonMain/kotlin/template/core/base/database/Room.kt
+++ b/core-base/database/src/commonMain/kotlin/template/core/base/database/Room.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.database
diff --git a/core-base/database/src/commonMain/kotlin/template/core/base/database/TypeConverter.kt b/core-base/database/src/commonMain/kotlin/template/core/base/database/TypeConverter.kt
index e708c2a5f..a7dc4d4ec 100644
--- a/core-base/database/src/commonMain/kotlin/template/core/base/database/TypeConverter.kt
+++ b/core-base/database/src/commonMain/kotlin/template/core/base/database/TypeConverter.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.database
diff --git a/core-base/database/src/desktopMain/kotlin/template/core/base/database/AppDatabaseFactory.kt b/core-base/database/src/desktopMain/kotlin/template/core/base/database/AppDatabaseFactory.kt
index 6e5afe2ec..0584d68ef 100644
--- a/core-base/database/src/desktopMain/kotlin/template/core/base/database/AppDatabaseFactory.kt
+++ b/core-base/database/src/desktopMain/kotlin/template/core/base/database/AppDatabaseFactory.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.database
diff --git a/core-base/database/src/nativeMain/kotlin/template/core/base/database/AppDatabaseFactory.kt b/core-base/database/src/nativeMain/kotlin/template/core/base/database/AppDatabaseFactory.kt
index f217e646e..7b5aac1d2 100644
--- a/core-base/database/src/nativeMain/kotlin/template/core/base/database/AppDatabaseFactory.kt
+++ b/core-base/database/src/nativeMain/kotlin/template/core/base/database/AppDatabaseFactory.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.database
diff --git a/core-base/database/src/nonJsCommonMain/kotlin/template/core/base/database/Room.nonJsCommon.kt b/core-base/database/src/nonJsCommonMain/kotlin/template/core/base/database/Room.nonJsCommon.kt
index f4762df10..9bf7f9a1d 100644
--- a/core-base/database/src/nonJsCommonMain/kotlin/template/core/base/database/Room.nonJsCommon.kt
+++ b/core-base/database/src/nonJsCommonMain/kotlin/template/core/base/database/Room.nonJsCommon.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.database
diff --git a/core-base/database/src/nonJsCommonMain/kotlin/template/core/base/database/TypeConverter.nonJsCommon.kt b/core-base/database/src/nonJsCommonMain/kotlin/template/core/base/database/TypeConverter.nonJsCommon.kt
index 3cc7c4000..a414eeed0 100644
--- a/core-base/database/src/nonJsCommonMain/kotlin/template/core/base/database/TypeConverter.nonJsCommon.kt
+++ b/core-base/database/src/nonJsCommonMain/kotlin/template/core/base/database/TypeConverter.nonJsCommon.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.database
diff --git a/core-base/designsystem/build.gradle.kts b/core-base/designsystem/build.gradle.kts
index 033a52f8f..4b06a3736 100644
--- a/core-base/designsystem/build.gradle.kts
+++ b/core-base/designsystem/build.gradle.kts
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
plugins {
alias(libs.plugins.kmp.library.convention)
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptMaterialTheme.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptMaterialTheme.kt
index 16493bd56..7b4f2aa42 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptMaterialTheme.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptMaterialTheme.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptTheme.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptTheme.kt
index 9ea79808d..5098f1fe0 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptTheme.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptTheme.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptThemeExtensions.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptThemeExtensions.kt
index 5990b2606..086203c52 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptThemeExtensions.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/KptThemeExtensions.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/BounceAnimation.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/BounceAnimation.kt
index f9104b9a0..ec142c486 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/BounceAnimation.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/BounceAnimation.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.component
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptAnimationSpecs.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptAnimationSpecs.kt
index eb57a3b51..542f012e5 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptAnimationSpecs.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptAnimationSpecs.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.component
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptShimmerLoadingBox.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptShimmerLoadingBox.kt
index 5c993ed58..6036c161d 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptShimmerLoadingBox.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptShimmerLoadingBox.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.component
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptSnackbarHost.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptSnackbarHost.kt
index aeb451220..514744e0c 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptSnackbarHost.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptSnackbarHost.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.component
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptTopAppBar.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptTopAppBar.kt
index f69ed446a..695c9a147 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptTopAppBar.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/KptTopAppBar.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
@file:OptIn(ExperimentalMaterial3Api::class)
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/SlideTransition.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/SlideTransition.kt
index 8c5632beb..15d3cf880 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/SlideTransition.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/component/SlideTransition.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.component
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/ComponentStateHolder.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/ComponentStateHolder.kt
index e97385567..7815df866 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/ComponentStateHolder.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/ComponentStateHolder.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.core
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/KptComponent.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/KptComponent.kt
index baa9e2dc6..94066f4ee 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/KptComponent.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/KptComponent.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.core
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/KptTopAppBarConfiguration.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/KptTopAppBarConfiguration.kt
index c0781973e..901e21849 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/KptTopAppBarConfiguration.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/core/KptTopAppBarConfiguration.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.core
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveListDetailPaneScaffold.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveListDetailPaneScaffold.kt
index a8df05c03..265b89367 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveListDetailPaneScaffold.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveListDetailPaneScaffold.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigableListDetailScaffold.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigableListDetailScaffold.kt
index f416f712b..3d5df1427 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigableListDetailScaffold.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigableListDetailScaffold.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigableSupportingPaneScaffold.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigableSupportingPaneScaffold.kt
index 200a4087e..1f3444862 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigableSupportingPaneScaffold.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigableSupportingPaneScaffold.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigationSuiteScaffold.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigationSuiteScaffold.kt
index 80a548ce8..14d8183c7 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigationSuiteScaffold.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/AdaptiveNavigationSuiteScaffold.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptFlowColumn.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptFlowColumn.kt
index 4f607da44..8222f3ab0 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptFlowColumn.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptFlowColumn.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptFlowRow.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptFlowRow.kt
index 5ebd108ac..c07c5ef02 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptFlowRow.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptFlowRow.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptGrid.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptGrid.kt
index e563b27d3..498edd508 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptGrid.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptGrid.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptMasonryGrid.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptMasonryGrid.kt
index e3ddfc6ad..6ad2338a5 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptMasonryGrid.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptMasonryGrid.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptResponsiveLayout.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptResponsiveLayout.kt
index 4e9dc8848..e1d0af5b7 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptResponsiveLayout.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptResponsiveLayout.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptSidebarLayout.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptSidebarLayout.kt
index e71824c5c..4d1a8c395 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptSidebarLayout.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptSidebarLayout.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptSplitPane.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptSplitPane.kt
index 8b3b355e1..fbc7a2b19 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptSplitPane.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptSplitPane.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptStack.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptStack.kt
index 1d961518b..545a1b4d6 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptStack.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/layout/KptStack.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.layout
diff --git a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/theme/KptColorSchemeImpl.kt b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/theme/KptColorSchemeImpl.kt
index 89f0f7fcb..95972a624 100644
--- a/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/theme/KptColorSchemeImpl.kt
+++ b/core-base/designsystem/src/commonMain/kotlin/template/core/base/designsystem/theme/KptColorSchemeImpl.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.designsystem.theme
diff --git a/core-base/network/build.gradle.kts b/core-base/network/build.gradle.kts
index 328896039..0a58def30 100644
--- a/core-base/network/build.gradle.kts
+++ b/core-base/network/build.gradle.kts
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
plugins {
alias(libs.plugins.kmp.library.convention)
@@ -13,7 +13,7 @@ plugins {
}
android {
- namespace = "org.mifos.corebase.network"
+ namespace = "template.core.base.network"
}
kotlin {
diff --git a/core-base/network/src/androidMain/kotlin/template/core/base/network/KtorHttpClient.android.kt b/core-base/network/src/androidMain/kotlin/template/core/base/network/KtorHttpClient.android.kt
new file mode 100644
index 000000000..65edfc330
--- /dev/null
+++ b/core-base/network/src/androidMain/kotlin/template/core/base/network/KtorHttpClient.android.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package template.core.base.network
+
+import io.ktor.client.HttpClient
+import io.ktor.client.HttpClientConfig
+import io.ktor.client.engine.okhttp.OkHttp
+
+actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(OkHttp) {
+ config(this)
+}
diff --git a/core-base/network/src/commonMain/kotlin/template/core/base/network/KtorHttpClient.kt b/core-base/network/src/commonMain/kotlin/template/core/base/network/KtorHttpClient.kt
new file mode 100644
index 000000000..e9d9584f4
--- /dev/null
+++ b/core-base/network/src/commonMain/kotlin/template/core/base/network/KtorHttpClient.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package template.core.base.network
+
+import io.ktor.client.HttpClient
+import io.ktor.client.HttpClientConfig
+import io.ktor.client.plugins.HttpTimeout
+import io.ktor.client.plugins.auth.Auth
+import io.ktor.client.plugins.auth.providers.BasicAuthCredentials
+import io.ktor.client.plugins.auth.providers.BearerTokens
+import io.ktor.client.plugins.auth.providers.DigestAuthCredentials
+import io.ktor.client.plugins.auth.providers.basic
+import io.ktor.client.plugins.auth.providers.bearer
+import io.ktor.client.plugins.auth.providers.digest
+import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
+import io.ktor.client.plugins.defaultRequest
+import io.ktor.client.plugins.logging.DEFAULT
+import io.ktor.client.plugins.logging.LogLevel
+import io.ktor.client.plugins.logging.Logger
+import io.ktor.client.plugins.logging.Logging
+import io.ktor.http.HttpHeaders
+import io.ktor.serialization.kotlinx.json.json
+import kotlinx.serialization.json.Json
+import co.touchlab.kermit.Logger.Companion as KermitLogger
+
+expect fun httpClient(config: HttpClientConfig<*>.() -> Unit): HttpClient
+
+/**
+ * Provides a default [HttpClientConfig] setup for use with a Ktor-based HTTP client.
+ *
+ * This function simplifies client configuration by handling common concerns such as:
+ * - Authentication (Bearer, Basic, Digest)
+ * - Default headers
+ * - Timeouts
+ * - Logging
+ * - JSON serialization
+ *
+ * It can be passed directly into a Ktor client builder via the `config` lambda:
+ * ```kotlin
+ * val client = httpClient(setupDefaultHttpClient(baseUrl = "https://api.example.com"))
+ * ```
+ *
+ * @param baseUrl The base URL to be applied to all requests unless explicitly overridden.
+ * @param authRequiredUrl A list of hostnames that require authentication.
+ * @param defaultHeaders Headers that are applied to every request.
+ * @param requestTimeout Timeout in milliseconds for entire request lifecycle.
+ * @param socketTimeout Timeout in milliseconds for socket-level communication.
+ * @param httpLogger A logger used for HTTP logging (defaults to `Logger.DEFAULT`).
+ * @param httpLogLevel Level of HTTP logging (e.g. `LogLevel.ALL`).
+ * @param loggableHosts A list of hostnames for which HTTP logging is enabled.
+ * @param sensitiveHeaders List of headers to be hidden in logs (defaults to Authorization).
+ * @param jsonConfig Custom [Json] configuration used by `ContentNegotiation`.
+ * @param basicCredentialsProvider Provider for Basic authentication credentials.
+ * @param digestCredentialsProvider Provider for Digest authentication credentials.
+ * @param bearerTokensProvider Provider for Bearer token authentication.
+ * @param bearerRefreshProvider Optional refresh logic for Bearer tokens (only used if Bearer auth is configured).
+ *
+ * @return A configuration lambda to be passed into the Ktor [HttpClient].
+ */
+fun setupDefaultHttpClient(
+ baseUrl: String,
+ authRequiredUrl: List = emptyList(),
+ defaultHeaders: Map = emptyMap(),
+ requestTimeout: Long = 60_000L,
+ socketTimeout: Long = 60_000L,
+ httpLogger: Logger = Logger.DEFAULT,
+ httpLogLevel: LogLevel = LogLevel.ALL,
+ loggableHosts: List = emptyList(),
+ sensitiveHeaders: List = listOf(HttpHeaders.Authorization),
+ jsonConfig: Json = Json {
+ prettyPrint = true
+ isLenient = true
+ ignoreUnknownKeys = true
+ explicitNulls = false
+ },
+ basicCredentialsProvider: (() -> BasicAuthCredentials)? = null,
+ digestCredentialsProvider: (() -> DigestAuthCredentials)? = null,
+ bearerTokensProvider: (() -> BearerTokens)? = null,
+ bearerRefreshProvider: (() -> BearerTokens)? = null,
+): HttpClientConfig<*>.() -> Unit = {
+ when {
+ bearerTokensProvider != null -> {
+ install(Auth) {
+ bearer {
+ loadTokens { bearerTokensProvider() }
+ if (bearerRefreshProvider != null) {
+ refreshTokens {
+ bearerRefreshProvider()
+ }
+ }
+ sendWithoutRequest { request ->
+ request.url.host in authRequiredUrl
+ }
+ }
+ }
+ }
+
+ basicCredentialsProvider != null -> {
+ install(Auth) {
+ basic {
+ credentials {
+ basicCredentialsProvider()
+ }
+ sendWithoutRequest { request ->
+ request.url.host in authRequiredUrl
+ }
+ }
+ }
+ }
+
+ digestCredentialsProvider != null -> {
+ install(Auth) {
+ digest {
+ credentials {
+ digestCredentialsProvider()
+ }
+ }
+ }
+ }
+ }
+
+ defaultRequest {
+ url(baseUrl)
+ defaultHeaders.forEach { (key, value) ->
+ headers.append(key, value)
+ }
+ }
+
+ install(HttpTimeout) {
+ requestTimeoutMillis = requestTimeout
+ socketTimeoutMillis = socketTimeout
+ }
+
+ install(Logging) {
+ logger = httpLogger
+ level = httpLogLevel
+ filter { request ->
+ loggableHosts.any { host ->
+ request.url.host.contains(host)
+ }
+ }
+ sanitizeHeader { header ->
+ header in sensitiveHeaders
+ }
+ logger = object : Logger {
+ override fun log(message: String) {
+ KermitLogger.d(tag = "KtorClient", messageString = message)
+ }
+ }
+ }
+
+ install(ContentNegotiation) {
+ json(jsonConfig)
+ }
+}
diff --git a/core-base/network/src/commonMain/kotlin/template/core/base/network/NetworkError.kt b/core-base/network/src/commonMain/kotlin/template/core/base/network/NetworkError.kt
new file mode 100644
index 000000000..c8c4f52f1
--- /dev/null
+++ b/core-base/network/src/commonMain/kotlin/template/core/base/network/NetworkError.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package template.core.base.network
+
+/**
+ * Represents standardized error types for remote or network operations.
+ *
+ * This enum is typically used with the [NetworkResult.Error] variant to describe what kind of failure occurred.
+ */
+enum class NetworkError {
+
+ /**
+ * The request was malformed or missing required parameters (HTTP 400).
+ */
+ BAD_REQUEST,
+
+ /**
+ * The requested resource could not be found (HTTP 404).
+ */
+ NOT_FOUND,
+
+ /**
+ * Authentication failed due to invalid or missing credentials (HTTP 401).
+ */
+ UNAUTHORIZED,
+
+ /**
+ * The request timed out, usually due to a slow or unresponsive network (HTTP 408 or socket timeout).
+ */
+ REQUEST_TIMEOUT,
+
+ /**
+ * The client has sent too many requests in a given amount of time (HTTP 429).
+ */
+ TOO_MANY_REQUESTS,
+
+ /**
+ * A server-side error occurred (HTTP 5xx).
+ */
+ SERVER,
+
+ /**
+ * The response could not be deserialized, likely due to mismatched or invalid data formats.
+ */
+ SERIALIZATION,
+
+ /**
+ * An unknown or unexpected error occurred, used as a fallback when the specific cause is not identifiable.
+ */
+ UNKNOWN,
+}
diff --git a/core-base/network/src/commonMain/kotlin/template/core/base/network/NetworkResult.kt b/core-base/network/src/commonMain/kotlin/template/core/base/network/NetworkResult.kt
new file mode 100644
index 000000000..abcf6845a
--- /dev/null
+++ b/core-base/network/src/commonMain/kotlin/template/core/base/network/NetworkResult.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package template.core.base.network
+
+/**
+ * Represents the result of a network or remote operation, encapsulating either a success or an error.
+ *
+ * This is a sealed interface with two implementations:
+ * - [Success] indicates the operation completed successfully and contains the resulting data.
+ * - [Error] represents a failure and contains a [NetworkError] describing the error condition.
+ *
+ * @param D The type of data returned on success.
+ * @param E The type of error returned on failure, constrained to [NetworkError].
+ */
+sealed interface NetworkResult {
+
+ /**
+ * Represents a successful result.
+ *
+ * @param D The type of the successful response data.
+ * @property data The actual result of the operation.
+ */
+ data class Success(val data: D) : NetworkResult
+
+ /**
+ * Represents a failed result due to a [NetworkError].
+ *
+ * @param E The specific type of [NetworkError] encountered.
+ * @property error Details about the error that occurred.
+ */
+ data class Error(val error: E) : NetworkResult
+}
diff --git a/core-base/network/src/commonMain/kotlin/template/core/base/network/factory/ResultSuspendConverterFactory.kt b/core-base/network/src/commonMain/kotlin/template/core/base/network/factory/ResultSuspendConverterFactory.kt
new file mode 100644
index 000000000..64f26cb87
--- /dev/null
+++ b/core-base/network/src/commonMain/kotlin/template/core/base/network/factory/ResultSuspendConverterFactory.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package template.core.base.network.factory
+
+import de.jensklingenberg.ktorfit.Ktorfit
+import de.jensklingenberg.ktorfit.converter.Converter
+import de.jensklingenberg.ktorfit.converter.KtorfitResult
+import de.jensklingenberg.ktorfit.converter.TypeData
+import io.ktor.client.call.NoTransformationFoundException
+import io.ktor.client.call.body
+import io.ktor.client.statement.HttpResponse
+import kotlinx.serialization.SerializationException
+import template.core.base.network.NetworkError
+import template.core.base.network.NetworkResult
+
+/**
+ * A custom [Converter.Factory] for Ktorfit that provides a suspend
+ * response converter which wraps successful or error HTTP responses into a
+ * sealed [NetworkResult] type.
+ *
+ * This is useful for abstracting error handling logic across your network
+ * layer while providing strong typing for both success and failure
+ * outcomes.
+ *
+ * This converter handles:
+ * - HTTP 2xx responses by deserializing the response body into the
+ * expected type.
+ * - Known HTTP error codes like 400, 401, 404, etc., by mapping them to
+ * [NetworkError] types.
+ * - Deserialization issues via [SerializationException].
+ * - Unknown failures via [KtorfitResult.Failure].
+ *
+ * Example usage:
+ * ```kotlin
+ * interface ApiService {
+ * @GET("users")
+ * suspend fun getUsers(): Result, RemoteError>
+ * }
+ * ```
+ */
+@Suppress("NestedBlockDepth")
+class ResultSuspendConverterFactory : Converter.Factory {
+
+ /**
+ * Creates a [Converter.SuspendResponseConverter] that wraps an HTTP
+ * response into a [NetworkResult] type.
+ *
+ * @param typeData Metadata about the expected response type.
+ * @param ktorfit The [Ktorfit] instance requesting this converter.
+ * @return A [Converter.SuspendResponseConverter] if the return type is
+ * `Result`, or `null` otherwise.
+ */
+ override fun suspendResponseConverter(
+ typeData: TypeData,
+ ktorfit: Ktorfit,
+ ): Converter.SuspendResponseConverter? {
+ if (typeData.typeInfo.type == NetworkResult::class) {
+ val successType = typeData.typeArgs.first().typeInfo
+ return object :
+ Converter.SuspendResponseConverter> {
+
+ /**
+ * Converts a [KtorfitResult] into a [NetworkResult], handling success and
+ * various failure scenarios.
+ *
+ * @param result The response wrapped in [KtorfitResult].
+ * @return A [NetworkResult.Success] if the response is successful, or a
+ * [NetworkResult.Error] if an error occurred.
+ */
+ override suspend fun convert(result: KtorfitResult): NetworkResult {
+ return when (result) {
+ is KtorfitResult.Failure -> {
+ println("Failure: " + result.throwable.message)
+ NetworkResult.Error(NetworkError.UNKNOWN)
+ }
+
+ is KtorfitResult.Success -> {
+ val status = result.response.status.value
+
+ when (status) {
+ in 200..209 -> {
+ try {
+ val data = result.response.body(successType) as Any
+ NetworkResult.Success(data)
+ } catch (e: NoTransformationFoundException) {
+ NetworkResult.Error(NetworkError.SERIALIZATION)
+ } catch (e: SerializationException) {
+ println("Serialization error: ${e.message}")
+ NetworkResult.Error(NetworkError.SERIALIZATION)
+ }
+ }
+
+ 400 -> NetworkResult.Error(NetworkError.BAD_REQUEST)
+ 401 -> NetworkResult.Error(NetworkError.UNAUTHORIZED)
+ 404 -> NetworkResult.Error(NetworkError.NOT_FOUND)
+ 408 -> NetworkResult.Error(NetworkError.REQUEST_TIMEOUT)
+ 429 -> NetworkResult.Error(NetworkError.TOO_MANY_REQUESTS)
+ in 500..599 -> NetworkResult.Error(NetworkError.SERVER)
+ else -> {
+ println("Status code $status")
+ NetworkResult.Error(NetworkError.UNKNOWN)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return null
+ }
+}
diff --git a/core-base/network/src/desktopMain/kotlin/template/core/base/network/KtorHttpClient.desktop.kt b/core-base/network/src/desktopMain/kotlin/template/core/base/network/KtorHttpClient.desktop.kt
new file mode 100644
index 000000000..65edfc330
--- /dev/null
+++ b/core-base/network/src/desktopMain/kotlin/template/core/base/network/KtorHttpClient.desktop.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package template.core.base.network
+
+import io.ktor.client.HttpClient
+import io.ktor.client.HttpClientConfig
+import io.ktor.client.engine.okhttp.OkHttp
+
+actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(OkHttp) {
+ config(this)
+}
diff --git a/core-base/network/src/jsMain/kotlin/template/core/base/network/KtorHttpClient.js.kt b/core-base/network/src/jsMain/kotlin/template/core/base/network/KtorHttpClient.js.kt
new file mode 100644
index 000000000..beaa36ea3
--- /dev/null
+++ b/core-base/network/src/jsMain/kotlin/template/core/base/network/KtorHttpClient.js.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package template.core.base.network
+
+import io.ktor.client.HttpClient
+import io.ktor.client.HttpClientConfig
+import io.ktor.client.engine.js.Js
+
+actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(Js) {
+ config(this)
+}
diff --git a/core-base/network/src/nativeMain/kotlin/template/core/base/network/KtorHttpClient.native.kt b/core-base/network/src/nativeMain/kotlin/template/core/base/network/KtorHttpClient.native.kt
new file mode 100644
index 000000000..3f6c9a4d4
--- /dev/null
+++ b/core-base/network/src/nativeMain/kotlin/template/core/base/network/KtorHttpClient.native.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package template.core.base.network
+
+import io.ktor.client.HttpClient
+import io.ktor.client.HttpClientConfig
+import io.ktor.client.engine.darwin.Darwin
+
+actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(Darwin) {
+ config(this)
+}
diff --git a/core-base/network/src/wasmJsMain/kotlin/template/core/base/network/KtorHttpClient.wasmJs.kt b/core-base/network/src/wasmJsMain/kotlin/template/core/base/network/KtorHttpClient.wasmJs.kt
new file mode 100644
index 000000000..beaa36ea3
--- /dev/null
+++ b/core-base/network/src/wasmJsMain/kotlin/template/core/base/network/KtorHttpClient.wasmJs.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
+ */
+package template.core.base.network
+
+import io.ktor.client.HttpClient
+import io.ktor.client.HttpClientConfig
+import io.ktor.client.engine.js.Js
+
+actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(Js) {
+ config(this)
+}
diff --git a/core-base/platform/build.gradle.kts b/core-base/platform/build.gradle.kts
index 54f02d6d3..b49cf6d7b 100644
--- a/core-base/platform/build.gradle.kts
+++ b/core-base/platform/build.gradle.kts
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
import org.gradle.kotlin.dsl.implementation
diff --git a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/LocalManagerProviders.android.kt b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/LocalManagerProviders.android.kt
index f634c52ea..5659bb404 100644
--- a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/LocalManagerProviders.android.kt
+++ b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/LocalManagerProviders.android.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform
diff --git a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/context/AppContext.android.kt b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/context/AppContext.android.kt
index 410abd97f..7c80cabff 100644
--- a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/context/AppContext.android.kt
+++ b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/context/AppContext.android.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.context
diff --git a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.android.kt b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.android.kt
index ac13801fc..8fd69b656 100644
--- a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.android.kt
+++ b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.android.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.garbage
diff --git a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/intent/IntentManagerImpl.kt b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/intent/IntentManagerImpl.kt
index 221df75dd..e3f04b245 100644
--- a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/intent/IntentManagerImpl.kt
+++ b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/intent/IntentManagerImpl.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.intent
diff --git a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/review/AppReviewManagerImpl.kt b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/review/AppReviewManagerImpl.kt
index 765fba1b4..40d0e277e 100644
--- a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/review/AppReviewManagerImpl.kt
+++ b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/review/AppReviewManagerImpl.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.review
diff --git a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/update/AppUpdateManagerImpl.kt b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/update/AppUpdateManagerImpl.kt
index eb35664dd..f78a49408 100644
--- a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/update/AppUpdateManagerImpl.kt
+++ b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/update/AppUpdateManagerImpl.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.update
diff --git a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/utils/AndroidBuildUtils.kt b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/utils/AndroidBuildUtils.kt
index a73fbe724..fdd9a9f3d 100644
--- a/core-base/platform/src/androidMain/kotlin/template/core/base/platform/utils/AndroidBuildUtils.kt
+++ b/core-base/platform/src/androidMain/kotlin/template/core/base/platform/utils/AndroidBuildUtils.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.utils
diff --git a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/LocalManagerProviders.kt b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/LocalManagerProviders.kt
index 7ada5dec2..4a78fdce6 100644
--- a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/LocalManagerProviders.kt
+++ b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/LocalManagerProviders.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform
diff --git a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/context/AppContext.kt b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/context/AppContext.kt
index 7fbfcc212..cdcd49f8e 100644
--- a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/context/AppContext.kt
+++ b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/context/AppContext.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.context
diff --git a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/di/PlatformModule.kt b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/di/PlatformModule.kt
index 4dc1ba195..35db29eda 100644
--- a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/di/PlatformModule.kt
+++ b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/di/PlatformModule.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.di
diff --git a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.kt b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.kt
index 6808ff5fe..961d451fd 100644
--- a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.kt
+++ b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.garbage
diff --git a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManagerImpl.kt b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManagerImpl.kt
index 325d82bc0..73ba12312 100644
--- a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManagerImpl.kt
+++ b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManagerImpl.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.garbage
diff --git a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/intent/IntentManager.kt b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/intent/IntentManager.kt
index a7db4c69d..034fb6c9f 100644
--- a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/intent/IntentManager.kt
+++ b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/intent/IntentManager.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.intent
diff --git a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/model/MimeType.kt b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/model/MimeType.kt
index 25970acde..f0340940e 100644
--- a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/model/MimeType.kt
+++ b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/model/MimeType.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.model
diff --git a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/review/AppReviewManager.kt b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/review/AppReviewManager.kt
index b3c4155d1..0d6e8de4e 100644
--- a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/review/AppReviewManager.kt
+++ b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/review/AppReviewManager.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.review
diff --git a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/update/AppUpdateManager.kt b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/update/AppUpdateManager.kt
index 287d8d97c..623381e4b 100644
--- a/core-base/platform/src/commonMain/kotlin/template/core/base/platform/update/AppUpdateManager.kt
+++ b/core-base/platform/src/commonMain/kotlin/template/core/base/platform/update/AppUpdateManager.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.update
diff --git a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/LocalManagerProviders.native.kt b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/LocalManagerProviders.native.kt
index e2b4cd54f..b824fbc86 100644
--- a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/LocalManagerProviders.native.kt
+++ b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/LocalManagerProviders.native.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform
diff --git a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/context/AppContext.native.kt b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/context/AppContext.native.kt
index 9da7ff9a3..f578dcb25 100644
--- a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/context/AppContext.native.kt
+++ b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/context/AppContext.native.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.context
diff --git a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.nonAndroid.kt b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.nonAndroid.kt
index 5250cc98b..2be8bc385 100644
--- a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.nonAndroid.kt
+++ b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/garbage/GarbageCollectionManager.nonAndroid.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.garbage
diff --git a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/intent/IntentManagerImpl.kt b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/intent/IntentManagerImpl.kt
index c9f9c0210..c7da0f592 100644
--- a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/intent/IntentManagerImpl.kt
+++ b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/intent/IntentManagerImpl.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.intent
diff --git a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/review/AppReviewManagerImpl.kt b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/review/AppReviewManagerImpl.kt
index d4c1d7be5..1a3f6d7d2 100644
--- a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/review/AppReviewManagerImpl.kt
+++ b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/review/AppReviewManagerImpl.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.review
diff --git a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/update/AppUpdateManagerImpl.kt b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/update/AppUpdateManagerImpl.kt
index c22f52ac4..eb4fc2731 100644
--- a/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/update/AppUpdateManagerImpl.kt
+++ b/core-base/platform/src/nonAndroidMain/kotlin/template/core/base/platform/update/AppUpdateManagerImpl.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.platform.update
diff --git a/core-base/ui/README.md b/core-base/ui/README.md
index eec63d023..91ec552b5 100644
--- a/core-base/ui/README.md
+++ b/core-base/ui/README.md
@@ -381,29 +381,6 @@ internal fun rememberDefaultImageLoader(context: PlatformContext): ImageLoader {
The `maxSizePercent` call is crucialβit adapts the cache size to the device's available memory,
ensuring efficient resource usage across a wide range of devices.
-### Request Optimization
-
-The image request builder includes memory cache optimization:
-
-```kotlin
-@Composable
-fun rememberImageRequest(
- context: PlatformContext,
- wallpaper: String,
-): ImageRequest {
- return remember(wallpaper) {
- ImageRequest.Builder(context)
- .data(wallpaper)
- .memoryCacheKey(wallpaper)
- .placeholderMemoryCacheKey(wallpaper)
- .build()
- }
-}
-```
-
-The use of `memoryCacheKey` and `placeholderMemoryCacheKey` ensures that images with the same URL
-share the same cache entry, reducing memory usage and improving load times.
-
### Common Use Patterns
For profile pictures and avatars:
@@ -411,8 +388,7 @@ For profile pictures and avatars:
```kotlin
@Composable
fun CircularProfileImage(url: String, size: Dp = 48.dp) {
- val context = LocalPlatformContext.current
- val imageLoader = rememberImageLoader(context)
+ val imageLoader = rememberImageLoader()
AsyncImage(
model = rememberImageRequest(context, url),
@@ -434,8 +410,7 @@ For background images:
```kotlin
@Composable
fun BackgroundImage(url: String, overlay: Color = Color.Black.copy(alpha = 0.3f)) {
- val context = LocalPlatformContext.current
- val imageLoader = rememberImageLoader(context)
+ val imageLoader = rememberImageLoader()
Box {
AsyncImage(
diff --git a/core-base/ui/build.gradle.kts b/core-base/ui/build.gradle.kts
index 12e071548..4ada49284 100644
--- a/core-base/ui/build.gradle.kts
+++ b/core-base/ui/build.gradle.kts
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
diff --git a/core-base/ui/src/androidMain/kotlin/template/core/base/ui/JankStatsExtensions.kt b/core-base/ui/src/androidMain/kotlin/template/core/base/ui/JankStatsExtensions.kt
index 9da46f17b..7a6782398 100644
--- a/core-base/ui/src/androidMain/kotlin/template/core/base/ui/JankStatsExtensions.kt
+++ b/core-base/ui/src/androidMain/kotlin/template/core/base/ui/JankStatsExtensions.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/androidMain/kotlin/template/core/base/ui/ReportDrawnExt.android.kt b/core-base/ui/src/androidMain/kotlin/template/core/base/ui/ReportDrawnExt.android.kt
index 45578737c..de004552f 100644
--- a/core-base/ui/src/androidMain/kotlin/template/core/base/ui/ReportDrawnExt.android.kt
+++ b/core-base/ui/src/androidMain/kotlin/template/core/base/ui/ReportDrawnExt.android.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/androidMain/kotlin/template/core/base/ui/ShareUtils.android.kt b/core-base/ui/src/androidMain/kotlin/template/core/base/ui/ShareUtils.android.kt
index a792cbe28..b97a13dca 100644
--- a/core-base/ui/src/androidMain/kotlin/template/core/base/ui/ShareUtils.android.kt
+++ b/core-base/ui/src/androidMain/kotlin/template/core/base/ui/ShareUtils.android.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
@@ -14,10 +14,12 @@ import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
+import android.provider.Settings
import android.util.Log
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.core.content.FileProvider
+import androidx.core.net.toUri
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.compose.resources.ExperimentalResourceApi
@@ -102,4 +104,71 @@ actual object ShareUtils {
}
}
}
+
+ actual fun openUrl(url: String) {
+ val context = ShareUtils.activityProvider.invoke().application.baseContext
+ val uri = url.let { url.toUri() }
+ val intent = Intent(Intent.ACTION_VIEW).apply {
+ data = uri
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ context.startActivity(intent)
+ }
+
+ actual fun openAppInfo() {
+ val context = activityProvider.invoke().application.baseContext
+ val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
+ data = Uri.parse("package:${context.packageName}")
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ context.startActivity(intent)
+ }
+
+ actual fun callPhone(number: String) {
+ val context = activityProvider.invoke().application.baseContext
+ val uri = Uri.parse("tel:$number")
+ val intent = Intent(Intent.ACTION_DIAL).apply {
+ data = uri
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ context.startActivity(intent)
+ }
+
+ actual fun sendEmail(to: String, subject: String?, body: String?) {
+ val context = activityProvider.invoke().application.baseContext
+ val uriBuilder = StringBuilder("mailto:").append(to)
+ val query = mutableListOf()
+ subject?.let { query.add("subject=" + Uri.encode(it)) }
+ body?.let { query.add("body=" + Uri.encode(it)) }
+ if (query.isNotEmpty()) {
+ uriBuilder.append("?").append(query.joinToString("&"))
+ }
+ val intent = Intent(Intent.ACTION_SENDTO).apply {
+ data = Uri.parse(uriBuilder.toString())
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ context.startActivity(intent)
+ }
+
+ actual fun sendViaSMS(number: String, message: String) {
+ val context = activityProvider.invoke().application.baseContext
+ val uri = if (number.isNotEmpty()) {
+ Uri.parse("sms:$number")
+ } else {
+ Uri.parse("sms:")
+ }
+ val intent = Intent(Intent.ACTION_SENDTO).apply {
+ data = uri
+ putExtra("sms_body", message)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ context.startActivity(intent)
+ }
+
+ actual fun copyText(text: String) {
+ val context = activityProvider.invoke().application.baseContext
+ val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
+ val clip = android.content.ClipData.newPlainText("Copied Text", text)
+ clipboardManager.setPrimaryClip(clip)
+ }
}
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/BackgroundEvent.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/BackgroundEvent.kt
index 914d1a3c2..5a54d833e 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/BackgroundEvent.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/BackgroundEvent.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt
index 3a72463d0..2b4b8550a 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/BaseViewModel.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/EventsEffect.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/EventsEffect.kt
index 46457b3bf..0a460c029 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/EventsEffect.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/EventsEffect.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ImageLoaderExt.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ImageLoaderExt.kt
index 4151e6d81..e5b5d40c6 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ImageLoaderExt.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ImageLoaderExt.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
@@ -15,78 +15,60 @@ import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import coil3.ImageLoader
import coil3.PlatformContext
+import coil3.compose.LocalPlatformContext
import coil3.memory.MemoryCache
-import coil3.request.ImageRequest
+import coil3.request.CachePolicy
import coil3.util.DebugLogger
import io.github.vinceglb.filekit.coil.addPlatformFileSupport
/**
- * CompositionLocal that provides access to an [ImageLoader] within the composition hierarchy.
- * Default value is null, requiring an explicit provider or fallback to default implementation.
- */
-internal val LocalAppImageLoader = compositionLocalOf { null }
-
-/**
- * Returns an [ImageLoader] from the current composition or creates a default one if none is available.
+ * A CompositionLocal instance used to provide a shared [ImageLoader] across the composable hierarchy.
+ * This is useful for performing image loading and caching within Jetpack Compose UIs.
*
- * @param context Platform context required for image loading
- * @return An [ImageLoader] instance that can be used for image loading operations
+ * If no [ImageLoader] is provided within the context, an error will be thrown when accessing this CompositionLocal.
*/
-@Composable
-fun rememberImageLoader(context: PlatformContext): ImageLoader {
- return LocalAppImageLoader.current ?: rememberDefaultImageLoader(context)
+internal val LocalAppImageLoader = compositionLocalOf {
+ error("No ImageLoader provided. Have you forgotten the LocalImageLoaderProvider?")
}
/**
- * Creates and remembers a default [ImageLoader] with memory cache and debug logging.
+ * Creates and remembers an instance of the default ImageLoader configured with platform-specific
+ * settings for caching and logging. The ImageLoader is initialized using the current platform context.
*
- * @param context Platform context required for image loading and memory calculations
- * @return A default configured [ImageLoader] instance
+ * @return A remembered instance of the default ImageLoader configured with common settings.
*/
@Composable
-internal fun rememberDefaultImageLoader(context: PlatformContext): ImageLoader {
- return remember(context) {
- ImageLoader.Builder(context)
- .memoryCache {
- MemoryCache.Builder()
- .maxSizePercent(context, 0.25)
- .build()
- }
- .logger(DebugLogger())
- .components {
- addPlatformFileSupport()
- }
+fun rememberImageLoader(): ImageLoader {
+ val context = LocalPlatformContext.current
+ return remember(context) { getDefaultImageLoader(context) }
+}
+
+/**
+ * Creates and returns a default instance of an ImageLoader configured with common settings such as
+ * logging, caching policies, memory caching, and platform-specific component support.
+ *
+ * @param context The platform-specific context required to initialize the ImageLoader
+ * @return A configured ImageLoader instance ready for use
+ */
+fun getDefaultImageLoader(context: PlatformContext): ImageLoader = ImageLoader
+ .Builder(context)
+ .logger(DebugLogger())
+ .networkCachePolicy(CachePolicy.ENABLED)
+ .memoryCachePolicy(CachePolicy.ENABLED)
+ .memoryCache {
+ MemoryCache.Builder()
+ .maxSizePercent(context, 0.25)
.build()
}
-}
+ .components {
+ addPlatformFileSupport()
+ }.build()
/**
- * Creates and remembers an [ImageRequest] for loading the specified wallpaper.
+ * Provides a composable local context for an `ImageLoader` to be used within a Composable hierarchy.
*
- * @param context Platform context required for the image request
- * @param wallpaper String identifier for the wallpaper to be loaded
- * @return An [ImageRequest] configured for the specified wallpaper
- */
-@Composable
-fun rememberImageRequest(
- context: PlatformContext,
- wallpaper: String,
-): ImageRequest {
- return remember(wallpaper) {
- ImageRequest.Builder(context)
- .data(wallpaper)
- .memoryCacheKey(wallpaper)
- .placeholderMemoryCacheKey(wallpaper)
- .build()
- }
-}
-
-/**
- * Provides the specified [ImageLoader] to all composables within the [content] lambda
- * via [CompositionLocalProvider].
- *
- * @param imageLoader The [ImageLoader] to provide downstream
- * @param content The composable content that will have access to the provided [ImageLoader]
+ * @param imageLoader The `ImageLoader` instance to be provided to the local composition.
+ * @param content A composable lambda function representing the UI content that can access the provided `ImageLoader`.
*/
@Composable
fun LocalImageLoaderProvider(imageLoader: ImageLoader, content: @Composable () -> Unit) {
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/JankStatsExtension.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/JankStatsExtension.kt
index 72b712f1b..aed073cfe 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/JankStatsExtension.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/JankStatsExtension.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/LifecycleEventEffect.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/LifecycleEventEffect.kt
index e59267fde..142019344 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/LifecycleEventEffect.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/LifecycleEventEffect.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/NavGraphBuilderExtensions.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/NavGraphBuilderExtensions.kt
index 609eb0dcb..0b69a46a6 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/NavGraphBuilderExtensions.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/NavGraphBuilderExtensions.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ReportDrawnExt.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ReportDrawnExt.kt
index d83a37ae9..a51c76d51 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ReportDrawnExt.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ReportDrawnExt.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ShareUtils.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ShareUtils.kt
index fe22188fb..105f7654e 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ShareUtils.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/ShareUtils.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
@@ -39,4 +39,50 @@ expect object ShareUtils {
* @param byte The raw image data as ByteArray
*/
suspend fun shareImage(title: String, byte: ByteArray)
+
+ /**
+ * Opens the specified URL in the device's default web browser.
+ *
+ * @param url The URL to open.
+ */
+ fun openUrl(url: String)
+
+ /**
+ * Opens the application info screen in the device settings.
+ *
+ * Typically used to allow users to manage app permissions,
+ * storage, or other app-specific settings.
+ */
+ fun openAppInfo()
+
+ /**
+ * Initiates a phone call using the platform dialer UI.
+ *
+ * @param number The phone number to dial, digits only or including country code.
+ */
+ fun callPhone(number: String)
+
+ /**
+ * Opens the platform email composer with the given parameters.
+ *
+ * @param to Recipient email address
+ * @param subject Optional subject line
+ * @param body Optional email body
+ */
+ fun sendEmail(to: String, subject: String? = null, body: String? = null)
+
+ /**
+ * Opens the platform SMS composer with the given parameters.
+ *
+ * @param number Recipient phone number (can be empty to let user choose)
+ * @param message The SMS message body
+ */
+ fun sendViaSMS(number: String, message: String)
+
+ /**
+ * Copies the given text to the system clipboard.
+ *
+ * @param text The text to copy to clipboard
+ */
+ fun copyText(text: String)
}
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/SharedElementExt.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/SharedElementExt.kt
index 440124c9b..f0ec8aa82 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/SharedElementExt.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/SharedElementExt.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
@file:OptIn(ExperimentalSharedTransitionApi::class)
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/StringExt.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/StringExt.kt
index 6b7455463..bc0f0bc11 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/StringExt.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/StringExt.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/Transition.kt b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/Transition.kt
index 63de01f91..875ef5716 100644
--- a/core-base/ui/src/commonMain/kotlin/template/core/base/ui/Transition.kt
+++ b/core-base/ui/src/commonMain/kotlin/template/core/base/ui/Transition.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/desktopMain/java/template/core/base/ui/ShareUtils.desktop.kt b/core-base/ui/src/desktopMain/java/template/core/base/ui/ShareUtils.desktop.kt
index c7233d7d1..d227c0871 100644
--- a/core-base/ui/src/desktopMain/java/template/core/base/ui/ShareUtils.desktop.kt
+++ b/core-base/ui/src/desktopMain/java/template/core/base/ui/ShareUtils.desktop.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
@@ -13,6 +13,8 @@ import androidx.compose.ui.graphics.asSkiaBitmap
import io.github.vinceglb.filekit.FileKit
import io.github.vinceglb.filekit.saveImageToGallery
import kotlinx.coroutines.DelicateCoroutinesApi
+import java.awt.Desktop
+import java.net.URI
actual object ShareUtils {
@OptIn(DelicateCoroutinesApi::class)
@@ -41,4 +43,69 @@ actual object ShareUtils {
filename = "$title.png",
)
}
+
+ actual fun openUrl(url: String) {
+ try {
+ if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
+ Desktop.getDesktop().browse(URI(url))
+ }
+ } catch (e: Exception) {
+ println("Error opening URL: ${e.message}")
+ }
+ }
+
+ actual fun openAppInfo() {
+ // Not applicable on Desktop; no-op
+ }
+
+ actual fun callPhone(number: String) {
+ try {
+ if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
+ Desktop.getDesktop().browse(URI("tel:$number"))
+ }
+ } catch (e: Exception) {
+ println("Error opening dialer: ${e.message}")
+ }
+ }
+
+ actual fun sendEmail(to: String, subject: String?, body: String?) {
+ val q = mutableListOf()
+ subject?.let { q.add("subject=" + java.net.URLEncoder.encode(it, Charsets.UTF_8)) }
+ body?.let { q.add("body=" + java.net.URLEncoder.encode(it, Charsets.UTF_8)) }
+ val query = if (q.isNotEmpty()) "?" + q.joinToString("&") else ""
+ val mailto = "mailto:$to$query"
+ try {
+ if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
+ Desktop.getDesktop().browse(URI(mailto))
+ }
+ } catch (e: Exception) {
+ println("Error opening email client: ${e.message}")
+ }
+ }
+
+ actual fun sendViaSMS(number: String, message: String) {
+ val encodedMessage = java.net.URLEncoder.encode(message, Charsets.UTF_8)
+ val smsUrl = if (number.isNotEmpty()) {
+ "sms:$number?body=$encodedMessage"
+ } else {
+ "sms:?body=$encodedMessage"
+ }
+ try {
+ if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
+ Desktop.getDesktop().browse(URI(smsUrl))
+ }
+ } catch (e: Exception) {
+ println("Error opening SMS: ${e.message}")
+ }
+ }
+
+ actual fun copyText(text: String) {
+ try {
+ val clipboard = java.awt.Toolkit.getDefaultToolkit().systemClipboard
+ val stringSelection = java.awt.datatransfer.StringSelection(text)
+ clipboard.setContents(stringSelection, null)
+ } catch (e: Exception) {
+ println("Error copying to clipboard: ${e.message}")
+ }
+ }
}
diff --git a/core-base/ui/src/jsCommonMain/kotlin/template/core/base/ui/ShareUtils.kt b/core-base/ui/src/jsCommonMain/kotlin/template/core/base/ui/ShareUtils.kt
index 06d39f423..7884921e2 100644
--- a/core-base/ui/src/jsCommonMain/kotlin/template/core/base/ui/ShareUtils.kt
+++ b/core-base/ui/src/jsCommonMain/kotlin/template/core/base/ui/ShareUtils.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
@@ -42,4 +42,22 @@ actual object ShareUtils {
fileName = "$title.png",
)
}
+
+ actual fun openUrl(url: String) {
+ }
+
+ actual fun openAppInfo() {
+ }
+
+ actual fun callPhone(number: String) {
+ }
+
+ actual fun sendEmail(to: String, subject: String?, body: String?) {
+ }
+
+ actual fun sendViaSMS(number: String, message: String) {
+ }
+
+ actual fun copyText(text: String) {
+ }
}
diff --git a/core-base/ui/src/nativeMain/kotlin/template/core/base/ui/ShareUtils.native.kt b/core-base/ui/src/nativeMain/kotlin/template/core/base/ui/ShareUtils.native.kt
index 585756f22..e554b3b01 100644
--- a/core-base/ui/src/nativeMain/kotlin/template/core/base/ui/ShareUtils.native.kt
+++ b/core-base/ui/src/nativeMain/kotlin/template/core/base/ui/ShareUtils.native.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
@@ -13,8 +13,10 @@ import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asSkiaBitmap
import io.github.vinceglb.filekit.FileKit
import io.github.vinceglb.filekit.saveImageToGallery
+import platform.Foundation.NSURL
import platform.UIKit.UIActivityViewController
import platform.UIKit.UIApplication
+import platform.UIKit.UIApplicationOpenSettingsURLString
actual object ShareUtils {
actual suspend fun shareText(text: String) {
@@ -42,4 +44,58 @@ actual object ShareUtils {
filename = "$title.png",
)
}
+
+ actual fun openUrl(url: String) {
+ val nsUrl = NSURL.URLWithString(url)
+ if (nsUrl != null) {
+ UIApplication.sharedApplication.openURL(nsUrl)
+ }
+ }
+
+ actual fun openAppInfo() {
+ val url = NSURL.URLWithString(UIApplicationOpenSettingsURLString)
+ if (url != null && UIApplication.sharedApplication.canOpenURL(url)) {
+ UIApplication.sharedApplication.openURL(url)
+ }
+ }
+
+ actual fun callPhone(number: String) {
+ val url = NSURL.URLWithString("tel:$number")
+ if (url != null && UIApplication.sharedApplication.canOpenURL(url)) {
+ UIApplication.sharedApplication.openURL(url)
+ }
+ }
+
+ actual fun sendEmail(to: String, subject: String?, body: String?) {
+ fun encode(s: String): String = s.replace(" ", "%20").replace("\n", "%0A")
+ val encodedSubject = subject?.let { encode(it) } ?: ""
+ val encodedBody = body?.let { encode(it) } ?: ""
+ val query = buildList {
+ if (encodedSubject.isNotEmpty()) add("subject=$encodedSubject")
+ if (encodedBody.isNotEmpty()) add("body=$encodedBody")
+ }.joinToString("&")
+ val mailto = if (query.isNotEmpty()) "mailto:$to?$query" else "mailto:$to"
+ val url = NSURL.URLWithString(mailto)
+ if (url != null && UIApplication.sharedApplication.canOpenURL(url)) {
+ UIApplication.sharedApplication.openURL(url)
+ }
+ }
+
+ actual fun sendViaSMS(number: String, message: String) {
+ fun encode(s: String): String = s.replace(" ", "%20").replace("\n", "%0A")
+ val encodedMessage = encode(message)
+ val smsUrl = if (number.isNotEmpty()) {
+ "sms:$number&body=$encodedMessage"
+ } else {
+ "sms:&body=$encodedMessage"
+ }
+ val url = NSURL.URLWithString(smsUrl)
+ if (url != null && UIApplication.sharedApplication.canOpenURL(url)) {
+ UIApplication.sharedApplication.openURL(url)
+ }
+ }
+
+ actual fun copyText(text: String) {
+ platform.UIKit.UIPasteboard.generalPasteboard.string = text
+ }
}
diff --git a/core-base/ui/src/nonAndroidMain/kotlin/template/core/base/ui/JankStatsExtension.jvmJs.kt b/core-base/ui/src/nonAndroidMain/kotlin/template/core/base/ui/JankStatsExtension.jvmJs.kt
index 79fac842a..6cb9e5877 100644
--- a/core-base/ui/src/nonAndroidMain/kotlin/template/core/base/ui/JankStatsExtension.jvmJs.kt
+++ b/core-base/ui/src/nonAndroidMain/kotlin/template/core/base/ui/JankStatsExtension.jvmJs.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/core-base/ui/src/nonAndroidMain/kotlin/template/core/base/ui/ReportDrawnExt.jvmJs.kt b/core-base/ui/src/nonAndroidMain/kotlin/template/core/base/ui/ReportDrawnExt.jvmJs.kt
index ef719f589..27ee6131e 100644
--- a/core-base/ui/src/nonAndroidMain/kotlin/template/core/base/ui/ReportDrawnExt.jvmJs.kt
+++ b/core-base/ui/src/nonAndroidMain/kotlin/template/core/base/ui/ReportDrawnExt.jvmJs.kt
@@ -5,7 +5,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
- * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ * See See https://github.com/openMF/kmp-project-template/blob/main/LICENSE
*/
package template.core.base.ui
diff --git a/fastlane/AppFile b/fastlane/AppFile
index 76cfee016..a355ac5ab 100644
--- a/fastlane/AppFile
+++ b/fastlane/AppFile
@@ -1,2 +1,12 @@
-json_key_file("cmp-android/playStorePublishServiceCredentialsFile.json")
-package_name("org.mifos.mobile") # e.g. org.mifos.mobile
\ No newline at end of file
+# Load project configuration using absolute path
+# require_relative doesn't work in AppFile due to eval context
+project_dir = File.expand_path('..', __dir__)
+require File.join(project_dir, 'fastlane-config', 'project_config')
+
+# Android configuration
+json_key_file(FastlaneConfig::ProjectConfig::ANDROID[:play_store_json_key])
+package_name(FastlaneConfig::ProjectConfig.android_package_name)
+
+# iOS configuration
+apple_id(FastlaneConfig::ProjectConfig::IOS[:app_identifier])
+team_id(FastlaneConfig::ProjectConfig::IOS[:team_id])
diff --git a/fastlane/FastFile b/fastlane/FastFile
index 929a49001..06362dec5 100644
--- a/fastlane/FastFile
+++ b/fastlane/FastFile
@@ -36,7 +36,7 @@ platform :android do
generateVersion = generateVersion()
buildAndSignApp(
- taskName: "bundle",
+ taskName: "assemble",
buildType: "Release",
**signing_config
)
@@ -324,80 +324,60 @@ platform :ios do
# Shared Private Lane Helpers
#############################
- private_lane :setup_ci_if_needed do
- unless ENV['CI']
- UI.message("π₯οΈ Running locally, skipping CI-specific setup.")
- else
- setup_ci
- end
+ private_lane :setup_ci_if_needed do |options|
+ unless ENV['CI']
+ UI.message("π₯οΈ Running locally, skipping CI-specific setup.")
+ else
+ ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
+ setup_ci(
+ provider: options[:ci_provider] || ios_config[:ci_provider]
+ )
+ end
end
private_lane :load_api_key do |options|
- ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
+ ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
- app_store_connect_api_key(
- key_id: options[:appstore_key_id] || ios_config[:key_id],
- issuer_id: options[:appstore_issuer_id] || ios_config[:issuer_id],
- key_filepath: options[:key_filepath] || ios_config[:key_filepath],
- duration: 1200
- )
+ app_store_connect_api_key(
+ key_id: options[:appstore_key_id] || ios_config[:key_id],
+ issuer_id: options[:appstore_issuer_id] || ios_config[:issuer_id],
+ key_filepath: options[:key_filepath] || ios_config[:key_filepath],
+ duration: 1200
+ )
end
private_lane :fetch_certificates_with_match do |options|
- ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
- match(
- type: options[:match_type] || ios_config[:match_type],
- app_identifier: options[:app_identifier] || ios_config[:app_identifier],
- readonly: true,
- git_url: options[:git_url] || ios_config[:git_url],
- git_branch: options[:git_branch] || ios_config[:git_branch],
- git_private_key: options[:git_private_key] || ios_config[:match_git_private_key],
- force_for_new_devices: true,
- api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
- )
+ ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
+ match(
+ type: options[:match_type] || ios_config[:match_type],
+ app_identifier: options[:app_identifier] || ios_config[:app_identifier],
+ readonly: false,
+ git_url: options[:git_url] || ios_config[:git_url],
+ git_branch: options[:git_branch] || ios_config[:git_branch],
+ git_private_key: options[:git_private_key] || ios_config[:match_git_private_key],
+ force_for_new_devices: true,
+ api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
+ )
end
private_lane :build_ios_project do |options|
- ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
- app_identifier = options[:app_identifier] || ios_config[:app_identifier]
- provisioning_profile_name = options[:provisioning_profile_name] || ios_config[:provisioning_profile_name]
+ ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
+ app_identifier = options[:app_identifier] || ios_config[:app_identifier]
+ provisioning_profile_name = options[:provisioning_profile_name] || ios_config[:provisioning_profile_name]
+ team_id = options[:team_id] || ios_config[:team_id]
- cocoapods(
- podfile: ios_config[:podfile_path],
- clean_install: true,
- repo_update: true
- )
-
- # Manual signing for your main app target
- update_code_signing_settings(
- use_automatic_signing: false,
- path: ios_config[:project_path],
- targets: [ios_config[:target]],
- team_id: ios_config[:team_id],
- code_sign_identity: ios_config[:code_sign_identity],
- profile_name: provisioning_profile_name,
- bundle_identifier: app_identifier
- )
-
- build_ios_app(
- scheme: ios_config[:scheme],
- workspace: ios_config[:workspace_path],
- output_name: ios_config[:output_name],
- output_directory: ios_config[:output_directory],
- configuration: ios_config[:configuration]
- )
- end
-
- private_lane :set_plist_values do
- ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
- update_plist(
- plist_path: ios_config[:plist_path],
- block: proc do |plist|
- plist["ITSAppUsesNonExemptEncryption"] = false
- plist["NSCameraUsageDescription"] = "We use the camera to scan QR codes for payments and to add beneficiaries. No images or video are stored."
- plist["NSPhotoLibraryUsageDescription"] = "Allow access to choose a photo or document you decide to upload (e.g., profile photo or ID)."
- end
- )
+ build_ios_app(
+ scheme: ios_config[:scheme],
+ workspace: ios_config[:workspace_path],
+ output_name: ios_config[:output_name],
+ output_directory: ios_config[:output_directory],
+ export_options: {
+ provisioningProfiles: {
+ app_identifier => provisioning_profile_name
+ }
+ },
+ xcargs: "CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY=\"Apple Distribution\" DEVELOPMENT_TEAM=#{team_id} PROVISIONING_PROFILE_SPECIFIER=\"#{provisioning_profile_name}\""
+ )
end
###################
@@ -406,30 +386,23 @@ platform :ios do
desc "Build Ios application"
lane :build_ios do |options|
- ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
-
- cocoapods(
- podfile: ios_config[:podfile_path],
- clean_install: true,
- repo_update: true
- )
-
- build_ios_app(
- scheme: ios_config[:scheme],
- workspace: ios_config[:workspace_path],
- output_name: ios_config[:output_name],
- output_directory: ios_config[:output_directory],
- skip_codesigning: true,
- skip_archive: true
- )
+ ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
+ build_ios_app(
+ scheme: ios_config[:scheme],
+ workspace: ios_config[:workspace_path],
+ output_name: ios_config[:output_name],
+ output_directory: ios_config[:output_directory],
+ skip_codesigning: true,
+ skip_archive: true
+ )
end
desc "Build Signed Ios application"
lane :build_signed_ios do |options|
- setup_ci_if_needed
- load_api_key(options)
- fetch_certificates_with_match(options)
- build_ios_project(options)
+ setup_ci_if_needed
+ load_api_key(options)
+ fetch_certificates_with_match(options)
+ build_ios_project(options)
end
desc "Increment build number from latest Firebase release"
@@ -464,14 +437,15 @@ platform :ios do
desc "Upload iOS application to Firebase App Distribution"
lane :deploy_on_firebase do |options|
firebase_config = FastlaneConfig.get_firebase_config(:ios)
+ ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
increment_version(serviceCredsFile: firebase_config[:serviceCredsFile])
build_signed_ios(
- options.merge(
- match_type: "adhoc",
- provisioning_profile_name: "match AdHoc org.mifos.mobile"
- )
+ options.merge(
+ match_type: ios_config[:match_type],
+ provisioning_profile_name: ios_config[:provisioning_profile_name]
+ )
)
releaseNotes = generateReleaseNote()
@@ -487,238 +461,111 @@ platform :ios do
desc "Upload beta build to TestFlight"
lane :beta do |options|
- ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
+ ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
- setup_ci_if_needed
- load_api_key(options)
- fetch_certificates_with_match(
- options.merge(match_type: "appstore")
- )
-
- increment_version_number(
- xcodeproj: ios_config[:project_path],
- version_number: ios_config[:version_number]
- )
-
- latest_build_number = latest_testflight_build_number(
- app_identifier: options[:app_identifier] || ios_config[:app_identifier],
- api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
- version: ios_config[:version_number]
- )
-
- increment_build_number(
- xcodeproj: ios_config[:project_path],
- build_number: latest_build_number + 1
- )
-
- set_plist_values
-
- build_ios_project(
- options.merge(
- provisioning_profile_name: "match AppStore org.mifos.mobile"
+ setup_ci_if_needed
+ load_api_key(options)
+ fetch_certificates_with_match(
+ options.merge(match_type: "appstore")
)
- )
- pilot(
- api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
- skip_waiting_for_build_processing: true
- )
+ increment_version_number(
+ xcodeproj: ios_config[:project_path],
+ version_number: ios_config[:version_number]
+ )
+
+ latest_build_number = latest_testflight_build_number(
+ app_identifier: options[:app_identifier] || ios_config[:app_identifier],
+ api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
+ )
+
+ increment_build_number(
+ xcodeproj: ios_config[:project_path],
+ build_number: latest_build_number + 1
+ )
+
+ build_ios_project(
+ options.merge(
+ provisioning_profile_name: ios_config[:provisioning_profile_appstore]
+ )
+ )
+
+ pilot(
+ api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
+ skip_waiting_for_build_processing: true
+ )
end
desc "Upload iOS Application to AppStore"
lane :release do |options|
- ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
+ ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
- setup_ci_if_needed
- load_api_key(options)
- fetch_certificates_with_match(
- options.merge(match_type: "appstore")
- )
-
- increment_version_number(
- xcodeproj: ios_config[:project_path],
- version_number: ios_config[:version_number]
- )
-
- latest_build_number = latest_testflight_build_number(
- app_identifier: options[:app_identifier] || ios_config[:app_identifier],
- api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
- version: ios_config[:version_number]
- )
-
- increment_build_number(
- xcodeproj: ios_config[:project_path],
- build_number: latest_build_number + 1
- )
-
- set_plist_values
-
- build_ios_project(
- options.merge(
- provisioning_profile_name: "match AppStore org.mifos.mobile"
+ setup_ci_if_needed
+ load_api_key(options)
+ fetch_certificates_with_match(
+ options.merge(match_type: "appstore")
)
- )
- deliver(
- screenshots_path: ios_config[:screenshots_ios_path],
- metadata_path: options[:metadata_path] || ios_config[:metadata_path],
- submit_for_review: false, # Set to true if you want to auto-submit for review
- automatic_release: true, # Set to true if you want to auto-release once it approved
- api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
- skip_app_version_update: false,
- force: true, # Skips HTML report verification
- precheck_include_in_app_purchases: false,
- overwrite_screenshots: true,
- reject_if_possible: true,
- app_rating_config_path: ios_config[:app_rating_config_path],
- submission_information: {
- add_id_info_uses_idfa: false,
- add_id_info_limits_tracking: false,
- add_id_info_serves_ads: false,
- add_id_info_tracks_action: false,
- add_id_info_tracks_install: false,
- content_rights_has_rights: true,
- content_rights_contains_third_party_content: false,
- export_compliance_platform: 'ios',
- export_compliance_compliance_required: false,
- export_compliance_encryption_updated: false,
- export_compliance_app_type: nil,
- export_compliance_uses_encryption: false,
- export_compliance_is_exempt: true,
- export_compliance_contains_third_party_cryptography: false,
- export_compliance_contains_proprietary_cryptography: false,
- export_compliance_available_on_french_store: true
- }
- )
- end
-end
+ increment_version_number(
+ xcodeproj: ios_config[:project_path],
+ version_number: ios_config[:version_number]
+ )
-platform :mac do
- #############################
- # Shared Private Lane Helpers
- #############################
+ latest_build_number = latest_testflight_build_number(
+ app_identifier: options[:app_identifier] || ios_config[:app_identifier],
+ api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
+ )
- private_lane :setup_ci_if_needed do
- if ENV['CI']
- setup_ci
- else
- UI.message("π₯οΈ Running locally, skipping CI-specific setup.")
- end
- end
+ increment_build_number(
+ xcodeproj: ios_config[:project_path],
+ build_number: latest_build_number + 1
+ )
- private_lane :load_api_key_macos do |options|
- ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
+ update_plist(
+ plist_path: ios_config[:plist_path],
+ block: proc do |plist|
+ plist['NSContactsUsageDescription'] = 'This app does not access your contacts. This message is required for compliance only.'
+ plist['NSLocationWhenInUseUsageDescription'] = 'This app does not access your location. This message is required for compliance only.'
+ plist['NSBluetoothAlwaysUsageDescription'] = 'This app does not use Bluetooth. This message is required for compliance only.'
+ end
+ )
- app_store_connect_api_key(
- key_id: options[:appstore_key_id] || ios_config[:key_id],
- issuer_id: options[:appstore_issuer_id] || ios_config[:issuer_id],
- key_filepath: options[:key_filepath] || ios_config[:key_filepath],
- duration: 1200
- )
- end
+ build_ios_project(
+ options.merge(
+ provisioning_profile_name: ios_config[:provisioning_profile_appstore]
+ )
+ )
- private_lane :next_macos_build_number do |options|
- ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
-
- latest = latest_testflight_build_number(
- app_identifier: options[:app_identifier] || ios_config[:app_identifier],
- api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
- platform: "osx",
- version: ios_config[:version_number]
- )
- (latest.to_i + 1).to_s
- end
-
- # Resolve the most-recent generated .pkg path
- private_lane :find_pkg_path do
- project_dir = File.expand_path('..', Dir.pwd)
- Dir[File.join(project_dir, 'cmp-desktop', 'build', 'release', '**', 'pkg', '*.pkg')]
- .max_by { |p| File.mtime(p) } || UI.user_error!('PKG not found!')
- end
-
- ###################
- # Public lanes
- ###################
-
- desc "Build & upload macOS (.pkg) to TestFlight"
- lane :desktop_testflight do |options|
- setup_ci_if_needed
- load_api_key_macos(options)
-
- new_build_number = next_macos_build_number(options)
-
- gradle(
- tasks: ["packageReleasePkg"],
- properties: {
- "buildNumber" => new_build_number,
- "macOsAppStoreRelease" => true
- }
- )
-
- pkg_path = find_pkg_path
- UI.message("Found PKG at: #{pkg_path}")
-
- pilot(
- api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
- pkg: pkg_path,
- app_platform: 'osx',
- skip_waiting_for_build_processing: true,
- )
- end
-
- desc "Build & submit macOS app to App Store (non-beta)"
- lane :desktop_release do |options|
- ios_config = FastlaneConfig::IosConfig::BUILD_CONFIG
- setup_ci_if_needed
- load_api_key_macos(options)
-
- new_build_number = next_macos_build_number(options)
-
- gradle(
- tasks: ["packageReleasePkg"],
- properties: {
- "buildNumber" => new_build_number,
- "macOsAppStoreRelease" => true
- }
- )
-
- # Locate the produced PKG (adjust pattern if you rename)
- pkg_path = find_pkg_path
- UI.message("Found PKG at: #{pkg_path}")
-
- deliver(
- platform: 'osx',
- pkg: pkg_path,
- screenshots_path: ios_config[:screenshots_macos_path],
- metadata_path: options[:metadata_path] || ios_config[:metadata_path],
- submit_for_review: true, # Set to true if you want to auto-submit for review
- automatic_release: true, # Set to true if you want to auto-release once it approved
- api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
- skip_app_version_update: false,
- force: true, # Skips HTML report verification
- precheck_include_in_app_purchases: false,
- overwrite_screenshots: true,
- reject_if_possible: true,
- app_rating_config_path: ios_config[:app_rating_config_path],
- submission_information: {
- add_id_info_uses_idfa: false,
- add_id_info_limits_tracking: false,
- add_id_info_serves_ads: false,
- add_id_info_tracks_action: false,
- add_id_info_tracks_install: false,
- content_rights_has_rights: true,
- content_rights_contains_third_party_content: false,
- export_compliance_platform: 'osx',
- export_compliance_compliance_required: false,
- export_compliance_encryption_updated: false,
- export_compliance_app_type: nil,
- export_compliance_uses_encryption: false,
- export_compliance_is_exempt: true,
- export_compliance_contains_third_party_cryptography: false,
- export_compliance_contains_proprietary_cryptography: false,
- export_compliance_available_on_french_store: true
- }
- )
+ deliver(
+ metadata_path: options[:metadata_path] || ios_config[:metadata_path],
+ submit_for_review: true, # Set to true if you want to auto-submit for review
+ automatic_release: true, # Set to true if you want to auto-release once it approved
+ api_key: Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY],
+ skip_app_version_update: true,
+ force: true, # Skips HTML report verification
+ precheck_include_in_app_purchases: false,
+ overwrite_screenshots: true,
+ reject_if_possible: true,
+ app_rating_config_path: ios_config[:app_rating_config_path],
+ submission_information: {
+ add_id_info_uses_idfa: false,
+ add_id_info_limits_tracking: false,
+ add_id_info_serves_ads: false,
+ add_id_info_tracks_action: false,
+ add_id_info_tracks_install: false,
+ content_rights_has_rights: true,
+ content_rights_contains_third_party_content: false,
+ export_compliance_platform: 'ios',
+ export_compliance_compliance_required: false,
+ export_compliance_encryption_updated: false,
+ export_compliance_app_type: nil,
+ export_compliance_uses_encryption: false,
+ export_compliance_is_exempt: true,
+ export_compliance_contains_third_party_cryptography: false,
+ export_compliance_contains_proprietary_cryptography: false,
+ export_compliance_available_on_french_store: true
+ }
+ )
end
end
\ No newline at end of file
diff --git a/fastlane/README.md b/fastlane/README.md
index c8de7f002..c37620477 100644
--- a/fastlane/README.md
+++ b/fastlane/README.md
@@ -114,7 +114,15 @@ Generate full release notes from specified tag or latest release tag
[bundle exec] fastlane ios build_ios
```
-Build iOS application
+Build Ios application
+
+### ios build_signed_ios
+
+```sh
+[bundle exec] fastlane ios build_signed_ios
+```
+
+Build Signed Ios application
### ios increment_version
@@ -122,15 +130,7 @@ Build iOS application
[bundle exec] fastlane ios increment_version
```
-
-
-### ios deploy_on_firebase
-
-```sh
-[bundle exec] fastlane ios deploy_on_firebase
-```
-
-Upload iOS application to Firebase App Distribution
+Increment build number from latest Firebase release
### ios generateReleaseNote
@@ -140,6 +140,30 @@ Upload iOS application to Firebase App Distribution
Generate release notes
+### ios deploy_on_firebase
+
+```sh
+[bundle exec] fastlane ios deploy_on_firebase
+```
+
+Upload iOS application to Firebase App Distribution
+
+### ios beta
+
+```sh
+[bundle exec] fastlane ios beta
+```
+
+Upload beta build to TestFlight
+
+### ios release
+
+```sh
+[bundle exec] fastlane ios release
+```
+
+Upload iOS Application to AppStore
+
----
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh
index 04c785606..2f87f220e 100644
--- a/scripts/pre-commit.sh
+++ b/scripts/pre-commit.sh
@@ -4,7 +4,7 @@
check_current_branch() {
echo "\nπ Checking the current git branch..."
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
- if [ "$CURRENT_BRANCH" = "master" ] || [ "$CURRENT_BRANCH" = "development" ]; then
+ if [ "$CURRENT_BRANCH" = "master" ] || [ "$CURRENT_BRANCH" = "dev" ]; then
echo "π Hold it right there! Committing directly to the '$CURRENT_BRANCH' branch? That's a big no-no!"
echo "π« Direct commits to '$CURRENT_BRANCH' are like trying to use a wrench to write codeβdoesn't work! π"
echo "\nABORTING COMMIT: You must navigate to a feature branch or create a new one to save the day! π¦ΈββοΈπ¦ΈββοΈ\n"
@@ -37,16 +37,19 @@ run_spotless_checks() {
# Function to run ktlint checks
run_dependency_guard() {
printf "\nπ Brace yourself! We're about to generate dependency guard baseline!"
- ./gradlew dependencyGuardBaseline
+ ./gradlew dependencyGuardBaseline > /tmp/dependency-result
KT_EXIT_CODE=$?
if [ ${KT_EXIT_CODE} -ne 0 ]; then
+ cat /tmp/dependency-result
+ rm /tmp/dependency-result
printf "\n*********************************************************************************"
echo " π₯ Oh no! Something went wrong! π₯"
echo " π‘ Unable to generate dependency baseline. π οΈ"
printf "*********************************************************************************\n"
exit ${KT_EXIT_CODE}
else
+ rm /tmp/dependency-result
echo "π Bravo! Dependency baseline has been generated successfully! Keep rocking that clean code! ππ«"
fi
}
diff --git a/scripts/pre-push.sh b/scripts/pre-push.sh
index 71bf5c432..d628bc07b 100644
--- a/scripts/pre-push.sh
+++ b/scripts/pre-push.sh
@@ -4,7 +4,7 @@
check_current_branch() {
printf "\nπ Checking the current git branch..."
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
- if [ "$CURRENT_BRANCH" = "master" ] || [ "$CURRENT_BRANCH" = "development" ]; then
+ if [ "$CURRENT_BRANCH" = "master" ] || [ "$CURRENT_BRANCH" = "dev" ]; then
echo "π Hold it right there! Committing directly to the '$CURRENT_BRANCH' branch? That's a big no-no!"
echo "π« Direct commits to '$CURRENT_BRANCH' are like trying to use a wrench to write codeβdoesn't work! π"
printf "\nABORTING COMMIT: You must navigate to a feature branch or create a new one to save the day! π¦ΈββοΈπ¦ΈββοΈ\n"
@@ -28,7 +28,9 @@ run_spotless_checks() {
echo " π‘ Tip: Check the reported issues and fix formatting errors. π οΈ"
echo "*********************************************************************************"
echo "π Attempting to apply Spotless formatting fixes..."
- ./gradlew spotlessApply --daemon
+ ./gradlew spotlessApply --daemon > /tmp/spotless-result
+ rm /tmp/spotless-result
+ echo "π Stellar job! Your code is pristine and has passed Spotless's formatting checks without a hitch! Keep shining bright! β¨π"
else
rm /tmp/spotless-result
echo "π Stellar job! Your code is pristine and has passed Spotless's formatting checks without a hitch! Keep shining bright! β¨π"
@@ -58,18 +60,23 @@ run_detekt_checks() {
# Function to run ktlint checks
run_dependency_guard() {
printf "\nπ Brace yourself! We're about to generate dependency guard baseline!"
- ./gradlew dependencyGuard
+ ./gradlew dependencyGuard > /tmp/dependency-result
KT_EXIT_CODE=$?
if [ ${KT_EXIT_CODE} -ne 0 ]; then
+ cat /tmp/dependency-result
+ rm /tmp/dependency-result
printf "\n*********************************************************************************"
echo " π₯ Oh no! Something went wrong! π₯"
echo " π‘ Unable to generate dependency baseline. π οΈ"
printf "*********************************************************************************\n"
echo "π Attempting to generate dependency baseline again..."
- ./gradlew dependencyGuardBaseline
- else
+ ./gradlew dependencyGuardBaseline > /tmp/dependency-result
+ rm /tmp/dependency-result
echo "π Bravo! Dependency baseline has been generated successfully! Keep rocking that clean code! ππ«"
+ else
+ rm /tmp/dependency-result
+ echo "π Bravo! Dependency baseline has been checked successfully! Keep rocking that clean code! ππ«"
fi
}