diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..6e43c2ea68 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,74 @@ +version: 2 +updates: + # Enable version updates for Gradle dependencies + - package-ecosystem: "gradle" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 10 + reviewers: + - "Catrobat/paintroid-maintainers" + labels: + - "dependencies" + - "gradle" + commit-message: + prefix: "chore" + include: "scope" + ignore: + # Ignore major version updates for stable dependencies + - dependency-name: "org.jetbrains.kotlin:*" + update-types: ["version-update:semver-major"] + groups: + # Group AndroidX updates together + androidx: + patterns: + - "androidx.*" + update-types: + - "minor" + - "patch" + # Group testing dependencies together + testing: + patterns: + - "*junit*" + - "*mockito*" + - "*espresso*" + update-types: + - "minor" + - "patch" + # Group Kotlin dependencies + kotlin: + patterns: + - "org.jetbrains.kotlin*" + - "org.jetbrains.kotlinx*" + + # Enable version updates for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "github-actions" + commit-message: + prefix: "ci" + include: "scope" + + # Enable Docker base image updates + - package-ecosystem: "docker" + directory: "/docker" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "docker" + commit-message: + prefix: "chore" + include: "scope" diff --git a/.github/workflows/android-ci.yml b/.github/workflows/android-ci.yml new file mode 100644 index 0000000000..713f140ce1 --- /dev/null +++ b/.github/workflows/android-ci.yml @@ -0,0 +1,206 @@ +name: Android CI + +on: + push: + branches: [ develop, master ] + pull_request: + branches: [ develop, master ] + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + timeout-minutes: 45 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Cache Gradle packages + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Build debug APK + run: ./gradlew assembleDebug --stacktrace + + - name: Upload debug APK + uses: actions/upload-artifact@v3 + with: + name: app-debug + path: app/build/outputs/apk/debug/paintroid-debug.apk + retention-days: 7 + + - name: Run unit tests + run: ./gradlew testDebugUnitTest --stacktrace + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-results + path: '**/build/test-results/**/*.xml' + retention-days: 7 + + static-analysis: + name: Static Code Analysis + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Run Lint + run: ./gradlew lint --stacktrace + continue-on-error: true + + - name: Upload Lint report + if: always() + uses: actions/upload-artifact@v3 + with: + name: lint-report + path: '**/build/reports/lint-*.html' + retention-days: 7 + + - name: Run Detekt + run: ./gradlew detekt --stacktrace + continue-on-error: true + + - name: Upload Detekt report + if: always() + uses: actions/upload-artifact@v3 + with: + name: detekt-report + path: '**/build/reports/detekt/detekt.html' + retention-days: 7 + + - name: Run Checkstyle + run: ./gradlew checkstyle --stacktrace + continue-on-error: true + + - name: Upload Checkstyle report + if: always() + uses: actions/upload-artifact@v3 + with: + name: checkstyle-report + path: '**/build/reports/checkstyle.html' + retention-days: 7 + + - name: Run PMD + run: ./gradlew pmd --stacktrace + continue-on-error: true + + - name: Upload PMD report + if: always() + uses: actions/upload-artifact@v3 + with: + name: pmd-report + path: '**/build/reports/pmd.html' + retention-days: 7 + + - name: Annotate PR with static analysis results + if: github.event_name == 'pull_request' + uses: github/codeql-action/upload-sarif@v2 + continue-on-error: true + with: + sarif_file: Paintroid/build/reports/detekt/detekt.sarif + + code-coverage: + name: Code Coverage + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Run tests with coverage + run: ./gradlew -PenableCoverage jacocoTestDebugUnitTestReport --stacktrace + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./Paintroid/build/reports/jacoco/jacocoTestDebugUnitTestReport/jacocoTestDebugUnitTestReport.xml + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + + - name: Generate coverage report comment + if: github.event_name == 'pull_request' + uses: madrapps/jacoco-report@v1.6.1 + with: + paths: | + ${{ github.workspace }}/Paintroid/build/reports/jacoco/jacocoTestDebugUnitTestReport/jacocoTestDebugUnitTestReport.xml + token: ${{ secrets.GITHUB_TOKEN }} + min-coverage-overall: 60 + min-coverage-changed-files: 70 + title: Code Coverage Report + + dependency-check: + name: Dependency Vulnerability Check + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Run dependency check + run: ./gradlew dependencyUpdates --stacktrace + continue-on-error: true + + - name: Upload dependency report + if: always() + uses: actions/upload-artifact@v3 + with: + name: dependency-report + path: '**/build/dependencyUpdates/report.txt' + retention-days: 7 diff --git a/Paintroid/proguard-rules.pro b/Paintroid/proguard-rules.pro index 81994c92e3..1dbf93e3d6 100644 --- a/Paintroid/proguard-rules.pro +++ b/Paintroid/proguard-rules.pro @@ -5,17 +5,175 @@ # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# resetToOrigin the original source file name. -#-renamesourcefileattribute SourceFile +# ============================================================================ +# General Android Configuration +# ============================================================================ + +# Preserve line numbers for debugging stack traces +-keepattributes SourceFile,LineNumberTable +-renamesourcefileattribute SourceFile + +# Keep annotations +-keepattributes *Annotation* +-keepattributes Signature +-keepattributes Exceptions +-keepattributes InnerClasses +-keepattributes EnclosingMethod + +# ============================================================================ +# AndroidX Libraries +# ============================================================================ + +# Keep AndroidX annotations +-keep class androidx.annotation.** { *; } +-dontwarn androidx.annotation.** + +# AndroidX AppCompat +-keep class androidx.appcompat.** { *; } +-dontwarn androidx.appcompat.** + +# Material Design Components +-keep class com.google.android.material.** { *; } +-dontwarn com.google.android.material.** + +# AndroidX Core +-keep class androidx.core.** { *; } +-dontwarn androidx.core.** + +# ============================================================================ +# Paintroid Model Classes (Critical for Serialization) +# ============================================================================ + +# Keep all model classes - these are serialized/deserialized +-keep class org.catrobat.paintroid.model.** { *; } +-keepclassmembers class org.catrobat.paintroid.model.** { *; } + +# Keep all command classes - used for undo/redo functionality +-keep class org.catrobat.paintroid.command.** { *; } +-keepclassmembers class org.catrobat.paintroid.command.** { *; } + +# Keep tool classes +-keep class org.catrobat.paintroid.tools.** { *; } +-keepclassmembers class org.catrobat.paintroid.tools.** { *; } + +# Keep contract interfaces +-keep interface org.catrobat.paintroid.contract.** { *; } + +# ============================================================================ +# Kryo Serialization Library +# ============================================================================ + +# Keep Kryo classes +-keep class com.esotericsoftware.kryo.** { *; } +-keepclassmembers class com.esotericsoftware.kryo.** { *; } +-dontwarn com.esotericsoftware.kryo.** + +# Keep serializers +-keep class com.esotericsoftware.kryo.serializers.** { *; } +-keepclassmembers class com.esotericsoftware.kryo.serializers.** { *; } + +# Keep classes that Kryo might serialize +-keepclassmembers class * implements java.io.Serializable { + static final long serialVersionUID; + private static final java.io.ObjectStreamField[] serialPersistentFields; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} + +# ============================================================================ +# Universal Image Loader +# ============================================================================ + +-keep class com.nostra13.universalimageloader.** { *; } +-keepclassmembers class com.nostra13.universalimageloader.** { *; } +-dontwarn com.nostra13.universalimageloader.** + +# ============================================================================ +# Image Compression Library +# ============================================================================ + +-keep class id.zelory.compressor.** { *; } +-dontwarn id.zelory.compressor.** + +# ============================================================================ +# Kotlin Coroutines +# ============================================================================ + +-keepclassmembernames class kotlinx.** { + volatile ; +} + +-keepclassmembers class kotlinx.coroutines.** { + volatile ; +} + +-dontwarn kotlinx.coroutines.** + +# ============================================================================ +# Test Libraries (Espresso Idling Resources) +# ============================================================================ + +# Keep Espresso idling resources if used in production code +-keep class androidx.test.espresso.idling.** { *; } +-dontwarn androidx.test.espresso.** + +# ============================================================================ +# Reflection and Native Methods +# ============================================================================ + +# Keep native methods +-keepclasseswithmembernames class * { + native ; +} + +# Keep classes with constructors used via reflection +-keepclassmembers class * { + public (android.content.Context); + public (android.content.Context, android.util.AttributeSet); + public (android.content.Context, android.util.AttributeSet, int); +} + +# ============================================================================ +# Enums +# ============================================================================ + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# ============================================================================ +# Parcelable Classes +# ============================================================================ + +-keepclassmembers class * implements android.os.Parcelable { + public static final ** CREATOR; +} + +# ============================================================================ +# Remove Logging (Optional - Uncomment for Release) +# ============================================================================ + +# Remove all logging in release builds +# -assumenosideeffects class android.util.Log { +# public static *** d(...); +# public static *** v(...); +# public static *** i(...); +# public static *** w(...); +# } + +# ============================================================================ +# Optimization Settings +# ============================================================================ + +# Enable optimization +-optimizationpasses 5 +-dontusemixedcaseclassnames +-verbose + +# Don't warn about missing classes in optional dependencies +-dontwarn javax.annotation.** +-dontwarn org.jetbrains.annotations.** +-dontwarn kotlin.Metadata diff --git a/Paintroid/src/main/AndroidManifest.xml b/Paintroid/src/main/AndroidManifest.xml index 416ef07bbc..af78373621 100644 --- a/Paintroid/src/main/AndroidManifest.xml +++ b/Paintroid/src/main/AndroidManifest.xml @@ -31,7 +31,8 @@ + android:supportsRtl="true" + android:networkSecurityConfig="@xml/network_security_config"> + + + + + + + + + + + + + + + + + + + + + + + +