Skip to content
Merged
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
122 changes: 58 additions & 64 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
name: CI/CD — build and publish release
name: Release — build & publish

on:
pull_request:
branches: [ main ]
push:
branches: [master]

jobs:
build-and-release:
name: Build APK & AAB and publish Release
release:
name: Build signed APK/AAB & publish
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout repository
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK 17
Expand All @@ -27,73 +27,67 @@ jobs:
path: |
~/.gradle/caches
~/.gradle/wrapper
.gradle
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*','**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: ${{ runner.os }}-gradle-

- name: Install Android SDK command-line tools and platforms
- name: Accept SDK licenses & install platform
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends wget unzip
mkdir -p $HOME/Android/Sdk/cmdline-tools
wget -q https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -O /tmp/cmdline-tools.zip
unzip -q /tmp/cmdline-tools.zip -d /tmp/cmdline-tools
mkdir -p $HOME/Android/Sdk/cmdline-tools/latest
mv /tmp/cmdline-tools/cmdline-tools/* $HOME/Android/Sdk/cmdline-tools/latest/
export ANDROID_HOME=$HOME/Android/Sdk
export PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATH
yes | sdkmanager --licenses
sdkmanager "platform-tools" "platforms;android-33" "build-tools;33.0.2" "platforms;android-35" "build-tools;35.0.0"
shell: bash
yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null 2>&1
$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager \
--channel=3 \
"platforms;android-37" \
"build-tools;35.0.0" \
> /dev/null 2>&1 || true

- name: Ensure gradlew is executable
- name: Make gradlew executable
run: chmod +x ./gradlew

- name: Extract versionName from Gradle
id: get_version
- name: Extract version
id: version
run: |
VERSION=$(grep -Po "versionName\s+'\\K[^']+" app/build.gradle | head -n1 || true)
if [ -z "$VERSION" ]; then echo "Failed to extract versionName"; exit 1; fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
CODE=$(grep -oP '(?<=def appVersionCode = )\d+' app/build.gradle | head -n1)
echo "code=$CODE" >> $GITHUB_OUTPUT
echo "name=2.5.$CODE" >> $GITHUB_OUTPUT

- name: Build release APK and AAB
id: build_release
- name: Decode keystore
run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > /tmp/release.jks

- name: Build signed release APK & AAB
env:
KEYSTORE_PATH: /tmp/release.jks
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: ./gradlew assembleRelease bundleRelease --no-daemon

- name: Collect artifacts
run: |
mkdir -p artifacts
cp app/build/outputs/apk/release/*.apk \
"artifacts/MyNotes-v${{ steps.version.outputs.name }}.apk"
cp app/build/outputs/bundle/release/*.aab \
"artifacts/MyNotes-v${{ steps.version.outputs.name }}.aab"

- name: Generate release notes from commits
id: notes
run: |
./gradlew clean assembleRelease bundleRelease --no-daemon
mkdir -p release_artifacts
APK_PATH=$(ls app/build/outputs/apk/release/*.apk 2>/dev/null | head -n1 || true)
AAB_PATH=$(ls app/build/outputs/bundle/release/*.aab 2>/dev/null | head -n1 || true)
VERSION=${{ steps.get_version.outputs.version }}
if [ -n "$APK_PATH" ]; then cp "$APK_PATH" "release_artifacts/MyNote${VERSION}.apk"; fi
if [ -n "$AAB_PATH" ]; then cp "$AAB_PATH" "release_artifacts/MyNote${VERSION}.aab"; fi
# expose outputs for later steps
echo "apk_path=$( [ -f release_artifacts/MyNote${VERSION}.apk ] && echo release_artifacts/MyNote${VERSION}.apk || echo )" >> $GITHUB_OUTPUT
echo "aab_path=$( [ -f release_artifacts/MyNote${VERSION}.aab ] && echo release_artifacts/MyNote${VERSION}.aab || echo )" >> $GITHUB_OUTPUT
shell: bash
PREV=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -n "$PREV" ]; then
NOTES=$(git log "$PREV"..HEAD --pretty=format:"- %s" --no-merges)
else
NOTES=$(git log --pretty=format:"- %s" --no-merges | head -30)
fi
echo "notes<<EOF" >> $GITHUB_OUTPUT
echo "$NOTES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Create or update GitHub Release
id: create_release
- name: Create GitHub Release
uses: ncipollo/release-action@v1
with:
tag: v${{ steps.get_version.outputs.version }}
name: MyNote${{ steps.get_version.outputs.version }}
tag: v${{ steps.version.outputs.name }}
name: "MyNotes v${{ steps.version.outputs.name }}"
body: ${{ steps.notes.outputs.notes }}
artifacts: "artifacts/*"
artifactErrorsFailBuild: true
makeLatest: true
token: ${{ secrets.GITHUB_TOKEN }}

- name: Upload APK to Release
if: steps.build_release.outputs.apk_path != ''
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ steps.build_release.outputs.apk_path }}
asset_name: MyNote${{ steps.get_version.outputs.version }}.apk
asset_content_type: application/vnd.android.package-archive

- name: Upload AAB to Release
if: steps.build_release.outputs.aab_path != ''
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ${{ steps.build_release.outputs.aab_path }}
asset_name: MyNote${{ steps.get_version.outputs.version }}.aab
asset_content_type: application/octet-stream
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CI — build check

on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
build:
name: Build debug & run tests
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'

- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: ${{ runner.os }}-gradle-

- name: Accept SDK licenses & install platform
run: |
yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null 2>&1
$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager \
--channel=3 \
"platforms;android-37" \
"build-tools;35.0.0" \
> /dev/null 2>&1 || true

- name: Make gradlew executable
run: chmod +x ./gradlew

- name: Build debug
run: ./gradlew assembleDebug --no-daemon

- name: Run unit tests
run: ./gradlew test --no-daemon
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
# CHANGELOG

## [2.5.45] - 10.05.2026

**New**

- **Reminders:** You can now set a date and time reminder on any note. Tap the bell icon in the
note editor toolbar or open the note options menu to set, edit, or delete a reminder. When the
time comes, you'll receive a notification — tap it to open the note directly. Reminders support
repeat intervals (daily, weekly, monthly) and a snooze option (10 minutes, 1 hour, or tomorrow
morning). Reminders are restored automatically after a device reboot.
- **Tasks:** A new dedicated screen for managing tasks and to-dos. Create tasks with titles and
optional descriptions, organize them into color-coded categories, reorder by dragging, and mark
them as complete. Completed tasks are grouped separately and can be cleared in bulk.
- **Task reminders:** Set a date and time reminder on any active task — a notification arrives at
the chosen time and tapping it opens the task list directly. Only future times are selectable.
Reminders are restored automatically after a device reboot.

**Fixes**

- Fixed memory leaks that could occur during note editing and when navigating between screens.

**Improvements**

- **Pin notes:** You can now pin any note to keep it at the top of the list. Tap the note options
menu and select "Pin note" — pinned notes always appear first, regardless of the current sort
order. A small pin icon is displayed on pinned note cards. Tap "Unpin note" to remove the pin.
- Delete confirmation dialogs for tasks and categories now display a clear visual style
with an error-tinted icon and the item name.

## [2.4.44] - 28.01.2026

- Fixed several issues reported in the previous version
Expand Down
64 changes: 55 additions & 9 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ apply from: "$projectDir/gradle/changelog-task.gradle"


def appVersionCode = 44
def appVersionName = "2.4.${appVersionCode}"
def appVersionName = "2.5.${appVersionCode}"

def gitCommitHashProvider = providers.exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
Expand All @@ -24,7 +24,7 @@ preBuild.dependsOn(copyChangelog)


android {
compileSdkVersion 36
compileSdkVersion 37
buildFeatures {
viewBinding = true
dataBinding = true
Expand All @@ -50,11 +50,13 @@ android {
defaultConfig {
applicationId "com.pasich.mynotes"
minSdkVersion 26
targetSdkVersion 36
targetSdkVersion 37

versionCode appVersionCode
versionName appVersionName

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

javaCompileOptions {
annotationProcessorOptions {
arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
Expand All @@ -64,12 +66,25 @@ android {
buildConfigField "String", "GIT_COMMIT_HASH", "\"${gitCommitHashProvider.get()}\""
}

signingConfigs {
release {
def ksPath = System.getenv("KEYSTORE_PATH")
if (ksPath) {
storeFile file(ksPath)
storePassword System.getenv("KEYSTORE_PASSWORD")
keyAlias System.getenv("KEY_ALIAS")
keyPassword System.getenv("KEY_PASSWORD")
}
}
}

buildTypes {
release {
debuggable false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig System.getenv("KEYSTORE_PATH") ? signingConfigs.release : null
}
debug {
debuggable true
Expand All @@ -90,6 +105,24 @@ android {
lint {
abortOnError false
}
testOptions {
unitTests {
returnDefaultValues = true
}
}
}

// Fix for Windows PATH with embedded quotes breaking java.library.path in test JVM
// The Gradle worker JVM command is built by DefaultWorkerProcessBuilder which reads
// java.library.path from the daemon JVM. We sanitize it by replacing the system property.
def rawLibPath = System.getProperty("java.library.path") ?: ""
def cleanLibPath = rawLibPath.split(File.pathSeparator).findAll { !it.contains('"') }.join(File.pathSeparator)
System.setProperty("java.library.path", cleanLibPath)

tasks.withType(Test).configureEach {
def jniDebug = "${project.projectDir}/src/testDebug/jniLibs"
def jniTest = "${project.projectDir}/src/test/jniLibs"
jvmArgs "-Djava.library.path=${jniDebug}${File.pathSeparator}${jniTest}"
}

// Only execute in the release build
Expand All @@ -111,9 +144,9 @@ dependencies {
implementation 'com.google.android.material:material:1.13.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'androidx.preference:preference:1.2.1'
implementation "androidx.lifecycle:lifecycle-viewmodel:2.9.4"
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation 'androidx.activity:activity:1.11.0'
implementation "androidx.lifecycle:lifecycle-viewmodel:2.10.0"
implementation 'androidx.core:core-splashscreen:1.2.0'
implementation 'androidx.activity:activity:1.13.0'

//DI (Hilt)
implementation "com.google.dagger:hilt-android:$daggerHilt"
Expand All @@ -129,17 +162,30 @@ dependencies {

//Pay Billing
implementation 'com.google.android.play:app-update:2.1.0'
implementation 'com.android.billingclient:billing:8.0.0'
implementation 'com.android.billingclient:billing:8.3.0'

//Other lib (Gson/PhotoView)
implementation 'com.google.code.gson:gson:2.13.2'
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'androidx.exifinterface:exifinterface:1.4.1'
implementation 'androidx.exifinterface:exifinterface:1.4.2'

//Markwon https://noties.io/Markwon/docs/v4/
implementation"io.noties.markwon:core:$markwon_version"
implementation"io.noties.markwon:ext-strikethrough:$markwon_version"
implementation"io.noties.markwon:linkify:$markwon_version"


// Unit tests
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.23.0'
testImplementation 'com.google.truth:truth:1.4.2'

// Room testing (integration tests)
testImplementation "androidx.room:room-testing:$roomVersion"
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation 'androidx.test:rules:1.7.0'
androidTestImplementation 'androidx.test:runner:1.7.0'
androidTestImplementation "androidx.room:room-testing:$roomVersion"
androidTestImplementation 'org.mockito:mockito-android:5.23.0'
androidTestImplementation 'com.google.truth:truth:1.4.5'

}
1 change: 0 additions & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@

# Keep line numbers and source file for debugging
-keepattributes SourceFile,LineNumberTable
-dontobfuscate

# Keep all debug and logging related code
-assumenosideeffects class android.util.Log {
Expand Down
Loading
Loading