Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b6bf04954 | ||
|
|
7644f5bf76 | ||
|
|
300dc67a7c | ||
|
|
c42a9c84d7 | ||
|
|
7942b73af3 | ||
|
|
b4d868434f | ||
|
|
ad5fd0a313 | ||
|
|
80a46e0e49 | ||
|
|
1338da9dde | ||
|
|
0c2d069443 | ||
|
|
70efc13ea4 | ||
|
|
55401977e3 |
152
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
152
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
name: 🐛 Bug Report / Fehlerbericht
|
||||||
|
description: Melde einen Fehler in der App / Report a bug in the app
|
||||||
|
title: "[BUG] "
|
||||||
|
labels: ["bug"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Danke für deinen Bug Report! / Thanks for reporting a bug!
|
||||||
|
|
||||||
|
Bitte fülle alle relevanten Felder aus. / Please fill out all relevant fields.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: 🐛 Beschreibung / Description
|
||||||
|
description: Beschreibe den Fehler kurz und präzise / Describe the bug briefly and precisely
|
||||||
|
placeholder: "z.B. Auto-Sync funktioniert nicht mehr nach App-Update / e.g. Auto-sync stopped working after app update"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: android-version
|
||||||
|
attributes:
|
||||||
|
label: 📱 Android Version
|
||||||
|
description: Welche Android Version verwendest du? / Which Android version are you using?
|
||||||
|
options:
|
||||||
|
- Android 14
|
||||||
|
- Android 13
|
||||||
|
- Android 12
|
||||||
|
- Android 11
|
||||||
|
- Android 10
|
||||||
|
- Android 9
|
||||||
|
- Android 8.1
|
||||||
|
- Android 8.0
|
||||||
|
- Andere / Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: app-version
|
||||||
|
attributes:
|
||||||
|
label: 📲 App Version
|
||||||
|
description: Welche Version der App verwendest du? (Einstellungen → Über) / Which app version? (Settings → About)
|
||||||
|
placeholder: "z.B. / e.g. v1.1.0"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: device
|
||||||
|
attributes:
|
||||||
|
label: 📱 Gerät / Device
|
||||||
|
description: Welches Gerät verwendest du? / Which device are you using?
|
||||||
|
placeholder: "z.B. Samsung Galaxy S21, Google Pixel 7, etc."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: 🔄 Schritte zum Reproduzieren / Steps to Reproduce
|
||||||
|
description: Wie kann der Fehler reproduziert werden? / How can the bug be reproduced?
|
||||||
|
placeholder: |
|
||||||
|
1. Öffne die App / Open the app
|
||||||
|
2. Gehe zu Einstellungen / Go to settings
|
||||||
|
3. Klicke auf ... / Click on ...
|
||||||
|
4. Fehler tritt auf / Bug occurs
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: ✅ Erwartetes Verhalten / Expected Behavior
|
||||||
|
description: Was sollte passieren? / What should happen?
|
||||||
|
placeholder: "z.B. Notizen sollten alle 30 Min synchronisiert werden / e.g. Notes should sync every 30 min"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: actual
|
||||||
|
attributes:
|
||||||
|
label: ❌ Tatsächliches Verhalten / Actual Behavior
|
||||||
|
description: Was passiert stattdessen? / What happens instead?
|
||||||
|
placeholder: "z.B. Sync funktioniert nicht, keine Notification / e.g. Sync doesn't work, no notification"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: sync-enabled
|
||||||
|
attributes:
|
||||||
|
label: <EFBFBD> Auto-Sync aktiviert? / Auto-Sync enabled?
|
||||||
|
options:
|
||||||
|
- "Ja / Yes"
|
||||||
|
- "Nein / No"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: battery-optimization
|
||||||
|
attributes:
|
||||||
|
label: 🔋 Akku-Optimierung deaktiviert? / Battery optimization disabled?
|
||||||
|
description: Einstellungen → Apps → Simple Notes → Akku → Nicht optimieren / Settings → Apps → Simple Notes → Battery → Don't optimize
|
||||||
|
options:
|
||||||
|
- "Ja, deaktiviert / Yes, disabled"
|
||||||
|
- "Nein, noch optimiert / No, still optimized"
|
||||||
|
- "Weiß nicht / Don't know"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: server-config
|
||||||
|
attributes:
|
||||||
|
label: 🌐 Server-Konfiguration / Server Configuration
|
||||||
|
description: Falls relevant / If relevant (KEINE Passwörter! / NO passwords!)
|
||||||
|
placeholder: |
|
||||||
|
- Server läuft lokal / Server runs locally
|
||||||
|
- Docker auf Raspberry Pi / Docker on Raspberry Pi
|
||||||
|
- Gleiche WiFi / Same WiFi
|
||||||
|
- Server-IP: 192.168.x.x (erste 3 Zahlen reichen / first 3 numbers sufficient)
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: 📋 Logs / Screenshots
|
||||||
|
description: |
|
||||||
|
Falls vorhanden: Screenshots oder LogCat Output / If available: Screenshots or LogCat output
|
||||||
|
|
||||||
|
LogCat Filter: `adb logcat -s SyncWorker NetworkMonitor WebDavSyncService`
|
||||||
|
placeholder: "Füge hier Logs oder Screenshots ein / Paste logs or screenshots here"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: additional
|
||||||
|
attributes:
|
||||||
|
label: 🔧 Zusätzliche Informationen / Additional Context
|
||||||
|
description: Gibt es noch etwas, das wir wissen sollten? / Is there anything else we should know?
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: ✅ Checklist
|
||||||
|
options:
|
||||||
|
- label: Ich habe die [Troubleshooting-Sektion](https://github.com/inventory69/simple-notes-sync/blob/main/QUICKSTART.md#troubleshooting) gelesen / I have read the troubleshooting section
|
||||||
|
required: false
|
||||||
|
- label: Ich habe "Verbindung testen" in den Einstellungen probiert / I have tried "Test connection" in settings
|
||||||
|
required: false
|
||||||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
blank_issues_enabled: true
|
||||||
|
contact_links:
|
||||||
|
- name: 📖 Dokumentation / Documentation
|
||||||
|
url: https://github.com/inventory69/simple-notes-sync/blob/main/README.md
|
||||||
|
about: Schau zuerst in die Dokumentation / Check documentation first
|
||||||
|
- name: 🚀 Quick Start Guide
|
||||||
|
url: https://github.com/inventory69/simple-notes-sync/blob/main/QUICKSTART.md
|
||||||
|
about: Schritt-für-Schritt Anleitung / Step-by-step guide
|
||||||
|
- name: 🐛 Troubleshooting
|
||||||
|
url: https://github.com/inventory69/simple-notes-sync/blob/main/QUICKSTART.md#troubleshooting
|
||||||
|
about: Häufige Probleme und Lösungen / Common issues and solutions
|
||||||
84
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
84
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
name: 💡 Feature Request / Feature-Wunsch
|
||||||
|
description: Schlage eine neue Funktion vor / Suggest a new feature
|
||||||
|
title: "[FEATURE] "
|
||||||
|
labels: ["enhancement"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Danke für deinen Feature-Vorschlag! / Thanks for your feature suggestion!
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: feature-description
|
||||||
|
attributes:
|
||||||
|
label: 💡 Feature-Beschreibung / Feature Description
|
||||||
|
description: Was möchtest du hinzugefügt haben? / What would you like to be added?
|
||||||
|
placeholder: "z.B. Notizen sollten Markdown-Formatierung unterstützen / e.g. Notes should support markdown formatting"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: 🎯 Problem / Motivation
|
||||||
|
description: Welches Problem würde dieses Feature lösen? / What problem would this feature solve?
|
||||||
|
placeholder: "z.B. Ich möchte Code-Snippets und Listen in meinen Notizen formatieren / e.g. I want to format code snippets and lists in my notes"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: solution
|
||||||
|
attributes:
|
||||||
|
label: 📝 Vorgeschlagene Lösung / Proposed Solution
|
||||||
|
description: Wie könnte das Feature funktionieren? / How could the feature work?
|
||||||
|
placeholder: "z.B. Markdown-Editor mit Live-Preview / e.g. Markdown editor with live preview"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: alternatives
|
||||||
|
attributes:
|
||||||
|
label: 🔄 Alternativen / Alternatives
|
||||||
|
description: Hast du andere Lösungsansätze in Betracht gezogen? / Have you considered other solutions?
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: platform
|
||||||
|
attributes:
|
||||||
|
label: 📱 Plattform / Platform
|
||||||
|
description: Für welche Komponente ist das Feature? / For which component is the feature?
|
||||||
|
options:
|
||||||
|
- Android App
|
||||||
|
- WebDAV Server
|
||||||
|
- Dokumentation / Documentation
|
||||||
|
- Andere / Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: priority
|
||||||
|
attributes:
|
||||||
|
label: 🌟 Priorität (aus deiner Sicht) / Priority (from your perspective)
|
||||||
|
options:
|
||||||
|
- Nice to have
|
||||||
|
- Wichtig / Important
|
||||||
|
- Sehr wichtig / Very important
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: willing-to-contribute
|
||||||
|
attributes:
|
||||||
|
label: 🤝 Beitragen / Contribute
|
||||||
|
options:
|
||||||
|
- label: Ich würde gerne bei der Implementierung helfen / I would like to help with implementation
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: additional
|
||||||
|
attributes:
|
||||||
|
label: 🔧 Zusätzliche Informationen / Additional Context
|
||||||
|
description: Screenshots, Mockups, Links, ähnliche Apps, etc.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
76
.github/ISSUE_TEMPLATE/question.yml
vendored
Normal file
76
.github/ISSUE_TEMPLATE/question.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
name: ❓ Question / Frage
|
||||||
|
description: Stelle eine Frage zur Nutzung / Ask a question about usage
|
||||||
|
title: "[QUESTION] "
|
||||||
|
labels: ["question"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Hast du eine Frage? Wir helfen gerne! / Have a question? We're happy to help!
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: question
|
||||||
|
attributes:
|
||||||
|
label: ❓ Frage / Question
|
||||||
|
description: Was möchtest du wissen? / What would you like to know?
|
||||||
|
placeholder: "z.B. Wie kann ich die Sync-URL für einen externen Server konfigurieren? / e.g. How can I configure the sync URL for an external server?"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: documentation-checked
|
||||||
|
attributes:
|
||||||
|
label: 📚 Dokumentation gelesen / Documentation checked
|
||||||
|
description: Hast du bereits in der Dokumentation nachgeschaut? / Have you already checked the documentation?
|
||||||
|
options:
|
||||||
|
- label: Ich habe die [README](https://github.com/inventory69/simple-notes-sync/blob/main/README.md) gelesen / I have read the README
|
||||||
|
required: false
|
||||||
|
- label: Ich habe den [Quick Start Guide](https://github.com/inventory69/simple-notes-sync/blob/main/QUICKSTART.md) gelesen / I have read the Quick Start Guide
|
||||||
|
required: false
|
||||||
|
- label: Ich habe das [Troubleshooting](https://github.com/inventory69/simple-notes-sync/blob/main/QUICKSTART.md#troubleshooting) durchgearbeitet / I have checked the troubleshooting section
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: tried
|
||||||
|
attributes:
|
||||||
|
label: 🔍 Was hast du bereits versucht? / What have you already tried?
|
||||||
|
description: Hilf uns, dir besser zu helfen / Help us help you better
|
||||||
|
placeholder: "z.B. Ich habe versucht die Server-URL anzupassen, aber... / e.g. I tried adjusting the server URL, but..."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: topic
|
||||||
|
attributes:
|
||||||
|
label: <EFBFBD> Thema / Topic
|
||||||
|
description: Um was geht es? / What is this about?
|
||||||
|
options:
|
||||||
|
- Server Setup / Server-Einrichtung
|
||||||
|
- App-Konfiguration / App configuration
|
||||||
|
- Sync-Probleme / Sync issues
|
||||||
|
- Netzwerk / Network
|
||||||
|
- Android-Einstellungen / Android settings
|
||||||
|
- Andere / Other
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: <EFBFBD> Kontext / Context
|
||||||
|
description: Zusätzliche Informationen die hilfreich sein könnten / Additional information that might be helpful
|
||||||
|
placeholder: |
|
||||||
|
- Android Version: Android 13
|
||||||
|
- App Version: v1.1.0
|
||||||
|
- Server: Raspberry Pi mit Docker / Raspberry Pi with Docker
|
||||||
|
- Netzwerk: Lokales WiFi / Local WiFi
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: additional
|
||||||
|
attributes:
|
||||||
|
label: 🔧 Screenshots / Config
|
||||||
|
description: Falls hilfreich (KEINE Passwörter!) / If helpful (NO passwords!)
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
219
.github/workflows/build-production-apk.yml
vendored
219
.github/workflows/build-production-apk.yml
vendored
@@ -2,11 +2,11 @@ name: Build Android Production APK
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ] # Trigger on push to main branch
|
branches: [ main ] # Nur bei Push/Merge auf main triggern
|
||||||
workflow_dispatch: # Allow manual trigger
|
workflow_dispatch: # Ermöglicht manuellen Trigger
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # Required for creating releases
|
contents: write # Fuer Release-Erstellung erforderlich
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -14,185 +14,164 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Code auschecken
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Java einrichten
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
|
|
||||||
- name: Generate Production version number
|
- name: Semantic Versionsnummer aus build.gradle.kts extrahieren
|
||||||
run: |
|
run: |
|
||||||
# Generate semantic version: YYYY.MM.DD
|
# Version aus build.gradle.kts fuer F-Droid Kompatibilität
|
||||||
VERSION_NAME="$(date +'%Y.%m.%d')"
|
VERSION_NAME=$(grep "versionName = " android/app/build.gradle.kts | sed 's/.*versionName = "\(.*\)".*/\1/')
|
||||||
|
VERSION_CODE=$(grep "versionCode = " android/app/build.gradle.kts | sed 's/.*versionCode = \([0-9]*\).*/\1/')
|
||||||
|
|
||||||
# Use GitHub run number as build number for production
|
# Semantische Versionierung (nicht datums-basiert)
|
||||||
BUILD_NUMBER="${{ github.run_number }}"
|
BUILD_NUMBER="$VERSION_CODE"
|
||||||
|
|
||||||
echo "VERSION_NAME=$VERSION_NAME" >> $GITHUB_ENV
|
echo "VERSION_NAME=$VERSION_NAME" >> $GITHUB_ENV
|
||||||
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
|
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
|
||||||
echo "VERSION_TAG=v$VERSION_NAME-prod.$BUILD_NUMBER" >> $GITHUB_ENV
|
echo "VERSION_TAG=v$VERSION_NAME" >> $GITHUB_ENV
|
||||||
|
|
||||||
echo "🚀 Generated PRODUCTION version: $VERSION_NAME+$BUILD_NUMBER"
|
echo "🚀 Baue Version: $VERSION_NAME (Code: $BUILD_NUMBER)"
|
||||||
|
|
||||||
- name: Update build.gradle.kts with Production version
|
- name: Version aus build.gradle.kts verifizieren
|
||||||
run: |
|
run: |
|
||||||
# Update versionCode and versionName in build.gradle.kts
|
echo "✅ Verwende Version aus build.gradle.kts:"
|
||||||
sed -i "s/versionCode = [0-9]*/versionCode = ${{ env.BUILD_NUMBER }}/" android/app/build.gradle.kts
|
|
||||||
sed -i "s/versionName = \".*\"/versionName = \"${{ env.VERSION_NAME }}\"/" android/app/build.gradle.kts
|
|
||||||
|
|
||||||
echo "✅ Updated build.gradle.kts:"
|
|
||||||
grep -E "versionCode|versionName" android/app/build.gradle.kts
|
grep -E "versionCode|versionName" android/app/build.gradle.kts
|
||||||
|
|
||||||
- name: Setup Android signing
|
- name: Android Signing konfigurieren
|
||||||
run: |
|
run: |
|
||||||
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/simple-notes-release.jks
|
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/simple-notes-release.jks
|
||||||
echo "storePassword=${{ secrets.KEYSTORE_PASSWORD }}" > android/key.properties
|
echo "storePassword=${{ secrets.KEYSTORE_PASSWORD }}" > android/key.properties
|
||||||
echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
|
echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
|
||||||
echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties
|
echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties
|
||||||
echo "storeFile=simple-notes-release.jks" >> android/key.properties
|
echo "storeFile=simple-notes-release.jks" >> android/key.properties
|
||||||
echo "✅ Signing configuration created"
|
echo "✅ Signing-Konfiguration erstellt"
|
||||||
|
|
||||||
- name: Build Production APK (Release)
|
- name: Produktions-APK bauen (Standard + F-Droid Flavors)
|
||||||
run: |
|
run: |
|
||||||
cd android
|
cd android
|
||||||
./gradlew assembleRelease --no-daemon --stacktrace
|
./gradlew assembleStandardRelease assembleFdroidRelease --no-daemon --stacktrace
|
||||||
|
|
||||||
- name: Copy APK variants to root with version names
|
- name: APK-Varianten mit Versionsnamen kopieren
|
||||||
run: |
|
run: |
|
||||||
mkdir -p apk-output
|
mkdir -p apk-output
|
||||||
|
|
||||||
|
# === Standard Flavor (mit Google Services) ===
|
||||||
|
# Universal APK (funktioniert auf allen Geraeten)
|
||||||
|
cp android/app/build/outputs/apk/standard/release/app-standard-universal-release.apk \
|
||||||
|
apk-output/simple-notes-sync-v${{ env.VERSION_NAME }}-standard-universal.apk
|
||||||
|
|
||||||
|
# ARM64 APK (moderne Geräte 2018+)
|
||||||
|
cp android/app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release.apk \
|
||||||
|
apk-output/simple-notes-sync-v${{ env.VERSION_NAME }}-standard-arm64-v8a.apk
|
||||||
|
|
||||||
|
# ARMv7 APK (ältere Geräte)
|
||||||
|
cp android/app/build/outputs/apk/standard/release/app-standard-armeabi-v7a-release.apk \
|
||||||
|
apk-output/simple-notes-sync-v${{ env.VERSION_NAME }}-standard-armeabi-v7a.apk
|
||||||
|
|
||||||
|
# === F-Droid Flavor (ohne Google Services) ===
|
||||||
# Universal APK
|
# Universal APK
|
||||||
cp android/app/build/outputs/apk/release/app-universal-release.apk \
|
cp android/app/build/outputs/apk/fdroid/release/app-fdroid-universal-release.apk \
|
||||||
apk-output/simple-notes-sync-v${{ env.VERSION_NAME }}-universal.apk
|
apk-output/simple-notes-sync-v${{ env.VERSION_NAME }}-fdroid-universal.apk
|
||||||
|
|
||||||
# ARM64 APK
|
# ARM64 APK
|
||||||
cp android/app/build/outputs/apk/release/app-arm64-v8a-release.apk \
|
cp android/app/build/outputs/apk/fdroid/release/app-fdroid-arm64-v8a-release.apk \
|
||||||
apk-output/simple-notes-sync-v${{ env.VERSION_NAME }}-arm64-v8a.apk
|
apk-output/simple-notes-sync-v${{ env.VERSION_NAME }}-fdroid-arm64-v8a.apk
|
||||||
|
|
||||||
# ARMv7 APK
|
# ARMv7 APK
|
||||||
cp android/app/build/outputs/apk/release/app-armeabi-v7a-release.apk \
|
cp android/app/build/outputs/apk/fdroid/release/app-fdroid-armeabi-v7a-release.apk \
|
||||||
apk-output/simple-notes-sync-v${{ env.VERSION_NAME }}-armeabi-v7a.apk
|
apk-output/simple-notes-sync-v${{ env.VERSION_NAME }}-fdroid-armeabi-v7a.apk
|
||||||
|
|
||||||
echo "✅ APK files prepared:"
|
echo "✅ APK-Dateien vorbereitet (Standard + F-Droid):"
|
||||||
ls -lh apk-output/
|
ls -lh apk-output/
|
||||||
|
|
||||||
- name: Upload APK artifacts
|
- name: APK-Artefakte hochladen
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: simple-notes-sync-apks-v${{ env.VERSION_NAME }}
|
name: simple-notes-sync-apks-v${{ env.VERSION_NAME }}
|
||||||
path: apk-output/*.apk
|
path: apk-output/*.apk
|
||||||
retention-days: 90 # Keep production builds longer
|
retention-days: 90 # Produktions-Builds länger aufbewahren
|
||||||
|
|
||||||
- name: Get commit info
|
- name: Commit-Informationen auslesen
|
||||||
run: |
|
run: |
|
||||||
echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||||
echo "COMMIT_DATE=$(git log -1 --format=%cd --date=iso-strict)" >> $GITHUB_ENV
|
echo "COMMIT_DATE=$(git log -1 --format=%cd --date=iso-strict)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: F-Droid Changelogs lesen
|
||||||
|
run: |
|
||||||
|
# Lese deutsche Changelog (Hauptsprache)
|
||||||
|
if [ -f "android/fastlane/metadata/android/de-DE/changelogs/${{ env.BUILD_NUMBER }}.txt" ]; then
|
||||||
|
{
|
||||||
|
echo 'CHANGELOG_DE<<EOF'
|
||||||
|
cat "android/fastlane/metadata/android/de-DE/changelogs/${{ env.BUILD_NUMBER }}.txt"
|
||||||
|
echo 'EOF'
|
||||||
|
} >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "CHANGELOG_DE=Keine deutschen Release Notes verfügbar." >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
# Get full commit message preserving newlines and emojis (UTF-8)
|
# Lese englische Changelog (optional)
|
||||||
{
|
if [ -f "android/fastlane/metadata/android/en-US/changelogs/${{ env.BUILD_NUMBER }}.txt" ]; then
|
||||||
echo 'COMMIT_MSG<<EOF'
|
{
|
||||||
git -c core.quotepath=false log -1 --pretty=%B
|
echo 'CHANGELOG_EN<<EOF'
|
||||||
echo 'EOF'
|
cat "android/fastlane/metadata/android/en-US/changelogs/${{ env.BUILD_NUMBER }}.txt"
|
||||||
} >> $GITHUB_ENV
|
echo 'EOF'
|
||||||
|
} >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "CHANGELOG_EN=" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Create Production Release
|
- name: Create Production Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ env.VERSION_TAG }}
|
tag_name: ${{ env.VERSION_TAG }}
|
||||||
name: "📝 Simple Notes Sync v${{ env.VERSION_NAME }} (Produktions-Release)"
|
name: "📝 Simple Notes Sync v${{ env.VERSION_NAME }}"
|
||||||
files: apk-output/*.apk
|
files: apk-output/*.apk
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
generate_release_notes: false
|
generate_release_notes: false
|
||||||
body: |
|
body: |
|
||||||
# 📝 Produktions-Release: Simple Notes Sync v${{ env.VERSION_NAME }}
|
## 📦 Downloads
|
||||||
|
|
||||||
## Build-Informationen
|
| Variante | Datei | Info |
|
||||||
|
|----------|-------|------|
|
||||||
|
| **🏆 Empfohlen** | `simple-notes-sync-v${{ env.VERSION_NAME }}-standard-universal.apk` | Funktioniert auf allen Android-Geraeten |
|
||||||
|
| Modern (2018+) | `simple-notes-sync-v${{ env.VERSION_NAME }}-standard-arm64-v8a.apk` | Kleinere Dateigröße fuer 64-bit Geräte |
|
||||||
|
| Aelter (<2018) | `simple-notes-sync-v${{ env.VERSION_NAME }}-standard-armeabi-v7a.apk` | Fuer 32-bit ARM Geräte |
|
||||||
|
| F-Droid Universal | `simple-notes-sync-v${{ env.VERSION_NAME }}-fdroid-universal.apk` | Fuer F-Droid Store |
|
||||||
|
| F-Droid ARM64 | `simple-notes-sync-v${{ env.VERSION_NAME }}-fdroid-arm64-v8a.apk` | F-Droid 64-bit |
|
||||||
|
| F-Droid ARMv7 | `simple-notes-sync-v${{ env.VERSION_NAME }}-fdroid-armeabi-v7a.apk` | F-Droid 32-bit |
|
||||||
|
|
||||||
- **Version:** ${{ env.VERSION_NAME }}+${{ env.BUILD_NUMBER }}
|
💡 **Nicht sicher?** → Nimm die **Universal** APK!
|
||||||
- **Build-Datum:** ${{ env.COMMIT_DATE }}
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Changelog / Release Notes
|
||||||
|
|
||||||
|
${{ env.CHANGELOG_EN }}
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><3E>🇪 Deutsche Version (zum Aufklappen)</summary>
|
||||||
|
|
||||||
|
${{ env.CHANGELOG_DE }}
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Build-Info
|
||||||
|
|
||||||
|
- **Version:** ${{ env.VERSION_NAME }} (Code: ${{ env.BUILD_NUMBER }})
|
||||||
|
- **Datum:** ${{ env.COMMIT_DATE }}
|
||||||
- **Commit:** ${{ env.SHORT_SHA }}
|
- **Commit:** ${{ env.SHORT_SHA }}
|
||||||
- **Umgebung:** 🟢 **PRODUKTION**
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 Änderungen
|
**[📖 Dokumentation](https://github.com/inventory69/simple-notes-sync/blob/main/QUICKSTART.md)** · **[🐛 Issue melden](https://github.com/inventory69/simple-notes-sync/issues)**
|
||||||
|
|
||||||
${{ env.COMMIT_MSG }}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📦 Download & Installation
|
|
||||||
|
|
||||||
### Welche APK soll ich herunterladen?
|
|
||||||
|
|
||||||
| Dein Gerät | Lade diese APK herunter | Größe | Kompatibilität |
|
|
||||||
|------------|------------------------|-------|----------------|
|
|
||||||
| 🤷 Nicht sicher? | `simple-notes-sync-v${{ env.VERSION_NAME }}-universal.apk` | ~5 MB | Funktioniert auf allen Geräten |
|
|
||||||
| Modern (2018+) | `simple-notes-sync-v${{ env.VERSION_NAME }}-arm64-v8a.apk` | ~3 MB | Schneller, kleiner |
|
|
||||||
| Ältere Geräte | `simple-notes-sync-v${{ env.VERSION_NAME }}-armeabi-v7a.apk` | ~3 MB | Ältere ARM-Chips |
|
|
||||||
|
|
||||||
### Installationsschritte
|
|
||||||
1. Lade die passende APK aus den Assets unten herunter
|
|
||||||
2. Aktiviere "Installation aus unbekannten Quellen" in den Android-Einstellungen
|
|
||||||
3. Öffne die heruntergeladene APK-Datei
|
|
||||||
4. Folge den Installationsanweisungen
|
|
||||||
5. Konfiguriere die WebDAV-Einstellungen in der App
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚙️ Funktionen
|
|
||||||
|
|
||||||
- ✅ Automatische WebDAV-Synchronisation alle 30 Minuten (~0,4% Akku/Tag)
|
|
||||||
- ✅ Intelligente Gateway-Erkennung (automatische Heimnetzwerk-Erkennung)
|
|
||||||
- ✅ Material Design 3 Oberfläche
|
|
||||||
- ✅ Datenschutzorientiert (kein Tracking, keine Analysen)
|
|
||||||
- ✅ Offline-First Architektur
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 Update von vorheriger Version
|
|
||||||
|
|
||||||
Installiere diese APK einfach über die bestehende Installation - alle Daten und Einstellungen bleiben erhalten.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📱 Obtanium - Auto-Update App
|
|
||||||
|
|
||||||
Erhalte automatische Updates mit [Obtanium](https://github.com/ImranR98/Obtanium/releases/latest).
|
|
||||||
|
|
||||||
**Einrichtung:**
|
|
||||||
1. Installiere Obtanium über den Link oben
|
|
||||||
2. Füge die App mit dieser URL hinzu: `https://github.com/dettmersLiq/simple-notes-sync`
|
|
||||||
3. Aktiviere Auto-Updates
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🆘 Support
|
|
||||||
|
|
||||||
Bei Problemen oder Fragen öffne bitte ein Issue auf GitHub.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔒 Datenschutz & Sicherheit
|
|
||||||
|
|
||||||
- Alle Daten werden über deinen eigenen WebDAV-Server synchronisiert
|
|
||||||
- Keine Drittanbieter-Analysen oder Tracking
|
|
||||||
- Keine Internet-Berechtigungen außer für WebDAV-Sync
|
|
||||||
- Alle Synchronisationsvorgänge verschlüsselt (HTTPS)
|
|
||||||
- Open Source - prüfe den Code selbst
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠️ Erstellt mit
|
|
||||||
|
|
||||||
- **Sprache:** Kotlin
|
|
||||||
- **UI:** Material Design 3
|
|
||||||
- **Sync:** WorkManager + WebDAV
|
|
||||||
- **Target SDK:** Android 16 (API 36)
|
|
||||||
- **Min SDK:** Android 8.0 (API 26)
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
113
.github/workflows/pr-build-check.yml
vendored
Normal file
113
.github/workflows/pr-build-check.yml
vendored
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
name: PR Build Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
paths:
|
||||||
|
- 'android/**'
|
||||||
|
- '.github/workflows/pr-build-check.yml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build & Test APK
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Code auschecken
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Java einrichten
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: 'temurin'
|
||||||
|
java-version: '17'
|
||||||
|
|
||||||
|
- name: Gradle Cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
|
||||||
|
- name: Version auslesen
|
||||||
|
run: |
|
||||||
|
VERSION_NAME=$(grep "versionName = " android/app/build.gradle.kts | sed 's/.*versionName = "\(.*\)".*/\1/')
|
||||||
|
VERSION_CODE=$(grep "versionCode = " android/app/build.gradle.kts | sed 's/.*versionCode = \([0-9]*\).*/\1/')
|
||||||
|
echo "VERSION_NAME=$VERSION_NAME" >> $GITHUB_ENV
|
||||||
|
echo "VERSION_CODE=$VERSION_CODE" >> $GITHUB_ENV
|
||||||
|
echo "📱 Version: $VERSION_NAME (Code: $VERSION_CODE)"
|
||||||
|
|
||||||
|
- name: Debug Build erstellen (ohne Signing)
|
||||||
|
run: |
|
||||||
|
cd android
|
||||||
|
./gradlew assembleStandardDebug assembleFdroidDebug --no-daemon --stacktrace
|
||||||
|
|
||||||
|
- name: Unit Tests ausfuehren
|
||||||
|
run: |
|
||||||
|
cd android
|
||||||
|
./gradlew test --no-daemon --stacktrace
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
- name: Build-Ergebnis pruefen
|
||||||
|
run: |
|
||||||
|
if [ -f "android/app/build/outputs/apk/standard/debug/app-standard-universal-debug.apk" ]; then
|
||||||
|
echo "✅ Standard Debug APK erfolgreich gebaut"
|
||||||
|
ls -lh android/app/build/outputs/apk/standard/debug/*.apk
|
||||||
|
else
|
||||||
|
echo "❌ Standard Debug APK Build fehlgeschlagen"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "android/app/build/outputs/apk/fdroid/debug/app-fdroid-universal-debug.apk" ]; then
|
||||||
|
echo "✅ F-Droid Debug APK erfolgreich gebaut"
|
||||||
|
ls -lh android/app/build/outputs/apk/fdroid/debug/*.apk
|
||||||
|
else
|
||||||
|
echo "❌ F-Droid Debug APK Build fehlgeschlagen"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Debug APKs hochladen (Artefakte)
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: debug-apks-pr-${{ github.event.pull_request.number }}
|
||||||
|
path: |
|
||||||
|
android/app/build/outputs/apk/standard/debug/*.apk
|
||||||
|
android/app/build/outputs/apk/fdroid/debug/*.apk
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Kommentar zu PR hinzufuegen
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
if: success()
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const standardApk = fs.readdirSync('android/app/build/outputs/apk/standard/debug/')
|
||||||
|
.filter(f => f.endsWith('.apk'));
|
||||||
|
const fdroidApk = fs.readdirSync('android/app/build/outputs/apk/fdroid/debug/')
|
||||||
|
.filter(f => f.endsWith('.apk'));
|
||||||
|
|
||||||
|
github.rest.issues.createComment({
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
body: `## ✅ Build erfolgreich!
|
||||||
|
|
||||||
|
**Version:** ${{ env.VERSION_NAME }} (Code: ${{ env.VERSION_CODE }})
|
||||||
|
|
||||||
|
### 📦 Debug APKs (Test-Builds)
|
||||||
|
|
||||||
|
Die Debug-APKs wurden erfolgreich gebaut und sind als Artefakte verfuegbar:
|
||||||
|
|
||||||
|
**Standard Flavor:**
|
||||||
|
${standardApk.map(f => '- `' + f + '`').join('\n')}
|
||||||
|
|
||||||
|
**F-Droid Flavor:**
|
||||||
|
${fdroidApk.map(f => '- `' + f + '`').join('\n')}
|
||||||
|
|
||||||
|
> ⚠️ **Hinweis:** Dies sind unsigned Debug-Builds zum Testen. Production Releases werden nur bei Merge auf \`main\` erstellt.
|
||||||
|
|
||||||
|
[📥 Download Artefakte](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`
|
||||||
|
})
|
||||||
263
CONTRIBUTING.md
Normal file
263
CONTRIBUTING.md
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
# Contributing to Simple Notes Sync 🤝
|
||||||
|
|
||||||
|
> Beiträge sind willkommen! / Contributions are welcome!
|
||||||
|
|
||||||
|
**🌍 Languages:** [Deutsch](#deutsch) · [English](#english)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deutsch
|
||||||
|
|
||||||
|
Danke, dass du zu Simple Notes Sync beitragen möchtest!
|
||||||
|
|
||||||
|
### 🚀 Schnellstart
|
||||||
|
|
||||||
|
1. **Fork & Clone**
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/DEIN-USERNAME/simple-notes-sync.git
|
||||||
|
cd simple-notes-sync
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Branch erstellen**
|
||||||
|
```bash
|
||||||
|
git checkout -b feature/mein-feature
|
||||||
|
# oder
|
||||||
|
git checkout -b fix/mein-bugfix
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Änderungen machen**
|
||||||
|
- Code schreiben
|
||||||
|
- Testen
|
||||||
|
- Committen mit aussagekräftiger Message
|
||||||
|
|
||||||
|
4. **Pull Request erstellen**
|
||||||
|
- Push deinen Branch: `git push origin feature/mein-feature`
|
||||||
|
- Gehe zu GitHub und erstelle einen Pull Request
|
||||||
|
- Beschreibe deine Änderungen
|
||||||
|
|
||||||
|
### 🧪 Automatische Tests
|
||||||
|
|
||||||
|
Wenn du einen Pull Request erstellst, läuft automatisch ein **Build Check**:
|
||||||
|
|
||||||
|
- ✅ Debug APKs werden gebaut (Standard + F-Droid)
|
||||||
|
- ✅ Unit Tests werden ausgeführt
|
||||||
|
- ✅ APKs werden als Artefakte hochgeladen (zum Testen)
|
||||||
|
- ✅ Build-Status wird als Kommentar im PR gepostet
|
||||||
|
|
||||||
|
**Wichtig:** Der Build muss erfolgreich sein (grüner Haken ✅) bevor der PR gemerged werden kann.
|
||||||
|
|
||||||
|
### 📱 Android App Development
|
||||||
|
|
||||||
|
**Build lokal testen:**
|
||||||
|
```bash
|
||||||
|
cd android
|
||||||
|
|
||||||
|
# Debug Build
|
||||||
|
./gradlew assembleStandardDebug
|
||||||
|
|
||||||
|
# Tests ausführen
|
||||||
|
./gradlew test
|
||||||
|
|
||||||
|
# Lint Check
|
||||||
|
./gradlew lint
|
||||||
|
```
|
||||||
|
|
||||||
|
**Anforderungen:**
|
||||||
|
- Android SDK 36 (Target)
|
||||||
|
- Android SDK 24 (Minimum)
|
||||||
|
- JDK 17
|
||||||
|
- Kotlin 1.9+
|
||||||
|
|
||||||
|
### 📝 Code Style
|
||||||
|
|
||||||
|
- **Kotlin:** Folge den [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html)
|
||||||
|
- **Formatierung:** Android Studio Default Formatter
|
||||||
|
- **Kommentare:** Deutsch oder Englisch (bevorzugt Englisch für Code)
|
||||||
|
|
||||||
|
### 🐛 Bug Reports
|
||||||
|
|
||||||
|
Nutze die [Bug Report Template](https://github.com/inventory69/simple-notes-sync/issues/new/choose) mit:
|
||||||
|
- Android Version
|
||||||
|
- App Version
|
||||||
|
- Schritte zum Reproduzieren
|
||||||
|
- Erwartetes vs. tatsächliches Verhalten
|
||||||
|
|
||||||
|
### 💡 Feature Requests
|
||||||
|
|
||||||
|
Nutze die [Feature Request Template](https://github.com/inventory69/simple-notes-sync/issues/new/choose) und beschreibe:
|
||||||
|
- Was soll hinzugefügt werden
|
||||||
|
- Warum ist es nützlich
|
||||||
|
- Wie könnte es funktionieren
|
||||||
|
|
||||||
|
### 📚 Dokumentation
|
||||||
|
|
||||||
|
Dokumentations-Verbesserungen sind auch Contributions!
|
||||||
|
|
||||||
|
**Dateien:**
|
||||||
|
- `README.md` / `README.en.md` - Übersicht
|
||||||
|
- `QUICKSTART.md` / `QUICKSTART.en.md` - Schritt-für-Schritt Anleitung
|
||||||
|
- `DOCS.md` / `DOCS.en.md` - Technische Details
|
||||||
|
- `server/README.md` / `server/README.en.md` - Server Setup
|
||||||
|
|
||||||
|
**Bitte:** Halte beide Sprachen (DE/EN) synchron!
|
||||||
|
|
||||||
|
### ✅ Pull Request Checklist
|
||||||
|
|
||||||
|
- [ ] Code kompiliert lokal (`./gradlew assembleStandardDebug`)
|
||||||
|
- [ ] Tests laufen durch (`./gradlew test`)
|
||||||
|
- [ ] Keine neuen Lint-Warnungen
|
||||||
|
- [ ] Commit-Messages sind aussagekräftig
|
||||||
|
- [ ] Dokumentation aktualisiert (falls nötig)
|
||||||
|
- [ ] Beide Sprachen aktualisiert (bei Doku-Änderungen)
|
||||||
|
|
||||||
|
### 🎯 Was wird akzeptiert?
|
||||||
|
|
||||||
|
**✅ Gerne:**
|
||||||
|
- Bug Fixes
|
||||||
|
- Performance-Verbesserungen
|
||||||
|
- Neue Features (nach Diskussion in einem Issue)
|
||||||
|
- Dokumentations-Verbesserungen
|
||||||
|
- Tests
|
||||||
|
- UI/UX Verbesserungen
|
||||||
|
|
||||||
|
**❌ Schwierig:**
|
||||||
|
- Breaking Changes (bitte erst als Issue diskutieren)
|
||||||
|
- Komplett neue Architektur
|
||||||
|
- Dependencies mit fragwürdigen Lizenzen
|
||||||
|
|
||||||
|
### 📄 Lizenz
|
||||||
|
|
||||||
|
Indem du contributest, stimmst du zu dass dein Code unter der [MIT License](LICENSE) veröffentlicht wird.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## English
|
||||||
|
|
||||||
|
Thanks for wanting to contribute to Simple Notes Sync!
|
||||||
|
|
||||||
|
### 🚀 Quick Start
|
||||||
|
|
||||||
|
1. **Fork & Clone**
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/YOUR-USERNAME/simple-notes-sync.git
|
||||||
|
cd simple-notes-sync
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Create Branch**
|
||||||
|
```bash
|
||||||
|
git checkout -b feature/my-feature
|
||||||
|
# or
|
||||||
|
git checkout -b fix/my-bugfix
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Make Changes**
|
||||||
|
- Write code
|
||||||
|
- Test
|
||||||
|
- Commit with meaningful message
|
||||||
|
|
||||||
|
4. **Create Pull Request**
|
||||||
|
- Push your branch: `git push origin feature/my-feature`
|
||||||
|
- Go to GitHub and create a Pull Request
|
||||||
|
- Describe your changes
|
||||||
|
|
||||||
|
### 🧪 Automated Tests
|
||||||
|
|
||||||
|
When you create a Pull Request, an automatic **Build Check** runs:
|
||||||
|
|
||||||
|
- ✅ Debug APKs are built (Standard + F-Droid)
|
||||||
|
- ✅ Unit tests are executed
|
||||||
|
- ✅ APKs are uploaded as artifacts (for testing)
|
||||||
|
- ✅ Build status is posted as comment in PR
|
||||||
|
|
||||||
|
**Important:** The build must succeed (green checkmark ✅) before the PR can be merged.
|
||||||
|
|
||||||
|
### 📱 Android App Development
|
||||||
|
|
||||||
|
**Test build locally:**
|
||||||
|
```bash
|
||||||
|
cd android
|
||||||
|
|
||||||
|
# Debug Build
|
||||||
|
./gradlew assembleStandardDebug
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
./gradlew test
|
||||||
|
|
||||||
|
# Lint Check
|
||||||
|
./gradlew lint
|
||||||
|
```
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Android SDK 36 (Target)
|
||||||
|
- Android SDK 24 (Minimum)
|
||||||
|
- JDK 17
|
||||||
|
- Kotlin 1.9+
|
||||||
|
|
||||||
|
### 📝 Code Style
|
||||||
|
|
||||||
|
- **Kotlin:** Follow [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html)
|
||||||
|
- **Formatting:** Android Studio Default Formatter
|
||||||
|
- **Comments:** German or English (preferably English for code)
|
||||||
|
|
||||||
|
### 🐛 Bug Reports
|
||||||
|
|
||||||
|
Use the [Bug Report Template](https://github.com/inventory69/simple-notes-sync/issues/new/choose) with:
|
||||||
|
- Android version
|
||||||
|
- App version
|
||||||
|
- Steps to reproduce
|
||||||
|
- Expected vs. actual behavior
|
||||||
|
|
||||||
|
### 💡 Feature Requests
|
||||||
|
|
||||||
|
Use the [Feature Request Template](https://github.com/inventory69/simple-notes-sync/issues/new/choose) and describe:
|
||||||
|
- What should be added
|
||||||
|
- Why is it useful
|
||||||
|
- How could it work
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
Documentation improvements are also contributions!
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- `README.md` / `README.en.md` - Overview
|
||||||
|
- `QUICKSTART.md` / `QUICKSTART.en.md` - Step-by-step guide
|
||||||
|
- `DOCS.md` / `DOCS.en.md` - Technical details
|
||||||
|
- `server/README.md` / `server/README.en.md` - Server setup
|
||||||
|
|
||||||
|
**Please:** Keep both languages (DE/EN) in sync!
|
||||||
|
|
||||||
|
### ✅ Pull Request Checklist
|
||||||
|
|
||||||
|
- [ ] Code compiles locally (`./gradlew assembleStandardDebug`)
|
||||||
|
- [ ] Tests pass (`./gradlew test`)
|
||||||
|
- [ ] No new lint warnings
|
||||||
|
- [ ] Commit messages are meaningful
|
||||||
|
- [ ] Documentation updated (if needed)
|
||||||
|
- [ ] Both languages updated (for doc changes)
|
||||||
|
|
||||||
|
### 🎯 What Gets Accepted?
|
||||||
|
|
||||||
|
**✅ Welcome:**
|
||||||
|
- Bug fixes
|
||||||
|
- Performance improvements
|
||||||
|
- New features (after discussion in an issue)
|
||||||
|
- Documentation improvements
|
||||||
|
- Tests
|
||||||
|
- UI/UX improvements
|
||||||
|
|
||||||
|
**❌ Difficult:**
|
||||||
|
- Breaking changes (please discuss in issue first)
|
||||||
|
- Completely new architecture
|
||||||
|
- Dependencies with questionable licenses
|
||||||
|
|
||||||
|
### 📄 License
|
||||||
|
|
||||||
|
By contributing, you agree that your code will be published under the [MIT License](LICENSE).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🆘 Fragen? / Questions?
|
||||||
|
|
||||||
|
Öffne ein [Issue](https://github.com/inventory69/simple-notes-sync/issues) oder nutze die [Question Template](https://github.com/inventory69/simple-notes-sync/issues/new/choose).
|
||||||
|
|
||||||
|
**Frohe Weihnachten & Happy Coding! 🎄**
|
||||||
529
DOCS.en.md
Normal file
529
DOCS.en.md
Normal file
@@ -0,0 +1,529 @@
|
|||||||
|
# Simple Notes Sync - Technical Documentation
|
||||||
|
|
||||||
|
This file contains detailed technical information about implementation, architecture, and advanced features.
|
||||||
|
|
||||||
|
**🌍 Languages:** [Deutsch](DOCS.md) · **English**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📐 Architecture
|
||||||
|
|
||||||
|
### Overall Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐
|
||||||
|
│ Android App │
|
||||||
|
│ (Kotlin) │
|
||||||
|
└────────┬────────┘
|
||||||
|
│ WebDAV/HTTP
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ WebDAV Server │
|
||||||
|
│ (Docker) │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Android App Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── models/
|
||||||
|
│ ├── Note.kt # Data class for notes
|
||||||
|
│ └── SyncStatus.kt # Sync status enum
|
||||||
|
├── storage/
|
||||||
|
│ └── NotesStorage.kt # Local JSON file storage
|
||||||
|
├── sync/
|
||||||
|
│ ├── WebDavSyncService.kt # WebDAV sync logic
|
||||||
|
│ ├── NetworkMonitor.kt # WiFi detection
|
||||||
|
│ ├── SyncWorker.kt # WorkManager background worker
|
||||||
|
│ └── BootReceiver.kt # Device reboot handler
|
||||||
|
├── adapters/
|
||||||
|
│ └── NotesAdapter.kt # RecyclerView adapter
|
||||||
|
├── utils/
|
||||||
|
│ ├── Constants.kt # App constants
|
||||||
|
│ ├── NotificationHelper.kt# Notification management
|
||||||
|
│ └── Logger.kt # Debug/release logging
|
||||||
|
└── activities/
|
||||||
|
├── MainActivity.kt # Main view with list
|
||||||
|
├── NoteEditorActivity.kt# Note editor
|
||||||
|
└── SettingsActivity.kt # Server configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Auto-Sync Implementation
|
||||||
|
|
||||||
|
### WorkManager Periodic Task
|
||||||
|
|
||||||
|
Auto-sync is based on **WorkManager** with the following configuration:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val constraints = Constraints.Builder()
|
||||||
|
.setRequiredNetworkType(NetworkType.UNMETERED) // WiFi only
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(
|
||||||
|
30, TimeUnit.MINUTES, // Every 30 minutes
|
||||||
|
10, TimeUnit.MINUTES // Flex interval
|
||||||
|
)
|
||||||
|
.setConstraints(constraints)
|
||||||
|
.build()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why WorkManager?**
|
||||||
|
- ✅ Runs even when app is closed
|
||||||
|
- ✅ Automatic restart after device reboot
|
||||||
|
- ✅ Battery-efficient (Android managed)
|
||||||
|
- ✅ Guaranteed execution when constraints are met
|
||||||
|
|
||||||
|
### Network Detection
|
||||||
|
|
||||||
|
Instead of SSID-based detection (Android 13+ privacy issues), we use **Gateway IP Comparison**:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun isInHomeNetwork(): Boolean {
|
||||||
|
val gatewayIP = getGatewayIP() // e.g. 192.168.0.1
|
||||||
|
val serverIP = extractIPFromUrl(serverUrl) // e.g. 192.168.0.188
|
||||||
|
|
||||||
|
return isSameNetwork(gatewayIP, serverIP) // Checks /24 network
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Advantages:**
|
||||||
|
- ✅ No location permissions needed
|
||||||
|
- ✅ Works with all Android versions
|
||||||
|
- ✅ Reliable and fast
|
||||||
|
|
||||||
|
### Sync Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. WorkManager wakes up (every 30 min)
|
||||||
|
↓
|
||||||
|
2. Check: WiFi connected?
|
||||||
|
↓
|
||||||
|
3. Check: Same network as server?
|
||||||
|
↓
|
||||||
|
4. Load local notes
|
||||||
|
↓
|
||||||
|
5. Upload new/changed notes → Server
|
||||||
|
↓
|
||||||
|
6. Download remote notes ← Server
|
||||||
|
↓
|
||||||
|
7. Merge & resolve conflicts
|
||||||
|
↓
|
||||||
|
8. Update local storage
|
||||||
|
↓
|
||||||
|
9. Show notification (if changes)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## <20> Sync Trigger Overview
|
||||||
|
|
||||||
|
The app uses **4 different sync triggers** with different use cases:
|
||||||
|
|
||||||
|
| Trigger | File | Function | When? | Pre-Check? |
|
||||||
|
|---------|------|----------|-------|------------|
|
||||||
|
| **1. Manual Sync** | `MainActivity.kt` | `triggerManualSync()` | User clicks sync button in menu | ✅ Yes |
|
||||||
|
| **2. Auto-Sync (onResume)** | `MainActivity.kt` | `triggerAutoSync()` | App opened/resumed | ✅ Yes |
|
||||||
|
| **3. Background Sync (Periodic)** | `SyncWorker.kt` | `doWork()` | Every 15/30/60 minutes (configurable) | ✅ Yes |
|
||||||
|
| **4. WiFi-Connect Sync** | `NetworkMonitor.kt` → `SyncWorker.kt` | `triggerWifiConnectSync()` | WiFi enabled/SSID changed | ✅ Yes |
|
||||||
|
|
||||||
|
### Server Reachability Check (Pre-Check)
|
||||||
|
|
||||||
|
**All 4 sync triggers** use a **pre-check** before the actual sync:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// WebDavSyncService.kt - isServerReachable()
|
||||||
|
suspend fun isServerReachable(): Boolean = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext try {
|
||||||
|
Socket().use { socket ->
|
||||||
|
socket.connect(InetSocketAddress(host, port), 2000) // 2s Timeout
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.d(TAG, "Server not reachable: ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why Socket Check instead of HTTP Request?**
|
||||||
|
- ⚡ **Faster:** Socket connect is instant, HTTP request takes longer
|
||||||
|
- 🔋 **Battery Efficient:** No HTTP overhead (headers, TLS handshake, etc.)
|
||||||
|
- 🎯 **More Precise:** Only checks network reachability, not server logic
|
||||||
|
- 🛡️ **Prevents Errors:** Detects foreign WiFi networks before sync error occurs
|
||||||
|
|
||||||
|
**When does the check fail?**
|
||||||
|
- ❌ Server offline/unreachable
|
||||||
|
- ❌ Wrong WiFi network (e.g. public café WiFi)
|
||||||
|
- ❌ Network not ready yet (DHCP/routing delay after WiFi connect)
|
||||||
|
- ❌ VPN blocks server access
|
||||||
|
- ❌ No WebDAV server URL configured
|
||||||
|
|
||||||
|
### Sync Behavior by Trigger Type
|
||||||
|
|
||||||
|
| Trigger | When server not reachable | On successful sync | Throttling |
|
||||||
|
|---------|--------------------------|-------------------|------------|
|
||||||
|
| Manual Sync | Toast: "Server not reachable" | Toast: "✅ Synced: X notes" | None |
|
||||||
|
| Auto-Sync (onResume) | Silent abort (no toast) | Toast: "✅ Synced: X notes" | Max. 1x/min |
|
||||||
|
| Background Sync | Silent abort (no toast) | Silent (LocalBroadcast only) | 15/30/60 min |
|
||||||
|
| WiFi-Connect Sync | Silent abort (no toast) | Silent (LocalBroadcast only) | SSID-based |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## <20>🔋 Battery Optimization
|
||||||
|
|
||||||
|
### Usage Analysis
|
||||||
|
|
||||||
|
| Component | Frequency | Usage | Details |
|
||||||
|
|------------|----------|-----------|---------|
|
||||||
|
| WorkManager Wakeup | Every 30 min | ~0.15 mAh | System wakes up |
|
||||||
|
| Network Check | 48x/day | ~0.03 mAh | Gateway IP check |
|
||||||
|
| WebDAV Sync | 2-3x/day | ~1.5 mAh | Only when changes |
|
||||||
|
| **Total** | - | **~12 mAh/day** | **~0.4%** at 3000mAh |
|
||||||
|
|
||||||
|
### Optimizations
|
||||||
|
|
||||||
|
1. **IP Caching**
|
||||||
|
```kotlin
|
||||||
|
private var cachedServerIP: String? = null
|
||||||
|
// DNS lookup only once at start, not every check
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Throttling**
|
||||||
|
```kotlin
|
||||||
|
private var lastSyncTime = 0L
|
||||||
|
private const val MIN_SYNC_INTERVAL_MS = 60_000L // Max 1 sync/min
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Conditional Logging**
|
||||||
|
```kotlin
|
||||||
|
object Logger {
|
||||||
|
fun d(tag: String, msg: String) {
|
||||||
|
if (BuildConfig.DEBUG) Log.d(tag, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Network Constraints**
|
||||||
|
- WiFi only (not mobile data)
|
||||||
|
- Only when server is reachable
|
||||||
|
- No permanent listeners
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 WebDAV Sync Details
|
||||||
|
|
||||||
|
### Upload Flow
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
suspend fun uploadNotes(): Int {
|
||||||
|
val localNotes = storage.loadAllNotes()
|
||||||
|
var uploadedCount = 0
|
||||||
|
|
||||||
|
for (note in localNotes) {
|
||||||
|
if (note.syncStatus == SyncStatus.PENDING) {
|
||||||
|
val jsonContent = note.toJson()
|
||||||
|
val remotePath = "$serverUrl/${note.id}.json"
|
||||||
|
|
||||||
|
sardine.put(remotePath, jsonContent.toByteArray())
|
||||||
|
|
||||||
|
note.syncStatus = SyncStatus.SYNCED
|
||||||
|
storage.saveNote(note)
|
||||||
|
uploadedCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uploadedCount
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download Flow
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
suspend fun downloadNotes(): DownloadResult {
|
||||||
|
val remoteFiles = sardine.list(serverUrl)
|
||||||
|
var downloadedCount = 0
|
||||||
|
var conflictCount = 0
|
||||||
|
|
||||||
|
for (file in remoteFiles) {
|
||||||
|
if (!file.name.endsWith(".json")) continue
|
||||||
|
|
||||||
|
val content = sardine.get(file.href)
|
||||||
|
val remoteNote = Note.fromJson(content)
|
||||||
|
val localNote = storage.loadNote(remoteNote.id)
|
||||||
|
|
||||||
|
if (localNote == null) {
|
||||||
|
// New note from server
|
||||||
|
storage.saveNote(remoteNote)
|
||||||
|
downloadedCount++
|
||||||
|
} else if (localNote.modifiedAt < remoteNote.modifiedAt) {
|
||||||
|
// Server has newer version
|
||||||
|
storage.saveNote(remoteNote)
|
||||||
|
downloadedCount++
|
||||||
|
} else if (localNote.modifiedAt > remoteNote.modifiedAt) {
|
||||||
|
// Local version is newer → Conflict
|
||||||
|
resolveConflict(localNote, remoteNote)
|
||||||
|
conflictCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DownloadResult(downloadedCount, conflictCount)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conflict Resolution
|
||||||
|
|
||||||
|
Strategy: **Last-Write-Wins** with **Conflict Copy**
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun resolveConflict(local: Note, remote: Note) {
|
||||||
|
// Rename remote note (conflict copy)
|
||||||
|
val conflictNote = remote.copy(
|
||||||
|
id = "${remote.id}_conflict_${System.currentTimeMillis()}",
|
||||||
|
title = "${remote.title} (Conflict)"
|
||||||
|
)
|
||||||
|
|
||||||
|
storage.saveNote(conflictNote)
|
||||||
|
|
||||||
|
// Local note remains
|
||||||
|
local.syncStatus = SyncStatus.SYNCED
|
||||||
|
storage.saveNote(local)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔔 Notifications
|
||||||
|
|
||||||
|
### Notification Channels
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
val channel = NotificationChannel(
|
||||||
|
"notes_sync_channel",
|
||||||
|
"Notes Synchronization",
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Success Notification
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun showSyncSuccess(context: Context, count: Int) {
|
||||||
|
val intent = Intent(context, MainActivity::class.java)
|
||||||
|
val pendingIntent = PendingIntent.getActivity(context, 0, intent, FLAGS)
|
||||||
|
|
||||||
|
val notification = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||||
|
.setContentTitle("Sync successful")
|
||||||
|
.setContentText("$count notes synchronized")
|
||||||
|
.setContentIntent(pendingIntent) // Click opens app
|
||||||
|
.setAutoCancel(true) // Dismiss on click
|
||||||
|
.build()
|
||||||
|
|
||||||
|
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ Permissions
|
||||||
|
|
||||||
|
The app requires **minimal permissions**:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- Network -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||||
|
|
||||||
|
<!-- Notifications -->
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
|
<!-- Boot Receiver -->
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
|
<!-- Battery Optimization (optional) -->
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
|
```
|
||||||
|
|
||||||
|
**No Location Permissions!**
|
||||||
|
Earlier versions required `ACCESS_FINE_LOCATION` for SSID detection. Now we use Gateway IP Comparison.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Testing
|
||||||
|
|
||||||
|
### Test Server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# WebDAV server reachable?
|
||||||
|
curl -u noteuser:password http://192.168.0.188:8080/
|
||||||
|
|
||||||
|
# Upload file
|
||||||
|
echo '{"test":"data"}' > test.json
|
||||||
|
curl -u noteuser:password -T test.json http://192.168.0.188:8080/test.json
|
||||||
|
|
||||||
|
# Download file
|
||||||
|
curl -u noteuser:password http://192.168.0.188:8080/test.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Android App
|
||||||
|
|
||||||
|
**Unit Tests:**
|
||||||
|
```bash
|
||||||
|
cd android
|
||||||
|
./gradlew test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Instrumented Tests:**
|
||||||
|
```bash
|
||||||
|
./gradlew connectedAndroidTest
|
||||||
|
```
|
||||||
|
|
||||||
|
**Manual Testing Checklist:**
|
||||||
|
|
||||||
|
- [ ] Create note → visible in list
|
||||||
|
- [ ] Edit note → changes saved
|
||||||
|
- [ ] Delete note → removed from list
|
||||||
|
- [ ] Manual sync → server status "Reachable"
|
||||||
|
- [ ] Auto-sync → notification after ~30 min
|
||||||
|
- [ ] Close app → auto-sync continues
|
||||||
|
- [ ] Device reboot → auto-sync starts automatically
|
||||||
|
- [ ] Server offline → error notification
|
||||||
|
- [ ] Notification click → app opens
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Build & Deployment
|
||||||
|
|
||||||
|
### Debug Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd android
|
||||||
|
./gradlew assembleDebug
|
||||||
|
# APK: app/build/outputs/apk/debug/app-debug.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
### Release Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew assembleRelease
|
||||||
|
# APK: app/build/outputs/apk/release/app-release-unsigned.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sign (for Distribution)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create keystore
|
||||||
|
keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias
|
||||||
|
|
||||||
|
# Sign APK
|
||||||
|
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 \
|
||||||
|
-keystore my-release-key.jks \
|
||||||
|
app-release-unsigned.apk my-alias
|
||||||
|
|
||||||
|
# Optimize
|
||||||
|
zipalign -v 4 app-release-unsigned.apk app-release.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Debugging
|
||||||
|
|
||||||
|
### LogCat Filter
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Only app logs
|
||||||
|
adb logcat -s SimpleNotesApp NetworkMonitor SyncWorker WebDavSyncService
|
||||||
|
|
||||||
|
# With timestamps
|
||||||
|
adb logcat -v time -s SyncWorker
|
||||||
|
|
||||||
|
# Save to file
|
||||||
|
adb logcat -s SyncWorker > sync_debug.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**Problem: Auto-sync not working**
|
||||||
|
```
|
||||||
|
Solution: Disable battery optimization
|
||||||
|
Settings → Apps → Simple Notes → Battery → Don't optimize
|
||||||
|
```
|
||||||
|
|
||||||
|
**Problem: Server not reachable**
|
||||||
|
```
|
||||||
|
Check:
|
||||||
|
1. Server running? → docker-compose ps
|
||||||
|
2. IP correct? → ip addr show
|
||||||
|
3. Port open? → telnet 192.168.0.188 8080
|
||||||
|
4. Firewall? → sudo ufw allow 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
**Problem: Notifications not appearing**
|
||||||
|
```
|
||||||
|
Check:
|
||||||
|
1. Notification permission granted?
|
||||||
|
2. Do Not Disturb active?
|
||||||
|
3. App in background? → Force stop & restart
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Dependencies
|
||||||
|
|
||||||
|
```gradle
|
||||||
|
// Core
|
||||||
|
androidx.core:core-ktx:1.12.0
|
||||||
|
androidx.appcompat:appcompat:1.6.1
|
||||||
|
com.google.android.material:material:1.11.0
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
androidx.lifecycle:lifecycle-runtime-ktx:2.7.0
|
||||||
|
|
||||||
|
// RecyclerView
|
||||||
|
androidx.recyclerview:recyclerview:1.3.2
|
||||||
|
|
||||||
|
// Coroutines
|
||||||
|
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3
|
||||||
|
|
||||||
|
// WorkManager
|
||||||
|
androidx.work:work-runtime-ktx:2.9.0
|
||||||
|
|
||||||
|
// WebDAV Client
|
||||||
|
com.github.thegrizzlylabs:sardine-android:0.8
|
||||||
|
|
||||||
|
// Broadcast (deprecated but working)
|
||||||
|
androidx.localbroadcastmanager:localbroadcastmanager:1.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔮 Roadmap
|
||||||
|
|
||||||
|
### v1.1
|
||||||
|
- [ ] Search & Filter
|
||||||
|
- [ ] Dark Mode
|
||||||
|
- [ ] Tags/Categories
|
||||||
|
- [ ] Markdown Preview
|
||||||
|
|
||||||
|
### v2.0
|
||||||
|
- [ ] Desktop Client (Flutter)
|
||||||
|
- [ ] End-to-End Encryption
|
||||||
|
- [ ] Shared Notes (Collaboration)
|
||||||
|
- [ ] Attachment Support
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 Further Documentation
|
||||||
|
|
||||||
|
- [Project Docs](https://github.com/inventory69/project-docs/tree/main/simple-notes-sync)
|
||||||
|
- [Sync Architecture](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/SYNC_ARCHITECTURE.md) - **Detailed Sync Trigger Documentation**
|
||||||
|
- [Android Guide](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/ANDROID_GUIDE.md)
|
||||||
|
- [Bugfix Documentation](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/BUGFIX_SYNC_SPAM_AND_NOTIFICATIONS.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last updated:** December 25, 2025
|
||||||
61
DOCS.md
61
DOCS.md
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
Diese Datei enthält detaillierte technische Informationen über die Implementierung, Architektur und erweiterte Funktionen.
|
Diese Datei enthält detaillierte technische Informationen über die Implementierung, Architektur und erweiterte Funktionen.
|
||||||
|
|
||||||
|
**🌍 Sprachen:** **Deutsch** · [English](DOCS.en.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📐 Architektur
|
## 📐 Architektur
|
||||||
@@ -116,7 +118,61 @@ fun isInHomeNetwork(): Boolean {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔋 Akku-Optimierung
|
## <EFBFBD> Sync-Trigger Übersicht
|
||||||
|
|
||||||
|
Die App verwendet **4 verschiedene Sync-Trigger** mit unterschiedlichen Anwendungsfällen:
|
||||||
|
|
||||||
|
| Trigger | Datei | Funktion | Wann? | Pre-Check? |
|
||||||
|
|---------|-------|----------|-------|------------|
|
||||||
|
| **1. Manueller Sync** | `MainActivity.kt` | `triggerManualSync()` | User klickt auf Sync-Button im Menü | ✅ Ja |
|
||||||
|
| **2. Auto-Sync (onResume)** | `MainActivity.kt` | `triggerAutoSync()` | App wird geöffnet/fortgesetzt | ✅ Ja |
|
||||||
|
| **3. Hintergrund-Sync (Periodic)** | `SyncWorker.kt` | `doWork()` | Alle 15/30/60 Minuten (konfigurierbar) | ✅ Ja |
|
||||||
|
| **4. WiFi-Connect Sync** | `NetworkMonitor.kt` → `SyncWorker.kt` | `triggerWifiConnectSync()` | WiFi an/SSID-Wechsel | ✅ Ja |
|
||||||
|
|
||||||
|
### Server-Erreichbarkeits-Check (Pre-Check)
|
||||||
|
|
||||||
|
**Alle 4 Sync-Trigger** verwenden vor dem eigentlichen Sync einen **Pre-Check**:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// WebDavSyncService.kt - isServerReachable()
|
||||||
|
suspend fun isServerReachable(): Boolean = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext try {
|
||||||
|
Socket().use { socket ->
|
||||||
|
socket.connect(InetSocketAddress(host, port), 2000) // 2s Timeout
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.d(TAG, "Server not reachable: ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warum Socket-Check statt HTTP-Request?**
|
||||||
|
- ⚡ **Schneller:** Socket-Connect ist instant, HTTP-Request dauert länger
|
||||||
|
- 🔋 **Akkuschonender:** Kein HTTP-Overhead (Headers, TLS Handshake, etc.)
|
||||||
|
- 🎯 **Präziser:** Prüft nur Netzwerk-Erreichbarkeit, nicht Server-Logik
|
||||||
|
- 🛡️ **Verhindert Fehler:** Erkennt fremde WiFi-Netze bevor Sync-Fehler entsteht
|
||||||
|
|
||||||
|
**Wann schlägt der Check fehl?**
|
||||||
|
- ❌ Server offline/nicht erreichbar
|
||||||
|
- ❌ Falsches WiFi-Netzwerk (z.B. öffentliches Café-WiFi)
|
||||||
|
- ❌ Netzwerk noch nicht bereit (DHCP/Routing-Delay nach WiFi-Connect)
|
||||||
|
- ❌ VPN blockiert Server-Zugriff
|
||||||
|
- ❌ Keine WebDAV-Server-URL konfiguriert
|
||||||
|
|
||||||
|
### Sync-Verhalten nach Trigger-Typ
|
||||||
|
|
||||||
|
| Trigger | Bei Server nicht erreichbar | Bei erfolgreichem Sync | Throttling |
|
||||||
|
|---------|----------------------------|----------------------|------------|
|
||||||
|
| Manueller Sync | Toast: "Server nicht erreichbar" | Toast: "✅ Gesynct: X Notizen" | Keins |
|
||||||
|
| Auto-Sync (onResume) | Silent abort (kein Toast) | Toast: "✅ Gesynct: X Notizen" | Max. 1x/Min |
|
||||||
|
| Hintergrund-Sync | Silent abort (kein Toast) | Silent (LocalBroadcast only) | 15/30/60 Min |
|
||||||
|
| WiFi-Connect Sync | Silent abort (kein Toast) | Silent (LocalBroadcast only) | SSID-basiert |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## <20>🔋 Akku-Optimierung
|
||||||
|
|
||||||
### Verbrauchsanalyse
|
### Verbrauchsanalyse
|
||||||
|
|
||||||
@@ -464,9 +520,10 @@ androidx.localbroadcastmanager:localbroadcastmanager:1.1.0
|
|||||||
## 📖 Weitere Dokumentation
|
## 📖 Weitere Dokumentation
|
||||||
|
|
||||||
- [Project Docs](https://github.com/inventory69/project-docs/tree/main/simple-notes-sync)
|
- [Project Docs](https://github.com/inventory69/project-docs/tree/main/simple-notes-sync)
|
||||||
|
- [Sync Architecture](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/SYNC_ARCHITECTURE.md) - **Detaillierte Sync-Trigger Dokumentation**
|
||||||
- [Android Guide](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/ANDROID_GUIDE.md)
|
- [Android Guide](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/ANDROID_GUIDE.md)
|
||||||
- [Bugfix Documentation](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/BUGFIX_SYNC_SPAM_AND_NOTIFICATIONS.md)
|
- [Bugfix Documentation](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/BUGFIX_SYNC_SPAM_AND_NOTIFICATIONS.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Letzte Aktualisierung:** 21. Dezember 2025
|
**Letzte Aktualisierung:** 25. Dezember 2025
|
||||||
|
|||||||
@@ -1,188 +0,0 @@
|
|||||||
# GitHub Actions Setup Guide
|
|
||||||
|
|
||||||
This guide explains how to set up the GitHub Actions workflow for automated APK builds with proper signing.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The workflow in `.github/workflows/build-production-apk.yml` automatically:
|
|
||||||
- Builds signed APKs on every push to `main`
|
|
||||||
- Generates version numbers using `YYYY.MM.DD` + build number
|
|
||||||
- Creates 3 APK variants (universal, arm64-v8a, armeabi-v7a)
|
|
||||||
- Creates GitHub releases with all APKs attached
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- GitHub CLI (`gh`) installed
|
|
||||||
- Java 17+ installed (for keytool)
|
|
||||||
- Git repository initialized with GitHub remote
|
|
||||||
|
|
||||||
## Step 1: Generate Signing Keystore
|
|
||||||
|
|
||||||
⚠️ **IMPORTANT**: Store the keystore securely! Without it, you cannot publish updates to your app.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Navigate to project root
|
|
||||||
cd /path/to/simple-notes-sync
|
|
||||||
|
|
||||||
# Generate keystore (replace values as needed)
|
|
||||||
keytool -genkey -v \
|
|
||||||
-keystore android/app/simple-notes-release.jks \
|
|
||||||
-keyalg RSA \
|
|
||||||
-keysize 2048 \
|
|
||||||
-validity 10000 \
|
|
||||||
-alias simple-notes
|
|
||||||
|
|
||||||
# You will be prompted for:
|
|
||||||
# - Keystore password (remember this!)
|
|
||||||
# - Key password (remember this!)
|
|
||||||
# - Your name, organization, etc.
|
|
||||||
```
|
|
||||||
|
|
||||||
**Store these securely:**
|
|
||||||
- Keystore password
|
|
||||||
- Key password
|
|
||||||
- Alias: `simple-notes`
|
|
||||||
- Keystore file: `android/app/simple-notes-release.jks`
|
|
||||||
|
|
||||||
⚠️ **BACKUP**: Make a backup of the keystore file in a secure location (NOT in the repository).
|
|
||||||
|
|
||||||
## Step 2: Base64 Encode Keystore
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create base64 encoded version
|
|
||||||
base64 android/app/simple-notes-release.jks > simple-notes-release.jks.b64
|
|
||||||
|
|
||||||
# Or on macOS:
|
|
||||||
base64 -i android/app/simple-notes-release.jks -o simple-notes-release.jks.b64
|
|
||||||
```
|
|
||||||
|
|
||||||
## Step 3: Set GitHub Secrets
|
|
||||||
|
|
||||||
Using GitHub CLI (recommended):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Set KEYSTORE_BASE64 secret
|
|
||||||
gh secret set KEYSTORE_BASE64 < simple-notes-release.jks.b64
|
|
||||||
|
|
||||||
# Set KEYSTORE_PASSWORD (will prompt for input)
|
|
||||||
gh secret set KEYSTORE_PASSWORD
|
|
||||||
|
|
||||||
# Set KEY_PASSWORD (will prompt for input)
|
|
||||||
gh secret set KEY_PASSWORD
|
|
||||||
|
|
||||||
# Set KEY_ALIAS (value: simple-notes)
|
|
||||||
printf "simple-notes" | gh secret set KEY_ALIAS
|
|
||||||
```
|
|
||||||
|
|
||||||
Or manually via GitHub web interface:
|
|
||||||
1. Go to repository Settings → Secrets and variables → Actions
|
|
||||||
2. Click "New repository secret"
|
|
||||||
3. Add these secrets:
|
|
||||||
- `KEYSTORE_BASE64`: Paste content of `simple-notes-release.jks.b64`
|
|
||||||
- `KEYSTORE_PASSWORD`: Your keystore password
|
|
||||||
- `KEY_PASSWORD`: Your key password
|
|
||||||
- `KEY_ALIAS`: `simple-notes`
|
|
||||||
|
|
||||||
## Step 4: Verify Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check secrets are set
|
|
||||||
gh secret list
|
|
||||||
|
|
||||||
# Expected output:
|
|
||||||
# KEYSTORE_BASE64 Updated YYYY-MM-DD
|
|
||||||
# KEYSTORE_PASSWORD Updated YYYY-MM-DD
|
|
||||||
# KEY_PASSWORD Updated YYYY-MM-DD
|
|
||||||
# KEY_ALIAS Updated YYYY-MM-DD
|
|
||||||
```
|
|
||||||
|
|
||||||
## Step 5: Cleanup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Remove sensitive files (they're in .gitignore, but double-check)
|
|
||||||
rm simple-notes-release.jks.b64
|
|
||||||
rm -f android/key.properties # Generated by workflow
|
|
||||||
|
|
||||||
# Verify keystore is NOT tracked by git
|
|
||||||
git status | grep -i jks
|
|
||||||
# Should return nothing
|
|
||||||
```
|
|
||||||
|
|
||||||
## Step 6: Trigger First Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Commit and push to main
|
|
||||||
git add .
|
|
||||||
git commit -m "🚀 feat: Add GitHub Actions deployment workflow"
|
|
||||||
git push origin main
|
|
||||||
|
|
||||||
# Or manually trigger workflow
|
|
||||||
gh workflow run build-production-apk.yml
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
1. Go to GitHub repository → Actions tab
|
|
||||||
2. Check workflow run status
|
|
||||||
3. Once complete, go to Releases tab
|
|
||||||
4. Verify release was created with 3 APK variants
|
|
||||||
5. Download and test one of the APKs
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Build fails with "Keystore not found"
|
|
||||||
- Check `KEYSTORE_BASE64` secret is set correctly
|
|
||||||
- Verify base64 encoding was done without line breaks
|
|
||||||
|
|
||||||
### Build fails with "Incorrect password"
|
|
||||||
- Verify `KEYSTORE_PASSWORD` and `KEY_PASSWORD` are correct
|
|
||||||
- Re-set secrets if needed
|
|
||||||
|
|
||||||
### APK files not found
|
|
||||||
- Check build logs for errors in assembleRelease step
|
|
||||||
- Verify APK output paths match workflow expectations
|
|
||||||
|
|
||||||
### Updates don't work
|
|
||||||
- Ensure you're using the same keystore for all builds
|
|
||||||
- Verify `applicationId` in build.gradle.kts matches
|
|
||||||
|
|
||||||
## Security Notes
|
|
||||||
|
|
||||||
- ✅ Keystore is base64-encoded in GitHub secrets (secure)
|
|
||||||
- ✅ Passwords are stored in GitHub secrets (encrypted)
|
|
||||||
- ✅ `key.properties` and `.jks` files are in `.gitignore`
|
|
||||||
- ⚠️ Never commit keystore files to repository
|
|
||||||
- ⚠️ Keep backup of keystore in secure location
|
|
||||||
- ⚠️ Don't share keystore passwords
|
|
||||||
|
|
||||||
## Versioning
|
|
||||||
|
|
||||||
Versions follow this pattern:
|
|
||||||
- **Version Name**: `YYYY.MM.DD` (e.g., `2025.01.15`)
|
|
||||||
- **Version Code**: GitHub run number (e.g., `42`)
|
|
||||||
- **Release Tag**: `vYYYY.MM.DD-prod.BUILD` (e.g., `v2025.01.15-prod.42`)
|
|
||||||
|
|
||||||
This ensures:
|
|
||||||
- Semantic versioning based on release date
|
|
||||||
- Incremental version codes for Play Store compatibility
|
|
||||||
- Clear distinction between builds
|
|
||||||
|
|
||||||
## APK Variants
|
|
||||||
|
|
||||||
The workflow generates 3 APK variants:
|
|
||||||
|
|
||||||
1. **Universal APK** (~5 MB)
|
|
||||||
- Works on all devices
|
|
||||||
- Larger file size
|
|
||||||
- Recommended for most users
|
|
||||||
|
|
||||||
2. **arm64-v8a APK** (~3 MB)
|
|
||||||
- For modern devices (2018+)
|
|
||||||
- Smaller, faster
|
|
||||||
- 64-bit ARM processors
|
|
||||||
|
|
||||||
3. **armeabi-v7a APK** (~3 MB)
|
|
||||||
- For older devices
|
|
||||||
- 32-bit ARM processors
|
|
||||||
|
|
||||||
Users can choose based on their device - Obtanium auto-updates work with all variants.
|
|
||||||
2338
IMPROVEMENT_PLAN.md
2338
IMPROVEMENT_PLAN.md
File diff suppressed because it is too large
Load Diff
269
QUICKSTART.en.md
Normal file
269
QUICKSTART.en.md
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
# Quick Start Guide - Simple Notes Sync 📝
|
||||||
|
|
||||||
|
> Step-by-step installation and setup guide
|
||||||
|
|
||||||
|
**🌍 Languages:** [Deutsch](QUICKSTART.md) · **English**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- ✅ Android 8.0+ smartphone/tablet
|
||||||
|
- ✅ WiFi connection
|
||||||
|
- ✅ Own server with Docker (optional - for self-hosting)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Option 1: With own server (Self-Hosted) 🏠
|
||||||
|
|
||||||
|
### Step 1: Setup WebDAV Server
|
||||||
|
|
||||||
|
On your server (e.g. Raspberry Pi, NAS, VPS):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone repository
|
||||||
|
git clone https://github.com/inventory69/simple-notes-sync.git
|
||||||
|
cd simple-notes-sync/server
|
||||||
|
|
||||||
|
# Configure environment variables
|
||||||
|
cp .env.example .env
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
**Adjust in `.env`:**
|
||||||
|
```env
|
||||||
|
WEBDAV_PASSWORD=your-secure-password-here
|
||||||
|
```
|
||||||
|
|
||||||
|
**Start server:**
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
**Find IP address:**
|
||||||
|
```bash
|
||||||
|
ip addr show | grep "inet " | grep -v 127.0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
➡️ **Note down:** `http://YOUR-SERVER-IP:8080/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 2: Install App
|
||||||
|
|
||||||
|
1. **Download APK:** [Latest version](https://github.com/inventory69/simple-notes-sync/releases/latest)
|
||||||
|
- Choose: `simple-notes-sync-vX.X.X-standard-universal.apk`
|
||||||
|
|
||||||
|
2. **Allow installation:**
|
||||||
|
- Android: Settings → Security → Enable "Unknown sources" for your browser
|
||||||
|
|
||||||
|
3. **Open and install APK**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 3: Configure App
|
||||||
|
|
||||||
|
1. **Open app**
|
||||||
|
|
||||||
|
2. **Open settings** (⚙️ icon top right)
|
||||||
|
|
||||||
|
3. **Configure server settings:**
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|------|------|
|
||||||
|
| **WebDAV Server URL** | `http://YOUR-SERVER-IP:8080/` |
|
||||||
|
| **Username** | `noteuser` |
|
||||||
|
| **Password** | (your password from `.env`) |
|
||||||
|
| **Gateway SSID** | Name of your WiFi network |
|
||||||
|
|
||||||
|
4. **Press "Test connection"**
|
||||||
|
- ✅ Success? → Continue to step 4
|
||||||
|
- ❌ Error? → See [Troubleshooting](#troubleshooting)
|
||||||
|
|
||||||
|
5. **Enable auto-sync** (toggle switch)
|
||||||
|
|
||||||
|
6. **Choose sync interval:**
|
||||||
|
- **15 min** - Maximum currency (~0.8% battery/day)
|
||||||
|
- **30 min** - Recommended (~0.4% battery/day) ⭐
|
||||||
|
- **60 min** - Maximum battery life (~0.2% battery/day)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 4: Create First Note
|
||||||
|
|
||||||
|
1. Back to main view (← arrow)
|
||||||
|
|
||||||
|
2. **"Add note"** (+ icon)
|
||||||
|
|
||||||
|
3. Enter title and text
|
||||||
|
|
||||||
|
4. **Save** (💾 icon)
|
||||||
|
|
||||||
|
5. **Wait for auto-sync** (or manually: ⚙️ → "Sync now")
|
||||||
|
|
||||||
|
🎉 **Done!** Your notes will be automatically synchronized!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Option 2: Local notes only (no server) 📱
|
||||||
|
|
||||||
|
You can also use Simple Notes **without a server**:
|
||||||
|
|
||||||
|
1. **Install app** (see step 2 above)
|
||||||
|
|
||||||
|
2. **Use without server configuration:**
|
||||||
|
- Notes are only stored locally
|
||||||
|
- No auto-sync
|
||||||
|
- Perfect for offline-only use
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔋 Disable Battery Optimization
|
||||||
|
|
||||||
|
For reliable auto-sync:
|
||||||
|
|
||||||
|
1. **Settings** → **Apps** → **Simple Notes Sync**
|
||||||
|
|
||||||
|
2. **Battery** → **Battery usage**
|
||||||
|
|
||||||
|
3. Select: **"Don't optimize"** or **"Unrestricted"**
|
||||||
|
|
||||||
|
💡 **Note:** Android Doze Mode may still delay sync in standby (~60 min). This is normal and affects all apps.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Sync Intervals in Detail
|
||||||
|
|
||||||
|
| Interval | Syncs/day | Battery/day | Battery/sync | Use case |
|
||||||
|
|-----------|-----------|----------|-----------|----------------|
|
||||||
|
| **15 min** | ~96 | ~0.8% (~23 mAh) | ~0.008% | ⚡ Maximum currency (multiple devices) |
|
||||||
|
| **30 min** | ~48 | ~0.4% (~12 mAh) | ~0.008% | ✓ **Recommended** - balanced |
|
||||||
|
| **60 min** | ~24 | ~0.2% (~6 mAh) | ~0.008% | 🔋 Maximum battery life |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Troubleshooting
|
||||||
|
|
||||||
|
### Connection test fails
|
||||||
|
|
||||||
|
**Problem:** "Connection failed" during test
|
||||||
|
|
||||||
|
**Solutions:**
|
||||||
|
|
||||||
|
1. **Server running?**
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
# Should show "Up"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Same WiFi?**
|
||||||
|
- Smartphone and server must be on same network
|
||||||
|
- Check SSID in app settings
|
||||||
|
|
||||||
|
3. **IP address correct?**
|
||||||
|
```bash
|
||||||
|
ip addr show | grep "inet "
|
||||||
|
# Check if IP in URL matches
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Firewall?**
|
||||||
|
```bash
|
||||||
|
# Open port 8080 (if firewall active)
|
||||||
|
sudo ufw allow 8080/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Check server logs:**
|
||||||
|
```bash
|
||||||
|
docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Auto-sync not working
|
||||||
|
|
||||||
|
**Problem:** Notes are not automatically synchronized
|
||||||
|
|
||||||
|
**Solutions:**
|
||||||
|
|
||||||
|
1. **Auto-sync enabled?**
|
||||||
|
- ⚙️ Settings → Toggle "Auto-sync" must be **ON**
|
||||||
|
|
||||||
|
2. **Battery optimization disabled?**
|
||||||
|
- See [Disable Battery Optimization](#-disable-battery-optimization)
|
||||||
|
|
||||||
|
3. **On correct WiFi?**
|
||||||
|
- Sync only works when SSID = Gateway SSID
|
||||||
|
- Check current SSID in Android settings
|
||||||
|
|
||||||
|
4. **Test manually:**
|
||||||
|
- ⚙️ Settings → "Sync now"
|
||||||
|
- Works? → Auto-sync should work too
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Notes not showing up
|
||||||
|
|
||||||
|
**Problem:** After installation, no notes visible even though they exist on server
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
|
||||||
|
1. **Manually sync once:**
|
||||||
|
- ⚙️ Settings → "Sync now"
|
||||||
|
|
||||||
|
2. **Check server data:**
|
||||||
|
```bash
|
||||||
|
docker compose exec webdav ls -la /data/
|
||||||
|
# Should show .json files
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Sync errors
|
||||||
|
|
||||||
|
**Problem:** Error message during sync
|
||||||
|
|
||||||
|
**Solutions:**
|
||||||
|
|
||||||
|
1. **"401 Unauthorized"** → Wrong password
|
||||||
|
- Check password in app settings
|
||||||
|
- Compare with `.env` on server
|
||||||
|
|
||||||
|
2. **"404 Not Found"** → Wrong URL
|
||||||
|
- Should end with `/` (e.g. `http://192.168.1.100:8080/`)
|
||||||
|
|
||||||
|
3. **"Network error"** → No connection
|
||||||
|
- See [Connection test fails](#connection-test-fails)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📱 Updates
|
||||||
|
|
||||||
|
### Automatic with Obtainium (recommended)
|
||||||
|
|
||||||
|
1. **[Install Obtainium](https://github.com/ImranR98/Obtanium/releases/latest)**
|
||||||
|
|
||||||
|
2. **Add app:**
|
||||||
|
- URL: `https://github.com/inventory69/simple-notes-sync`
|
||||||
|
- Enable auto-update
|
||||||
|
|
||||||
|
3. **Done!** Obtainium notifies you of new versions
|
||||||
|
|
||||||
|
### Manual
|
||||||
|
|
||||||
|
1. Download new APK from [Releases](https://github.com/inventory69/simple-notes-sync/releases/latest)
|
||||||
|
|
||||||
|
2. Install (overwrites old version)
|
||||||
|
|
||||||
|
3. All data remains intact!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🆘 Further Help
|
||||||
|
|
||||||
|
- **GitHub Issues:** [Report problem](https://github.com/inventory69/simple-notes-sync/issues)
|
||||||
|
- **Complete docs:** [DOCS.en.md](DOCS.en.md)
|
||||||
|
- **Server setup details:** [server/README.en.md](server/README.en.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Version:** 1.1.0 · **Created:** December 2025
|
||||||
465
QUICKSTART.md
465
QUICKSTART.md
@@ -1,208 +1,269 @@
|
|||||||
# 🚀 Quick Start Guide
|
# Quick Start Guide - Simple Notes Sync 📝
|
||||||
|
|
||||||
## ✅ Server ist bereits gestartet!
|
> Schritt-für-Schritt Anleitung zur Installation und Einrichtung
|
||||||
|
|
||||||
Der WebDAV-Server läuft bereits auf:
|
**🌍 Sprachen:** **Deutsch** · [English](QUICKSTART.en.md)
|
||||||
- **Lokal:** `http://localhost:8080/`
|
|
||||||
- **Im Netzwerk:** `http://192.168.0.188:8080/`
|
|
||||||
|
|
||||||
### Credentials
|
|
||||||
- **Username:** `noteuser`
|
|
||||||
- **Password:** `SimpleNotes2025!`
|
|
||||||
|
|
||||||
## 📱 Nächste Schritte: Android App erstellen
|
|
||||||
|
|
||||||
### Option 1: Mit Android Studio (Empfohlen)
|
|
||||||
|
|
||||||
1. **Android Studio öffnen**
|
|
||||||
```
|
|
||||||
File → New → New Project
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Template wählen:**
|
|
||||||
- Empty Views Activity
|
|
||||||
|
|
||||||
3. **Projekt konfigurieren:**
|
|
||||||
```
|
|
||||||
Name: Simple Notes
|
|
||||||
Package: com.example.simplenotes
|
|
||||||
Save location: /home/liq/gitProjects/simple-notes-sync/android/
|
|
||||||
Language: Kotlin
|
|
||||||
Minimum SDK: API 24 (Android 7.0)
|
|
||||||
Build configuration: Kotlin DSL
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Dependencies hinzufügen:**
|
|
||||||
|
|
||||||
Öffne `app/build.gradle.kts` und füge hinzu:
|
|
||||||
```kotlin
|
|
||||||
dependencies {
|
|
||||||
// ... existing dependencies
|
|
||||||
|
|
||||||
// WebDAV
|
|
||||||
implementation("com.github.thegrizzlylabs:sardine-android:0.8")
|
|
||||||
|
|
||||||
// Coroutines
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
|
|
||||||
|
|
||||||
// JSON
|
|
||||||
implementation("com.google.code.gson:gson:2.10.1")
|
|
||||||
|
|
||||||
// WorkManager
|
|
||||||
implementation("androidx.work:work-runtime-ktx:2.9.0")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Und in `settings.gradle.kts`:
|
|
||||||
```kotlin
|
|
||||||
dependencyResolutionManagement {
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
maven { url = uri("https://jitpack.io") } // Für Sardine
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Code implementieren:**
|
|
||||||
|
|
||||||
Alle Code-Beispiele findest du in:
|
|
||||||
- [ANDROID_GUIDE.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/ANDROID_GUIDE.md)
|
|
||||||
|
|
||||||
Kopiere der Reihe nach:
|
|
||||||
- `models/Note.kt`
|
|
||||||
- `models/SyncStatus.kt`
|
|
||||||
- `storage/NotesStorage.kt`
|
|
||||||
- `utils/DeviceIdGenerator.kt`
|
|
||||||
- `utils/NotificationHelper.kt`
|
|
||||||
- `utils/Extensions.kt`
|
|
||||||
- `utils/Constants.kt`
|
|
||||||
- UI Layouts aus dem Guide
|
|
||||||
- Activities (MainActivity, NoteEditorActivity, SettingsActivity)
|
|
||||||
- `sync/WebDavSyncService.kt`
|
|
||||||
- `sync/WifiSyncReceiver.kt`
|
|
||||||
- `sync/SyncWorker.kt`
|
|
||||||
- `adapters/NotesAdapter.kt`
|
|
||||||
|
|
||||||
6. **AndroidManifest.xml anpassen:**
|
|
||||||
```xml
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
||||||
|
|
||||||
<application
|
|
||||||
...
|
|
||||||
android:usesCleartextTraffic="true">
|
|
||||||
```
|
|
||||||
|
|
||||||
7. **Build & Run:**
|
|
||||||
```
|
|
||||||
Build → Make Project
|
|
||||||
Run → Run 'app'
|
|
||||||
```
|
|
||||||
|
|
||||||
8. **In der App konfigurieren:**
|
|
||||||
- Einstellungen öffnen
|
|
||||||
- Server URL: `http://192.168.0.188:8080/`
|
|
||||||
- Username: `noteuser`
|
|
||||||
- Password: `SimpleNotes2025!`
|
|
||||||
- Heim-WLAN SSID: `DeinWLANName`
|
|
||||||
- "Verbindung testen" → sollte erfolgreich sein ✓
|
|
||||||
|
|
||||||
### Option 2: Schritt-für-Schritt Implementation
|
|
||||||
|
|
||||||
Folge dem [IMPLEMENTATION_PLAN.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/IMPLEMENTATION_PLAN.md) mit 6 Sprints:
|
|
||||||
|
|
||||||
1. **Sprint 1:** Server & Foundation (bereits done ✓)
|
|
||||||
2. **Sprint 2:** Basic UI (4-6h)
|
|
||||||
3. **Sprint 3:** Settings & WebDAV (6h)
|
|
||||||
4. **Sprint 4:** Auto-Sync (6h)
|
|
||||||
5. **Sprint 5:** Conflicts & Errors (6h)
|
|
||||||
6. **Sprint 6:** Polish & Testing (6h)
|
|
||||||
|
|
||||||
## 🧪 Server testen
|
|
||||||
|
|
||||||
Der Server läuft bereits. Teste ihn:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Einfacher Test
|
|
||||||
curl -u noteuser:SimpleNotes2025! http://localhost:8080/
|
|
||||||
|
|
||||||
# Test-Notiz hochladen
|
|
||||||
echo '{"id":"test-123","title":"Test","content":"Hello World","createdAt":1703001234567,"updatedAt":1703001234567,"deviceId":"test","syncStatus":"SYNCED"}' > test.json
|
|
||||||
|
|
||||||
curl -u noteuser:SimpleNotes2025! \
|
|
||||||
-T test.json \
|
|
||||||
http://localhost:8080/test.json
|
|
||||||
|
|
||||||
# Test-Notiz abrufen
|
|
||||||
curl -u noteuser:SimpleNotes2025! http://localhost:8080/test.json
|
|
||||||
|
|
||||||
# Löschen
|
|
||||||
curl -u noteuser:SimpleNotes2025! \
|
|
||||||
-X DELETE \
|
|
||||||
http://localhost:8080/test.json
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📊 Server Management
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /home/liq/gitProjects/simple-notes-sync/server
|
|
||||||
|
|
||||||
# Status
|
|
||||||
docker-compose ps
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
docker-compose logs -f
|
|
||||||
|
|
||||||
# Stoppen
|
|
||||||
docker-compose down
|
|
||||||
|
|
||||||
# Neu starten
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
# Daten ansehen
|
|
||||||
ls -la notes-data/
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Troubleshooting
|
|
||||||
|
|
||||||
### Server nicht erreichbar von Android
|
|
||||||
|
|
||||||
1. **Firewall prüfen:**
|
|
||||||
```bash
|
|
||||||
sudo ufw status
|
|
||||||
sudo ufw allow 8080
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Ping-Test:**
|
|
||||||
```bash
|
|
||||||
ping 192.168.0.188
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Port-Test:**
|
|
||||||
```bash
|
|
||||||
telnet 192.168.0.188 8080
|
|
||||||
```
|
|
||||||
|
|
||||||
### Permission Denied in Android
|
|
||||||
|
|
||||||
- Android 13+: POST_NOTIFICATIONS Permission akzeptieren
|
|
||||||
- Internet Permission in Manifest vorhanden?
|
|
||||||
- `usesCleartextTraffic="true"` gesetzt?
|
|
||||||
|
|
||||||
## 📚 Weitere Hilfe
|
|
||||||
|
|
||||||
- **Vollständige Doku:** [project-docs/simple-notes-sync](https://github.com/inventory69/project-docs/tree/main/simple-notes-sync)
|
|
||||||
- **Android Code:** [ANDROID_GUIDE.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/ANDROID_GUIDE.md)
|
|
||||||
- **Server Setup:** [SERVER_SETUP.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/SERVER_SETUP.md)
|
|
||||||
- **Notifications:** [NOTIFICATIONS.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/NOTIFICATIONS.md)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Server Status:** ✅ Running on `http://192.168.0.188:8080/`
|
## Voraussetzungen
|
||||||
**Next:** Android App in Android Studio erstellen
|
|
||||||
**Estimated Time:** 18-24 Stunden für vollständige App
|
|
||||||
|
|
||||||
Viel Erfolg! 🚀
|
- ✅ Android 8.0+ Smartphone/Tablet
|
||||||
|
- ✅ WLAN-Verbindung
|
||||||
|
- ✅ Eigener Server mit Docker (optional - für Self-Hosting)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Option 1: Mit eigenem Server (Self-Hosted) 🏠
|
||||||
|
|
||||||
|
### Schritt 1: WebDAV Server einrichten
|
||||||
|
|
||||||
|
Auf deinem Server (z.B. Raspberry Pi, NAS, VPS):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Repository klonen
|
||||||
|
git clone https://github.com/inventory69/simple-notes-sync.git
|
||||||
|
cd simple-notes-sync/server
|
||||||
|
|
||||||
|
# Umgebungsvariablen konfigurieren
|
||||||
|
cp .env.example .env
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
**In `.env` anpassen:**
|
||||||
|
```env
|
||||||
|
WEBDAV_PASSWORD=dein-sicheres-passwort-hier
|
||||||
|
```
|
||||||
|
|
||||||
|
**Server starten:**
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
**IP-Adresse finden:**
|
||||||
|
```bash
|
||||||
|
ip addr show | grep "inet " | grep -v 127.0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
➡️ **Notiere dir:** `http://DEINE-SERVER-IP:8080/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Schritt 2: App installieren
|
||||||
|
|
||||||
|
1. **APK herunterladen:** [Neueste Version](https://github.com/inventory69/simple-notes-sync/releases/latest)
|
||||||
|
- Wähle: `simple-notes-sync-vX.X.X-standard-universal.apk`
|
||||||
|
|
||||||
|
2. **Installation erlauben:**
|
||||||
|
- Android: Einstellungen → Sicherheit → "Unbekannte Quellen" für deinen Browser aktivieren
|
||||||
|
|
||||||
|
3. **APK öffnen und installieren**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Schritt 3: App konfigurieren
|
||||||
|
|
||||||
|
1. **App öffnen**
|
||||||
|
|
||||||
|
2. **Einstellungen öffnen** (⚙️ Icon oben rechts)
|
||||||
|
|
||||||
|
3. **Server-Einstellungen konfigurieren:**
|
||||||
|
|
||||||
|
| Feld | Wert |
|
||||||
|
|------|------|
|
||||||
|
| **WebDAV Server URL** | `http://DEINE-SERVER-IP:8080/` |
|
||||||
|
| **Benutzername** | `noteuser` |
|
||||||
|
| **Passwort** | (dein Passwort aus `.env`) |
|
||||||
|
| **Gateway SSID** | Name deines WLAN-Netzwerks |
|
||||||
|
|
||||||
|
4. **"Verbindung testen"** drücken
|
||||||
|
- ✅ Erfolg? → Weiter zu Schritt 4
|
||||||
|
- ❌ Fehler? → Siehe [Troubleshooting](#troubleshooting)
|
||||||
|
|
||||||
|
5. **Auto-Sync aktivieren** (Toggle Switch)
|
||||||
|
|
||||||
|
6. **Sync-Intervall wählen:**
|
||||||
|
- **15 Min** - Maximale Aktualität (~0.8% Akku/Tag)
|
||||||
|
- **30 Min** - Empfohlen (~0.4% Akku/Tag) ⭐
|
||||||
|
- **60 Min** - Maximale Akkulaufzeit (~0.2% Akku/Tag)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Schritt 4: Erste Notiz erstellen
|
||||||
|
|
||||||
|
1. Zurück zur Hauptansicht (← Pfeil)
|
||||||
|
|
||||||
|
2. **"Notiz hinzufügen"** (+ Icon)
|
||||||
|
|
||||||
|
3. Titel und Text eingeben
|
||||||
|
|
||||||
|
4. **Speichern** (💾 Icon)
|
||||||
|
|
||||||
|
5. **Warten auf Auto-Sync** (oder manuell: ⚙️ → "Jetzt synchronisieren")
|
||||||
|
|
||||||
|
🎉 **Fertig!** Deine Notizen werden automatisch synchronisiert!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Option 2: Nur lokale Notizen (kein Server) 📱
|
||||||
|
|
||||||
|
Du kannst Simple Notes auch **ohne Server** nutzen:
|
||||||
|
|
||||||
|
1. **App installieren** (siehe Schritt 2 oben)
|
||||||
|
|
||||||
|
2. **Ohne Server-Konfiguration verwenden:**
|
||||||
|
- Notizen werden nur lokal gespeichert
|
||||||
|
- Kein Auto-Sync
|
||||||
|
- Perfekt für reine Offline-Nutzung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔋 Akku-Optimierung deaktivieren
|
||||||
|
|
||||||
|
Für zuverlässigen Auto-Sync:
|
||||||
|
|
||||||
|
1. **Einstellungen** → **Apps** → **Simple Notes Sync**
|
||||||
|
|
||||||
|
2. **Akku** → **Akkuverbrauch**
|
||||||
|
|
||||||
|
3. Wähle: **"Nicht optimieren"** oder **"Unbeschränkt"**
|
||||||
|
|
||||||
|
💡 **Hinweis:** Android Doze Mode kann trotzdem Sync im Standby verzögern (~60 Min). Das ist normal und betrifft alle Apps.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Sync-Intervalle im Detail
|
||||||
|
|
||||||
|
| Intervall | Syncs/Tag | Akku/Tag | Akku/Sync | Anwendungsfall |
|
||||||
|
|-----------|-----------|----------|-----------|----------------|
|
||||||
|
| **15 Min** | ~96 | ~0.8% (~23 mAh) | ~0.008% | ⚡ Maximal aktuell (mehrere Geräte) |
|
||||||
|
| **30 Min** | ~48 | ~0.4% (~12 mAh) | ~0.008% | ✓ **Empfohlen** - ausgewogen |
|
||||||
|
| **60 Min** | ~24 | ~0.2% (~6 mAh) | ~0.008% | 🔋 Maximale Akkulaufzeit |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Troubleshooting
|
||||||
|
|
||||||
|
### Verbindungstest schlägt fehl
|
||||||
|
|
||||||
|
**Problem:** "Verbindung fehlgeschlagen" beim Test
|
||||||
|
|
||||||
|
**Lösungen:**
|
||||||
|
|
||||||
|
1. **Server läuft?**
|
||||||
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
# Sollte "Up" zeigen
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Gleiche WLAN?**
|
||||||
|
- Smartphone und Server müssen im selben Netzwerk sein
|
||||||
|
- Prüfe SSID in App-Einstellungen
|
||||||
|
|
||||||
|
3. **IP-Adresse korrekt?**
|
||||||
|
```bash
|
||||||
|
ip addr show | grep "inet "
|
||||||
|
# Prüfe ob IP in URL stimmt
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Firewall?**
|
||||||
|
```bash
|
||||||
|
# Port 8080 öffnen (falls Firewall aktiv)
|
||||||
|
sudo ufw allow 8080/tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Server-Logs prüfen:**
|
||||||
|
```bash
|
||||||
|
docker compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Auto-Sync funktioniert nicht
|
||||||
|
|
||||||
|
**Problem:** Notizen werden nicht automatisch synchronisiert
|
||||||
|
|
||||||
|
**Lösungen:**
|
||||||
|
|
||||||
|
1. **Auto-Sync aktiviert?**
|
||||||
|
- ⚙️ Einstellungen → Toggle "Auto-Sync" muss **AN** sein
|
||||||
|
|
||||||
|
2. **Akku-Optimierung deaktiviert?**
|
||||||
|
- Siehe [Akku-Optimierung](#-akku-optimierung-deaktivieren)
|
||||||
|
|
||||||
|
3. **Im richtigen WLAN?**
|
||||||
|
- Sync funktioniert nur wenn SSID = Gateway SSID
|
||||||
|
- Prüfe aktuelle SSID in Android-Einstellungen
|
||||||
|
|
||||||
|
4. **Manuell testen:**
|
||||||
|
- ⚙️ Einstellungen → "Jetzt synchronisieren"
|
||||||
|
- Funktioniert das? → Auto-Sync sollte auch funktionieren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Notizen werden nicht angezeigt
|
||||||
|
|
||||||
|
**Problem:** Nach Installation sind keine Notizen da, obwohl welche auf dem Server liegen
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
|
||||||
|
1. **Einmalig manuell synchronisieren:**
|
||||||
|
- ⚙️ Einstellungen → "Jetzt synchronisieren"
|
||||||
|
|
||||||
|
2. **Server-Daten prüfen:**
|
||||||
|
```bash
|
||||||
|
docker compose exec webdav ls -la /data/
|
||||||
|
# Sollte .json Dateien zeigen
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Fehler beim Sync
|
||||||
|
|
||||||
|
**Problem:** Fehlermeldung beim Synchronisieren
|
||||||
|
|
||||||
|
**Lösungen:**
|
||||||
|
|
||||||
|
1. **"401 Unauthorized"** → Passwort falsch
|
||||||
|
- Prüfe Passwort in App-Einstellungen
|
||||||
|
- Vergleiche mit `.env` auf Server
|
||||||
|
|
||||||
|
2. **"404 Not Found"** → URL falsch
|
||||||
|
- Sollte enden mit `/` (z.B. `http://192.168.1.100:8080/`)
|
||||||
|
|
||||||
|
3. **"Network error"** → Keine Verbindung
|
||||||
|
- Siehe [Verbindungstest schlägt fehl](#verbindungstest-schlägt-fehl)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📱 Updates
|
||||||
|
|
||||||
|
### Automatisch mit Obtainium (empfohlen)
|
||||||
|
|
||||||
|
1. **[Obtainium installieren](https://github.com/ImranR98/Obtanium/releases/latest)**
|
||||||
|
|
||||||
|
2. **App hinzufügen:**
|
||||||
|
- URL: `https://github.com/inventory69/simple-notes-sync`
|
||||||
|
- Auto-Update aktivieren
|
||||||
|
|
||||||
|
3. **Fertig!** Obtainium benachrichtigt dich bei neuen Versionen
|
||||||
|
|
||||||
|
### Manuell
|
||||||
|
|
||||||
|
1. Neue APK von [Releases](https://github.com/inventory69/simple-notes-sync/releases/latest) herunterladen
|
||||||
|
|
||||||
|
2. Installieren (überschreibt alte Version)
|
||||||
|
|
||||||
|
3. Alle Daten bleiben erhalten!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🆘 Weitere Hilfe
|
||||||
|
|
||||||
|
- **GitHub Issues:** [Problem melden](https://github.com/inventory69/simple-notes-sync/issues)
|
||||||
|
- **Vollständige Docs:** [DOCS.md](DOCS.md)
|
||||||
|
- **Server Setup Details:** [server/README.md](server/README.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Version:** 1.1.0 · **Erstellt:** Dezember 2025
|
||||||
|
|||||||
78
README.en.md
Normal file
78
README.en.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# Simple Notes Sync 📝
|
||||||
|
|
||||||
|
> Minimalist offline notes with auto-sync to your own server
|
||||||
|
|
||||||
|
[](https://www.android.com/)
|
||||||
|
[](https://m3.material.io/)
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
|
**📱 [APK Download](https://github.com/inventory69/simple-notes-sync/releases/latest)** · **📖 [Documentation](DOCS.en.md)** · **🚀 [Quick Start](QUICKSTART.en.md)**
|
||||||
|
|
||||||
|
**🌍 Languages:** [Deutsch](README.md) · **English**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- 📝 Offline-First - Notes always available
|
||||||
|
- 🔄 Auto-Sync - Configurable intervals (15/30/60 min)
|
||||||
|
- 🏠 Self-Hosted - WebDAV on your server
|
||||||
|
- 🔐 Privacy-First - No cloud, no tracking
|
||||||
|
- 🔋 Battery-friendly - ~0.2-0.8% per day
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### 1. Server Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd server
|
||||||
|
cp .env.example .env
|
||||||
|
# Set password in .env
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
➡️ **Details:** [Server Setup Guide](server/README.en.md)
|
||||||
|
|
||||||
|
### 2. App Installation
|
||||||
|
|
||||||
|
1. [Download APK](https://github.com/inventory69/simple-notes-sync/releases/latest)
|
||||||
|
2. Install & open
|
||||||
|
3. ⚙️ Settings → Configure server
|
||||||
|
4. Enable auto-sync
|
||||||
|
|
||||||
|
➡️ **Details:** [Complete guide](QUICKSTART.en.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Documentation
|
||||||
|
|
||||||
|
- **[Quick Start Guide](QUICKSTART.en.md)** - Step-by-step guide for end users
|
||||||
|
- **[Server Setup](server/README.en.md)** - Configure WebDAV server
|
||||||
|
- **[Complete Docs](DOCS.en.md)** - Features, troubleshooting, build instructions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd android
|
||||||
|
./gradlew assembleStandardRelease
|
||||||
|
```
|
||||||
|
|
||||||
|
➡️ **Details:** [Build instructions in DOCS.en.md](DOCS.en.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
MIT License - see [LICENSE](LICENSE)
|
||||||
|
|
||||||
|
**v1.1.0** · Built with Kotlin + Material Design 3
|
||||||
127
README.md
127
README.md
@@ -1,130 +1,73 @@
|
|||||||
# Simple Notes Sync 📝
|
# Simple Notes Sync 📝
|
||||||
|
|
||||||
> **Minimalistische Android Notiz-App mit automatischer WLAN-Synchronisierung**
|
> Minimalistische Offline-Notizen mit Auto-Sync zu deinem eigenen Server
|
||||||
|
|
||||||
[](https://www.android.com/)
|
[](https://www.android.com/)
|
||||||
[](https://kotlinlang.org/)
|
|
||||||
[](https://m3.material.io/)
|
[](https://m3.material.io/)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
Schlanke Offline-Notizen ohne Schnickschnack - deine Daten bleiben bei dir. Automatische Synchronisierung zu deinem eigenen WebDAV-Server, kein Google, kein Microsoft, keine Cloud.
|
**📱 [APK Download](https://github.com/inventory69/simple-notes-sync/releases/latest)** · **📖 [Dokumentation](DOCS.md)** · **🚀 [Quick Start](QUICKSTART.md)**
|
||||||
|
|
||||||
## ✨ Features
|
**🌍 Sprachen:** **Deutsch** · [English](README.en.md)
|
||||||
|
|
||||||
- 📝 **Offline-First** - Notizen lokal gespeichert, immer verfügbar
|
|
||||||
- 🔄 **Auto-Sync** - Konfigurierbare Intervalle (15/30/60 Min.) mit ~0.2-0.8% Akku/Tag
|
|
||||||
- 🏠 **Self-Hosted** - Deine Daten auf deinem Server (WebDAV)
|
|
||||||
- 🎨 **Material Design 3** - Modern & Dynamic Theming
|
|
||||||
- 🔋 **Akkuschonend** - Optimiert für Hintergrund-Synchronisierung
|
|
||||||
- 🔐 **Privacy-First** - Kein Tracking, keine Analytics, keine Cloud
|
|
||||||
- 🚫 **Keine Berechtigungen** - Nur Internet für WebDAV Sync
|
|
||||||
|
|
||||||
## 📥 Quick Download
|
|
||||||
|
|
||||||
**Android APK:** [📱 Neueste Version herunterladen](https://github.com/inventory69/simple-notes-sync/releases/latest)
|
|
||||||
|
|
||||||
💡 **Tipp:** Nutze [Obtainium](https://github.com/ImranR98/Obtainium) für automatische Updates!
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Schnellstart
|
## Features
|
||||||
|
|
||||||
### 1️⃣ WebDAV Server starten
|
- 📝 Offline-First - Notizen immer verfügbar
|
||||||
|
- 🔄 Auto-Sync - Konfigurierbare Intervalle (15/30/60 Min)
|
||||||
|
- 🏠 Self-Hosted - WebDAV auf deinem Server
|
||||||
|
- 🔐 Privacy-First - Keine Cloud, kein Tracking
|
||||||
|
- 🔋 Akkuschonend - ~0.2-0.8% pro Tag
|
||||||
|
|
||||||
```fish
|
---
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### 1. Server Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
cd server
|
cd server
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
# Passwort in .env anpassen
|
# Passwort in .env setzen
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2️⃣ App installieren & konfigurieren
|
➡️ **Details:** [Server Setup Guide](server/README.md)
|
||||||
|
|
||||||
1. APK herunterladen und installieren
|
### 2. App Installation
|
||||||
2. App öffnen → **Einstellungen** (⚙️)
|
|
||||||
3. Server konfigurieren:
|
|
||||||
- URL: `http://192.168.0.XXX:8080/notes`
|
|
||||||
- Benutzername: `noteuser`
|
|
||||||
- Passwort: (aus `.env`)
|
|
||||||
4. **Auto-Sync aktivieren**
|
|
||||||
5. **Sync-Intervall wählen** (15/30/60 Min.)
|
|
||||||
|
|
||||||
**Fertig!** Notizen werden automatisch synchronisiert 🎉
|
1. [APK herunterladen](https://github.com/inventory69/simple-notes-sync/releases/latest)
|
||||||
|
2. Installieren & öffnen
|
||||||
|
3. ⚙️ Einstellungen → Server konfigurieren
|
||||||
|
4. Auto-Sync aktivieren
|
||||||
|
|
||||||
|
➡️ **Details:** [Vollständige Anleitung](QUICKSTART.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚙️ Sync-Intervalle
|
## 📚 Dokumentation
|
||||||
|
|
||||||
| Intervall | Akku/Tag | Anwendungsfall |
|
- **[Quick Start Guide](QUICKSTART.md)** - Schritt-für-Schritt Anleitung für Endbenutzer
|
||||||
|-----------|----------|----------------|
|
- **[Server Setup](server/README.md)** - WebDAV Server konfigurieren
|
||||||
| **15 Min** | ~0.8% (~23 mAh) | ⚡ Maximale Aktualität |
|
- **[Vollständige Docs](DOCS.md)** - Features, Troubleshooting, Build-Anleitung
|
||||||
| **30 Min** | ~0.4% (~12 mAh) | ✓ Empfohlen - Ausgewogen |
|
|
||||||
| **60 Min** | ~0.2% (~6 mAh) | 🔋 Maximale Akkulaufzeit |
|
|
||||||
|
|
||||||
💡 **Hinweis:** Android Doze Mode kann Sync im Standby auf ~60 Min. verzögern (betrifft alle Apps).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## <EFBFBD> Neue Features in v1.1.0
|
## 🛠️ Entwicklung
|
||||||
|
|
||||||
### Konfigurierbare Sync-Intervalle
|
```bash
|
||||||
- ⏱️ Wählbare Intervalle: 15/30/60 Minuten
|
|
||||||
- 📊 Transparente Akkuverbrauchs-Anzeige
|
|
||||||
- <20> Sofortige Anwendung ohne App-Neustart
|
|
||||||
|
|
||||||
### Über-Sektion
|
|
||||||
- <20> App-Version & Build-Datum
|
|
||||||
- 🌐 Links zu GitHub Repo & Entwickler
|
|
||||||
- ⚖️ Lizenz-Information
|
|
||||||
|
|
||||||
### Verbesserungen
|
|
||||||
- 🎯 Benutzerfreundliche Doze-Mode Erklärung
|
|
||||||
- 🔕 Keine störenden Sync-Fehler Toasts im Hintergrund
|
|
||||||
- 📝 Erweiterte Debug-Logs für Troubleshooting
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠️ Selbst bauen
|
|
||||||
|
|
||||||
```fish
|
|
||||||
cd android
|
cd android
|
||||||
./gradlew assembleStandardRelease
|
./gradlew assembleStandardRelease
|
||||||
# APK: android/app/build/outputs/apk/standard/release/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
➡️ **Details:** [Build-Anleitung in DOCS.md](DOCS.md)
|
||||||
|
|
||||||
## 🐛 Troubleshooting
|
|
||||||
|
|
||||||
### Auto-Sync funktioniert nicht
|
|
||||||
|
|
||||||
1. **Akku-Optimierung deaktivieren**
|
|
||||||
- Einstellungen → Apps → Simple Notes → Akku → Nicht optimieren
|
|
||||||
2. **WLAN-Verbindung prüfen**
|
|
||||||
- Funktioniert nur im selben Netzwerk wie Server
|
|
||||||
3. **Server-Status checken**
|
|
||||||
- Settings → "Verbindung testen"
|
|
||||||
|
|
||||||
### Server nicht erreichbar
|
|
||||||
|
|
||||||
```fish
|
|
||||||
# Status prüfen
|
|
||||||
docker compose ps
|
|
||||||
|
|
||||||
# Logs ansehen
|
|
||||||
docker compose logs -f
|
|
||||||
|
|
||||||
# IP-Adresse finden
|
|
||||||
ip addr show | grep "inet " | grep -v 127.0.0.1
|
|
||||||
```
|
|
||||||
|
|
||||||
Mehr Details: [📖 Dokumentation](DOCS.md)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🤝 Contributing
|
## 🤝 Contributing
|
||||||
|
|
||||||
Contributions sind willkommen! Bitte öffne ein Issue oder Pull Request.
|
Beiträge sind willkommen! Siehe [CONTRIBUTING.md](CONTRIBUTING.md) für Details.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -132,6 +75,4 @@ Contributions sind willkommen! Bitte öffne ein Issue oder Pull Request.
|
|||||||
|
|
||||||
MIT License - siehe [LICENSE](LICENSE)
|
MIT License - siehe [LICENSE](LICENSE)
|
||||||
|
|
||||||
---
|
**v1.1.0** · Gebaut mit Kotlin + Material Design 3
|
||||||
|
|
||||||
**Version:** 1.1.0 · **Status:** ✅ Produktiv · **Gebaut mit:** Kotlin + Material Design 3
|
|
||||||
|
|||||||
335
README.old.md
335
README.old.md
@@ -1,335 +0,0 @@
|
|||||||
# Simple Notes Sync 📝
|
|
||||||
|
|
||||||
> Minimalistische Android-App für Offline-Notizen mit automatischer WLAN-Synchronisierung
|
|
||||||
|
|
||||||
Eine schlanke Notiz-App ohne Schnickschnack - perfekt für schnelle Gedanken, die automatisch zu Hause synchronisiert werden.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ Features
|
|
||||||
|
|
||||||
- 📝 **Offline-first** - Notizen werden lokal gespeichert und sind immer verfügbar
|
|
||||||
- 🔄 **Auto-Sync** - Automatische Synchronisierung wenn du im Heimnetzwerk bist
|
|
||||||
- 🏠 **WebDAV Server** - Deine Daten bleiben bei dir (Docker-Container)
|
|
||||||
- 🔋 **Akkuschonend** - Nur ~0.4% Akkuverbrauch pro Tag
|
|
||||||
- 🚫 **Keine Cloud** - Keine Google, keine Microsoft, keine Drittanbieter
|
|
||||||
- 🔐 **Privacy** - Keine Tracking, keine Analytics, keine Standort-Berechtigungen
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📥 Installation
|
|
||||||
|
|
||||||
### Android App
|
|
||||||
|
|
||||||
**Option 1: APK herunterladen**
|
|
||||||
|
|
||||||
1. Neueste [Release](../../releases/latest) öffnen
|
|
||||||
2. `app-debug.apk` herunterladen
|
|
||||||
3. APK auf dem Handy installieren
|
|
||||||
|
|
||||||
**Option 2: Selbst bauen**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd android
|
|
||||||
./gradlew assembleDebug
|
|
||||||
# APK: android/app/build/outputs/apk/debug/app-debug.apk
|
|
||||||
```
|
|
||||||
|
|
||||||
### WebDAV Server
|
|
||||||
|
|
||||||
Der Server läuft als Docker-Container und speichert deine Notizen.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd server
|
|
||||||
cp .env.example .env
|
|
||||||
nano .env # Passwort anpassen!
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
**Server testen:**
|
|
||||||
```bash
|
|
||||||
curl -u noteuser:dein_passwort http://192.168.0.XXX:8080/
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 Schnellstart
|
|
||||||
|
|
||||||
1. **Server starten** (siehe oben)
|
|
||||||
2. **App installieren** und öffnen
|
|
||||||
3. **Einstellungen öffnen** (⚙️ Symbol oben rechts)
|
|
||||||
4. **Server konfigurieren:**
|
|
||||||
- Server-URL: `http://192.168.0.XXX:8080/notes`
|
|
||||||
- Benutzername: `noteuser`
|
|
||||||
- Passwort: (aus `.env` Datei)
|
|
||||||
- Auto-Sync: **AN**
|
|
||||||
5. **Fertig!** Notizen werden jetzt automatisch synchronisiert
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 Wie funktioniert Auto-Sync?
|
|
||||||
|
|
||||||
Die App prüft **alle 30 Minuten**, ob:
|
|
||||||
- ✅ WLAN verbunden ist
|
|
||||||
- ✅ Server im gleichen Netzwerk erreichbar ist
|
|
||||||
- ✅ Neue Notizen vorhanden sind
|
|
||||||
|
|
||||||
Wenn alle Bedingungen erfüllt → **Automatische Synchronisierung**
|
|
||||||
|
|
||||||
**Wichtig:** Funktioniert nur im selben Netzwerk wie der Server (kein Internet-Zugriff nötig!)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔋 Akkuverbrauch
|
|
||||||
|
|
||||||
| Komponente | Verbrauch/Tag |
|
|
||||||
|------------|---------------|
|
|
||||||
| WorkManager (alle 30 Min) | ~0.3% |
|
|
||||||
| Netzwerk-Checks | ~0.1% |
|
|
||||||
| **Total** | **~0.4%** |
|
|
||||||
|
|
||||||
Bei einem 3000 mAh Akku entspricht das ~12 mAh pro Tag.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📱 Screenshots
|
|
||||||
|
|
||||||
_TODO: Screenshots hinzufügen_
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠️ Technische Details
|
|
||||||
|
|
||||||
Mehr Infos zur Architektur und Implementierung findest du in der [technischen Dokumentation](DOCS.md).
|
|
||||||
|
|
||||||
**Stack:**
|
|
||||||
- **Android:** Kotlin, Material Design 3, WorkManager
|
|
||||||
- **Server:** Docker, WebDAV (bytemark/webdav)
|
|
||||||
- **Sync:** Sardine Android (WebDAV Client)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📄 Lizenz
|
|
||||||
|
|
||||||
[Lizenz hier einfügen]
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🤝 Beitragen
|
|
||||||
|
|
||||||
Contributions sind willkommen! Bitte öffne ein Issue oder Pull Request.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📄 Lizenz
|
|
||||||
|
|
||||||
MIT License - siehe [LICENSE](LICENSE)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Projekt Start:** 19. Dezember 2025
|
|
||||||
**Status:** ✅ Funktional & Produktiv
|
|
||||||
|
|
||||||
## 📖 Dokumentation
|
|
||||||
|
|
||||||
### In diesem Repository:
|
|
||||||
|
|
||||||
- **[QUICKSTART.md](QUICKSTART.md)** - Schnellstart-Anleitung
|
|
||||||
- **[server/README.md](server/README.md)** - Server-Verwaltung
|
|
||||||
|
|
||||||
### Vollständige Dokumentation (project-docs):
|
|
||||||
|
|
||||||
- [README.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/README.md) - Projekt-Übersicht & Architektur
|
|
||||||
- [IMPLEMENTATION_PLAN.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/IMPLEMENTATION_PLAN.md) - Detaillierter Sprint-Plan
|
|
||||||
- [SERVER_SETUP.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/SERVER_SETUP.md) - Server-Setup Details
|
|
||||||
- [ANDROID_GUIDE.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/ANDROID_GUIDE.md) - 📱 Kompletter Android-Code
|
|
||||||
- [NOTIFICATIONS.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/NOTIFICATIONS.md) - Notification-System Details
|
|
||||||
- [WINDOWS_SETUP.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/WINDOWS_SETUP.md) - 🪟 Windows + Android Studio Setup
|
|
||||||
- [CODE_REFERENCE.md](https://github.com/inventory69/project-docs/blob/main/simple-notes-sync/CODE_REFERENCE.md) - Schnelle Code-Referenz
|
|
||||||
|
|
||||||
## ⚙️ Server Konfiguration
|
|
||||||
|
|
||||||
**Standard-Credentials:**
|
|
||||||
- Username: `noteuser`
|
|
||||||
- Password: Siehe `.env` im `server/` Verzeichnis
|
|
||||||
|
|
||||||
**Server-URL:**
|
|
||||||
- Lokal: `http://localhost:8080/`
|
|
||||||
- Im Netzwerk: `http://YOUR_IP:8080/`
|
|
||||||
|
|
||||||
IP-Adresse finden:
|
|
||||||
```bash
|
|
||||||
ip addr show | grep "inet " | grep -v 127.0.0.1
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📱 Android App Setup
|
|
||||||
|
|
||||||
### Vorraussetzungen
|
|
||||||
|
|
||||||
- Android Studio Hedgehog (2023.1.1) oder neuer
|
|
||||||
- JDK 17
|
|
||||||
- Min SDK 24 (Android 7.0)
|
|
||||||
- Target SDK 34 (Android 14)
|
|
||||||
|
|
||||||
### In App konfigurieren
|
|
||||||
|
|
||||||
1. App starten
|
|
||||||
2. Einstellungen öffnen
|
|
||||||
3. Server-URL eintragen (z.B. `http://192.168.1.100:8080/`)
|
|
||||||
4. Username & Passwort eingeben
|
|
||||||
5. Heim-WLAN SSID eingeben
|
|
||||||
6. "Verbindung testen"
|
|
||||||
|
|
||||||
## 🔧 Entwicklung
|
|
||||||
|
|
||||||
### Server-Management
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Status prüfen
|
|
||||||
docker-compose ps
|
|
||||||
|
|
||||||
# Logs anschauen
|
|
||||||
docker-compose logs -f
|
|
||||||
|
|
||||||
# Neustarten
|
|
||||||
docker-compose restart
|
|
||||||
|
|
||||||
# Stoppen
|
|
||||||
docker-compose down
|
|
||||||
```
|
|
||||||
|
|
||||||
### Android-Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd android
|
|
||||||
./gradlew assembleDebug
|
|
||||||
|
|
||||||
# APK Location:
|
|
||||||
# app/build/outputs/apk/debug/app-debug.apk
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🧪 Testing
|
|
||||||
|
|
||||||
### Server-Test
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Testdatei hochladen
|
|
||||||
echo '{"id":"test","title":"Test","content":"Hello"}' > test.json
|
|
||||||
curl -u noteuser:password -T test.json http://localhost:8080/test.json
|
|
||||||
|
|
||||||
# Datei abrufen
|
|
||||||
curl -u noteuser:password http://localhost:8080/test.json
|
|
||||||
|
|
||||||
# Datei löschen
|
|
||||||
curl -u noteuser:password -X DELETE http://localhost:8080/test.json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Android-App
|
|
||||||
|
|
||||||
1. Notiz erstellen → speichern → in Liste sichtbar ✓
|
|
||||||
2. WLAN verbinden → Auto-Sync ✓
|
|
||||||
3. Server offline → Fehlermeldung ✓
|
|
||||||
4. Konflikt-Szenario → Auflösung ✓
|
|
||||||
|
|
||||||
## 📦 Deployment
|
|
||||||
|
|
||||||
### Server (Production)
|
|
||||||
|
|
||||||
**Option 1: Lokaler Server (Raspberry Pi, etc.)**
|
|
||||||
```bash
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
**Option 2: VPS (DigitalOcean, Hetzner, etc.)**
|
|
||||||
```bash
|
|
||||||
# Mit HTTPS (empfohlen)
|
|
||||||
# Zusätzlich: Reverse Proxy (nginx/Caddy) + Let's Encrypt
|
|
||||||
```
|
|
||||||
|
|
||||||
### Android App
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Release Build
|
|
||||||
./gradlew assembleRelease
|
|
||||||
|
|
||||||
# APK signieren
|
|
||||||
# Play Store Upload oder Direct Install
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔐 Security
|
|
||||||
|
|
||||||
**Entwicklung:**
|
|
||||||
- ✅ HTTP Basic Auth
|
|
||||||
- ✅ Nur im lokalen Netzwerk
|
|
||||||
|
|
||||||
**Produktion:**
|
|
||||||
- ⚠️ HTTPS mit SSL/TLS (empfohlen)
|
|
||||||
- ⚠️ Starkes Passwort
|
|
||||||
- ⚠️ Firewall-Regeln
|
|
||||||
- ⚠️ VPN für externen Zugriff
|
|
||||||
|
|
||||||
## 🐛 Troubleshooting
|
|
||||||
|
|
||||||
### Server startet nicht
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Port bereits belegt?
|
|
||||||
sudo netstat -tlnp | grep 8080
|
|
||||||
|
|
||||||
# Logs checken
|
|
||||||
docker-compose logs webdav
|
|
||||||
```
|
|
||||||
|
|
||||||
### Android kann nicht verbinden
|
|
||||||
|
|
||||||
- Ist Android im gleichen WLAN?
|
|
||||||
- Ist die Server-IP korrekt?
|
|
||||||
- Firewall blockiert Port 8080?
|
|
||||||
- Credentials korrekt?
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Ping zum Server
|
|
||||||
ping YOUR_SERVER_IP
|
|
||||||
|
|
||||||
# Port erreichbar?
|
|
||||||
telnet YOUR_SERVER_IP 8080
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📝 TODO / Roadmap
|
|
||||||
|
|
||||||
### Version 1.0 (MVP)
|
|
||||||
- [x] Docker WebDAV Server
|
|
||||||
- [ ] Android Basic CRUD
|
|
||||||
- [ ] Auto-Sync bei WLAN
|
|
||||||
- [ ] Error Handling
|
|
||||||
- [ ] Notifications
|
|
||||||
|
|
||||||
### Version 1.1
|
|
||||||
- [ ] Suche
|
|
||||||
- [ ] Dark Mode
|
|
||||||
- [ ] Markdown-Support
|
|
||||||
|
|
||||||
### Version 2.0
|
|
||||||
- [ ] Desktop-Client (Flutter Desktop)
|
|
||||||
- [ ] Tags/Kategorien
|
|
||||||
- [ ] Verschlüsselung
|
|
||||||
- [ ] Shared Notes
|
|
||||||
|
|
||||||
## 📄 License
|
|
||||||
|
|
||||||
MIT License - siehe [LICENSE](LICENSE)
|
|
||||||
|
|
||||||
## 👤 Author
|
|
||||||
|
|
||||||
Created for personal use - 2025
|
|
||||||
|
|
||||||
## 🙏 Acknowledgments
|
|
||||||
|
|
||||||
- [bytemark/webdav](https://hub.docker.com/r/bytemark/webdav) - Docker WebDAV Server
|
|
||||||
- [Sardine Android](https://github.com/thegrizzlylabs/sardine-android) - WebDAV Client
|
|
||||||
- [Android WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) - Background Tasks
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Project Start:** 19. Dezember 2025
|
|
||||||
**Status:** 🚧 In Development
|
|
||||||
5
android/.gitignore
vendored
5
android/.gitignore
vendored
@@ -13,3 +13,8 @@
|
|||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
|
# Signing configuration (contains sensitive keys)
|
||||||
|
key.properties
|
||||||
|
*.jks
|
||||||
|
*.keystore
|
||||||
|
|||||||
157
android/LOCAL_BUILDS.md
Normal file
157
android/LOCAL_BUILDS.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# Lokale Gradle Builds mit Release-Signierung
|
||||||
|
|
||||||
|
Dieses Dokument erklärt, wie du lokal signierte APKs erstellst, die mit den GitHub Release-APKs kompatibel sind.
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
- **GitHub Actions** erstellt signierte Release-APKs mit dem Production-Keystore
|
||||||
|
- **Lokale Debug-Builds** verwenden einen temporären Debug-Key
|
||||||
|
- ❌ **Resultat:** Nutzer können lokale Debug-APKs NICHT über Release-APKs installieren (Signature Mismatch!)
|
||||||
|
|
||||||
|
## Lösung: Lokale Release-Builds mit Production-Key
|
||||||
|
|
||||||
|
### 1️⃣ Keystore-Konfiguration einrichten
|
||||||
|
|
||||||
|
Du hast bereits den Keystore: `/android/app/simple-notes-release.jks`
|
||||||
|
|
||||||
|
Erstelle eine `key.properties` Datei im `/android/` Ordner:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/liq/gitProjects/simple-notes-sync/android
|
||||||
|
cp key.properties.example key.properties
|
||||||
|
```
|
||||||
|
|
||||||
|
Bearbeite `key.properties` mit den echten Werten:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
storeFile=simple-notes-release.jks
|
||||||
|
storePassword=<dein-keystore-password>
|
||||||
|
keyAlias=<dein-key-alias>
|
||||||
|
keyPassword=<dein-key-password>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wichtig:** Die Werte müssen **exakt** mit den GitHub Secrets übereinstimmen:
|
||||||
|
- `KEYSTORE_PASSWORD` → `storePassword`
|
||||||
|
- `KEY_ALIAS` → `keyAlias`
|
||||||
|
- `KEY_PASSWORD` → `keyPassword`
|
||||||
|
|
||||||
|
### 2️⃣ Lokal signierte Release-APKs bauen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd android
|
||||||
|
./gradlew assembleStandardRelease
|
||||||
|
```
|
||||||
|
|
||||||
|
Die signierten APKs findest du dann hier:
|
||||||
|
```
|
||||||
|
android/app/build/outputs/apk/standard/release/
|
||||||
|
├── app-standard-universal-release.apk
|
||||||
|
├── app-standard-arm64-v8a-release.apk
|
||||||
|
└── app-standard-armeabi-v7a-release.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3️⃣ F-Droid Flavor bauen (optional)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew assembleFdroidRelease
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4️⃣ Beide Flavors gleichzeitig bauen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew assembleStandardRelease assembleFdroidRelease
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verifizierung der Signatur
|
||||||
|
|
||||||
|
Um zu prüfen, ob dein lokaler Build die gleiche Signatur wie die Release-Builds hat:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Signatur von lokalem Build anzeigen
|
||||||
|
keytool -printcert -jarfile app/build/outputs/apk/standard/release/app-standard-universal-release.apk
|
||||||
|
|
||||||
|
# Signatur von GitHub Release-APK anzeigen (zum Vergleich)
|
||||||
|
keytool -printcert -jarfile ~/Downloads/simple-notes-sync-v1.1.0-standard-universal.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
Die **SHA256** Fingerprints müssen **identisch** sein!
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### ❌ Build schlägt fehl: "Keystore not found"
|
||||||
|
|
||||||
|
**Problem:** `key.properties` oder Keystore-Datei fehlt
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Prüfe, ob `key.properties` existiert: `ls -la key.properties`
|
||||||
|
2. Prüfe, ob der Keystore existiert: `ls -la app/simple-notes-release.jks`
|
||||||
|
|
||||||
|
### ❌ "Signature mismatch" beim Update
|
||||||
|
|
||||||
|
**Problem:** Der lokale Build verwendet einen anderen Key als die Release-Builds
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Vergleiche die Signaturen mit `keytool` (siehe oben)
|
||||||
|
2. Stelle sicher, dass `key.properties` die **exakten** GitHub Secret-Werte enthält
|
||||||
|
3. Deinstalliere die alte Version und installiere die neue (als letzter Ausweg)
|
||||||
|
|
||||||
|
### ❌ Build verwendet Debug-Signatur
|
||||||
|
|
||||||
|
**Problem:** `build.gradle.kts` findet `key.properties` nicht
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
```bash
|
||||||
|
# Prüfe, ob die Datei im richtigen Verzeichnis liegt
|
||||||
|
ls -la android/key.properties # ✅ Richtig
|
||||||
|
ls -la android/app/key.properties # ❌ Falsch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sicherheitshinweise
|
||||||
|
|
||||||
|
⚠️ **NIEMALS** diese Dateien committen:
|
||||||
|
- `key.properties` (in `.gitignore`)
|
||||||
|
- `*.jks` / `*.keystore` (in `.gitignore`)
|
||||||
|
|
||||||
|
✅ **Schon in `.gitignore`:**
|
||||||
|
```gitignore
|
||||||
|
key.properties
|
||||||
|
*.jks
|
||||||
|
*.keystore
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ Die GitHub Secrets (`KEYSTORE_PASSWORD`, etc.) und die lokale `key.properties` müssen **synchron** bleiben!
|
||||||
|
|
||||||
|
## Workflow-Vergleich
|
||||||
|
|
||||||
|
### GitHub Actions Build
|
||||||
|
```yaml
|
||||||
|
- Lädt Keystore aus Base64 Secret
|
||||||
|
- Erstellt key.properties aus Secrets
|
||||||
|
- Baut mit: ./gradlew assembleStandardRelease
|
||||||
|
- ✅ Produktions-signiert
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lokaler Build
|
||||||
|
```bash
|
||||||
|
# Mit key.properties konfiguriert:
|
||||||
|
./gradlew assembleStandardRelease
|
||||||
|
# ✅ Produktions-signiert (gleiche Signatur wie GitHub!)
|
||||||
|
|
||||||
|
# Ohne key.properties:
|
||||||
|
./gradlew assembleStandardRelease
|
||||||
|
# ⚠️ Debug-signiert (inkompatibel mit Releases!)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Release-APK bauen (signiert, klein, optimiert)
|
||||||
|
./gradlew assembleStandardRelease
|
||||||
|
|
||||||
|
# Debug-APK bauen (unsigniert, groß, debuggable)
|
||||||
|
./gradlew assembleStandardDebug
|
||||||
|
|
||||||
|
# APK per HTTP Server verteilen
|
||||||
|
cd app/build/outputs/apk/standard/release
|
||||||
|
python3 -m http.server 8892
|
||||||
|
```
|
||||||
@@ -17,8 +17,8 @@ android {
|
|||||||
applicationId = "dev.dettmer.simplenotes"
|
applicationId = "dev.dettmer.simplenotes"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 2 // 🔥 F-Droid Release v1.1.0
|
versionCode = 3 // 🔥 Bugfix: Spurious Sync Error Notifications + Sync Icon Bug
|
||||||
versionName = "1.1.0" // 🔥 Configurable Sync Interval + About Section
|
versionName = "1.1.1" // 🔥 Bugfix: Server-Erreichbarkeits-Check + Notification-Improvements
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private lateinit var adapter: NotesAdapter
|
private lateinit var adapter: NotesAdapter
|
||||||
private val storage by lazy { NotesStorage(this) }
|
private val storage by lazy { NotesStorage(this) }
|
||||||
|
|
||||||
|
// Track pending deletions to prevent flicker when notes reload
|
||||||
|
private val pendingDeletions = mutableSetOf<String>()
|
||||||
|
|
||||||
private val prefs by lazy {
|
private val prefs by lazy {
|
||||||
getSharedPreferences(Constants.PREFS_NAME, Context.MODE_PRIVATE)
|
getSharedPreferences(Constants.PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
}
|
}
|
||||||
@@ -91,6 +94,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
Logger.enableFileLogging(this)
|
Logger.enableFileLogging(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alte Sync-Notifications beim App-Start löschen
|
||||||
|
NotificationHelper.clearSyncNotifications(this)
|
||||||
|
|
||||||
// Permission für Notifications (Android 13+)
|
// Permission für Notifications (Android 13+)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
requestNotificationPermission()
|
requestNotificationPermission()
|
||||||
@@ -117,7 +123,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
Logger.d(TAG, "📡 BroadcastReceiver registered (sync-completed)")
|
Logger.d(TAG, "📡 BroadcastReceiver registered (sync-completed)")
|
||||||
|
|
||||||
// Reload notes
|
// Reload notes (scroll to top wird in loadNotes() gemacht)
|
||||||
loadNotes()
|
loadNotes()
|
||||||
|
|
||||||
// Trigger Auto-Sync beim App-Wechsel in Vordergrund (Toast)
|
// Trigger Auto-Sync beim App-Wechsel in Vordergrund (Toast)
|
||||||
@@ -142,10 +148,21 @@ class MainActivity : AppCompatActivity() {
|
|||||||
// Update last sync timestamp
|
// Update last sync timestamp
|
||||||
prefs.edit().putLong(PREF_LAST_AUTO_SYNC_TIME, System.currentTimeMillis()).apply()
|
prefs.edit().putLong(PREF_LAST_AUTO_SYNC_TIME, System.currentTimeMillis()).apply()
|
||||||
|
|
||||||
// GLEICHER Sync-Code wie manueller Sync (funktioniert!)
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
try {
|
try {
|
||||||
val syncService = WebDavSyncService(this@MainActivity)
|
val syncService = WebDavSyncService(this@MainActivity)
|
||||||
|
|
||||||
|
// ⭐ WICHTIG: Server-Erreichbarkeits-Check VOR Sync (wie in SyncWorker)
|
||||||
|
val isReachable = withContext(Dispatchers.IO) {
|
||||||
|
syncService.isServerReachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isReachable) {
|
||||||
|
Logger.d(TAG, "⏭️ Auto-sync ($source): Server not reachable - skipping silently")
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server ist erreichbar → Sync durchführen
|
||||||
val result = withContext(Dispatchers.IO) {
|
val result = withContext(Dispatchers.IO) {
|
||||||
syncService.syncNotes()
|
syncService.syncNotes()
|
||||||
}
|
}
|
||||||
@@ -236,6 +253,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val note = adapter.currentList[position]
|
val note = adapter.currentList[position]
|
||||||
val notesCopy = adapter.currentList.toMutableList()
|
val notesCopy = adapter.currentList.toMutableList()
|
||||||
|
|
||||||
|
// Track pending deletion to prevent flicker
|
||||||
|
pendingDeletions.add(note.id)
|
||||||
|
|
||||||
// Remove from list immediately for visual feedback
|
// Remove from list immediately for visual feedback
|
||||||
notesCopy.removeAt(position)
|
notesCopy.removeAt(position)
|
||||||
adapter.submitList(notesCopy)
|
adapter.submitList(notesCopy)
|
||||||
@@ -246,13 +266,15 @@ class MainActivity : AppCompatActivity() {
|
|||||||
"Notiz gelöscht",
|
"Notiz gelöscht",
|
||||||
Snackbar.LENGTH_LONG
|
Snackbar.LENGTH_LONG
|
||||||
).setAction("RÜCKGÄNGIG") {
|
).setAction("RÜCKGÄNGIG") {
|
||||||
// UNDO: Restore note in list
|
// UNDO: Remove from pending deletions and restore
|
||||||
|
pendingDeletions.remove(note.id)
|
||||||
loadNotes()
|
loadNotes()
|
||||||
}.addCallback(object : Snackbar.Callback() {
|
}.addCallback(object : Snackbar.Callback() {
|
||||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||||
if (event != DISMISS_EVENT_ACTION) {
|
if (event != DISMISS_EVENT_ACTION) {
|
||||||
// Snackbar dismissed without UNDO → Actually delete the note
|
// Snackbar dismissed without UNDO → Actually delete the note
|
||||||
storage.deleteNote(note.id)
|
storage.deleteNote(note.id)
|
||||||
|
pendingDeletions.remove(note.id)
|
||||||
loadNotes()
|
loadNotes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,10 +298,21 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun loadNotes() {
|
private fun loadNotes() {
|
||||||
val notes = storage.loadAllNotes()
|
val notes = storage.loadAllNotes()
|
||||||
adapter.submitList(notes)
|
|
||||||
|
// Filter out notes that are pending deletion (prevent flicker)
|
||||||
|
val filteredNotes = notes.filter { it.id !in pendingDeletions }
|
||||||
|
|
||||||
|
// Submit list with callback to scroll to top after list is updated
|
||||||
|
adapter.submitList(filteredNotes) {
|
||||||
|
// Scroll to top after list update is complete
|
||||||
|
// Wichtig: Nach dem Erstellen/Bearbeiten einer Notiz
|
||||||
|
if (filteredNotes.isNotEmpty()) {
|
||||||
|
recyclerViewNotes.scrollToPosition(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Material 3 Empty State Card
|
// Material 3 Empty State Card
|
||||||
emptyStateCard.visibility = if (notes.isEmpty()) {
|
emptyStateCard.visibility = if (filteredNotes.isEmpty()) {
|
||||||
android.view.View.VISIBLE
|
android.view.View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
android.view.View.GONE
|
android.view.View.GONE
|
||||||
@@ -305,8 +338,21 @@ class MainActivity : AppCompatActivity() {
|
|||||||
try {
|
try {
|
||||||
showToast("Starte Synchronisation...")
|
showToast("Starte Synchronisation...")
|
||||||
|
|
||||||
// Start sync
|
// Create sync service
|
||||||
val syncService = WebDavSyncService(this@MainActivity)
|
val syncService = WebDavSyncService(this@MainActivity)
|
||||||
|
|
||||||
|
// ⭐ WICHTIG: Server-Erreichbarkeits-Check VOR Sync (wie in SyncWorker)
|
||||||
|
val isReachable = withContext(Dispatchers.IO) {
|
||||||
|
syncService.isServerReachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isReachable) {
|
||||||
|
Logger.d(TAG, "⏭️ Manual Sync: Server not reachable - aborting")
|
||||||
|
showToast("Server nicht erreichbar")
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server ist erreichbar → Sync durchführen
|
||||||
val result = withContext(Dispatchers.IO) {
|
val result = withContext(Dispatchers.IO) {
|
||||||
syncService.syncNotes()
|
syncService.syncNotes()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.dettmer.simplenotes.adapters
|
package dev.dettmer.simplenotes.adapters
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@@ -11,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import dev.dettmer.simplenotes.R
|
import dev.dettmer.simplenotes.R
|
||||||
import dev.dettmer.simplenotes.models.Note
|
import dev.dettmer.simplenotes.models.Note
|
||||||
import dev.dettmer.simplenotes.models.SyncStatus
|
import dev.dettmer.simplenotes.models.SyncStatus
|
||||||
|
import dev.dettmer.simplenotes.utils.Constants
|
||||||
import dev.dettmer.simplenotes.utils.toReadableTime
|
import dev.dettmer.simplenotes.utils.toReadableTime
|
||||||
import dev.dettmer.simplenotes.utils.truncate
|
import dev.dettmer.simplenotes.utils.truncate
|
||||||
|
|
||||||
@@ -39,14 +41,25 @@ class NotesAdapter(
|
|||||||
textViewContent.text = note.content.truncate(100)
|
textViewContent.text = note.content.truncate(100)
|
||||||
textViewTimestamp.text = note.updatedAt.toReadableTime()
|
textViewTimestamp.text = note.updatedAt.toReadableTime()
|
||||||
|
|
||||||
// Sync status icon
|
// Sync Icon nur zeigen wenn Sync konfiguriert ist
|
||||||
val syncIcon = when (note.syncStatus) {
|
val prefs = itemView.context.getSharedPreferences(Constants.PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
SyncStatus.SYNCED -> android.R.drawable.ic_menu_upload
|
val serverUrl = prefs.getString(Constants.KEY_SERVER_URL, null)
|
||||||
SyncStatus.PENDING -> android.R.drawable.ic_popup_sync
|
val isSyncConfigured = !serverUrl.isNullOrEmpty()
|
||||||
SyncStatus.CONFLICT -> android.R.drawable.ic_dialog_alert
|
|
||||||
SyncStatus.LOCAL_ONLY -> android.R.drawable.ic_menu_save
|
if (isSyncConfigured) {
|
||||||
|
// Sync status icon
|
||||||
|
val syncIcon = when (note.syncStatus) {
|
||||||
|
SyncStatus.SYNCED -> android.R.drawable.ic_menu_upload
|
||||||
|
SyncStatus.PENDING -> android.R.drawable.ic_popup_sync
|
||||||
|
SyncStatus.CONFLICT -> android.R.drawable.ic_dialog_alert
|
||||||
|
SyncStatus.LOCAL_ONLY -> android.R.drawable.ic_menu_save
|
||||||
|
}
|
||||||
|
imageViewSyncStatus.setImageResource(syncIcon)
|
||||||
|
imageViewSyncStatus.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
// Sync nicht konfiguriert → Icon verstecken
|
||||||
|
imageViewSyncStatus.visibility = View.GONE
|
||||||
}
|
}
|
||||||
imageViewSyncStatus.setImageResource(syncIcon)
|
|
||||||
|
|
||||||
itemView.setOnClickListener {
|
itemView.setOnClickListener {
|
||||||
onNoteClick(note)
|
onNoteClick(note)
|
||||||
|
|||||||
@@ -52,7 +52,28 @@ class SyncWorker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Logger.d(TAG, "📍 Step 2: Before syncNotes() call")
|
Logger.d(TAG, "📍 Step 2: Checking server reachability (Pre-Check)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ⭐ KRITISCH: Server-Erreichbarkeits-Check VOR Sync
|
||||||
|
// Verhindert Fehler-Notifications in fremden WiFi-Netzen
|
||||||
|
// Wartet bis Netzwerk bereit ist (DHCP, Routing, Gateway)
|
||||||
|
if (!syncService.isServerReachable()) {
|
||||||
|
Logger.d(TAG, "⏭️ Server not reachable - skipping sync (no error)")
|
||||||
|
Logger.d(TAG, " Reason: Server offline/wrong network/network not ready/not configured")
|
||||||
|
Logger.d(TAG, " This is normal in foreign WiFi or during network initialization")
|
||||||
|
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Logger.d(TAG, "✅ SyncWorker.doWork() SUCCESS (silent skip)")
|
||||||
|
Logger.d(TAG, "═══════════════════════════════════════")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success zurückgeben (kein Fehler, Server ist halt nicht erreichbar)
|
||||||
|
return@withContext Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Logger.d(TAG, "📍 Step 3: Server reachable - proceeding with sync")
|
||||||
Logger.d(TAG, " SyncService: $syncService")
|
Logger.d(TAG, " SyncService: $syncService")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,13 +94,13 @@ class SyncWorker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Logger.d(TAG, "📍 Step 3: Processing result")
|
Logger.d(TAG, "📍 Step 4: Processing result")
|
||||||
Logger.d(TAG, "📦 Sync result: success=${result.isSuccess}, count=${result.syncedCount}, error=${result.errorMessage}")
|
Logger.d(TAG, "📦 Sync result: success=${result.isSuccess}, count=${result.syncedCount}, error=${result.errorMessage}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Logger.d(TAG, "📍 Step 4: Success path")
|
Logger.d(TAG, "📍 Step 5: Success path")
|
||||||
}
|
}
|
||||||
Logger.i(TAG, "✅ Sync successful: ${result.syncedCount} notes")
|
Logger.i(TAG, "✅ Sync successful: ${result.syncedCount} notes")
|
||||||
|
|
||||||
@@ -109,7 +130,7 @@ class SyncWorker(
|
|||||||
Result.success()
|
Result.success()
|
||||||
} else {
|
} else {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Logger.d(TAG, "📍 Step 4: Failure path")
|
Logger.d(TAG, "📍 Step 5: Failure path")
|
||||||
}
|
}
|
||||||
Logger.e(TAG, "❌ Sync failed: ${result.errorMessage}")
|
Logger.e(TAG, "❌ Sync failed: ${result.errorMessage}")
|
||||||
NotificationHelper.showSyncError(
|
NotificationHelper.showSyncError(
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.net.InetSocketAddress
|
|||||||
import java.net.NetworkInterface
|
import java.net.NetworkInterface
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.net.Socket
|
import java.net.Socket
|
||||||
|
import java.net.URL
|
||||||
import javax.net.SocketFactory
|
import javax.net.SocketFactory
|
||||||
|
|
||||||
class WebDavSyncService(private val context: Context) {
|
class WebDavSyncService(private val context: Context) {
|
||||||
@@ -188,6 +189,40 @@ class WebDavSyncService(private val context: Context) {
|
|||||||
return prefs.getString(Constants.KEY_SERVER_URL, null)
|
return prefs.getString(Constants.KEY_SERVER_URL, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft ob WebDAV-Server erreichbar ist (ohne Sync zu starten)
|
||||||
|
* Verwendet Socket-Check für schnelle Erreichbarkeitsprüfung
|
||||||
|
*
|
||||||
|
* @return true wenn Server erreichbar ist, false sonst
|
||||||
|
*/
|
||||||
|
suspend fun isServerReachable(): Boolean = withContext(Dispatchers.IO) {
|
||||||
|
return@withContext try {
|
||||||
|
val serverUrl = getServerUrl()
|
||||||
|
if (serverUrl == null) {
|
||||||
|
Logger.d(TAG, "❌ Server URL not configured")
|
||||||
|
return@withContext false
|
||||||
|
}
|
||||||
|
|
||||||
|
val url = URL(serverUrl)
|
||||||
|
val host = url.host
|
||||||
|
val port = if (url.port > 0) url.port else url.defaultPort
|
||||||
|
|
||||||
|
Logger.d(TAG, "🔍 Checking server reachability: $host:$port")
|
||||||
|
|
||||||
|
// Socket-Check mit 2s Timeout
|
||||||
|
// Gibt dem Netzwerk Zeit für Initialisierung (DHCP, Routing, Gateway)
|
||||||
|
val socket = Socket()
|
||||||
|
socket.connect(InetSocketAddress(host, port), 2000)
|
||||||
|
socket.close()
|
||||||
|
|
||||||
|
Logger.d(TAG, "✅ Server is reachable")
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.d(TAG, "❌ Server not reachable: ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun testConnection(): SyncResult = withContext(Dispatchers.IO) {
|
suspend fun testConnection(): SyncResult = withContext(Dispatchers.IO) {
|
||||||
return@withContext try {
|
return@withContext try {
|
||||||
val sardine = getSardine() ?: return@withContext SyncResult(
|
val sardine = getSardine() ?: return@withContext SyncResult(
|
||||||
|
|||||||
@@ -6,12 +6,15 @@ import android.app.PendingIntent
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import dev.dettmer.simplenotes.MainActivity
|
import dev.dettmer.simplenotes.MainActivity
|
||||||
|
|
||||||
object NotificationHelper {
|
object NotificationHelper {
|
||||||
|
|
||||||
|
private const val TAG = "NotificationHelper"
|
||||||
private const val CHANNEL_ID = "notes_sync_channel"
|
private const val CHANNEL_ID = "notes_sync_channel"
|
||||||
private const val CHANNEL_NAME = "Notizen Synchronisierung"
|
private const val CHANNEL_NAME = "Notizen Synchronisierung"
|
||||||
private const val CHANNEL_DESCRIPTION = "Benachrichtigungen über Sync-Status"
|
private const val CHANNEL_DESCRIPTION = "Benachrichtigungen über Sync-Status"
|
||||||
@@ -38,6 +41,17 @@ object NotificationHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Löscht alle Sync-Notifications
|
||||||
|
* Sollte beim App-Start aufgerufen werden um alte Notifications zu entfernen
|
||||||
|
*/
|
||||||
|
fun clearSyncNotifications(context: Context) {
|
||||||
|
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||||
|
as NotificationManager
|
||||||
|
manager.cancel(SYNC_NOTIFICATION_ID)
|
||||||
|
Logger.d(TAG, "🗑️ Cleared old sync notifications")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeigt Erfolgs-Notification nach Sync
|
* Zeigt Erfolgs-Notification nach Sync
|
||||||
*/
|
*/
|
||||||
@@ -240,6 +254,7 @@ object NotificationHelper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeigt Fehler-Notification
|
* Zeigt Fehler-Notification
|
||||||
|
* Auto-Cancel nach 30 Sekunden
|
||||||
*/
|
*/
|
||||||
fun showSyncError(context: Context, message: String) {
|
fun showSyncError(context: Context, message: String) {
|
||||||
// PendingIntent für App-Öffnung
|
// PendingIntent für App-Öffnung
|
||||||
@@ -266,5 +281,11 @@ object NotificationHelper {
|
|||||||
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE)
|
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE)
|
||||||
as NotificationManager
|
as NotificationManager
|
||||||
manager.notify(SYNC_NOTIFICATION_ID, notification)
|
manager.notify(SYNC_NOTIFICATION_ID, notification)
|
||||||
|
|
||||||
|
// ⭐ NEU: Auto-Cancel nach 30 Sekunden
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
|
manager.cancel(SYNC_NOTIFICATION_ID)
|
||||||
|
Logger.d(TAG, "🗑️ Auto-cancelled error notification after 30s timeout")
|
||||||
|
}, 30_000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
android/fastlane/metadata/android/de-DE/changelogs/3.txt
Normal file
20
android/fastlane/metadata/android/de-DE/changelogs/3.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
🐛 Bugfixes v1.1.1
|
||||||
|
|
||||||
|
✅ Keine Fehler-Notifications mehr in fremden WiFi-Netzwerken!
|
||||||
|
- Server-Erreichbarkeits-Check vor jedem Sync (2s Timeout)
|
||||||
|
- Stiller Abbruch wenn Server nicht erreichbar
|
||||||
|
- 80% schnellerer Abbruch: 2s statt 10+ Sekunden
|
||||||
|
|
||||||
|
✅ Keine Fehler beim WiFi-Connect / Nach-Hause-Kommen!
|
||||||
|
- Pre-Check wartet bis Netzwerk bereit ist (DHCP, Routing, Gateway)
|
||||||
|
- Kein Fehler mehr bei Netzwerk-Initialisierung
|
||||||
|
|
||||||
|
🔧 Notification-Verbesserungen:
|
||||||
|
- Alte Notifications werden beim App-Start gelöscht
|
||||||
|
- Fehler-Notifications verschwinden automatisch nach 30 Sekunden
|
||||||
|
- Bessere Batterie-Effizienz (keine langen Timeouts mehr)
|
||||||
|
|
||||||
|
📱 UI-Fixes:
|
||||||
|
- Sync-Icon wird nicht mehr angezeigt wenn Sync nicht konfiguriert ist
|
||||||
|
- Swipe-to-Delete: Kein Flackern mehr beim schnellen Löschen mehrerer Notizen
|
||||||
|
- Nach dem Speichern einer Notiz landet man automatisch ganz oben in der Liste
|
||||||
20
android/fastlane/metadata/android/en-US/changelogs/3.txt
Normal file
20
android/fastlane/metadata/android/en-US/changelogs/3.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
🐛 Bugfixes v1.1.1
|
||||||
|
|
||||||
|
✅ No more error notifications in foreign WiFi networks!
|
||||||
|
- Server reachability check before each sync (2s timeout)
|
||||||
|
- Silent abort when server is unreachable
|
||||||
|
- 80% faster abort: 2s instead of 10+ seconds
|
||||||
|
|
||||||
|
✅ No more errors when connecting to WiFi / arriving home!
|
||||||
|
- Pre-check waits until network is ready (DHCP, routing, gateway)
|
||||||
|
- No more errors during network initialization
|
||||||
|
|
||||||
|
🔧 Notification improvements:
|
||||||
|
- Old notifications are cleared on app start
|
||||||
|
- Error notifications disappear automatically after 30 seconds
|
||||||
|
- Better battery efficiency (no more long timeouts)
|
||||||
|
|
||||||
|
📱 UI fixes:
|
||||||
|
- Sync icon no longer shown when sync is not configured
|
||||||
|
- Swipe-to-delete: No more flickering when quickly deleting multiple notes
|
||||||
|
- After saving a note, you automatically land at the top of the list
|
||||||
37
android/fastlane/metadata/android/en-US/full_description.txt
Normal file
37
android/fastlane/metadata/android/en-US/full_description.txt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
Simple Notes Sync is a minimalist note-taking app with WebDAV synchronization.
|
||||||
|
|
||||||
|
KEY FEATURES:
|
||||||
|
|
||||||
|
• Create and edit simple notes
|
||||||
|
• WebDAV synchronization with your own server
|
||||||
|
• Automatic synchronization on home WiFi
|
||||||
|
• Configurable sync interval (15/30/60 minutes)
|
||||||
|
• Transparent battery usage display
|
||||||
|
• Material Design 3 with Dynamic Colors (Android 12+)
|
||||||
|
• Swipe-to-delete with confirmation dialog
|
||||||
|
• Server backup & restore
|
||||||
|
• Fully usable offline
|
||||||
|
• No ads, no trackers
|
||||||
|
|
||||||
|
PRIVACY:
|
||||||
|
|
||||||
|
Your data stays with you! The app only communicates with your own WebDAV server. No cloud services, no tracking libraries, no analytics tools.
|
||||||
|
|
||||||
|
SYNCHRONIZATION:
|
||||||
|
|
||||||
|
• Supports all WebDAV servers (Nextcloud, ownCloud, etc.)
|
||||||
|
• Configurable interval: 15, 30, or 60 minutes
|
||||||
|
• Measured battery consumption: only ~0.4% per day (at 30min)
|
||||||
|
• Doze Mode optimized for reliable background syncs
|
||||||
|
• Manual synchronization available anytime
|
||||||
|
• Conflict-free merging through timestamps
|
||||||
|
|
||||||
|
MATERIAL DESIGN 3:
|
||||||
|
|
||||||
|
• Modern user interface
|
||||||
|
• Dynamic Colors (Material You) on Android 12+
|
||||||
|
• Dark Mode support
|
||||||
|
• Intuitive gestures (Swipe-to-delete)
|
||||||
|
|
||||||
|
Open Source under MIT License
|
||||||
|
Source code: https://github.com/inventory69/simple-notes-sync
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Simple note-taking app with WebDAV synchronization
|
||||||
1
android/fastlane/metadata/android/en-US/title.txt
Normal file
1
android/fastlane/metadata/android/en-US/title.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Simple Notes Sync
|
||||||
21
android/key.properties.example
Normal file
21
android/key.properties.example
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Android Signing Configuration
|
||||||
|
#
|
||||||
|
# ANLEITUNG FÜR LOKALE BUILDS:
|
||||||
|
# 1. Kopiere diese Datei nach "key.properties" (ohne .example)
|
||||||
|
# 2. Fülle die Werte mit deinen echten Keystore-Daten aus
|
||||||
|
# 3. Die key.properties Datei ist in .gitignore und wird NICHT committet
|
||||||
|
#
|
||||||
|
# WICHTIG: Diese Datei darf NIEMALS ins Git-Repository gelangen!
|
||||||
|
# Sie enthält sensible Signing-Keys für die App-Veröffentlichung.
|
||||||
|
|
||||||
|
# Pfad zum Keystore (relativ zum android/app Ordner)
|
||||||
|
storeFile=simple-notes-release.jks
|
||||||
|
|
||||||
|
# Keystore Password
|
||||||
|
storePassword=DEIN_KEYSTORE_PASSWORD
|
||||||
|
|
||||||
|
# Key Alias (meist "key0" oder ein selbst gewählter Name)
|
||||||
|
keyAlias=DEIN_KEY_ALIAS
|
||||||
|
|
||||||
|
# Key Password
|
||||||
|
keyPassword=DEIN_KEY_PASSWORD
|
||||||
192
android/scripts/README.md
Normal file
192
android/scripts/README.md
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
# Android Build Scripts
|
||||||
|
|
||||||
|
Nützliche Scripts für die lokale Entwicklung und Release-Erstellung.
|
||||||
|
|
||||||
|
## 📜 Verfügbare Scripts
|
||||||
|
|
||||||
|
### 1. `create-keystore.fish` - Neuen Release-Keystore erstellen
|
||||||
|
|
||||||
|
**Wann verwenden:**
|
||||||
|
- ✅ Erstmaliges Setup des Projekts
|
||||||
|
- ✅ Keystore-Passwort vergessen
|
||||||
|
- ✅ Keystore beschädigt oder verloren
|
||||||
|
- ❌ **NICHT** verwenden, wenn bereits User existieren (macht alte APKs inkompatibel!)
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
```bash
|
||||||
|
cd /home/liq/gitProjects/simple-notes-sync/android
|
||||||
|
./scripts/create-keystore.fish
|
||||||
|
```
|
||||||
|
|
||||||
|
**Das Script:**
|
||||||
|
1. Erstellt einen neuen 4096-Bit RSA-Keystore
|
||||||
|
2. Generiert `app/simple-notes-release.jks`
|
||||||
|
3. Erstellt `key.properties` mit den Zugangsdaten
|
||||||
|
4. Zeigt Base64-kodierten Keystore für GitHub Secrets
|
||||||
|
5. Gibt SHA256-Fingerprint zur Verifikation aus
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- ✅ `app/simple-notes-release.jks` - Der Keystore
|
||||||
|
- ✅ `key.properties` - Lokale Signing-Konfiguration
|
||||||
|
- 📋 GitHub Secrets zum Kopieren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. `verify-secrets.fish` - GitHub Secrets & Keystore verifizieren
|
||||||
|
|
||||||
|
**Wann verwenden:**
|
||||||
|
- ✅ Nach `create-keystore.fish` zur Verifikation
|
||||||
|
- ✅ Vor einem Release-Build zum Troubleshooting
|
||||||
|
- ✅ Um zu prüfen ob alles korrekt konfiguriert ist
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
```bash
|
||||||
|
cd /home/liq/gitProjects/simple-notes-sync/android
|
||||||
|
./scripts/verify-secrets.fish
|
||||||
|
```
|
||||||
|
|
||||||
|
**Das Script prüft:**
|
||||||
|
- GitHub CLI Installation & Authentifizierung
|
||||||
|
- Ob alle 4 erforderlichen GitHub Secrets gesetzt sind
|
||||||
|
- Ob `key.properties` lokal existiert
|
||||||
|
- Ob der Keystore existiert
|
||||||
|
- Zeigt SHA256-Fingerprint des Keystores
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- ✅ Status aller Secrets
|
||||||
|
- ✅ Status der lokalen Konfiguration
|
||||||
|
- 💡 Empfehlungen bei Problemen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. `build-release-local.fish` - Lokal signierte Release-APKs bauen
|
||||||
|
|
||||||
|
**Wann verwenden:**
|
||||||
|
- ✅ Lokale Test-APKs erstellen, die mit Releases kompatibel sind
|
||||||
|
- ✅ APKs vor dem GitHub Release testen
|
||||||
|
- ✅ Schneller als GitHub Actions für Tests
|
||||||
|
|
||||||
|
**Voraussetzung:**
|
||||||
|
- `key.properties` muss existieren (via `create-keystore.fish` erstellt)
|
||||||
|
|
||||||
|
**Verwendung:**
|
||||||
|
```bash
|
||||||
|
cd /home/liq/gitProjects/simple-notes-sync/android
|
||||||
|
./scripts/build-release-local.fish
|
||||||
|
```
|
||||||
|
|
||||||
|
**Interaktive Auswahl:**
|
||||||
|
1. Standard Flavor (empfohlen)
|
||||||
|
2. F-Droid Flavor
|
||||||
|
3. Beide Flavors
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- `app/build/outputs/apk/standard/release/` - Signierte Standard APKs
|
||||||
|
- `app/build/outputs/apk/fdroid/release/` - Signierte F-Droid APKs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Kompletter Workflow (von 0 auf Release)
|
||||||
|
|
||||||
|
### Erstmaliges Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/liq/gitProjects/simple-notes-sync/android
|
||||||
|
|
||||||
|
# 1. Keystore erstellen (mit automatischer GitHub Secrets-Konfiguration!)
|
||||||
|
./scripts/create-keystore.fish
|
||||||
|
# → Folge den Anweisungen, speichere die Passwörter!
|
||||||
|
# → GitHub Secrets werden automatisch via GitHub CLI gesetzt
|
||||||
|
|
||||||
|
# 2. Verifiziere die Konfiguration
|
||||||
|
./scripts/verify-secrets.fish
|
||||||
|
# → Prüft ob alle Secrets gesetzt sind
|
||||||
|
# → Zeigt Keystore-Informationen
|
||||||
|
|
||||||
|
# 3. Teste lokalen Build
|
||||||
|
./scripts/build-release-local.fish
|
||||||
|
# → Wähle "1" für Standard Flavor
|
||||||
|
|
||||||
|
# 4. Verifiziere Signatur
|
||||||
|
keytool -printcert -jarfile app/build/outputs/apk/standard/release/app-standard-universal-release.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vor jedem Release
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Code committen und pushen
|
||||||
|
git add .
|
||||||
|
git commit -m "✨ Neue Features"
|
||||||
|
git push origin main
|
||||||
|
|
||||||
|
# 2. GitHub Actions erstellt automatisch Release
|
||||||
|
# → Workflow läuft: .github/workflows/build-production-apk.yml
|
||||||
|
# → Erstellt Release mit signierten APKs
|
||||||
|
|
||||||
|
# Optional: Lokalen Test-Build vorher
|
||||||
|
./scripts/build-release-local.fish
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Sicherheitshinweise
|
||||||
|
|
||||||
|
### ⚠️ Diese Dateien NIEMALS committen:
|
||||||
|
- `key.properties` - Enthält Keystore-Passwörter
|
||||||
|
- `*.jks` / `*.keystore` - Der Keystore selbst
|
||||||
|
- Beide sind bereits in `.gitignore`
|
||||||
|
|
||||||
|
### ✅ Diese Werte sicher speichern:
|
||||||
|
- Keystore-Passwort
|
||||||
|
- Key-Alias
|
||||||
|
- Key-Passwort
|
||||||
|
- Base64-kodierter Keystore (für GitHub Secrets)
|
||||||
|
|
||||||
|
**Empfehlung:** Nutze einen Passwort-Manager (Bitwarden, 1Password, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Troubleshooting
|
||||||
|
|
||||||
|
### "Keystore not found"
|
||||||
|
```bash
|
||||||
|
# Prüfe ob Keystore existiert
|
||||||
|
ls -la app/simple-notes-release.jks
|
||||||
|
|
||||||
|
# Falls nicht: Neu erstellen
|
||||||
|
./scripts/create-keystore.fish
|
||||||
|
```
|
||||||
|
|
||||||
|
### "key.properties not found"
|
||||||
|
```bash
|
||||||
|
# Prüfe ob Datei existiert
|
||||||
|
ls -la key.properties
|
||||||
|
|
||||||
|
# Falls nicht: Keystore neu erstellen oder manuell anlegen
|
||||||
|
./scripts/create-keystore.fish
|
||||||
|
```
|
||||||
|
|
||||||
|
### "Signature mismatch" beim App-Update
|
||||||
|
**Problem:** Lokaler Build hat andere Signatur als GitHub Release
|
||||||
|
|
||||||
|
**Ursache:** Unterschiedliche Keystores oder Passwörter
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Vergleiche SHA256-Fingerprints:
|
||||||
|
```bash
|
||||||
|
# Lokal
|
||||||
|
keytool -list -v -keystore app/simple-notes-release.jks
|
||||||
|
|
||||||
|
# GitHub Release-APK
|
||||||
|
keytool -printcert -jarfile ~/Downloads/simple-notes-v1.1.0.apk
|
||||||
|
```
|
||||||
|
2. Müssen **identisch** sein!
|
||||||
|
3. Falls nicht: GitHub Secrets mit lokaler `key.properties` synchronisieren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Weitere Dokumentation
|
||||||
|
|
||||||
|
- `../LOCAL_BUILDS.md` - Detaillierte Anleitung für lokale Builds
|
||||||
|
- `../.github/workflows/build-production-apk.yml` - GitHub Actions Workflow
|
||||||
|
- `../app/build.gradle.kts` - Build-Konfiguration
|
||||||
104
android/scripts/build-release-local.fish
Executable file
104
android/scripts/build-release-local.fish
Executable file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env fish
|
||||||
|
|
||||||
|
# Simple Notes Sync - Lokaler Release Build
|
||||||
|
# Erstellt signierte APKs, die mit GitHub Release-APKs kompatibel sind
|
||||||
|
|
||||||
|
set -l SCRIPT_DIR (dirname (status --current-filename))
|
||||||
|
set -l ANDROID_DIR (realpath "$SCRIPT_DIR/..")
|
||||||
|
set -l KEY_PROPERTIES "$ANDROID_DIR/key.properties"
|
||||||
|
|
||||||
|
echo "🔨 Simple Notes Sync - Lokaler Release Build"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 1. Prüfe ob key.properties existiert
|
||||||
|
if not test -f "$KEY_PROPERTIES"
|
||||||
|
echo "❌ Fehler: key.properties nicht gefunden!"
|
||||||
|
echo ""
|
||||||
|
echo "Bitte erstelle die Datei:"
|
||||||
|
echo " cd $ANDROID_DIR"
|
||||||
|
echo " cp key.properties.example key.properties"
|
||||||
|
echo ""
|
||||||
|
echo "Und fülle sie mit den echten Keystore-Daten aus."
|
||||||
|
echo "Siehe: android/LOCAL_BUILDS.md"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# 2. Prüfe ob Keystore existiert
|
||||||
|
set -l KEYSTORE "$ANDROID_DIR/app/simple-notes-release.jks"
|
||||||
|
if not test -f "$KEYSTORE"
|
||||||
|
echo "❌ Fehler: Keystore nicht gefunden!"
|
||||||
|
echo " Erwartet: $KEYSTORE"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
echo "✅ key.properties gefunden"
|
||||||
|
echo "✅ Keystore gefunden"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 3. Build-Typ abfragen
|
||||||
|
echo "Welchen Build möchtest du erstellen?"
|
||||||
|
echo " 1) Standard Flavor (empfohlen)"
|
||||||
|
echo " 2) F-Droid Flavor"
|
||||||
|
echo " 3) Beide Flavors"
|
||||||
|
echo ""
|
||||||
|
read -P "Auswahl [1-3]: " -n 1 choice
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
switch $choice
|
||||||
|
case 1
|
||||||
|
echo "🏗️ Baue Standard Release APKs..."
|
||||||
|
cd "$ANDROID_DIR"
|
||||||
|
./gradlew assembleStandardRelease --no-daemon
|
||||||
|
|
||||||
|
if test $status -eq 0
|
||||||
|
echo ""
|
||||||
|
echo "✅ Build erfolgreich!"
|
||||||
|
echo ""
|
||||||
|
echo "📦 APKs findest du hier:"
|
||||||
|
echo " $ANDROID_DIR/app/build/outputs/apk/standard/release/"
|
||||||
|
ls -lh "$ANDROID_DIR/app/build/outputs/apk/standard/release/"*.apk
|
||||||
|
end
|
||||||
|
|
||||||
|
case 2
|
||||||
|
echo "🏗️ Baue F-Droid Release APKs..."
|
||||||
|
cd "$ANDROID_DIR"
|
||||||
|
./gradlew assembleFdroidRelease --no-daemon
|
||||||
|
|
||||||
|
if test $status -eq 0
|
||||||
|
echo ""
|
||||||
|
echo "✅ Build erfolgreich!"
|
||||||
|
echo ""
|
||||||
|
echo "📦 APKs findest du hier:"
|
||||||
|
echo " $ANDROID_DIR/app/build/outputs/apk/fdroid/release/"
|
||||||
|
ls -lh "$ANDROID_DIR/app/build/outputs/apk/fdroid/release/"*.apk
|
||||||
|
end
|
||||||
|
|
||||||
|
case 3
|
||||||
|
echo "🏗️ Baue Standard + F-Droid Release APKs..."
|
||||||
|
cd "$ANDROID_DIR"
|
||||||
|
./gradlew assembleStandardRelease assembleFdroidRelease --no-daemon
|
||||||
|
|
||||||
|
if test $status -eq 0
|
||||||
|
echo ""
|
||||||
|
echo "✅ Build erfolgreich!"
|
||||||
|
echo ""
|
||||||
|
echo "📦 Standard APKs:"
|
||||||
|
echo " $ANDROID_DIR/app/build/outputs/apk/standard/release/"
|
||||||
|
ls -lh "$ANDROID_DIR/app/build/outputs/apk/standard/release/"*.apk
|
||||||
|
echo ""
|
||||||
|
echo "📦 F-Droid APKs:"
|
||||||
|
echo " $ANDROID_DIR/app/build/outputs/apk/fdroid/release/"
|
||||||
|
ls -lh "$ANDROID_DIR/app/build/outputs/apk/fdroid/release/"*.apk
|
||||||
|
end
|
||||||
|
|
||||||
|
case '*'
|
||||||
|
echo "❌ Ungültige Auswahl"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "💡 Tipp: Du kannst die APK per HTTP Server verteilen:"
|
||||||
|
echo " cd app/build/outputs/apk/standard/release"
|
||||||
|
echo " python3 -m http.server 8892"
|
||||||
279
android/scripts/create-keystore.fish
Executable file
279
android/scripts/create-keystore.fish
Executable file
@@ -0,0 +1,279 @@
|
|||||||
|
#!/usr/bin/env fish
|
||||||
|
|
||||||
|
# Simple Notes Sync - Keystore Generator
|
||||||
|
# Erstellt einen neuen Release-Keystore für App-Signierung
|
||||||
|
|
||||||
|
set -l SCRIPT_DIR (dirname (status --current-filename))
|
||||||
|
set -l ANDROID_DIR (realpath "$SCRIPT_DIR/..")
|
||||||
|
set -l KEYSTORE_PATH "$ANDROID_DIR/app/simple-notes-release.jks"
|
||||||
|
set -l KEY_PROPERTIES "$ANDROID_DIR/key.properties"
|
||||||
|
|
||||||
|
echo "🔐 Simple Notes Sync - Keystore Generator"
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ WICHTIG: Dieser Keystore wird für alle zukünftigen App-Releases verwendet!"
|
||||||
|
echo " Speichere die Zugangsdaten sicher ab (z.B. in einem Passwort-Manager)."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Prüfe ob Keystore bereits existiert
|
||||||
|
if test -f "$KEYSTORE_PATH"
|
||||||
|
echo "⚠️ Ein Keystore existiert bereits:"
|
||||||
|
echo " $KEYSTORE_PATH"
|
||||||
|
echo ""
|
||||||
|
read -P "Möchtest du ihn überschreiben? (Dies macht alte APKs inkompatibel!) [j/N]: " -n 1 overwrite
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if not string match -qi "j" $overwrite
|
||||||
|
echo "❌ Abgebrochen."
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
echo "🗑️ Lösche alten Keystore..."
|
||||||
|
rm "$KEYSTORE_PATH"
|
||||||
|
end
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📝 Bitte gib die folgenden Informationen ein:"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# App-Informationen sammeln
|
||||||
|
read -P "Dein Name (z.B. 'Max Mustermann'): " developer_name
|
||||||
|
read -P "Organisation (z.B. 'dettmer.dev'): " organization
|
||||||
|
read -P "Stadt: " city
|
||||||
|
read -P "Land (z.B. 'DE'): " country
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🔒 Keystore-Passwörter:"
|
||||||
|
echo ""
|
||||||
|
echo "Möchtest du sichere Passwörter automatisch generieren lassen?"
|
||||||
|
read -P "[J/n]: " -n 1 auto_generate
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if string match -qi "n" $auto_generate
|
||||||
|
# Manuelle Passwort-Eingabe
|
||||||
|
echo ""
|
||||||
|
echo "📝 Manuelle Passwort-Eingabe:"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
while true
|
||||||
|
read -sP "Keystore-Passwort: " keystore_password
|
||||||
|
echo ""
|
||||||
|
read -sP "Keystore-Passwort (Bestätigung): " keystore_password_confirm
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if test "$keystore_password" = "$keystore_password_confirm"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "❌ Passwörter stimmen nicht überein. Bitte erneut eingeben."
|
||||||
|
echo ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
while true
|
||||||
|
read -sP "Key-Passwort: " key_password
|
||||||
|
echo ""
|
||||||
|
read -sP "Key-Passwort (Bestätigung): " key_password_confirm
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if test "$key_password" = "$key_password_confirm"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo "❌ Passwörter stimmen nicht überein. Bitte erneut eingeben."
|
||||||
|
echo ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Automatische Passwort-Generierung
|
||||||
|
echo ""
|
||||||
|
echo "🔐 Generiere sichere Passwörter (32 Zeichen, alphanumerisch)..."
|
||||||
|
|
||||||
|
# Generiere sichere, zufällige Passwörter (alphanumerisch, 32 Zeichen)
|
||||||
|
set keystore_password (openssl rand -base64 32 | tr -d '/+=' | head -c 32)
|
||||||
|
set key_password (openssl rand -base64 32 | tr -d '/+=' | head -c 32)
|
||||||
|
|
||||||
|
echo "✅ Passwörter generiert"
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ WICHTIG: Speichere diese Passwörter jetzt in deinem Passwort-Manager!"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Keystore-Passwort: $keystore_password"
|
||||||
|
echo "Key-Passwort: $key_password"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
read -P "Passwörter gespeichert? Drücke Enter zum Fortfahren..."
|
||||||
|
end
|
||||||
|
|
||||||
|
set -l key_alias "simple-notes-key"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🏗️ Erstelle Keystore..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Keystore erstellen
|
||||||
|
keytool -genkey \
|
||||||
|
-v \
|
||||||
|
-keystore "$KEYSTORE_PATH" \
|
||||||
|
-alias "$key_alias" \
|
||||||
|
-keyalg RSA \
|
||||||
|
-keysize 4096 \
|
||||||
|
-validity 10000 \
|
||||||
|
-storepass "$keystore_password" \
|
||||||
|
-keypass "$key_password" \
|
||||||
|
-dname "CN=$developer_name, OU=Simple Notes Sync, O=$organization, L=$city, C=$country"
|
||||||
|
|
||||||
|
if test $status -ne 0
|
||||||
|
echo ""
|
||||||
|
echo "❌ Fehler beim Erstellen des Keystores!"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Keystore erfolgreich erstellt!"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# key.properties erstellen
|
||||||
|
echo "📝 Erstelle key.properties..."
|
||||||
|
echo "storeFile=simple-notes-release.jks" > "$KEY_PROPERTIES"
|
||||||
|
echo "storePassword=$keystore_password" >> "$KEY_PROPERTIES"
|
||||||
|
echo "keyAlias=$key_alias" >> "$KEY_PROPERTIES"
|
||||||
|
echo "keyPassword=$key_password" >> "$KEY_PROPERTIES"
|
||||||
|
|
||||||
|
echo "✅ key.properties erstellt"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Keystore-Info anzeigen
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "📋 KEYSTORE-INFORMATIONEN - SICHER SPEICHERN!"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
echo "Keystore-Pfad: $KEYSTORE_PATH"
|
||||||
|
echo "Key-Alias: $key_alias"
|
||||||
|
echo "Keystore-Passwort: $keystore_password"
|
||||||
|
echo "Key-Passwort: $key_password"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Base64-kodierten Keystore für GitHub Secrets
|
||||||
|
echo "🔐 Base64-kodierter Keystore für GitHub Secrets:"
|
||||||
|
echo ""
|
||||||
|
set -l keystore_base64 (base64 -w 0 "$KEYSTORE_PATH")
|
||||||
|
echo "$keystore_base64"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# GitHub Secrets konfigurieren
|
||||||
|
echo "<22> GitHub Secrets konfigurieren..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Prüfe ob GitHub CLI installiert ist
|
||||||
|
if not command -v gh &> /dev/null
|
||||||
|
echo "⚠️ GitHub CLI (gh) nicht gefunden!"
|
||||||
|
echo ""
|
||||||
|
echo "📝 Manuelle Konfiguration erforderlich:"
|
||||||
|
echo ""
|
||||||
|
echo "1. Gehe zu: https://github.com/inentory69/simple-notes-sync/settings/secrets/actions"
|
||||||
|
echo "2. Erstelle/Aktualisiere folgende Secrets:"
|
||||||
|
echo ""
|
||||||
|
echo " KEYSTORE_BASE64:"
|
||||||
|
echo " $keystore_base64"
|
||||||
|
echo ""
|
||||||
|
echo " KEYSTORE_PASSWORD:"
|
||||||
|
echo " $keystore_password"
|
||||||
|
echo ""
|
||||||
|
echo " KEY_ALIAS:"
|
||||||
|
echo " $key_alias"
|
||||||
|
echo ""
|
||||||
|
echo " KEY_PASSWORD:"
|
||||||
|
echo " $key_password"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
# GitHub CLI verfügbar - automatisch Secrets erstellen
|
||||||
|
echo "✅ GitHub CLI gefunden"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Prüfe ob authentifiziert
|
||||||
|
if not gh auth status &> /dev/null
|
||||||
|
echo "⚠️ Nicht bei GitHub authentifiziert!"
|
||||||
|
echo ""
|
||||||
|
read -P "Möchtest du dich jetzt authentifizieren? [j/N]: " -n 1 do_auth
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if string match -qi "j" $do_auth
|
||||||
|
gh auth login
|
||||||
|
else
|
||||||
|
echo "❌ Überspringe automatische Secret-Konfiguration"
|
||||||
|
echo ""
|
||||||
|
echo "📝 Manuelle Konfiguration erforderlich (siehe oben)"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
echo "🔐 Erstelle/Aktualisiere GitHub Secrets..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
set -l repo "inventory69/simple-notes-sync"
|
||||||
|
|
||||||
|
# KEYSTORE_BASE64
|
||||||
|
echo "$keystore_base64" | gh secret set KEYSTORE_BASE64 --repo $repo
|
||||||
|
if test $status -eq 0
|
||||||
|
echo "✅ KEYSTORE_BASE64 gesetzt"
|
||||||
|
else
|
||||||
|
echo "❌ Fehler beim Setzen von KEYSTORE_BASE64"
|
||||||
|
end
|
||||||
|
|
||||||
|
# KEYSTORE_PASSWORD
|
||||||
|
echo "$keystore_password" | gh secret set KEYSTORE_PASSWORD --repo $repo
|
||||||
|
if test $status -eq 0
|
||||||
|
echo "✅ KEYSTORE_PASSWORD gesetzt"
|
||||||
|
else
|
||||||
|
echo "❌ Fehler beim Setzen von KEYSTORE_PASSWORD"
|
||||||
|
end
|
||||||
|
|
||||||
|
# KEY_ALIAS
|
||||||
|
echo "$key_alias" | gh secret set KEY_ALIAS --repo $repo
|
||||||
|
if test $status -eq 0
|
||||||
|
echo "✅ KEY_ALIAS gesetzt"
|
||||||
|
else
|
||||||
|
echo "❌ Fehler beim Setzen von KEY_ALIAS"
|
||||||
|
end
|
||||||
|
|
||||||
|
# KEY_PASSWORD
|
||||||
|
echo "$key_password" | gh secret set KEY_PASSWORD --repo $repo
|
||||||
|
if test $status -eq 0
|
||||||
|
echo "✅ KEY_PASSWORD gesetzt"
|
||||||
|
else
|
||||||
|
echo "❌ Fehler beim Setzen von KEY_PASSWORD"
|
||||||
|
end
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
echo "✅ GitHub Secrets erfolgreich konfiguriert!"
|
||||||
|
echo ""
|
||||||
|
echo "🔍 Verifizieren:"
|
||||||
|
echo " gh secret list --repo $repo"
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
end
|
||||||
|
|
||||||
|
# Signatur-Fingerprint anzeigen
|
||||||
|
echo "🔑 SHA256-Fingerprint (zur Verifikation):"
|
||||||
|
keytool -list -v -keystore "$KEYSTORE_PATH" -storepass "$keystore_password" | grep "SHA256:"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "✅ Setup abgeschlossen!"
|
||||||
|
echo ""
|
||||||
|
echo "💡 Nächste Schritte:"
|
||||||
|
echo " 1. Speichere die obigen Informationen in einem Passwort-Manager"
|
||||||
|
echo " 2. Konfiguriere die GitHub Secrets (siehe oben)"
|
||||||
|
echo " 3. Teste den lokalen Build:"
|
||||||
|
echo " cd $ANDROID_DIR"
|
||||||
|
echo " ./gradlew assembleStandardRelease"
|
||||||
|
echo ""
|
||||||
150
android/scripts/verify-secrets.fish
Executable file
150
android/scripts/verify-secrets.fish
Executable file
@@ -0,0 +1,150 @@
|
|||||||
|
#!/usr/bin/env fish
|
||||||
|
|
||||||
|
# Simple Notes Sync - GitHub Secrets Verifier
|
||||||
|
# Verifiziert ob die GitHub Secrets korrekt konfiguriert sind
|
||||||
|
|
||||||
|
set -l repo "inventory69/simple-notes-sync"
|
||||||
|
|
||||||
|
echo "🔍 GitHub Secrets Verifier"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Prüfe ob GitHub CLI installiert ist
|
||||||
|
if not command -v gh &> /dev/null
|
||||||
|
echo "❌ GitHub CLI (gh) nicht gefunden!"
|
||||||
|
echo ""
|
||||||
|
echo "Installation:"
|
||||||
|
echo " Arch Linux: sudo pacman -S github-cli"
|
||||||
|
echo " Ubuntu: sudo apt install gh"
|
||||||
|
echo " macOS: brew install gh"
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Prüfe Authentifizierung
|
||||||
|
if not gh auth status &> /dev/null
|
||||||
|
echo "❌ Nicht bei GitHub authentifiziert!"
|
||||||
|
echo ""
|
||||||
|
echo "Authentifizierung starten:"
|
||||||
|
echo " gh auth login"
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
echo "✅ GitHub CLI authentifiziert"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Liste alle Secrets auf
|
||||||
|
echo "📋 Konfigurierte Secrets für $repo:"
|
||||||
|
echo ""
|
||||||
|
gh secret list --repo $repo
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Prüfe ob alle erforderlichen Secrets vorhanden sind
|
||||||
|
set -l required_secrets "KEYSTORE_BASE64" "KEYSTORE_PASSWORD" "KEY_ALIAS" "KEY_PASSWORD"
|
||||||
|
set -l missing_secrets
|
||||||
|
|
||||||
|
for secret in $required_secrets
|
||||||
|
if not gh secret list --repo $repo | grep -q "^$secret"
|
||||||
|
set -a missing_secrets $secret
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if test (count $missing_secrets) -gt 0
|
||||||
|
echo "❌ Fehlende Secrets:"
|
||||||
|
for secret in $missing_secrets
|
||||||
|
echo " - $secret"
|
||||||
|
end
|
||||||
|
echo ""
|
||||||
|
echo "💡 Tipp: Führe create-keystore.fish aus, um die Secrets zu erstellen"
|
||||||
|
else
|
||||||
|
echo "✅ Alle erforderlichen Secrets sind konfiguriert!"
|
||||||
|
echo ""
|
||||||
|
echo "Required Secrets:"
|
||||||
|
for secret in $required_secrets
|
||||||
|
echo " ✓ $secret"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Prüfe ob key.properties lokal existiert
|
||||||
|
set -l SCRIPT_DIR (dirname (status --current-filename))
|
||||||
|
set -l ANDROID_DIR (realpath "$SCRIPT_DIR/..")
|
||||||
|
set -l KEY_PROPERTIES "$ANDROID_DIR/key.properties"
|
||||||
|
|
||||||
|
if test -f "$KEY_PROPERTIES"
|
||||||
|
echo "✅ Lokale key.properties gefunden: $KEY_PROPERTIES"
|
||||||
|
echo ""
|
||||||
|
echo "📋 Inhalt (Passwörter verborgen):"
|
||||||
|
cat "$KEY_PROPERTIES" | sed 's/\(Password=\).*/\1***HIDDEN***/g'
|
||||||
|
else
|
||||||
|
echo "⚠️ Lokale key.properties nicht gefunden: $KEY_PROPERTIES"
|
||||||
|
echo ""
|
||||||
|
echo "💡 Tipp: Führe create-keystore.fish aus, um sie zu erstellen"
|
||||||
|
end
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Prüfe ob Keystore existiert
|
||||||
|
set -l KEYSTORE "$ANDROID_DIR/app/simple-notes-release.jks"
|
||||||
|
if test -f "$KEYSTORE"
|
||||||
|
echo "✅ Keystore gefunden: $KEYSTORE"
|
||||||
|
|
||||||
|
# Zeige Keystore-Info (wenn key.properties existiert)
|
||||||
|
if test -f "$KEY_PROPERTIES"
|
||||||
|
set -l store_password (grep "storePassword=" "$KEY_PROPERTIES" | cut -d'=' -f2)
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🔑 Keystore-Informationen:"
|
||||||
|
keytool -list -v -keystore "$KEYSTORE" -storepass "$store_password" 2>/dev/null | grep -E "(Alias|Creation date|Valid|SHA256)" | head -10
|
||||||
|
end
|
||||||
|
else
|
||||||
|
echo "⚠️ Keystore nicht gefunden: $KEYSTORE"
|
||||||
|
echo ""
|
||||||
|
echo "💡 Tipp: Führe create-keystore.fish aus, um ihn zu erstellen"
|
||||||
|
end
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Zusammenfassung
|
||||||
|
set -l issues 0
|
||||||
|
|
||||||
|
if test (count $missing_secrets) -gt 0
|
||||||
|
set issues (math $issues + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not test -f "$KEY_PROPERTIES"
|
||||||
|
set issues (math $issues + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not test -f "$KEYSTORE"
|
||||||
|
set issues (math $issues + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
if test $issues -eq 0
|
||||||
|
echo "🎉 Alles konfiguriert! Du kannst jetzt Releases erstellen."
|
||||||
|
echo ""
|
||||||
|
echo "🚀 Nächste Schritte:"
|
||||||
|
echo " 1. Lokalen Build testen:"
|
||||||
|
echo " ./scripts/build-release-local.fish"
|
||||||
|
echo ""
|
||||||
|
echo " 2. Code committen und pushen:"
|
||||||
|
echo " git push origin main"
|
||||||
|
echo ""
|
||||||
|
echo " 3. GitHub Actions erstellt automatisch Release"
|
||||||
|
else
|
||||||
|
echo "⚠️ $issues Problem(e) gefunden - siehe oben"
|
||||||
|
echo ""
|
||||||
|
echo "💡 Lösung: Führe create-keystore.fish aus"
|
||||||
|
end
|
||||||
|
|
||||||
|
echo ""
|
||||||
86
server/README.en.md
Normal file
86
server/README.en.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# WebDAV Server - Simple Notes Sync
|
||||||
|
|
||||||
|
**🌍 Languages:** [Deutsch](README.md) · **English**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Adjust environment variables
|
||||||
|
cp .env.example .env
|
||||||
|
nano .env
|
||||||
|
|
||||||
|
# 2. Start server
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# 3. Check logs
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# 4. Test
|
||||||
|
curl -u noteuser:your_password http://localhost:8080/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Server URL
|
||||||
|
|
||||||
|
**Local:** `http://localhost:8080/`
|
||||||
|
|
||||||
|
**On network:** `http://YOUR_IP:8080/` (e.g. `http://192.168.1.100:8080/`)
|
||||||
|
|
||||||
|
Find IP address:
|
||||||
|
```bash
|
||||||
|
ip addr show | grep "inet " | grep -v 127.0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Credentials
|
||||||
|
|
||||||
|
Default (see `.env`):
|
||||||
|
- Username: `noteuser`
|
||||||
|
- Password: See `.env` file
|
||||||
|
|
||||||
|
## Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check status
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# Restart
|
||||||
|
docker-compose restart
|
||||||
|
|
||||||
|
# Stop
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# Update
|
||||||
|
docker-compose pull
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Location
|
||||||
|
|
||||||
|
Your notes are stored in: `./data/`
|
||||||
|
|
||||||
|
**Backup:**
|
||||||
|
```bash
|
||||||
|
# Create backup
|
||||||
|
tar -czf notes-backup-$(date +%Y%m%d).tar.gz data/
|
||||||
|
|
||||||
|
# Restore backup
|
||||||
|
tar -xzf notes-backup-YYYYMMDD.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
## External Access (HTTPS)
|
||||||
|
|
||||||
|
For access from outside your home network, use a reverse proxy like Caddy or nginx:
|
||||||
|
|
||||||
|
**Example with Caddy:**
|
||||||
|
|
||||||
|
```Caddyfile
|
||||||
|
notes.yourdomain.com {
|
||||||
|
reverse_proxy localhost:8080
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important:** Always use HTTPS for external access to protect your credentials!
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
# WebDAV Server - Simple Notes Sync
|
# WebDAV Server - Simple Notes Sync
|
||||||
|
|
||||||
|
**🌍 Sprachen:** **Deutsch** · [English](README.en.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Reference in New Issue
Block a user