Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 51 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,22 @@ on:
push:
tags:
- 'v*'

pull_request:
types: [labeled, synchronize] # 'synchronize' triggers on new commits

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' &&
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
pull-requests: write # Required to post the comment in the PR history

Comment thread
alexdremov marked this conversation as resolved.
steps:
- name: Checkout code
Expand All @@ -29,12 +38,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
Expand All @@ -43,13 +50,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})`
})
63 changes: 57 additions & 6 deletions app/src/main/java/com/alexdremov/notate/ui/OnyxCanvasView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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
Expand Down Expand Up @@ -42,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
Expand Down Expand Up @@ -737,13 +739,62 @@ class OnyxCanvasView
}
}

private fun getPhysicalDisplaySize(): Pair<Int, Int>? {
val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager ?: 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
}

private fun scaleRectForEpd(logicalRect: Rect): Rect {
val metrics = context.resources.displayMetrics
val physicalSize = getPhysicalDisplaySize() ?: return logicalRect
val (physicalWidth, physicalHeight) = physicalSize
val normalMapping =
physicalWidth / metrics.widthPixels.toFloat() to
physicalHeight / metrics.heightPixels.toFloat()
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 (abs(normalMapping.first - normalMapping.second) <=
abs(transposedMapping.first - transposedMapping.second)
) {
normalMapping
} else {
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
// the limit/exclusion regions after scaling.
return Rect(
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(),
)
}

fun setExclusionRects(rects: List<Rect>) {
exclusionRects.clear()
exclusionRects.addAll(rects)

val limit = Rect()
getGlobalVisibleRect(limit)

// Scale the bounds to raw hardware pixels
val hardwareLimit = scaleRectForEpd(limit)
val hardwareExclusions = exclusionRects.map { scaleRectForEpd(it) }

touchHelper?.let {
val limit = Rect()
getLocalVisibleRect(limit)
it.setLimitRect(limit, exclusionRects)
it.setLimitRect(hardwareLimit, hardwareExclusions)
}
}

Expand Down Expand Up @@ -864,10 +915,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)
Expand Down
Loading