From 7299dc23165331adf0eaf285c5d8ee24c385ee26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Tue, 1 Oct 2024 13:43:05 +0200 Subject: [PATCH 1/8] Update `.gitmodules` --- .gitmodules | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index b80fa2c..5fb4a5c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,3 @@ [submodule "libs/privacy-friendly-backup-api"] path = libs/privacy-friendly-backup-api - url = git@github.com:SecUSo/privacy-friendly-backup-api.git -[submodule "libs/privacy-friendly-backup-api/"] url = https://github.com/SecUSo/privacy-friendly-backup-api.git From 4b9bfcfc58e18d7605f0e421b1ef0ac5fd6f162e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Tue, 1 Oct 2024 13:59:15 +0200 Subject: [PATCH 2/8] Upgrade AGP to version 8.6.0 --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 3 +-- build.gradle | 8 ++++---- gradle.properties | 3 +++ gradle/wrapper/gradle-wrapper.properties | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 72e29ac..28edda3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,6 +18,7 @@ android { } productFlavors { } + namespace 'org.secuso.privacyfriendlycameraruler' } dependencies { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 18d6bf5..866bdf7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> Date: Tue, 15 Oct 2024 14:11:31 +0200 Subject: [PATCH 3/8] Update dependencies and target sdk --- app/build.gradle | 28 ++++++++----------- .../PFTapeMeasure.kt | 5 ++-- .../cameraruler/CameraActivity.java | 1 + build.gradle | 2 +- libs/privacy-friendly-backup-api | 2 +- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 28edda3..1377b55 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,11 +2,11 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' android { - compileSdkVersion 33 defaultConfig { applicationId 'org.secuso.privacyfriendlytapemeasure' minSdkVersion 21 - targetSdkVersion 33 + compileSdk 34 + targetSdkVersion 34 versionCode 4 versionName '1.1' } @@ -19,32 +19,26 @@ android { productFlavors { } namespace 'org.secuso.privacyfriendlycameraruler' + kotlin { + jvmToolchain(17) + } } dependencies { - testImplementation 'junit:junit:4.12' - implementation 'androidx.appcompat:appcompat:1.0.0' + testImplementation 'junit:junit:4.13.2' + implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'androidx.drawerlayout:drawerlayout:1.2.0' - implementation 'androidx.fragment:fragment:1.6.0' + implementation 'androidx.fragment:fragment-ktx:1.8.3' implementation 'androidx.viewpager:viewpager:1.0.0' - implementation 'com.google.android.material:material:1.0.0' + implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'com.getbase:floatingactionbutton:1.10.1' // Backup implementation project(path: ':backup-api') - def work_version = "2.4.0" + def work_version = "2.9.1" implementation "androidx.work:work-runtime:$work_version" implementation "androidx.work:work-runtime-ktx:$work_version" androidTestImplementation "androidx.work:work-testing:$work_version" - implementation 'androidx.sqlite:sqlite-ktx:2.3.1' - - constraints { - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") { - because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib") - } - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") { - because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib") - } - } + implementation 'androidx.sqlite:sqlite-ktx:2.4.0' } diff --git a/app/src/main/java/org/secuso/privacyfriendlycameraruler/PFTapeMeasure.kt b/app/src/main/java/org/secuso/privacyfriendlycameraruler/PFTapeMeasure.kt index 7fffa06..6bdad4b 100644 --- a/app/src/main/java/org/secuso/privacyfriendlycameraruler/PFTapeMeasure.kt +++ b/app/src/main/java/org/secuso/privacyfriendlycameraruler/PFTapeMeasure.kt @@ -15,7 +15,6 @@ class PFTapeMeasure : Application(), Configuration.Provider { BackupManager.backupRestorer = BackupRestorer() } - override fun getWorkManagerConfiguration(): Configuration { - return Configuration.Builder().setMinimumLoggingLevel(Log.INFO).build() - } + override val workManagerConfiguration = Configuration.Builder().setMinimumLoggingLevel(Log.INFO).build() + } \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlycameraruler/cameraruler/CameraActivity.java b/app/src/main/java/org/secuso/privacyfriendlycameraruler/cameraruler/CameraActivity.java index d582913..a98ea1b 100644 --- a/app/src/main/java/org/secuso/privacyfriendlycameraruler/cameraruler/CameraActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlycameraruler/cameraruler/CameraActivity.java @@ -416,6 +416,7 @@ public boolean onOptionsItemSelected(MenuItem item) { */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); InputStream stream = null; if (resultCode == Activity.RESULT_OK) { if (requestCode == PICK_IMAGE_REQUEST) { diff --git a/build.gradle b/build.gradle index 073ed9b..6c9ba76 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } - ext.kotlin_version = "1.7.20" + ext.kotlin_version = "1.9.20" dependencies { classpath 'com.android.tools.build:gradle:8.6.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" diff --git a/libs/privacy-friendly-backup-api b/libs/privacy-friendly-backup-api index 2548856..8687a8d 160000 --- a/libs/privacy-friendly-backup-api +++ b/libs/privacy-friendly-backup-api @@ -1 +1 @@ -Subproject commit 254885602ee4501a572f56306fbf2b3712cfc834 +Subproject commit 8687a8dbd170866707dc41bcb879375400ef697a From c8e4f6434a72f5330e31f5a0e3f7c2c4803bb4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Tue, 15 Oct 2024 14:15:50 +0200 Subject: [PATCH 4/8] Add github workflows --- .github/workflows/android-test.yml | 99 ++++++++++++++++++++++++++++++ .github/workflows/changelog.yml | 3 +- .github/workflows/ci.yml | 67 ++++++++++++++++++++ 3 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/android-test.yml create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/android-test.yml b/.github/workflows/android-test.yml new file mode 100644 index 0000000..e499e2f --- /dev/null +++ b/.github/workflows/android-test.yml @@ -0,0 +1,99 @@ +name: Android Emulator Tests +on: [ push, pull_request ] + +jobs: + check-if-tests-exist: + runs-on: ubuntu-latest + outputs: + status: ${{ steps.check-androidTest.outputs.NOT_EMPTY }} + min-sdk-version: ${{ steps.get-sdk-version.outputs.MIN_SDK_VERSION }} + target-sdk-version: ${{ steps.get-sdk-version.outputs.TARGET_SDK_VERSION }} + app-id: ${{ steps.get-app-id.outputs.APP_ID }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: "recursive" + - name: Check if androidTest folder is not empty + run: | + echo "NOT_EMPTY=$([ "$(ls -A app/src/androidTest)" ] && echo 'true' || echo 'false')" + echo "NOT_EMPTY=$([ "$(ls -A app/src/androidTest)" ] && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT + id: check-androidTest + - name: Get min and target sdk + if: steps.check-androidTest.outputs.NOT_EMPTY == 'true' + id: get-sdk-version + run: | + echo "MIN_SDK_VERSION=$(cat app/build.gradle | grep minSdkVersion | rev | cut -d' ' -f 1 | rev)" >> $GITHUB_OUTPUT + echo "TARGET_SDK_VERSION=$(cat app/build.gradle | grep targetSdkVersion | rev | cut -d' ' -f 1 | rev)" >> $GITHUB_OUTPUT + - name: Get app ID + id: get-app-id + run: | + echo "APP_ID=$(cat app/build.gradle | grep applicationId | rev | cut -d' ' -f 1 | rev | tr -d '"')" >> $GITHUB_OUTPUT + + test: + needs: check-if-tests-exist + if: needs.check-if-tests-exist.outputs.status == 'true' + runs-on: ubuntu-latest + strategy: + matrix: + api-level: [34, "${{ needs.check-if-tests-exist.outputs.min-sdk-version }}", "${{ needs.check-if-tests-exist.outputs.target-sdk-version }}"] + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Enable KVM group perms + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Gradle cache + uses: gradle/gradle-build-action@v3 + + - name: AVD cache + uses: actions/cache@v4 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-${{ matrix.api-level }} + + - name: Set up JDK environment + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 17 + + - name: create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + target: ${{ matrix.api-level >= 30 && 'google_apis' || 'default' }} + arch: ${{ matrix.api-level < 21 && 'x86' || 'x86_64' }} + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: false + script: echo "Generated AVD snapshot for caching." + + - name: Run connected tests + uses: ReactiveCircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + target: ${{ matrix.api-level >= 30 && 'google_apis' || 'default' }} + arch: ${{ matrix.api-level < 21 && 'x86' || 'x86_64' }} + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: true + script: | + adb uninstall ${{needs.check-if-tests-exist.outputs.app-id}} || true + adb uninstall ${{needs.check-if-tests-exist.outputs.app-id}}.test || true + adb uninstall ${{needs.check-if-tests-exist.outputs.app-id}}.androidTest || true + chmod +x gradlew + ./gradlew :app:connectedCheck --stacktrace + adb uninstall ${{needs.check-if-tests-exist.outputs.app-id}} || true + adb uninstall ${{needs.check-if-tests-exist.outputs.app-id}}.test || true + adb uninstall ${{needs.check-if-tests-exist.outputs.app-id}}.androidTest || true diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index ad4099a..6e50db1 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -2,7 +2,7 @@ name: Changelog Generation on: release: - types: [published, released] + types: [published] workflow_dispatch: jobs: @@ -16,6 +16,7 @@ jobs: - uses: rhysd/changelog-from-release/action@v3 with: file: CHANGELOG.md + pull_request: true github_token: ${{ secrets.GITHUB_TOKEN }} commit_summary_template: 'update changelog for %s changes' args: -l 2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fe5b0a9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,67 @@ +name: Continuous Integration +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: "recursive" + + - name: Set up JDK environment + uses: actions/setup-java@v3 + with: + distribution: "zulu" + java-version: 17 + + - name: Make gradlew executable + run: chmod +x ./gradlew + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Run local unit tests + run: bash ./gradlew test --stacktrace + + build: + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: "recursive" + + - name: Set up JDK environment + uses: actions/setup-java@v3 + with: + distribution: "zulu" + java-version: 17 + + - name: Make gradlew executable + run: chmod +x ./gradlew + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Run lint check + run: bash ./gradlew lint + + - name: Upload lint result + uses: actions/upload-artifact@v4 + with: + name: lint-results-debug + path: app/build/reports/lint-results-debug.html + + - name: Build the app + run: bash ./gradlew build --stacktrace + + - name: Build debug apk + run: bash ./gradlew assembleDebug + + - name: Upload debug apk + uses: actions/upload-artifact@v4 + with: + name: debug-apk + path: app/build/outputs/apk/debug/*.apk \ No newline at end of file From 4a82b0b5055d076fc1c36e73d22f84b6d38f7740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Tue, 15 Oct 2024 14:16:09 +0200 Subject: [PATCH 5/8] Remove `ApplicationTest.java` --- .../privacyfriendlycameraruler/ApplicationTest.java | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 app/src/androidTest/java/org/secuso/privacyfriendlycameraruler/ApplicationTest.java diff --git a/app/src/androidTest/java/org/secuso/privacyfriendlycameraruler/ApplicationTest.java b/app/src/androidTest/java/org/secuso/privacyfriendlycameraruler/ApplicationTest.java deleted file mode 100644 index d705ae3..0000000 --- a/app/src/androidTest/java/org/secuso/privacyfriendlycameraruler/ApplicationTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.secuso.privacyfriendlycameraruler; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } -} \ No newline at end of file From 724a74e50c20cbd01f48cbdf57a32ca1a7e0f525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Tue, 15 Oct 2024 16:35:35 +0200 Subject: [PATCH 6/8] Fix gallery access for API > 33 - Fix #24 --- app/src/main/AndroidManifest.xml | 3 +- .../cameraruler/CameraActivity.java | 52 +++++++++++++------ app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 866bdf7..475f3ac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,7 +11,8 @@ android:xlargeScreens="true" /> - + = Build.VERSION_CODES.M) { - // check if we have the permission we need -> if not request it and turn on the light afterwards - if (ContextCompat.checkSelfPermission(thisActivity, - Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(thisActivity, - new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 0); - return; - } - } - Intent galleryIntent = new Intent(); - galleryIntent.setType("image/*"); - galleryIntent.setAction(Intent.ACTION_GET_CONTENT); - galleryIntent.addCategory(Intent.CATEGORY_OPENABLE); - startActivityForResult(galleryIntent, PICK_IMAGE_REQUEST); + openImagePicker(); } }); @@ -258,6 +247,25 @@ public void onClick(View view) { overridePendingTransition(0, 0); } + private void openImagePicker() { + Intent getIntent = new Intent(Intent.ACTION_GET_CONTENT); + getIntent.setType("image/*"); + Intent pickIntent = new Intent(Intent.ACTION_PICK); + pickIntent.setDataAndType(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); + Intent chooserIntent = Intent.createChooser(getIntent, getResources().getString(R.string.select_image_from_gallery)); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{pickIntent}); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSION_READ_EXTERNAL_STORAGE_REQUEST); + } else { + startActivityForResult(chooserIntent, PICK_IMAGE_REQUEST); + } + } else { + startActivityForResult(chooserIntent, PICK_IMAGE_REQUEST); + } + } + @Override public boolean onTouchEvent(MotionEvent event) { int touchPoint = drawView.clickInTouchpoint(event); @@ -407,6 +415,20 @@ public boolean onOptionsItemSelected(MenuItem item) { return false; } + + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == PERMISSION_READ_EXTERNAL_STORAGE_REQUEST) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + openImagePicker(); + } else { + Toast.makeText(this, "storage permission denied", Toast.LENGTH_LONG).show(); + } + } + } + /** * Receive response from external camera and gallery apps. * @@ -419,7 +441,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); InputStream stream = null; if (resultCode == Activity.RESULT_OK) { - if (requestCode == PICK_IMAGE_REQUEST) { + if (requestCode == PICK_IMAGE_REQUEST && data.getData() != null) { try { if (photo != null) { photo.recycle(); diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index caae844..01268e4 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -270,4 +270,5 @@ Sie können einstellen, welche Maßeinheit im Kameralineal benutzt wird. Sie können bestimmte Kategorien von vordefinierten Referenzobjekten ausblenden lassen. Diese werden dann nicht im Menü bei der Referenzobjektwahl angezeigt. Hier können Sie (bis zu 10) nutzerdefinierte Referenzobjekte anlegen und verwalten. + Bild aus der Galerie auswählen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d485007..89fe4cc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -274,4 +274,5 @@ You can set the units of measurements to be used in the camera ruler. You can disable certain types of predefined reference objects. They won\'t show up in the menu during reference object choice. Here you can define and manage user defined reference objects (up to 10). + Select image from gallery From 38206d01b21b40ecbca0486e6f1a8f5cd1e5a949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Tue, 15 Oct 2024 16:41:35 +0200 Subject: [PATCH 7/8] Change lint severity for `MissingTranslation` to warning --- app/build.gradle | 3 +++ app/lint.xml | 10 ++++++++++ 2 files changed, 13 insertions(+) create mode 100644 app/lint.xml diff --git a/app/build.gradle b/app/build.gradle index 1377b55..d14eff5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,6 +19,9 @@ android { productFlavors { } namespace 'org.secuso.privacyfriendlycameraruler' + lint { + lintConfig = file("lint.xml") + } kotlin { jvmToolchain(17) } diff --git a/app/lint.xml b/app/lint.xml new file mode 100644 index 0000000..1b66a25 --- /dev/null +++ b/app/lint.xml @@ -0,0 +1,10 @@ + + + + + + + + + + From 7a12dc9f8b85c9738d72a17f5dcecabeb1b929df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20L=C3=A4nge?= Date: Tue, 15 Oct 2024 16:48:19 +0200 Subject: [PATCH 8/8] Bump version to 1.1.1 (5) --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d14eff5..36467a6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { minSdkVersion 21 compileSdk 34 targetSdkVersion 34 - versionCode 4 - versionName '1.1' + versionCode 5 + versionName '1.1.1' } buildTypes { release {