From 1c187776044115babfacd028b656b743fa5ec26e Mon Sep 17 00:00:00 2001 From: alexdremov Date: Thu, 7 May 2026 19:56:09 +0200 Subject: [PATCH 1/8] fix active area --- .../alexdremov/notate/ui/OnyxCanvasView.kt | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt b/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt index 28b99c5..e211692 100644 --- a/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt +++ b/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt @@ -737,13 +737,38 @@ class OnyxCanvasView } } + private fun scaleRectForEpd(context: Context, logicalRect: Rect): Rect { + val metrics = context.resources.displayMetrics + val wm = context.getSystemService(Context.WINDOW_SERVICE) as android.view.WindowManager + val realMetrics = android.util.DisplayMetrics() + wm.defaultDisplay.getRealMetrics(realMetrics) + + // Calculate the scaling ratio caused by Onyx App Optimization / DPI settings + val scaleX = realMetrics.widthPixels / metrics.widthPixels.toFloat() + val scaleY = realMetrics.heightPixels / metrics.heightPixels.toFloat() + + // Return the hardware-mapped coordinates + return Rect( + (logicalRect.left * scaleX).toInt(), + (logicalRect.top * scaleY).toInt(), + (logicalRect.right * scaleX).toInt(), + (logicalRect.bottom * scaleY).toInt() + ) + } + fun setExclusionRects(rects: List) { exclusionRects.clear() exclusionRects.addAll(rects) + + val limit = Rect() + getGlobalVisibleRect(limit) + + // Scale the bounds to raw hardware pixels + val hardwareLimit = scaleRectForEpd(context, limit) + val hardwareExclusions = exclusionRects.map { scaleRectForEpd(context, it) } + touchHelper?.let { - val limit = Rect() - getLocalVisibleRect(limit) - it.setLimitRect(limit, exclusionRects) + it.setLimitRect(hardwareLimit, hardwareExclusions) } } @@ -864,10 +889,10 @@ class OnyxCanvasView } com.alexdremov.notate.util.OnyxSystemHelper .ignoreSystemSideButton(this) - val limit = Rect() - getLocalVisibleRect(limit) + + setExclusionRects(exclusionRects.toList()) + touchHelper?.apply { - setLimitRect(limit, exclusionRects) openRawDrawing() setRawDrawingEnabled(true) setRawDrawingRenderEnabled(true) From 931ccccd04d3d3f24a46edb8348e875e64d03d3f Mon Sep 17 00:00:00 2001 From: alexdremov Date: Thu, 7 May 2026 19:56:18 +0200 Subject: [PATCH 2/8] fix active area --- .../main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt b/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt index e211692..5246f8c 100644 --- a/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt +++ b/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt @@ -737,7 +737,10 @@ class OnyxCanvasView } } - private fun scaleRectForEpd(context: Context, logicalRect: Rect): Rect { + private fun scaleRectForEpd( + context: Context, + logicalRect: Rect, + ): Rect { val metrics = context.resources.displayMetrics val wm = context.getSystemService(Context.WINDOW_SERVICE) as android.view.WindowManager val realMetrics = android.util.DisplayMetrics() @@ -752,7 +755,7 @@ class OnyxCanvasView (logicalRect.left * scaleX).toInt(), (logicalRect.top * scaleY).toInt(), (logicalRect.right * scaleX).toInt(), - (logicalRect.bottom * scaleY).toInt() + (logicalRect.bottom * scaleY).toInt(), ) } From 31f5b69ef8c0c30904b37b08b7bec5a7ffc1b888 Mon Sep 17 00:00:00 2001 From: Aleksandr Dremov Date: Thu, 7 May 2026 20:38:04 +0200 Subject: [PATCH 3/8] Update release.yml --- .github/workflows/release.yml | 51 +++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f1402f..d254e4c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,13 +4,17 @@ on: push: tags: - 'v*' - + pull_request: + types: [labeled] # Triggers only when a label is applied jobs: build: + # Run for tag pushes OR if the 'build-apk' label is applied to a PR + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.label.name == 'build-apk') runs-on: ubuntu-latest permissions: contents: write + pull-requests: write # Required to post the comment in the PR history steps: - name: Checkout code @@ -29,12 +33,10 @@ jobs: - name: Decode Keystore env: KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} - KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }} run: | echo "$KEYSTORE_BASE64" | base64 --decode > release.keystore # Create keystore.properties - # We point to ../release.keystore because this property is read by the :app module echo "storeFile=../release.keystore" > keystore.properties echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> keystore.properties echo "storePassword=${{ secrets.STORE_PASSWORD }}" >> keystore.properties @@ -43,13 +45,52 @@ jobs: - name: Build Release APK run: ./gradlew assembleRelease + - name: Determine APK Name + id: apk-name + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo "filename=notate-pr-${{ github.event.pull_request.number }}.apk" >> $GITHUB_OUTPUT + else + echo "filename=notate-${{ github.ref_name }}.apk" >> $GITHUB_OUTPUT + fi + - name: Rename APK - run: mv app/build/outputs/apk/release/app-release.apk notate-${{ github.ref_name }}.apk + run: mv app/build/outputs/apk/release/app-release.apk ${{ steps.apk-name.outputs.filename }} + # --------------------------------------------------------- + # PATH A: TAG PUSH -> Create GitHub Release + # --------------------------------------------------------- - name: Create Release + if: github.event_name == 'push' uses: softprops/action-gh-release@v1 with: - files: notate-${{ github.ref_name }}.apk + files: ${{ steps.apk-name.outputs.filename }} draft: true prerelease: false generate_release_notes: true + + # --------------------------------------------------------- + # PATH B: PULL REQUEST -> Upload Artifact & Comment + # --------------------------------------------------------- + - name: Upload APK as Artifact + if: github.event_name == 'pull_request' + id: upload-artifact + uses: actions/upload-artifact@v4 + with: + name: PR-Release-APK-${{ github.event.pull_request.number }} + path: ${{ steps.apk-name.outputs.filename }} + retention-days: 7 + + - name: Comment on PR with Artifact Link + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + env: + ARTIFACT_URL: ${{ steps.upload-artifact.outputs.artifact-url }} + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `✅ **APK Build Successful!**\n\nAn admin triggered a release build for this PR. You can download the generated APK here: [Download APK](${process.env.ARTIFACT_URL})` + }) From 1362c3fbf75fa9e3ce29e4cd749beaa7d5cf3852 Mon Sep 17 00:00:00 2001 From: Aleksandr Dremov Date: Thu, 7 May 2026 20:39:33 +0200 Subject: [PATCH 4/8] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../java/com/alexdremov/notate/ui/OnyxCanvasView.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt b/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt index 5246f8c..c5304fd 100644 --- a/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt +++ b/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt @@ -750,12 +750,13 @@ class OnyxCanvasView val scaleX = realMetrics.widthPixels / metrics.widthPixels.toFloat() val scaleY = realMetrics.heightPixels / metrics.heightPixels.toFloat() - // Return the hardware-mapped coordinates + // Return the hardware-mapped coordinates, rounding outward to avoid shrinking + // the limit/exclusion regions after scaling. return Rect( - (logicalRect.left * scaleX).toInt(), - (logicalRect.top * scaleY).toInt(), - (logicalRect.right * scaleX).toInt(), - (logicalRect.bottom * scaleY).toInt(), + kotlin.math.floor(logicalRect.left * scaleX.toDouble()).toInt(), + kotlin.math.floor(logicalRect.top * scaleY.toDouble()).toInt(), + kotlin.math.ceil(logicalRect.right * scaleX.toDouble()).toInt(), + kotlin.math.ceil(logicalRect.bottom * scaleY.toDouble()).toInt(), ) } From 0817e8fd5f46e9d65d52d67d8996330cc33a296b Mon Sep 17 00:00:00 2001 From: Aleksandr Dremov Date: Thu, 7 May 2026 20:46:23 +0200 Subject: [PATCH 5/8] Update release.yml --- .github/workflows/release.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d254e4c..70b5b49 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,12 +5,14 @@ on: tags: - 'v*' pull_request: - types: [labeled] # Triggers only when a label is applied + types: [labeled, synchronize] # 'synchronize' triggers on new commits jobs: build: - # Run for tag pushes OR if the 'build-apk' label is applied to a PR - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.label.name == 'build-apk') + # Runs if it's a tag push OR if the PR currently has the 'build-apk' label applied + if: > + github.event_name == 'push' || + (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'build-apk')) runs-on: ubuntu-latest permissions: contents: write From 792a42622e126a26ed914363339cc295a817aab3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 13:42:29 +0000 Subject: [PATCH 6/8] Use physical display mode for EPD active-area scaling Agent-Logs-Url: https://github.com/alexdremov/notate/sessions/4a4cc63c-0006-4e57-bd82-70d37340a024 Co-authored-by: alexdremov <25539425+alexdremov@users.noreply.github.com> --- .../alexdremov/notate/ui/OnyxCanvasView.kt | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt b/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt index c5304fd..3a7cc03 100644 --- a/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt +++ b/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt @@ -8,9 +8,11 @@ import android.graphics.Paint import android.graphics.Path import android.graphics.Rect import android.graphics.RectF +import android.hardware.display.DisplayManager import android.os.Looper import android.util.AttributeSet import android.view.Choreographer +import android.view.Display import android.view.MotionEvent import android.view.SurfaceHolder import android.view.SurfaceView @@ -737,18 +739,30 @@ class OnyxCanvasView } } - private fun scaleRectForEpd( - context: Context, - logicalRect: Rect, - ): Rect { - val metrics = context.resources.displayMetrics - val wm = context.getSystemService(Context.WINDOW_SERVICE) as android.view.WindowManager - val realMetrics = android.util.DisplayMetrics() - wm.defaultDisplay.getRealMetrics(realMetrics) + private fun getPhysicalDisplaySize(): Pair? { + val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager ?: return null + val display = displayManager.getDisplay(Display.DEFAULT_DISPLAY) ?: return null + val mode = display.mode ?: return null + return mode.physicalWidth to mode.physicalHeight + } - // Calculate the scaling ratio caused by Onyx App Optimization / DPI settings - val scaleX = realMetrics.widthPixels / metrics.widthPixels.toFloat() - val scaleY = realMetrics.heightPixels / metrics.heightPixels.toFloat() + private fun scaleRectForEpd(logicalRect: Rect): Rect { + val metrics = context.resources.displayMetrics + val physicalSize = getPhysicalDisplaySize() ?: return logicalRect + val (physicalWidth, physicalHeight) = physicalSize + val optionA = + physicalWidth / metrics.widthPixels.toFloat() to + physicalHeight / metrics.heightPixels.toFloat() + val optionB = + physicalHeight / metrics.widthPixels.toFloat() to + physicalWidth / metrics.heightPixels.toFloat() + val (scaleX, scaleY) = + if (kotlin.math.abs(optionA.first - optionA.second) <= kotlin.math.abs(optionB.first - optionB.second)) { + optionA + } else { + optionB + } + if (scaleX <= 1f && scaleY <= 1f) return logicalRect // Return the hardware-mapped coordinates, rounding outward to avoid shrinking // the limit/exclusion regions after scaling. @@ -768,8 +782,8 @@ class OnyxCanvasView getGlobalVisibleRect(limit) // Scale the bounds to raw hardware pixels - val hardwareLimit = scaleRectForEpd(context, limit) - val hardwareExclusions = exclusionRects.map { scaleRectForEpd(context, it) } + val hardwareLimit = scaleRectForEpd(limit) + val hardwareExclusions = exclusionRects.map { scaleRectForEpd(it) } touchHelper?.let { it.setLimitRect(hardwareLimit, hardwareExclusions) From f160355efdeacf4086c7b5ac0f4c8abd9091c85f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 13:45:46 +0000 Subject: [PATCH 7/8] Refine EPD scaling mapping with physical display mode lookup Agent-Logs-Url: https://github.com/alexdremov/notate/sessions/4a4cc63c-0006-4e57-bd82-70d37340a024 Co-authored-by: alexdremov <25539425+alexdremov@users.noreply.github.com> --- .../alexdremov/notate/ui/OnyxCanvasView.kt | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt b/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt index 3a7cc03..42343bf 100644 --- a/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt +++ b/app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt @@ -12,7 +12,6 @@ import android.hardware.display.DisplayManager import android.os.Looper import android.util.AttributeSet import android.view.Choreographer -import android.view.Display import android.view.MotionEvent import android.view.SurfaceHolder import android.view.SurfaceView @@ -44,6 +43,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import java.util.LinkedList +import kotlin.math.abs class OnyxCanvasView @JvmOverloads @@ -741,8 +741,11 @@ class OnyxCanvasView private fun getPhysicalDisplaySize(): Pair? { val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager ?: return null - val display = displayManager.getDisplay(Display.DEFAULT_DISPLAY) ?: return null - val mode = display.mode ?: return null + val targetDisplayId = display?.displayId + val targetDisplay = targetDisplayId?.let { displayManager.getDisplay(it) } + val fallbackDisplay = displayManager.displays.firstOrNull() + val activeDisplay = targetDisplay ?: fallbackDisplay ?: return null + val mode = activeDisplay.mode ?: return null return mode.physicalWidth to mode.physicalHeight } @@ -750,18 +753,23 @@ class OnyxCanvasView val metrics = context.resources.displayMetrics val physicalSize = getPhysicalDisplaySize() ?: return logicalRect val (physicalWidth, physicalHeight) = physicalSize - val optionA = + val normalMapping = physicalWidth / metrics.widthPixels.toFloat() to physicalHeight / metrics.heightPixels.toFloat() - val optionB = + val transposedMapping = physicalHeight / metrics.widthPixels.toFloat() to physicalWidth / metrics.heightPixels.toFloat() + // Pick the axis mapping where X/Y scales are closest. With correct width↔width and + // height↔height pairing, both axes should need nearly the same scale factor. val (scaleX, scaleY) = - if (kotlin.math.abs(optionA.first - optionA.second) <= kotlin.math.abs(optionB.first - optionB.second)) { - optionA + if (abs(normalMapping.first - normalMapping.second) <= + abs(transposedMapping.first - transposedMapping.second) + ) { + normalMapping } else { - optionB + transposedMapping } + // Scales at or below 1 mean no hardware-space expansion is needed. if (scaleX <= 1f && scaleY <= 1f) return logicalRect // Return the hardware-mapped coordinates, rounding outward to avoid shrinking From 72280c1dae2fbb82e4f9180e8d529fdc7d8d1810 Mon Sep 17 00:00:00 2001 From: Aleksandr Dremov Date: Fri, 8 May 2026 15:59:00 +0200 Subject: [PATCH 8/8] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 70b5b49..250735b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,9 +10,12 @@ on: jobs: build: # Runs if it's a tag push OR if the PR currently has the 'build-apk' label applied + # for a same-repository PR where signing secrets and PR write permissions are available if: > github.event_name == 'push' || - (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'build-apk')) + (github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository && + contains(github.event.pull_request.labels.*.name, 'build-apk')) runs-on: ubuntu-latest permissions: contents: write