From 2e41d03f70ce042f0fcc4448ffc0d5bb54d7a2cf Mon Sep 17 00:00:00 2001 From: Khalid Nasralla Date: Thu, 18 Sep 2025 07:47:39 +0200 Subject: [PATCH] Update to AGP-8 and Android-15 (API 35+) --- Jenkinsfile | 87 +++++++-- Paintroid/build.gradle | 177 +++++++++--------- Paintroid/gradle/code_quality_tasks.gradle | 20 +- .../CatrobatImageIOIntegrationTest.kt | 42 ++++- .../test/espresso/LayerBackgroundTest.kt | 40 ++-- .../test/espresso/OraFileIntentTest.kt | 2 +- .../SaveCompressImageIntegrationTest.kt | 11 +- .../junit/command/ClipboardCommandTest.kt | 19 +- .../test/junit/command/CropCommandTest.kt | 108 ++++++----- .../test/junit/command/FlipCommandTest.kt | 8 +- .../junit/command/MergeLayersCommandTest.kt | 18 +- .../test/junit/command/PathCommandTest.kt | 8 +- .../test/junit/command/PointCommandTest.kt | 10 +- .../java/org/catrobat/paintroid/FileIO.kt | 2 +- .../org/catrobat/paintroid/MainActivity.kt | 12 +- .../command/implementation/ClippingCommand.kt | 4 +- .../command/implementation/CropCommand.kt | 2 +- .../command/implementation/FlipCommand.kt | 3 +- .../implementation/MergeLayersCommand.kt | 3 +- .../catrobat/paintroid/dialog/AboutDialog.kt | 2 +- .../implementation/DefaultContextCallback.kt | 2 +- .../tools/implementation/DefaultToolPaint.kt | 2 +- .../paintroid/ui/tools/BrushToolView.kt | 2 +- .../web/MediaGalleryWebViewClient.java | 88 +++++++-- app/build.gradle | 78 ++++---- app/src/main/AndroidManifest.xml | 9 +- build.gradle | 20 +- colorpicker/build.gradle | 11 +- .../ColorPickerPresetColorButton.kt | 2 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 4 +- settings.gradle | 24 ++- 32 files changed, 507 insertions(+), 317 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d0ecd8e85a..7910c3890d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,45 @@ #!groovy +class DockerParameters { + + // 'docker build' would normally copy the whole build-dir to the container, changing the + // docker build directory avoids that overhead + def dir = 'docker' + def args = '--device /dev/kvm:/dev/kvm -v /var/local/container_shared/gradle_cache/$EXECUTOR_NUMBER:/home/user/.gradle -m=6.5G' + def label = 'LimitedEmulator' + def image = 'floriankanduth/paintroid_java17:latest' + +} + +def dockerParameters = new DockerParameters() + +def startEmulator(String android_version, String stageName) { + sh 'adb start-server' + // creates a new avd, and if it already exists it does nothing. + sh "echo no | avdmanager create avd --force --name android${android_version}" + " --package 'system-images;android-${android_version};default;x86_64'" + sh "/home/user/android/sdk/emulator/emulator -no-window -no-boot-anim -noaudio -avd android${android_version} > ${stageName}_emulator.log 2>&1 &" +} + +def waitForEmulatorAndPressWakeUpKey() { + sh 'adb devices' + sh 'timeout 5m adb wait-for-device' + sh '''#!/bin/bash +adb devices +timeout 5m adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; +done' +echo "Emulator started" +''' + sh ''' + adb shell settings put global window_animation_scale 0 & + adb shell settings put global transition_animation_scale 0 & + adb shell settings put global animator_duration_scale 0 & + ''' + + + // In case the device went to sleep + sh 'adb shell input keyevent KEYCODE_WAKEUP' +} + def reports = 'Paintroid/build/reports' // place the cobertura xml relative to the source, so that the source can be found @@ -22,7 +62,21 @@ def useDebugLabelParameter(defaultLabel) { return env.DEBUG_LABEL?.trim() ? env.DEBUG_LABEL : defaultLabel } +def checkAnimationScale(scaleName) { + def output = sh(script: "adb shell settings get global ${scaleName}", returnStdout: true).trim() + if (output != "0" && output != "0.0") { + error("Animation scale '${scaleName}' is NOT disabled. Current value: ${output}") + } else { + echo("Animation scale '${scaleName}' is disabled (Value: ${output})") + } + } + pipeline { + environment { + ANDROID_VERSION = 33 + ADB_INSTALL_TIMEOUT = 60 + } + parameters { string name: 'DEBUG_LABEL', defaultValue: '', description: 'For debugging when entered will be used as label to decide on which slaves the jobs will run.' booleanParam name: 'BUILD_WITH_CATROID', defaultValue: false, description: 'When checked then the current Paintroid build will be built with the current develop branch of Catroid' @@ -30,10 +84,10 @@ pipeline { } agent { - docker { - image 'catrobat/catrobat-paintroid:stable' - args '--device /dev/kvm:/dev/kvm -v /var/local/container_shared/gradle_cache/$EXECUTOR_NUMBER:/home/user/.gradle -m=6.5G' - label 'LimitedEmulator' + docker { + image dockerParameters.image + args dockerParameters.args + label dockerParameters.label alwaysPull true } } @@ -70,14 +124,14 @@ pipeline { sh 'rm -rf Catroid; mkdir Catroid' dir('Catroid') { git branch: params.CATROID_BRANCH, url: 'https://github.com/Catrobat/Catroid.git' - sh "rm -f catroid/src/main/libs/*.aar" - sh "mv -f ../colorpicker/build/outputs/aar/colorpicker-debug.aar catroid/src/main/libs/colorpicker-LOCAL.aar" - sh "mv -f ../Paintroid/build/outputs/aar/Paintroid-debug.aar catroid/src/main/libs/Paintroid-LOCAL.aar" + sh 'rm -f catroid/src/main/libs/*.aar' + sh 'mv -f ../colorpicker/build/outputs/aar/colorpicker-debug.aar catroid/src/main/libs/colorpicker-LOCAL.aar' + sh 'mv -f ../Paintroid/build/outputs/aar/Paintroid-debug.aar catroid/src/main/libs/Paintroid-LOCAL.aar' } renameApks("${env.BRANCH_NAME}-${env.BUILD_NUMBER}") dir('Catroid') { - archiveArtifacts "catroid/src/main/libs/*.aar" - sh "./gradlew assembleCatroidDebug" + archiveArtifacts 'catroid/src/main/libs/*.aar' + sh './gradlew assembleCatroidDebug' archiveArtifacts 'catroid/build/outputs/apk/catroid/debug/catroid-catroid-debug.apk' } } @@ -114,9 +168,16 @@ pipeline { stage('Device Tests') { steps { - sh "echo no | avdmanager create avd --force --name android28 --package 'system-images;android-28;default;x86_64'" - sh "/home/user/android/sdk/emulator/emulator -no-window -no-boot-anim -noaudio -avd android28 > /dev/null 2>&1 &" - sh './gradlew -PenableCoverage -Pjenkins -Pemulator=android28 -Pci createDebugCoverageReport -i' + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { + startEmulator(ANDROID_VERSION, 'device_tests') + waitForEmulatorAndPressWakeUpKey() + script { + checkAnimationScale("window_animation_scale") + checkAnimationScale("transition_animation_scale") + checkAnimationScale("animator_duration_scale") + } + sh "./gradlew disableAnimations -PenableCoverage -Pjenkins -Pemulator=android${android_version} -Pci createDebugCoverageReport -i" + } } post { always { @@ -145,4 +206,4 @@ pipeline { notifyChat() } } -} +} \ No newline at end of file diff --git a/Paintroid/build.gradle b/Paintroid/build.gradle index 9ffbde1915..c0c90f81c2 100644 --- a/Paintroid/build.gradle +++ b/Paintroid/build.gradle @@ -17,35 +17,33 @@ * along with this program. If not, see . */ -apply plugin: 'com.android.library' -apply plugin: 'com.hiya.jacoco-android' -apply plugin: 'com.getkeepsafe.dexcount' -apply plugin: 'kotlin-android' -apply plugin: 'org.catrobat.gradle.androidemulators' -apply plugin: 'maven-publish' +plugins { + id "com.android.library" + id "jacoco" + id "com.getkeepsafe.dexcount" + id "org.jetbrains.kotlin.android" + id "org.catrobat.gradle.androidemulators" + id "maven-publish" +} -apply from: 'gradle/adb_tasks.gradle' -apply from: 'gradle/code_quality_tasks.gradle' +apply from: "gradle/adb_tasks.gradle" +apply from: "gradle/code_quality_tasks.gradle" emulators { - install project.hasProperty('installSdk') - - dependencies { - sdk() - } + install project.hasProperty("installSdk") + dependencies { sdk() } - emulator 'android28', { + emulator "android28", { avd { - systemImage = 'system-images;android-28;default;x86_64' + systemImage = "system-images;android-28;default;x86_64" sdcardSizeMb = 1024 - hardwareProperties += ['hw.ramSize': 4096, 'vm.heapSize': 1024] - screenDensity = 'xhdpi' + hardwareProperties += ["hw.ramSize": 4096, "vm.heapSize": 1024] + screenDensity = "xhdpi" } - parameters { - resolution = '768x1280' - language = 'en' - country = 'US' + resolution = "768x1280" + language = "en" + country = "US" } } } @@ -54,54 +52,50 @@ jacoco { toolVersion = "0.8.7" } -jacocoAndroidUnitTestReport { - csv.enabled false - html.enabled true - xml.enabled true - destination project.getBuildDir().getPath() + "/reports/jacoco/jacocoTestDebugUnitTestReport" -} - android { - compileSdkVersion rootProject.ext.androidCompileSdkVersion + namespace "org.catrobat.paintroid" + + // New AGP DSL + compileSdk = rootProject.ext.androidCompileSdkVersion defaultConfig { - minSdkVersion rootProject.ext.androidMinSdkVersion - targetSdkVersion rootProject.ext.androidTargetSdkVersion - testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - versionCode rootProject.ext.androidVersionCode - versionName rootProject.ext.androidVersionName + minSdk = rootProject.ext.androidMinSdkVersion + targetSdk = rootProject.ext.androidTargetSdkVersion + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildFeatures { + buildConfig = true } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } + kotlinOptions { jvmTarget = "17" } buildTypes { release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - signedRelease { + minifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android.txt"), + "proguard-rules.pro" + ) } + create("signedRelease") debug { - testCoverageEnabled = project.hasProperty('enableCoverage') - // Multidex is required as espresso and mockito/bytebuddy are adding more functions - // than should be allowed by law. - // See https://github.com/mockito/mockito/issues/1112 - multiDexEnabled true } } - lintOptions { - // specific ignores should be defined via lint.xml file - lintConfig file('config/lint.xml') - ignore 'ClickableViewAccessibility', 'StaticFieldLeak', 'GradleDependency', 'OldTargetApi', 'LintBaseline' - textReport true - xmlReport true - htmlReport true - xmlOutput file("build/reports/lint-report.xml") - htmlOutput file("build/reports/lint-report.html") + lint { + lintConfig file("config/lint.xml") + disable "ClickableViewAccessibility", "StaticFieldLeak", + "GradleDependency", "OldTargetApi", "LintBaseline" + textReport = true + xmlReport = true + htmlReport = true + xmlOutput = file("build/reports/lint-report.xml") + htmlOutput = file("build/reports/lint-report.html") } testOptions { @@ -111,54 +105,51 @@ android { packagingOptions { resources { - excludes += ['META-INF/AL2.0', 'META-INF/LGPL2.1', "**/attach_hotspot_windows.dll"] - merges += ['META-INF/licenses/ASM'] + excludes += ["META-INF/AL2.0", "META-INF/LGPL2.1", "**/attach_hotspot_windows.dll"] + merges += ["META-INF/licenses/ASM"] } } } dependencies { - implementation 'androidx.appcompat:appcompat:1.0.0' - implementation 'com.google.android.material:material:1.2.1' - implementation project(':colorpicker') + implementation "androidx.appcompat:appcompat:1.0.0" + implementation "com.google.android.material:material:1.2.1" + implementation project(":colorpicker") - implementation 'androidx.core:core-ktx:1.3.2' + implementation "androidx.core:core-ktx:1.3.2" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8' - implementation 'androidx.exifinterface:exifinterface:1.3.2' - implementation 'com.esotericsoftware:kryo:5.5.0' - implementation 'id.zelory:compressor:2.1.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - - debugImplementation 'androidx.multidex:multidex:2.0.0' - - implementation 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' - implementation 'com.jraska:falcon:2.2.0' - - testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.18.3' - testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' - - androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.3' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test:rules:1.1.1' - androidTestImplementation 'org.mockito:mockito-android:3.6.28' - androidTestImplementation 'tools.fastlane:screengrab:2.1.0' - androidTestImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' - - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.0' - androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0' + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8" + implementation "androidx.exifinterface:exifinterface:1.3.2" + implementation "com.esotericsoftware:kryo:5.5.0" + implementation "id.zelory:compressor:2.1.1" + implementation "androidx.constraintlayout:constraintlayout:2.1.4" + implementation 'androidx.test:monitor:1.8.0' + + debugImplementation "androidx.multidex:multidex:2.0.0" + + implementation "com.nostra13.universalimageloader:universal-image-loader:1.9.5" + implementation "com.jraska:falcon:2.2.0" + + testImplementation "junit:junit:4.12" + testImplementation "org.mockito:mockito-core:2.18.3" + testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0" + + androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.3" + androidTestImplementation "androidx.test.ext:junit:1.1.3" + androidTestImplementation "androidx.test:rules:1.1.1" + androidTestImplementation "org.mockito:mockito-android:3.6.28" + androidTestImplementation "tools.fastlane:screengrab:2.1.0" + androidTestImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0" + + androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1" + androidTestImplementation "androidx.test.espresso:espresso-contrib:3.1.0" + androidTestImplementation "androidx.test.espresso:espresso-intents:3.1.0" androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0" testImplementation "androidx.test:core-ktx:1.4.0" - implementation 'com.android.support.test.espresso:espresso-idling-resource:3.1.0' -} -tasks.withType(Javadoc).all { - enabled = false + implementation "androidx.test.espresso:espresso-idling-resource:3.5.1" } -if (project.hasProperty('jenkins')) { - android.dexOptions.preDexLibraries = false -} +tasks.withType(Javadoc).configureEach { enabled = false } + diff --git a/Paintroid/gradle/code_quality_tasks.gradle b/Paintroid/gradle/code_quality_tasks.gradle index c86a9788de..00d17898e0 100644 --- a/Paintroid/gradle/code_quality_tasks.gradle +++ b/Paintroid/gradle/code_quality_tasks.gradle @@ -42,30 +42,30 @@ task checkstyle(type: Checkstyle) { classpath = files() - // needed for console output of warnings/errors - showViolations true - ignoreFailures false + // console printing is automatic; use ignoreFailures to fail or not + ignoreFailures = false reports { - xml.enabled = true - xml.destination file("build/reports/checkstyle.xml") + xml.required.set(true) + html.required.set(false) + xml.outputLocation.set(file("$buildDir/reports/checkstyle.xml")) } } task pmd(type: Pmd) { ruleSetFiles = files('config/pmd.xml') - ruleSets = [] + ruleSets = [] // don't use built-in default sets source '.' include '**/*.java' exclude '**/gen/**', '**/build/**', '**/res/**' - ignoreFailures false + ignoreFailures = false reports { - xml.enabled = true - html.enabled = true - xml.destination file("build/reports/pmd.xml") + xml.required.set(true) + html.required.set(true) + xml.outputLocation.set(file("$buildDir/reports/pmd.xml")) } } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt index 86ee6283b3..2fe9b47de3 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/CatrobatImageIOIntegrationTest.kt @@ -20,7 +20,9 @@ package org.catrobat.paintroid.test.espresso +import android.Manifest import android.net.Uri +import android.os.Build import android.os.Environment import androidx.test.espresso.Espresso import androidx.test.espresso.Espresso.onView @@ -32,15 +34,14 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.ActivityTestRule -import androidx.test.rule.GrantPermissionRule import org.catrobat.paintroid.FileIO import org.catrobat.paintroid.MainActivity import org.catrobat.paintroid.command.serialization.CommandSerializer import org.catrobat.paintroid.R import org.catrobat.paintroid.common.CATROBAT_IMAGE_ENDING import org.catrobat.paintroid.test.espresso.util.DrawingSurfaceLocationProvider -import org.catrobat.paintroid.test.espresso.util.EspressoUtils import org.catrobat.paintroid.test.espresso.util.UiInteractions import org.catrobat.paintroid.test.espresso.util.wrappers.DrawingSurfaceInteraction.Companion.onDrawingSurfaceView import org.catrobat.paintroid.test.espresso.util.wrappers.TopBarViewInteraction @@ -52,6 +53,7 @@ import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test +import org.junit.rules.RuleChain import org.junit.runner.RunWith import java.io.File @@ -59,14 +61,12 @@ import java.io.File class CatrobatImageIOIntegrationTest { @get:Rule - val launchActivityRule = ActivityTestRule(MainActivity::class.java) + val launchActivityRule = + ActivityTestRule(MainActivity::class.java, true, false) @get:Rule val screenshotOnFailRule = ScreenshotOnFailRule() - @get:Rule - val grantPermissionRule: GrantPermissionRule = EspressoUtils.grantPermissionRulesVersionCheck() - private var uriFile: Uri? = null private lateinit var activity: MainActivity @@ -75,12 +75,38 @@ class CatrobatImageIOIntegrationTest { } @Before - fun setUp() { activity = launchActivityRule.activity } + fun setUp() { + val inst = InstrumentationRegistry.getInstrumentation() + val targetPkg = inst.targetContext.packageName + val perms = when { + Build.VERSION.SDK_INT >= 34 -> arrayOf( + Manifest.permission.READ_MEDIA_IMAGES, + Manifest.permission.READ_MEDIA_VIDEO, + "android.permission.READ_MEDIA_VISUAL_USER_SELECTED" + ) + Build.VERSION.SDK_INT >= 33 -> arrayOf( + Manifest.permission.READ_MEDIA_IMAGES, + Manifest.permission.READ_MEDIA_VIDEO + ) + else -> arrayOf( + Manifest.permission.READ_EXTERNAL_STORAGE + ) + } + perms.forEach { + try { + inst.uiAutomation.grantRuntimePermission(targetPkg, it) + } catch (_: Exception) { + } + } + + launchActivityRule.launchActivity(null) + activity = launchActivityRule.activity + } @After fun tearDown() { val imagesDirectory = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() val pathToFile = imagesDirectory + File.separator + IMAGE_NAME + "." + CATROBAT_IMAGE_ENDING val imageFile = File(pathToFile) if (imageFile.exists()) { diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerBackgroundTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerBackgroundTest.kt index bfbf53ff64..d9bf34e538 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerBackgroundTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LayerBackgroundTest.kt @@ -49,7 +49,7 @@ class LayerBackgroundTest(private val language: String) { @Test fun testOneLayer() { var actualBackground = getActualBackground(0) - Assert.assertEquals(actualBackground, getSingleBackground()?.constantState) + Assert.assertEquals(actualBackground, getSingleBackground().constantState) } @Test @@ -61,8 +61,8 @@ class LayerBackgroundTest(private val language: String) { var backgroundTop = getActualBackground(0) var backgroundBottom = getActualBackground(1) - Assert.assertEquals(backgroundTop, getTopBackground(true)?.constantState) - Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + Assert.assertEquals(backgroundTop, getTopBackground(true).constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false).constantState) } @Test @@ -75,8 +75,8 @@ class LayerBackgroundTest(private val language: String) { var backgroundTop = getActualBackground(0) var backgroundBottom = getActualBackground(1) - Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) - Assert.assertEquals(backgroundBottom, getBottomBackground(true)?.constantState) + Assert.assertEquals(backgroundTop, getTopBackground(false).constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(true).constantState) } @Test @@ -90,9 +90,9 @@ class LayerBackgroundTest(private val language: String) { var backgroundCenter = getActualBackground(1) var backgroundBottom = getActualBackground(2) - Assert.assertEquals(backgroundTop, getTopBackground(true)?.constantState) + Assert.assertEquals(backgroundTop, getTopBackground(true).constantState) Assert.assertEquals(backgroundCenter, getCenterBackground(false)?.constantState) - Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false).constantState) } @Test @@ -107,9 +107,9 @@ class LayerBackgroundTest(private val language: String) { var backgroundCenter = getActualBackground(1) var backgroundBottom = getActualBackground(2) - Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundTop, getTopBackground(false).constantState) Assert.assertEquals(backgroundCenter, getCenterBackground(true)?.constantState) - Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false).constantState) } @Test @@ -125,9 +125,9 @@ class LayerBackgroundTest(private val language: String) { var backgroundCenter = getActualBackground(1) var backgroundBottom = getActualBackground(2) - Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundTop, getTopBackground(false).constantState) Assert.assertEquals(backgroundCenter, getCenterBackground(false)?.constantState) - Assert.assertEquals(backgroundBottom, getBottomBackground(true)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(true).constantState) } @Test @@ -143,10 +143,10 @@ class LayerBackgroundTest(private val language: String) { val backgroundLowerCenter = getActualBackground(2) val backgroundBottom = getActualBackground(3) - Assert.assertEquals(backgroundTop, getTopBackground(true)?.constantState) + Assert.assertEquals(backgroundTop, getTopBackground(true).constantState) Assert.assertEquals(backgroundUpperCenter, getCenterBackground(false)?.constantState) Assert.assertEquals(backgroundLowerCenter, getCenterBackground(false)?.constantState) - Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false).constantState) } @Test @@ -163,10 +163,10 @@ class LayerBackgroundTest(private val language: String) { val backgroundLowerCenter = getActualBackground(2) val backgroundBottom = getActualBackground(3) - Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundTop, getTopBackground(false).constantState) Assert.assertEquals(backgroundUpperCenter, getCenterBackground(true)?.constantState) Assert.assertEquals(backgroundLowerCenter, getCenterBackground(false)?.constantState) - Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false).constantState) } @Test @@ -184,10 +184,10 @@ class LayerBackgroundTest(private val language: String) { val backgroundLowerCenter = getActualBackground(2) val backgroundBottom = getActualBackground(3) - Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundTop, getTopBackground(false).constantState) Assert.assertEquals(backgroundUpperCenter, getCenterBackground(false)?.constantState) Assert.assertEquals(backgroundLowerCenter, getCenterBackground(true)?.constantState) - Assert.assertEquals(backgroundBottom, getBottomBackground(false)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(false).constantState) } @Test @@ -205,14 +205,14 @@ class LayerBackgroundTest(private val language: String) { val backgroundLowerCenter = getActualBackground(2) val backgroundBottom = getActualBackground(3) - Assert.assertEquals(backgroundTop, getTopBackground(false)?.constantState) + Assert.assertEquals(backgroundTop, getTopBackground(false).constantState) Assert.assertEquals(backgroundUpperCenter, getCenterBackground(false)?.constantState) Assert.assertEquals(backgroundLowerCenter, getCenterBackground(false)?.constantState) - Assert.assertEquals(backgroundBottom, getBottomBackground(true)?.constantState) + Assert.assertEquals(backgroundBottom, getBottomBackground(true).constantState) } private fun getActualBackground(position: Int): Drawable.ConstantState? { - val layout = layerAdapter?.getViewHolderAt(position)?.getViewLayout() + val layout = layerAdapter.getViewHolderAt(position)?.getViewLayout() return layout?.background?.constantState } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.kt index bad8c3b90c..1a6cb40583 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/OraFileIntentTest.kt @@ -124,7 +124,7 @@ class OraFileIntentTest { } catch (e: IOException) { throw AssertionError("Picture file could not be created.", e) } - val imageFile = File(imageUri!!.path, "testfile.ora") + val imageFile = File(imageUri.path, "testfile.ora") deletionFileList!!.add(imageFile) return imageUri } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.kt index 8695392047..ed66b8fe93 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/SaveCompressImageIntegrationTest.kt @@ -119,11 +119,12 @@ class SaveCompressImageIntegrationTest { val options = BitmapFactory.Options() options.inMutable = true - val compressedBitmap = Objects.requireNonNull( - activity?.model?.savedPictureUri - )?.let { - activity?.let { it1 -> - getScaledBitmapFromUri(it1.contentResolver, it, activity?.applicationContext) + val savedUri = activity?.model?.savedPictureUri + Assert.assertNotNull("Saved picture URI should not be null", savedUri) // Fail test if null + + val compressedBitmap = savedUri?.let { uri -> // 'uri' is now non-nullable Uri + activity?.let { activityNonNull -> + getScaledBitmapFromUri(activityNonNull.contentResolver, uri, activityNonNull.applicationContext) } } val testBitmap = getBitmapFromFile(testImageFile) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/ClipboardCommandTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/ClipboardCommandTest.kt index c4908c687a..e95f7e99b6 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/ClipboardCommandTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/ClipboardCommandTest.kt @@ -52,7 +52,7 @@ class ClipboardCommandTest { val bitmapUnderTest = canvasBitmapUnderTest.copy(Bitmap.Config.ARGB_8888, true) val layerUnderTest = Layer(bitmapUnderTest) canvasUnderTest = Canvas() - canvasUnderTest!!.setBitmap(canvasBitmapUnderTest) + canvasUnderTest.setBitmap(canvasBitmapUnderTest) pointUnderTest = PointF((INITIAL_WIDTH / 2).toFloat(), (INITIAL_HEIGHT / 2).toFloat()) layerModel!!.addLayerAt(0, layerUnderTest) layerModel!!.currentLayer = layerUnderTest @@ -70,7 +70,7 @@ class ClipboardCommandTest { val model = LayerModel() model.addLayerAt(0, layer) model.currentLayer = layer - commandUnderTest!!.run(canvasUnderTest!!, model) + commandUnderTest!!.run(canvasUnderTest, model) PaintroidAsserts.assertBitmapEquals(stampBitmapUnderTest, canvasBitmapUnderTest) Assert.assertNull("Stamp bitmap not recycled.", commandUnderTest!!.bitmap) Assert.assertNotNull("Bitmap not stored", commandUnderTest!!.fileToStoredBitmap) @@ -78,17 +78,18 @@ class ClipboardCommandTest { val secondModel = LayerModel() secondModel.addLayerAt(0, secondLayer) secondModel.currentLayer = secondLayer - commandUnderTest!!.run(canvasUnderTest!!, secondModel) + commandUnderTest!!.run(canvasUnderTest, secondModel) PaintroidAsserts.assertBitmapEquals(stampBitmapUnderTest, canvasBitmapUnderTest) } @Test fun testRunRotateStamp() { - stampBitmapUnderTest!!.setPixel(0, 0, Color.GREEN) - commandUnderTest = ClipboardCommand(stampBitmapUnderTest!!, Point(pointUnderTest!!.x.toInt(), pointUnderTest!!.y.toInt()), canvasBitmapUnderTest!!.width.toFloat(), canvasBitmapUnderTest!!.height.toFloat(), 180f) - commandUnderTest!!.run(canvasUnderTest!!, LayerModel()) - stampBitmapUnderTest!!.setPixel(0, 0, Color.CYAN) - stampBitmapUnderTest!!.setPixel(stampBitmapUnderTest!!.width - 1, stampBitmapUnderTest!!.height - 1, + stampBitmapUnderTest.setPixel(0, 0, Color.GREEN) + commandUnderTest = ClipboardCommand(stampBitmapUnderTest, Point(pointUnderTest!!.x.toInt(), pointUnderTest!!.y.toInt()), canvasBitmapUnderTest!!.width.toFloat(), canvasBitmapUnderTest!!.height.toFloat(), 180f) + commandUnderTest!!.run(canvasUnderTest, LayerModel()) + stampBitmapUnderTest.setPixel(0, 0, Color.CYAN) + stampBitmapUnderTest.setPixel( + stampBitmapUnderTest.width - 1, stampBitmapUnderTest.height - 1, Color.GREEN) PaintroidAsserts.assertBitmapEquals(stampBitmapUnderTest, canvasBitmapUnderTest) Assert.assertNull("Stamp bitmap not recycled.", commandUnderTest!!.bitmap) @@ -120,7 +121,7 @@ class ClipboardCommandTest { fun testStoreBitmap() { var storedBitmap: File? = null try { - val bitmapCopy = canvasBitmapUnderTest!!.copy(canvasBitmapUnderTest!!.config, canvasBitmapUnderTest!!.isMutable) + val bitmapCopy = canvasBitmapUnderTest.copy(canvasBitmapUnderTest.config ?: Bitmap.Config.ARGB_8888, canvasBitmapUnderTest.isMutable) commandUnderTest!!.storeBitmap(bitmapCopy, bitmapCopy.width.toFloat(), bitmapCopy.height.toFloat()) storedBitmap = commandUnderTest!!.fileToStoredBitmap Assert.assertNotNull(storedBitmap) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/CropCommandTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/CropCommandTest.kt index 7bbd3b5009..632936c77f 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/CropCommandTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/CropCommandTest.kt @@ -43,16 +43,16 @@ class CropCommandTest { @Before fun setUp() { layerModel = LayerModel() - layerModel!!.width = INITIAL_WIDTH - layerModel!!.height = INITIAL_HEIGHT + layerModel.width = INITIAL_WIDTH + layerModel.height = INITIAL_HEIGHT val canvasBitmapUnderTest = Bitmap.createBitmap(INITIAL_WIDTH, INITIAL_HEIGHT, Bitmap.Config.ARGB_8888) canvasBitmapUnderTest.eraseColor(BITMAP_BASE_COLOR) bitmapUnderTest = canvasBitmapUnderTest.copy(Bitmap.Config.ARGB_8888, true) layerUnderTest = Layer(bitmapUnderTest) canvasUnderTest = Canvas() - canvasUnderTest!!.setBitmap(canvasBitmapUnderTest) - layerModel!!.addLayerAt(0, layerUnderTest!!) - layerModel!!.currentLayer = layerUnderTest + canvasUnderTest.setBitmap(canvasBitmapUnderTest) + layerModel.addLayerAt(0, layerUnderTest) + layerModel.currentLayer = layerUnderTest resizeCoordinateXLeft = 0 resizeCoordinateYTop = 0 resizeCoordinateXRight = bitmapUnderTest.width - 1 @@ -64,16 +64,16 @@ class CropCommandTest { @Test fun testIfBitmapIsCropped() { - val widthOriginal = bitmapUnderTest!!.width - val heightOriginal = bitmapUnderTest!!.height + val widthOriginal = bitmapUnderTest.width + val heightOriginal = bitmapUnderTest.height resizeCoordinateXLeft = 1 resizeCoordinateYTop = 1 - resizeCoordinateXRight = bitmapUnderTest!!.width - 2 - resizeCoordinateYBottom = bitmapUnderTest!!.height - 2 + resizeCoordinateXRight = bitmapUnderTest.width - 2 + resizeCoordinateYBottom = bitmapUnderTest.height - 2 commandUnderTest = CropCommand(resizeCoordinateXLeft, resizeCoordinateYTop, resizeCoordinateXRight, resizeCoordinateYBottom, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - val croppedBitmap = layerUnderTest!!.bitmap + commandUnderTest.run(canvasUnderTest, layerModel) + val croppedBitmap = layerUnderTest.bitmap Assert.assertEquals("Cropping failed, width not correct ", (widthOriginal - resizeCoordinateXLeft - (widthOriginal - (resizeCoordinateXRight + 1))).toLong(), croppedBitmap.width.toLong()) Assert.assertEquals("Cropping failed, height not correct ", (heightOriginal - resizeCoordinateYTop - (widthOriginal - (resizeCoordinateYBottom + 1))).toLong(), croppedBitmap.height.toLong()) croppedBitmap.recycle() @@ -81,16 +81,16 @@ class CropCommandTest { @Test fun testIfBitmapIsEnlarged() { - val widthOriginal = bitmapUnderTest!!.width - val heightOriginal = bitmapUnderTest!!.height + val widthOriginal = bitmapUnderTest.width + val heightOriginal = bitmapUnderTest.height resizeCoordinateXLeft = -1 resizeCoordinateYTop = -1 - resizeCoordinateXRight = bitmapUnderTest!!.width - resizeCoordinateYBottom = bitmapUnderTest!!.height + resizeCoordinateXRight = bitmapUnderTest.width + resizeCoordinateYBottom = bitmapUnderTest.height commandUnderTest = CropCommand(resizeCoordinateXLeft, resizeCoordinateYTop, resizeCoordinateXRight, resizeCoordinateYBottom, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - val enlargedBitmap = layerUnderTest!!.bitmap + commandUnderTest.run(canvasUnderTest, layerModel) + val enlargedBitmap = layerUnderTest.bitmap Assert.assertEquals("Enlarging failed, width not correct ", (widthOriginal - resizeCoordinateXLeft - (widthOriginal - (resizeCoordinateXRight + 1))).toLong(), enlargedBitmap.width.toLong()) Assert.assertEquals("Enlarging failed, height not correct ", (heightOriginal - resizeCoordinateYTop - (widthOriginal - (resizeCoordinateYBottom + 1))).toLong(), enlargedBitmap.height.toLong()) enlargedBitmap.recycle() @@ -98,16 +98,16 @@ class CropCommandTest { @Test fun testIfBitmapIsShifted() { - val widthOriginal = bitmapUnderTest!!.width - val heightOriginal = bitmapUnderTest!!.height - resizeCoordinateXLeft = bitmapUnderTest!!.width / 2 - 1 - resizeCoordinateYTop = bitmapUnderTest!!.height / 2 - 1 - resizeCoordinateXRight = resizeCoordinateXLeft + bitmapUnderTest!!.width - 1 - resizeCoordinateYBottom = resizeCoordinateYTop + bitmapUnderTest!!.height - 1 + val widthOriginal = bitmapUnderTest.width + val heightOriginal = bitmapUnderTest.height + resizeCoordinateXLeft = bitmapUnderTest.width / 2 - 1 + resizeCoordinateYTop = bitmapUnderTest.height / 2 - 1 + resizeCoordinateXRight = resizeCoordinateXLeft + bitmapUnderTest.width - 1 + resizeCoordinateYBottom = resizeCoordinateYTop + bitmapUnderTest.height - 1 commandUnderTest = CropCommand(resizeCoordinateXLeft, resizeCoordinateYTop, resizeCoordinateXRight, resizeCoordinateYBottom, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - val enlargedBitmap = layerUnderTest!!.bitmap + commandUnderTest.run(canvasUnderTest, layerModel) + val enlargedBitmap = layerUnderTest.bitmap Assert.assertEquals("Enlarging failed, width not correct ", widthOriginal.toLong(), enlargedBitmap.width.toLong()) Assert.assertEquals("Enlarging failed, height not correct ", heightOriginal.toLong(), enlargedBitmap.height.toLong()) enlargedBitmap.recycle() @@ -115,41 +115,49 @@ class CropCommandTest { @Test fun testIfMaximumResolutionIsRespected() { - val widthOriginal = bitmapUnderTest!!.width - val heightOriginal = bitmapUnderTest!!.height + val widthOriginal = bitmapUnderTest.width + val heightOriginal = bitmapUnderTest.height commandUnderTest = CropCommand(0, 0, widthOriginal * 2, heightOriginal * 2, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - Assert.assertEquals("Width should not have changed", widthOriginal.toLong(), layerUnderTest!!.bitmap.width.toLong()) - Assert.assertEquals("Height should not have changed", heightOriginal.toLong(), layerUnderTest!!.bitmap.height.toLong()) + commandUnderTest.run(canvasUnderTest, layerModel) + Assert.assertEquals("Width should not have changed", widthOriginal.toLong(), layerUnderTest.bitmap.width.toLong()) + Assert.assertEquals("Height should not have changed", heightOriginal.toLong(), layerUnderTest.bitmap.height.toLong()) } @Test fun testIfBitmapIsNotResizedWithInvalidBounds() { - val originalBitmap = layerUnderTest!!.bitmap - commandUnderTest = CropCommand(bitmapUnderTest!!.width, 0, bitmapUnderTest!!.width, + val originalBitmap = layerUnderTest.bitmap + commandUnderTest = CropCommand( + bitmapUnderTest.width, 0, bitmapUnderTest.width, 0, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - Assert.assertTrue("bitmap must not change if X left is larger than bitmap scope", originalBitmap.sameAs(layerUnderTest!!.bitmap)) + commandUnderTest.run(canvasUnderTest, layerModel) + Assert.assertTrue("bitmap must not change if X left is larger than bitmap scope", originalBitmap.sameAs( + layerUnderTest.bitmap)) commandUnderTest = CropCommand(-1, 0, -1, 0, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - Assert.assertTrue("bitmap must not change if X right is smaller than bitmap scope", originalBitmap.sameAs(layerUnderTest!!.bitmap)) - commandUnderTest = CropCommand(0, bitmapUnderTest!!.height, 0, - bitmapUnderTest!!.height, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - Assert.assertTrue("bitmap must not change if Y top is larger than bitmap scope", originalBitmap.sameAs(layerUnderTest!!.bitmap)) + commandUnderTest.run(canvasUnderTest, layerModel) + Assert.assertTrue("bitmap must not change if X right is smaller than bitmap scope", originalBitmap.sameAs( + layerUnderTest.bitmap)) + commandUnderTest = CropCommand(0, bitmapUnderTest.height, 0, + bitmapUnderTest.height, maximumBitmapResolution) + commandUnderTest.run(canvasUnderTest, layerModel) + Assert.assertTrue("bitmap must not change if Y top is larger than bitmap scope", originalBitmap.sameAs( + layerUnderTest.bitmap)) commandUnderTest = CropCommand(0, -1, 0, -1, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - Assert.assertTrue("bitmap must not change if Y bottom is smaller than bitmap scope", originalBitmap.sameAs(layerUnderTest!!.bitmap)) + commandUnderTest.run(canvasUnderTest, layerModel) + Assert.assertTrue("bitmap must not change if Y bottom is smaller than bitmap scope", originalBitmap.sameAs( + layerUnderTest.bitmap)) commandUnderTest = CropCommand(1, 0, 0, 0, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - Assert.assertTrue("bitmap must not change with widthXRight < widthXLeft bound", originalBitmap.sameAs(layerUnderTest!!.bitmap)) + commandUnderTest.run(canvasUnderTest, layerModel) + Assert.assertTrue("bitmap must not change with widthXRight < widthXLeft bound", originalBitmap.sameAs( + layerUnderTest.bitmap)) commandUnderTest = CropCommand(0, 1, 0, 0, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - Assert.assertTrue("bitmap must not change with widthYBottom < widthYTop bound", originalBitmap.sameAs(layerUnderTest!!.bitmap)) - commandUnderTest = CropCommand(0, 0, bitmapUnderTest!!.width - 1, - bitmapUnderTest!!.height - 1, maximumBitmapResolution) - commandUnderTest.run(canvasUnderTest!!, layerModel!!) - Assert.assertTrue("bitmap must not change because bounds are the same as original bitmap", originalBitmap.sameAs(layerUnderTest!!.bitmap)) + commandUnderTest.run(canvasUnderTest, layerModel) + Assert.assertTrue("bitmap must not change with widthYBottom < widthYTop bound", originalBitmap.sameAs( + layerUnderTest.bitmap)) + commandUnderTest = CropCommand(0, 0, bitmapUnderTest.width - 1, + bitmapUnderTest.height - 1, maximumBitmapResolution) + commandUnderTest.run(canvasUnderTest, layerModel) + Assert.assertTrue("bitmap must not change because bounds are the same as original bitmap", originalBitmap.sameAs( + layerUnderTest.bitmap)) } companion object { diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/FlipCommandTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/FlipCommandTest.kt index fc837defb0..5758ee19a0 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/FlipCommandTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/FlipCommandTest.kt @@ -53,18 +53,18 @@ class FlipCommandTest { @Test fun testVerticalFlip() { commandUnderTest = FlipCommand(FlipDirection.FLIP_VERTICAL) - bitmapUnderTest!!.setPixel(0, INITIAL_HEIGHT / 2, PAINT_BASE_COLOR) + bitmapUnderTest.setPixel(0, INITIAL_HEIGHT / 2, PAINT_BASE_COLOR) commandUnderTest.run(canvasUnderTest!!, layerModel!!) - val pixel = bitmapUnderTest!!.getPixel(INITIAL_WIDTH - 1, INITIAL_WIDTH / 2) + val pixel = bitmapUnderTest.getPixel(INITIAL_WIDTH - 1, INITIAL_WIDTH / 2) Assert.assertEquals(PAINT_BASE_COLOR.toLong(), pixel.toLong()) } @Test fun testHorizontalFlip() { commandUnderTest = FlipCommand(FlipDirection.FLIP_HORIZONTAL) - bitmapUnderTest!!.setPixel(INITIAL_WIDTH / 2, 0, PAINT_BASE_COLOR) + bitmapUnderTest.setPixel(INITIAL_WIDTH / 2, 0, PAINT_BASE_COLOR) commandUnderTest.run(canvasUnderTest!!, layerModel!!) - val pixel = bitmapUnderTest!!.getPixel(INITIAL_WIDTH / 2, INITIAL_WIDTH - 1) + val pixel = bitmapUnderTest.getPixel(INITIAL_WIDTH / 2, INITIAL_WIDTH - 1) Assert.assertEquals(PAINT_BASE_COLOR.toLong(), pixel.toLong()) } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/MergeLayersCommandTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/MergeLayersCommandTest.kt index 7dcdb3c100..c4a754cccc 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/MergeLayersCommandTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/MergeLayersCommandTest.kt @@ -74,10 +74,10 @@ class MergeLayersCommandTest { @Test fun testRun() { commandUnderTest.run(canvasUnderTest, layerModel) - Assert.assertEquals(layerModel.getLayerAt(0)!!.bitmap!!.getPixel(5, 5), PAINT_BASE_COLOR) - Assert.assertEquals(layerModel.getLayerAt(0)!!.bitmap!!.getPixel(8, 8), PAINT_BASE_COLOR) - Assert.assertEquals(layerModel.getLayerAt(0)!!.bitmap!!.getPixel(3, 3), 0) - Assert.assertEquals(layerModel.getLayerAt(0)!!.bitmap!!.getPixel(0, 0), 0) + Assert.assertEquals(layerModel.getLayerAt(0)!!.bitmap.getPixel(5, 5), PAINT_BASE_COLOR) + Assert.assertEquals(layerModel.getLayerAt(0)!!.bitmap.getPixel(8, 8), PAINT_BASE_COLOR) + Assert.assertEquals(layerModel.getLayerAt(0)!!.bitmap.getPixel(3, 3), 0) + Assert.assertEquals(layerModel.getLayerAt(0)!!.bitmap.getPixel(0, 0), 0) } @Test @@ -92,10 +92,10 @@ class MergeLayersCommandTest { fun testRunMergeSeparatedLayers() { commandUnderTest = MergeLayersCommand(2, 0) commandUnderTest.run(canvasUnderTest, layerModel) - Assert.assertEquals(layerModel.currentLayer!!.bitmap!!.getPixel(0, 0), PAINT_BASE_COLOR) - Assert.assertEquals(layerModel.currentLayer!!.bitmap!!.getPixel(8, 8), PAINT_BASE_COLOR) - Assert.assertEquals(layerModel.currentLayer!!.bitmap!!.getPixel(3, 3), 0) - Assert.assertEquals(layerModel.currentLayer!!.bitmap!!.getPixel(5, 5), 0) - Assert.assertEquals(layerModel.getLayerAt(1)!!.bitmap!!.getPixel(5, 5), PAINT_BASE_COLOR) + Assert.assertEquals(layerModel.currentLayer!!.bitmap.getPixel(0, 0), PAINT_BASE_COLOR) + Assert.assertEquals(layerModel.currentLayer!!.bitmap.getPixel(8, 8), PAINT_BASE_COLOR) + Assert.assertEquals(layerModel.currentLayer!!.bitmap.getPixel(3, 3), 0) + Assert.assertEquals(layerModel.currentLayer!!.bitmap.getPixel(5, 5), 0) + Assert.assertEquals(layerModel.getLayerAt(1)!!.bitmap.getPixel(5, 5), PAINT_BASE_COLOR) } } diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/PathCommandTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/PathCommandTest.kt index 507eb0749a..1ee5e3ed36 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/PathCommandTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/PathCommandTest.kt @@ -66,10 +66,10 @@ class PathCommandTest { @Test fun testPathOutOfBounds() { val path = Path() - val left = (canvasBitmapUnderTest!!.width + 50).toFloat() - val top = (canvasBitmapUnderTest!!.height + 50).toFloat() - val right = (canvasBitmapUnderTest!!.width + 100).toFloat() - val bottom = (canvasBitmapUnderTest!!.height + 100).toFloat() + val left = (canvasBitmapUnderTest.width + 50).toFloat() + val top = (canvasBitmapUnderTest.height + 50).toFloat() + val right = (canvasBitmapUnderTest.width + 100).toFloat() + val bottom = (canvasBitmapUnderTest.height + 100).toFloat() path.addRect(RectF(left, top, right, bottom), Path.Direction.CW) commandUnderTest = PathCommand(paintUnderTest!!, path) commandUnderTest.run(canvasUnderTest!!, LayerModel()) diff --git a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/PointCommandTest.kt b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/PointCommandTest.kt index 78533fb97d..55a27af4d1 100644 --- a/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/PointCommandTest.kt +++ b/Paintroid/src/androidTest/java/org/catrobat/paintroid/test/junit/command/PointCommandTest.kt @@ -63,17 +63,17 @@ class PointCommandTest { @Test fun testRun() { - bitmapUnderTest!!.setPixel(pointUnderTest!!.x.toInt(), pointUnderTest!!.y.toInt(), paintUnderTest!!.color) - commandUnderTest!!.run(canvasUnderTest!!, LayerModel()) - assertBitmapEquals(bitmapUnderTest!!, canvasBitmapUnderTest) + bitmapUnderTest.setPixel(pointUnderTest!!.x.toInt(), pointUnderTest!!.y.toInt(), paintUnderTest!!.color) + commandUnderTest.run(canvasUnderTest!!, LayerModel()) + assertBitmapEquals(bitmapUnderTest, canvasBitmapUnderTest) } @Test fun testRunOutOfBounds() { - pointUnderTest = PointF((canvasBitmapUnderTest!!.height + 1).toFloat(), (canvasBitmapUnderTest!!.width + 1).toFloat()) + pointUnderTest = PointF((canvasBitmapUnderTest.height + 1).toFloat(), (canvasBitmapUnderTest.width + 1).toFloat()) commandUnderTest = PointCommand(paintUnderTest!!, pointUnderTest!!) commandUnderTest.run(canvasUnderTest!!, LayerModel()) - assertBitmapEquals(bitmapUnderTest!!, canvasBitmapUnderTest) + assertBitmapEquals(bitmapUnderTest, canvasBitmapUnderTest) } companion object { diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/FileIO.kt b/Paintroid/src/main/java/org/catrobat/paintroid/FileIO.kt index 432399538f..a61e19e3ac 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/FileIO.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/FileIO.kt @@ -115,7 +115,7 @@ object FileIO { require(currentBitmap != null && !currentBitmap.isRecycled) { "Bitmap is invalid" } if (compressFormat == CompressFormat.JPEG) { val newBitmap = - Bitmap.createBitmap(currentBitmap.width, currentBitmap.height, currentBitmap.config) + Bitmap.createBitmap(currentBitmap.width, currentBitmap.height, currentBitmap.config ?: Bitmap.Config.ARGB_8888) val canvas = Canvas(newBitmap) canvas.drawColor(Color.WHITE) canvas.drawBitmap(currentBitmap, 0f, 0f, null) diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/MainActivity.kt b/Paintroid/src/main/java/org/catrobat/paintroid/MainActivity.kt index dffb83df93..23988489f8 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/MainActivity.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/MainActivity.kt @@ -21,6 +21,7 @@ package org.catrobat.paintroid import android.content.ContentResolver import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.graphics.Color import android.graphics.Paint import android.graphics.PointF @@ -810,7 +811,14 @@ class MainActivity : AppCompatActivity(), MainView, CommandListener { } } - fun getVersionCode(): String = runCatching { - packageManager.getPackageInfo(packageName, 0).versionName + fun getVersionName(): String = runCatching { + val pm = packageManager + val pkgInfo = if (Build.VERSION.SDK_INT >= 33) { + pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(0)) + } else { + @Suppress("DEPRECATION") + pm.getPackageInfo(packageName, 0) + } + pkgInfo.versionName ?: "" }.getOrDefault("") } diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/ClippingCommand.kt b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/ClippingCommand.kt index 2ebf7f70e8..c38a393cf5 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/ClippingCommand.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/ClippingCommand.kt @@ -12,8 +12,8 @@ import org.catrobat.paintroid.contract.LayerContracts class ClippingCommand(bitmap: Bitmap, pathBitmap: Bitmap) : Command { - var bitmap: Bitmap? = bitmap.copy(bitmap.config, true); private set - var pathBitmap: Bitmap? = pathBitmap.copy(pathBitmap.config, true); private set + var bitmap: Bitmap? = bitmap.copy(bitmap.config ?: Bitmap.Config.ARGB_8888, true); private set + var pathBitmap: Bitmap? = pathBitmap.copy(pathBitmap.config ?: Bitmap.Config.ARGB_8888, true); private set override fun run(canvas: Canvas, layerModel: LayerContracts.Model) { val bitmapToDraw = bitmap diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/CropCommand.kt b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/CropCommand.kt index 874d249ad7..58eb0bbd4f 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/CropCommand.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/CropCommand.kt @@ -57,7 +57,7 @@ class CropCommand( while (iterator.hasNext()) { val currentLayer = iterator.next() val currentBitmap = currentLayer.bitmap ?: Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) - val resizedBitmap = Bitmap.createBitmap(width, height, currentBitmap.config) + val resizedBitmap = Bitmap.createBitmap(width, height, currentBitmap.config ?: Bitmap.Config.ARGB_8888) val resizedCanvas = Canvas(resizedBitmap) resizedCanvas.drawBitmap(currentBitmap, -resizeCoordinateXLeft.toFloat(), -resizeCoordinateYTop.toFloat(), null) currentLayer.bitmap = resizedBitmap diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/FlipCommand.kt b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/FlipCommand.kt index aefd9a5dcf..ea930150c6 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/FlipCommand.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/FlipCommand.kt @@ -19,6 +19,7 @@ package org.catrobat.paintroid.command.implementation +import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color import android.graphics.Matrix @@ -44,7 +45,7 @@ class FlipCommand(flipDirection: FlipDirection) : Command { } } layerModel.currentLayer?.bitmap?.let { bitmap -> - val bitmapCopy = bitmap.copy(bitmap.config, bitmap.isMutable) + val bitmapCopy = bitmap.copy(bitmap.config ?: Bitmap.Config.ARGB_8888, bitmap.isMutable) val flipCanvas = Canvas(bitmap) bitmap.eraseColor(Color.TRANSPARENT) flipCanvas.drawBitmap(bitmapCopy, flipMatrix, Paint()) diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/MergeLayersCommand.kt b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/MergeLayersCommand.kt index c4b43491f6..e4b579681b 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/MergeLayersCommand.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/command/implementation/MergeLayersCommand.kt @@ -19,6 +19,7 @@ package org.catrobat.paintroid.command.implementation +import android.graphics.Bitmap import android.graphics.Canvas import android.util.Log import org.catrobat.paintroid.command.Command @@ -40,7 +41,7 @@ class MergeLayersCommand(position: Int, mergeWith: Int) : Command { if (sourceLayer != null && destinationLayer != null) { val destinationBitmap = destinationLayer.bitmap destinationBitmap ?: return - val copyBitmap = destinationBitmap.copy(destinationBitmap.config, true) + val copyBitmap = destinationBitmap.copy(destinationBitmap.config ?: Bitmap.Config.ARGB_8888, true) val copyCanvas = Canvas(copyBitmap) copyCanvas.drawBitmap(sourceLayer.bitmap ?: return, 0f, 0f, null) if (layerModel.removeLayerAt(position)) { diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/dialog/AboutDialog.kt b/Paintroid/src/main/java/org/catrobat/paintroid/dialog/AboutDialog.kt index d6a44a777c..aeae3ec679 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/dialog/AboutDialog.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/dialog/AboutDialog.kt @@ -52,7 +52,7 @@ class AboutDialog : AppCompatDialogFragment() { val aboutLicenseView = view.findViewById(R.id.pocketpaint_about_license_url) val aboutCatrobatView = view.findViewById(R.id.pocketpaint_about_catrobat_url) val activity = requireActivity() as MainActivity - val aboutVersion = getString(R.string.pocketpaint_about_version, activity.getVersionCode()) + val aboutVersion = getString(R.string.pocketpaint_about_version, activity.getVersionName()) aboutVersionView.text = aboutVersion val aboutContent = getString( R.string.pocketpaint_about_content, diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultContextCallback.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultContextCallback.kt index b963cff2ba..a173bf305a 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultContextCallback.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultContextCallback.kt @@ -37,7 +37,7 @@ import androidx.annotation.StringRes import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat -import org.catrobat.paintroid.R +import org.catrobat.paintroid.colorpicker.R import org.catrobat.paintroid.tools.ContextCallback import org.catrobat.paintroid.tools.ContextCallback.NotificationDuration import org.catrobat.paintroid.tools.ContextCallback.NotificationDuration.SHORT diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultToolPaint.kt b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultToolPaint.kt index 230792c5d9..99909d9c22 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultToolPaint.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/tools/implementation/DefaultToolPaint.kt @@ -27,7 +27,7 @@ import android.graphics.Paint.Cap import android.graphics.PorterDuff import android.graphics.PorterDuffXfermode import android.graphics.Shader -import org.catrobat.paintroid.R +import org.catrobat.paintroid.colorpicker.R import org.catrobat.paintroid.tools.ToolPaint const val STROKE_25 = 25f diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/ui/tools/BrushToolView.kt b/Paintroid/src/main/java/org/catrobat/paintroid/ui/tools/BrushToolView.kt index f5bb3c6542..74efedfbaa 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/ui/tools/BrushToolView.kt +++ b/Paintroid/src/main/java/org/catrobat/paintroid/ui/tools/BrushToolView.kt @@ -27,7 +27,7 @@ import android.graphics.Paint import android.graphics.Shader import android.util.AttributeSet import android.view.View -import org.catrobat.paintroid.R +import org.catrobat.paintroid.colorpicker.R import org.catrobat.paintroid.tools.ToolType import org.catrobat.paintroid.tools.options.BrushToolOptionsView.OnBrushPreviewListener import org.catrobat.paintroid.tools.options.BrushToolPreview diff --git a/Paintroid/src/main/java/org/catrobat/paintroid/web/MediaGalleryWebViewClient.java b/Paintroid/src/main/java/org/catrobat/paintroid/web/MediaGalleryWebViewClient.java index 4690e2aa6e..dbc72a31aa 100644 --- a/Paintroid/src/main/java/org/catrobat/paintroid/web/MediaGalleryWebViewClient.java +++ b/Paintroid/src/main/java/org/catrobat/paintroid/web/MediaGalleryWebViewClient.java @@ -22,34 +22,36 @@ */ package org.catrobat.paintroid.web; -import android.app.ProgressDialog; import android.graphics.Bitmap; import android.webkit.WebView; import android.webkit.WebViewClient; - -import org.catrobat.paintroid.R; +import android.net.Uri; +import android.view.View; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import androidx.appcompat.app.AlertDialog; +import androidx.core.view.ViewCompat; +import android.widget.ProgressBar; public class MediaGalleryWebViewClient extends WebViewClient { - private ProgressDialog webViewLoadingDialog; - private WebClientCallback callback; + private AlertDialog loadingDialog; + private final WebClientCallback callback; public interface WebClientCallback { void finish(); } public MediaGalleryWebViewClient(WebClientCallback callback) { - super(); this.callback = callback; } + private static final String GALLERY_ROOT = "https://share.catrob.at/pocketcode/"; + @Override - public void onPageStarted(WebView view, String urlClient, Bitmap favicon) { - if (webViewLoadingDialog == null && !urlClient.matches("https://share.catrob.at/pocketcode/")) { - webViewLoadingDialog = new ProgressDialog(view.getContext(), R.style.WebViewLoadingCircle); - webViewLoadingDialog.setCancelable(true); - webViewLoadingDialog.setCanceledOnTouchOutside(false); - webViewLoadingDialog.setProgressStyle(android.R.style.Widget_ProgressBar_Small); - webViewLoadingDialog.show(); + public void onPageStarted(WebView view, String url, Bitmap favicon) { + // avoid regex 'matches' – we only want a prefix check + if (!startsWithGalleryRoot(url)) { + showLoading(view); } else { callback.finish(); } @@ -57,19 +59,71 @@ public void onPageStarted(WebView view, String urlClient, Bitmap favicon) { @Override public void onPageFinished(WebView view, String url) { - if (webViewLoadingDialog != null) { - webViewLoadingDialog.dismiss(); - webViewLoadingDialog = null; - } + dismissLoading(); } + // New API (M+) — preferred + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + // Return false to let the WebView load the URL + return false; + } + + // Legacy shim for < M + @SuppressWarnings("deprecation") @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return false; } + // New API (M+) — preferred + @Override + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + dismissLoading(); + callback.finish(); + } + + // Legacy shim for < M + @SuppressWarnings("deprecation") @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + dismissLoading(); callback.finish(); } + + private boolean startsWithGalleryRoot(String url) { + try { + Uri u = Uri.parse(url); + // normalize to compare robustly + String normalized = u.getScheme() + "://" + u.getHost() + (u.getPort() != -1 ? (":" + u.getPort()) : "") + u.getPath(); + return normalized.startsWith(GALLERY_ROOT); + } catch (Exception ignored) { + return false; + } + } + + private void showLoading(View anchor) { + if (loadingDialog != null) return; + + ProgressBar progress = new ProgressBar(anchor.getContext()); + // make it large enough and accessible + int padding = (int) (anchor.getResources().getDisplayMetrics().density * 24); + progress.setPadding(padding, padding, padding, padding); + ViewCompat.setImportantForAccessibility(progress, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); + + loadingDialog = new AlertDialog.Builder(anchor.getContext()) + .setView(progress) + .setCancelable(true) + .create(); + loadingDialog.setCanceledOnTouchOutside(false); + loadingDialog.show(); + } + + private void dismissLoading() { + if (loadingDialog != null) { + loadingDialog.dismiss(); + loadingDialog = null; + } + } } + diff --git a/app/build.gradle b/app/build.gradle index 2c7e76738b..0aa6689eab 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,42 +19,48 @@ import io.gitlab.arturbosch.detekt.Detekt * along with this program. If not, see . */ -apply plugin: 'com.android.application' +plugins { + id "com.android.application" + id "org.jetbrains.kotlin.android" +} ext { - appId = 'org.catrobat.paintroid' - appName = '@string/pocketpaint_app_name' + appId = "org.catrobat.paintroid" + appName = "@string/pocketpaint_app_name" } -// When -Pindependent was provided on the gradle command the APP name is changed. -// This allows to have multiple Paintroid versions installed in parallel for testing purposes. -// Furthermore these installations do not interfere with the actual Paintroid app. -if (project.hasProperty('independent')) { +// Allow multiple side-by-side installs when run with -Pindependent +if (project.hasProperty("independent")) { def today = new Date() - appId += '.independent_' + today.format('YYYYMMdd_HHmm') - appName = property('independent') ?: 'Paint ' + today.format('MMdd HH:mm') + appId += ".independent_${today.format('yyyyMMdd_HHmm')}" + appName = property("independent") ?: "Paint ${today.format('MMdd HH:mm')}" } +// Provide a dummy keystore if none is passed if (!project.hasProperty("signingKeystore")) { ext.signingKeystore = "dummyKeystore" } android { - compileSdkVersion rootProject.ext.androidCompileSdkVersion + namespace "org.catrobat.paintroid.app" + compileSdk = rootProject.ext.androidCompileSdkVersion + defaultConfig { applicationId appId - minSdkVersion rootProject.ext.androidMinSdkVersion - //noinspection OldTargetApi - targetSdkVersion rootProject.ext.androidTargetSdkVersion - versionCode rootProject.ext.androidVersionCode - versionName rootProject.ext.androidVersionName + minSdk = rootProject.ext.androidMinSdkVersion + targetSdk = rootProject.ext.androidTargetSdkVersion + versionCode = rootProject.ext.androidVersionCode + versionName = rootProject.ext.androidVersionName manifestPlaceholders += [appName: appName] setProperty("archivesBaseName", "paintroid") } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" } signingConfigs { @@ -66,40 +72,48 @@ android { } } - if (project.hasProperty("signingKeystore") && project.hasProperty("signingKeystorePassword") && - project.hasProperty("signingKeyAlias") && project.hasProperty("signingKeyPassword")) { - android.signingConfigs.signedRelease.storePassword = project.property("signingKeystorePassword") - android.signingConfigs.signedRelease.keyAlias = project.property("signingKeyAlias") - android.signingConfigs.signedRelease.keyPassword = project.property("signingKeyPassword") + if (project.hasProperty("signingKeystore") && + project.hasProperty("signingKeystorePassword") && + project.hasProperty("signingKeyAlias") && + project.hasProperty("signingKeyPassword")) { + + signingConfigs.signedRelease.storePassword = project.property("signingKeystorePassword") + signingConfigs.signedRelease.keyAlias = project.property("signingKeyAlias") + signingConfigs.signedRelease.keyPassword = project.property("signingKeyPassword") } buildTypes { release { minifyEnabled true shrinkResources true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } - signedRelease.initWith(buildTypes.release) - signedRelease { - signingConfig signingConfigs.signedRelease + create("signedRelease") { + initWith(buildTypes.release) + signingConfig = signingConfigs.signedRelease } } publishing { - singleVariant('release') { + singleVariant("release") { withSourcesJar() } } + packaging { + jniLibs { + useLegacyPackaging false + } + } } -tasks.withType(Detekt).configureEach { - jvmTarget = "1.8" +tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach { + jvmTarget = "17" exclude("**/resources/**") exclude("**/build/**") } dependencies { - implementation project(':Paintroid') - - implementation 'com.android.support:support-core-utils:28.0.0' + implementation project(":Paintroid") + implementation "com.android.support:support-core-utils:28.0.0" } + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4ffe27c5a3..7811fe1b30 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,11 +21,16 @@ xmlns:tools="http://schemas.android.com/tools" package="org.catrobat.paintroid.app"> + + + + + android:resource="@xml/filepaths" /> . */ -// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.9.24' repositories { - mavenCentral() google() + mavenCentral() maven { url "https://plugins.gradle.org/m2/" } maven { url 'https://jitpack.io' } } dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' - classpath 'com.hiya:jacoco-android:0.2' classpath 'com.github.Catrobat:Gradle:1.6.2' - classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:3.1.0' - classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.20.0" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } plugins { - id "io.gitlab.arturbosch.detekt" version "1.20.0" + id "com.android.application" version '8.13.0' apply false + id "com.android.library" version '8.13.0' apply false + id "org.jetbrains.kotlin.android" version "1.9.24" apply false + id "io.gitlab.arturbosch.detekt" version "1.23.6" apply false + id "com.getkeepsafe.dexcount" version "4.0.0" apply false } ext { - androidCompileSdkVersion = 34 + androidCompileSdkVersion = 35 + androidTargetSdkVersion = 35 androidMinSdkVersion = 21 - androidTargetSdkVersion = 34 androidSupportLibraryVersion = '28.0.0' diff --git a/colorpicker/build.gradle b/colorpicker/build.gradle index e1f8ea1e11..3af101c7a4 100644 --- a/colorpicker/build.gradle +++ b/colorpicker/build.gradle @@ -21,23 +21,22 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { + namespace "org.catrobat.paintroid.colorpicker" compileSdkVersion rootProject.ext.androidCompileSdkVersion defaultConfig { minSdkVersion rootProject.ext.androidMinSdkVersion targetSdkVersion rootProject.ext.androidTargetSdkVersion testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' - versionCode rootProject.ext.androidVersionCode - versionName rootProject.ext.androidVersionName } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = JavaVersion.VERSION_11.toString() + jvmTarget = "17" } buildTypes { @@ -57,4 +56,4 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8' -} +} \ No newline at end of file diff --git a/colorpicker/src/main/java/org/catrobat/paintroid/colorpicker/ColorPickerPresetColorButton.kt b/colorpicker/src/main/java/org/catrobat/paintroid/colorpicker/ColorPickerPresetColorButton.kt index 7176cabef7..f072acf183 100644 --- a/colorpicker/src/main/java/org/catrobat/paintroid/colorpicker/ColorPickerPresetColorButton.kt +++ b/colorpicker/src/main/java/org/catrobat/paintroid/colorpicker/ColorPickerPresetColorButton.kt @@ -32,7 +32,7 @@ class ColorPickerPresetColorButton @JvmOverloads constructor( context: Context, @field:ColorInt @get:ColorInt @param:ColorInt var color: Int = Color.BLACK -) : AppCompatImageButton(context, null, R.attr.borderlessButtonStyle) { +) : AppCompatImageButton(context, null, android.R.attr.borderlessButtonStyle) { init { val checkeredBitmap = diff --git a/gradle.properties b/gradle.properties index 5e9758afa7..d91fa61429 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ android.enableJetifier=true android.useAndroidX=true -org.gradle.jvmargs=-XX:MaxPermSize=1024m -Xmx4096m -android.disableAutomaticComponentCreation=true \ No newline at end of file +org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 +android.nonTransitiveRClass=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index eb250c9aae..af685a4a17 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Feb 02 16:23:53 CET 2021 +#Thu Sep 04 16:50:59 CEST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip diff --git a/settings.gradle b/settings.gradle index ee2027bde3..da41808e20 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,4 +17,26 @@ * along with this program. If not, see . */ -include ':Paintroid', ':app', ':colorpicker' +// settings.gradle (Groovy) + +pluginManagement { + repositories { + google() // needed for com.android.application / com.android.library + mavenCentral() + gradlePluginPortal() // Gradle Central Plugin Repository + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.PREFER_PROJECT) + repositories { + google() + mavenCentral() + mavenLocal() + } +} + +rootProject.name = "Paintroid" + +include(":Paintroid", ":app", ":colorpicker") +