diff --git a/.gitguardian.yml b/.gitguardian.yml new file mode 100644 index 0000000..c156c6a --- /dev/null +++ b/.gitguardian.yml @@ -0,0 +1,2 @@ +paths-ignore: + - "app/build.gradle.kts" diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..c073aaa --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,53 @@ +version: 2 + +updates: + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + commit-message: + prefix: "deps" + labels: + - "dependencies" + - "automated" + groups: + minor-and-patch: + patterns: + - "*" + update-types: + - "minor" + - "patch" + gradle-security: + applies-to: security-updates + patterns: + - "*" + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-major"] + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 + commit-message: + prefix: "ci" + labels: + - "ci" + - "automated" + groups: + actions-minor-and-patch: + patterns: + - "*" + update-types: + - "minor" + - "patch" + actions-security: + applies-to: security-updates + patterns: + - "*" + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-major"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..790ee12 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Build debug APK + run: ./gradlew assembleDebug + + - name: Run lint + run: ./gradlew lintDebug + + - name: Upload debug APK + uses: actions/upload-artifact@v4 + with: + name: app-debug + path: app/build/outputs/apk/debug/app-debug.apk diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b260d12 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,69 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + + - name: Compute version from tag + id: version + run: | + TAG="${GITHUB_REF#refs/tags/}" + VERSION="${TAG#v}" + # versionCode: major*10000 + minor*100 + patch (e.g. 1.2.3 -> 10203) + IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION" + CODE=$(( ${MAJOR:-0} * 10000 + ${MINOR:-0} * 100 + ${PATCH:-0} )) + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "name=$VERSION" >> "$GITHUB_OUTPUT" + echo "code=$CODE" >> "$GITHUB_OUTPUT" + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Decode keystore + run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > app/release.keystore + + - name: Build release AAB + run: ./gradlew bundleRelease -PVERSION_NAME=${{ steps.version.outputs.name }} -PVERSION_CODE=${{ steps.version.outputs.code }} + env: + CASHPILOT_KEYSTORE_PATH: release.keystore + CASHPILOT_KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + CASHPILOT_KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + CASHPILOT_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + + - name: Build release APK + run: ./gradlew assembleRelease -PVERSION_NAME=${{ steps.version.outputs.name }} -PVERSION_CODE=${{ steps.version.outputs.code }} + env: + CASHPILOT_KEYSTORE_PATH: release.keystore + CASHPILOT_KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + CASHPILOT_KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + CASHPILOT_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + + - name: Remove keystore + if: always() + run: rm -f app/release.keystore + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.version.outputs.tag }} + name: ${{ steps.version.outputs.tag }} + generate_release_notes: true + files: | + app/build/outputs/bundle/release/app-release.aab + app/build/outputs/apk/release/app-release.apk diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ce692c2..f3dbfbd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,31 @@ android { applicationId = "com.cashpilot.android" minSdk = 26 // Android 8.0 — NotificationListenerService, NetworkStatsManager targetSdk = 35 - versionCode = 1 - versionName = "0.1.0" + + val ciVersionName = findProperty("VERSION_NAME") as String? ?: "0.1.0" + val ciVersionCode = (findProperty("VERSION_CODE") as String?)?.toIntOrNull() ?: 1 + versionCode = ciVersionCode + versionName = ciVersionName + } + + signingConfigs { + create("release") { + val keystorePath = findProperty("CASHPILOT_KEYSTORE_PATH") as String? + ?: System.getenv("CASHPILOT_KEYSTORE_PATH") + val keystorePass = findProperty("CASHPILOT_KEYSTORE_PASSWORD") as String? + ?: System.getenv("CASHPILOT_KEYSTORE_PASSWORD") + val keyAliasValue = findProperty("CASHPILOT_KEY_ALIAS") as String? + ?: System.getenv("CASHPILOT_KEY_ALIAS") + val keyPass = findProperty("CASHPILOT_KEY_PASSWORD") as String? + ?: System.getenv("CASHPILOT_KEY_PASSWORD") + + if (keystorePath != null && keystorePass != null && keyAliasValue != null && keyPass != null) { + storeFile = file(keystorePath) + storePassword = keystorePass + keyAlias = keyAliasValue + keyPassword = keyPass + } + } } buildTypes { @@ -25,6 +48,10 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) + val releaseConfig = signingConfigs.findByName("release") + if (releaseConfig?.storeFile != null) { + signingConfig = releaseConfig + } } } @@ -33,11 +60,18 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" + kotlin { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + } + } + + lint { + baseline = file("lint-baseline.xml") } buildFeatures { + buildConfig = true compose = true } } diff --git a/app/lint-baseline.xml b/app/lint-baseline.xml new file mode 100644 index 0000000..d9e2dd4 --- /dev/null +++ b/app/lint-baseline.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/app/src/main/java/com/cashpilot/android/service/AppNotificationListener.kt b/app/src/main/java/com/cashpilot/android/service/AppNotificationListener.kt index 0693088..8ad0789 100644 --- a/app/src/main/java/com/cashpilot/android/service/AppNotificationListener.kt +++ b/app/src/main/java/com/cashpilot/android/service/AppNotificationListener.kt @@ -55,7 +55,7 @@ class AppNotificationListener : NotificationListenerService() { fun isAppNotificationActive(packageName: String): Boolean { if (!connected.get()) return false - return packageName in activeNotifications_ + return activeNotifications_.containsKey(packageName) } fun isConnected(): Boolean = connected.get() diff --git a/settings.gradle.kts b/settings.gradle.kts index 3aa8928..99c09a1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,7 +12,9 @@ pluginManagement { } } -dependencyResolution { +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral()