Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a56dd8128 | ||
|
|
63af7d30dc | ||
|
|
62423f5a5b | ||
|
|
9eabc9a5f0 | ||
|
|
015b90d56e |
6
.github/workflows/build-production-apk.yml
vendored
6
.github/workflows/build-production-apk.yml
vendored
@@ -151,12 +151,12 @@ jobs:
|
|||||||
|
|
||||||
## 📋 Changelog / Release Notes
|
## 📋 Changelog / Release Notes
|
||||||
|
|
||||||
${{ env.CHANGELOG_EN }}
|
${{ env.CHANGELOG_DE }}
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><EFBFBD>🇪 Deutsche Version (zum Aufklappen)</summary>
|
<summary>🌍 English Version</summary>
|
||||||
|
|
||||||
${{ env.CHANGELOG_DE }}
|
${{ env.CHANGELOG_EN }}
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|||||||
111
CHANGELOG.md
111
CHANGELOG.md
@@ -6,6 +6,117 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [1.3.0] - 2026-01-07
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **🚀 Multi-Device Sync** (Thanks to Thomas from Bielefeld for reporting!)
|
||||||
|
- Automatic download of new notes from other devices
|
||||||
|
- Deletion tracking prevents "zombie notes" (deleted notes don't come back)
|
||||||
|
- Smart cleanup: Re-created notes (newer timestamp) are downloaded
|
||||||
|
- Works with all devices: v1.2.0, v1.2.1, v1.2.2, and v1.3.0
|
||||||
|
|
||||||
|
- **🗑️ Server Deletion via Swipe Gesture**
|
||||||
|
- Swipe left on notes to delete from server (requires confirmation)
|
||||||
|
- Prevents duplicate notes on other devices
|
||||||
|
- Works with deletion tracking system
|
||||||
|
- Material Design confirmation dialog
|
||||||
|
|
||||||
|
- **⚡ E-Tag Performance Optimization**
|
||||||
|
- Smart server checking with E-Tag caching (~150ms vs 3000ms for "no changes")
|
||||||
|
- 20x faster when server has no updates
|
||||||
|
- E-Tag hybrid approach: E-Tag for JSON (fast), timestamp for Markdown (reliable)
|
||||||
|
- Battery-friendly with minimal server requests
|
||||||
|
|
||||||
|
- **📥 Markdown Auto-Sync Toggle**
|
||||||
|
- NEW: Unified Auto-Sync toggle in Settings (replaces separate Export/Auto-Import toggles)
|
||||||
|
- When enabled: Notes export to Markdown AND import changes automatically
|
||||||
|
- When disabled: Manual sync button appears for on-demand synchronization
|
||||||
|
- Performance: Auto-Sync OFF = 0ms overhead
|
||||||
|
|
||||||
|
- **🔘 Manual Markdown Sync Button**
|
||||||
|
- Manual sync button for performance-conscious users
|
||||||
|
- Shows import/export counts after completion
|
||||||
|
- Only visible when Auto-Sync is disabled
|
||||||
|
- On-demand synchronization (~150-200ms only when triggered)
|
||||||
|
|
||||||
|
- **⚙️ Server-Restore Modes**
|
||||||
|
- MERGE: Keep local notes + add server notes
|
||||||
|
- REPLACE: Delete all local + download from server
|
||||||
|
- OVERWRITE: Update duplicates, keep non-duplicates
|
||||||
|
- Restore modes now work correctly for WebDAV restore
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
- New `DeletionTracker` model with JSON persistence
|
||||||
|
- `NotesStorage`: Added deletion tracking methods
|
||||||
|
- `WebDavSyncService.hasUnsyncedChanges()`: Intelligent server checks with E-Tag caching
|
||||||
|
- `WebDavSyncService.downloadRemoteNotes()`: Deletion-aware downloads
|
||||||
|
- `WebDavSyncService.restoreFromServer()`: Support for restore modes
|
||||||
|
- `WebDavSyncService.deleteNoteFromServer()`: Server deletion with YAML frontmatter scanning
|
||||||
|
- `WebDavSyncService.importMarkdownFiles()`: Automatic Markdown import during sync
|
||||||
|
- `WebDavSyncService.manualMarkdownSync()`: Manual sync with result counts
|
||||||
|
- `MainActivity.setupSwipeToDelete()`: Two-stage swipe deletion with confirmation
|
||||||
|
- E-Tag caching in SharedPreferences for performance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [1.2.2] - 2026-01-06
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Backward Compatibility for v1.2.0 Users (Critical)**
|
||||||
|
- App now reads BOTH old (Root) AND new (`/notes/`) folder structures
|
||||||
|
- Users upgrading from v1.2.0 no longer lose their existing notes
|
||||||
|
- Server-Restore now finds notes from v1.2.0 stored in Root folder
|
||||||
|
- Automatic deduplication prevents loading the same note twice
|
||||||
|
- Graceful error handling if Root folder is not accessible
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
- `WebDavSyncService.downloadRemoteNotes()` - Dual-mode download (Root + /notes/)
|
||||||
|
- `WebDavSyncService.restoreFromServer()` - Now uses dual-mode download
|
||||||
|
- Migration happens naturally: new uploads go to `/notes/`, old notes stay readable
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [1.2.1] - 2026-01-05
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Markdown Initial Export Bugfix**
|
||||||
|
- Existing notes are now exported as Markdown when Desktop Integration is activated
|
||||||
|
- Previously, only new notes created after activation were exported
|
||||||
|
- Progress dialog shows export status with current/total counter
|
||||||
|
- Error handling for network issues during export
|
||||||
|
- Individual note failures don't abort the entire export
|
||||||
|
|
||||||
|
- **Markdown Directory Structure Fix**
|
||||||
|
- Markdown files now correctly land in `/notes-md/` folder
|
||||||
|
- Smart URL detection supports both Root-URL and `/notes` URL structures
|
||||||
|
- Previously, MD files were incorrectly placed in the root directory
|
||||||
|
- Markdown import now finds files correctly
|
||||||
|
|
||||||
|
- **JSON URL Normalization**
|
||||||
|
- Simplified server configuration: enter only base URL (e.g., `http://server:8080/`)
|
||||||
|
- App automatically creates `/notes/` for JSON files and `/notes-md/` for Markdown
|
||||||
|
- Smart detection: both `http://server:8080/` and `http://server:8080/notes/` work correctly
|
||||||
|
- Backward compatible: existing setups with `/notes` in URL continue to work
|
||||||
|
- No migration required for existing users
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **Markdown Directory Creation**
|
||||||
|
- `notes-md/` folder is now created on first sync (regardless of Desktop Integration setting)
|
||||||
|
- Prevents 404 errors when mounting WebDAV folder
|
||||||
|
- Better user experience: folder is visible before enabling the feature
|
||||||
|
|
||||||
|
- **Settings UI Improvements**
|
||||||
|
- Updated example URL from `/webdav` to `/notes` to match app behavior
|
||||||
|
- Example now shows: `http://192.168.0.188:8080/notes`
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
- `WebDavSyncService.ensureMarkdownDirectoryExists()` - Creates MD folder early
|
||||||
|
- `WebDavSyncService.getMarkdownUrl()` - Smart URL detection for both structures
|
||||||
|
- `WebDavSyncService.exportAllNotesToMarkdown()` - Exports all local notes with progress callback
|
||||||
|
- `SettingsActivity.onMarkdownExportToggled()` - Triggers initial export with ProgressDialog
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [1.2.0] - 2026-01-04
|
## [1.2.0] - 2026-01-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ ip addr show | grep "inet " | grep -v 127.0.0.1
|
|||||||
| **Password** | (your password from `.env`) |
|
| **Password** | (your password from `.env`) |
|
||||||
| **Gateway SSID** | Name of your WiFi network |
|
| **Gateway SSID** | Name of your WiFi network |
|
||||||
|
|
||||||
4. **Press "Test connection"**
|
> **💡 Note:** Enter only the base URL (without `/notes`). The app automatically creates `/notes/` for JSON files and `/notes-md/` for Markdown export.
|
||||||
|
|
||||||
|
4. **Press "Test connection"****
|
||||||
- ✅ Success? → Continue to step 4
|
- ✅ Success? → Continue to step 4
|
||||||
- ❌ Error? → See [Troubleshooting](#troubleshooting)
|
- ❌ Error? → See [Troubleshooting](#troubleshooting)
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ ip addr show | grep "inet " | grep -v 127.0.0.1
|
|||||||
| **Passwort** | (dein Passwort aus `.env`) |
|
| **Passwort** | (dein Passwort aus `.env`) |
|
||||||
| **Gateway SSID** | Name deines WLAN-Netzwerks |
|
| **Gateway SSID** | Name deines WLAN-Netzwerks |
|
||||||
|
|
||||||
|
> **💡 Hinweis:** Gib nur die Base-URL ein (ohne `/notes`). Die App erstellt automatisch `/notes/` für JSON-Dateien und `/notes-md/` für Markdown-Export.
|
||||||
|
|
||||||
4. **"Verbindung testen"** drücken
|
4. **"Verbindung testen"** drücken
|
||||||
- ✅ Erfolg? → Weiter zu Schritt 4
|
- ✅ Erfolg? → Weiter zu Schritt 4
|
||||||
- ❌ Fehler? → Siehe [Troubleshooting](#troubleshooting)
|
- ❌ Fehler? → Siehe [Troubleshooting](#troubleshooting)
|
||||||
|
|||||||
186
README.en.md
186
README.en.md
@@ -6,7 +6,9 @@
|
|||||||
[](https://m3.material.io/)
|
[](https://m3.material.io/)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
**📱 [APK Download](https://github.com/inventory69/simple-notes-sync/releases/latest)** · **📖 [Documentation](DOCS.en.md)** · **🚀 [Quick Start](QUICKSTART.en.md)**
|
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it on IzzyOnDroid" height="80">](https://apt.izzysoft.de/fdroid/index/apk/dev.dettmer.simplenotes)
|
||||||
|
|
||||||
|
**📱 [APK Download](https://github.com/inventory69/simple-notes-sync/releases/latest)** · **📖 [Documentation](docs/DOCS.en.md)** · **🚀 [Quick Start](QUICKSTART.en.md)**
|
||||||
|
|
||||||
**🌍 Languages:** [Deutsch](README.md) · **English**
|
**🌍 Languages:** [Deutsch](README.md) · **English**
|
||||||
|
|
||||||
@@ -22,53 +24,27 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
## ✨ Highlights
|
||||||
|
|
||||||
### 📝 Notes
|
- 📝 **Offline-first** - Works without internet
|
||||||
* Simple text notes with auto-save
|
- 🔄 **Auto-sync** - Home WiFi only (15/30/60 min)
|
||||||
* Swipe-to-delete with confirmation
|
- 🔒 **Self-hosted** - Your data stays with you (WebDAV)
|
||||||
* Material Design 3 editor
|
- 💾 **Local backup** - Export/Import as JSON file
|
||||||
|
- 🖥️ **Desktop integration** - Markdown export for VS Code, Typora, etc.
|
||||||
|
- 🔋 **Battery-friendly** - ~0.2-0.8% per day
|
||||||
|
- 🎨 **Material Design 3** - Dark mode & dynamic colors
|
||||||
|
|
||||||
### 💾 Backup & Restore **NEW in v1.2.0**
|
➡️ **Complete feature list:** [FEATURES.en.md](docs/FEATURES.en.md)
|
||||||
* **Local backup** - Export all notes as JSON file
|
|
||||||
* **Flexible restore** - 3 modes (Merge, Replace, Overwrite)
|
|
||||||
* **Automatic safety net** - Auto-backup before every restore
|
|
||||||
* **Independent from server** - Works completely offline
|
|
||||||
|
|
||||||
### 🖥️ Desktop Integration **NEW in v1.2.0**
|
|
||||||
* **Markdown export** - Notes are automatically exported as `.md` files
|
|
||||||
* **WebDAV access** - Mount WebDAV as network drive for direct access
|
|
||||||
* **Editor compatibility** - VS Code, Typora, Notepad++, or any Markdown editor
|
|
||||||
* **Last-Write-Wins** - Intelligent conflict resolution via timestamps
|
|
||||||
* **Dual-format** - JSON sync remains master, Markdown is optional mirror
|
|
||||||
|
|
||||||
### 🔄 Synchronization
|
|
||||||
* **Pull-to-refresh** for manual sync
|
|
||||||
* **Auto-sync** (15/30/60 min) only on home WiFi
|
|
||||||
* **Smart server check** - No errors on foreign networks
|
|
||||||
* **Conflict-free merging** - Your changes are never lost
|
|
||||||
* **6 sync triggers** - Periodic, app-start, WiFi, manual, pull-to-refresh, settings
|
|
||||||
|
|
||||||
### 🔒 Privacy & Self-Hosted
|
|
||||||
* **WebDAV server** (Nextcloud, ownCloud, etc.)
|
|
||||||
* **Docker setup guide** included in docs
|
|
||||||
* **Your data stays with you** - No tracking, no cloud
|
|
||||||
* **HTTP only local** - HTTPS for external servers
|
|
||||||
* **100% open source** (MIT License)
|
|
||||||
|
|
||||||
### 🔋 Performance
|
|
||||||
* **Battery-friendly** (~0.2-0.8% per day)
|
|
||||||
* **Offline-first** - Works without internet
|
|
||||||
* **Dark mode** & dynamic colors
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
### 1. Server Setup
|
### 1. Server Setup (5 minutes)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd server
|
git clone https://github.com/inventory69/simple-notes-sync.git
|
||||||
|
cd simple-notes-sync/server
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
# Set password in .env
|
# Set password in .env
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
@@ -76,114 +52,30 @@ docker compose up -d
|
|||||||
|
|
||||||
➡️ **Details:** [Server Setup Guide](server/README.en.md)
|
➡️ **Details:** [Server Setup Guide](server/README.en.md)
|
||||||
|
|
||||||
### 2. App Installation
|
### 2. App Installation (2 minutes)
|
||||||
|
|
||||||
1. [Download APK](https://github.com/inventory69/simple-notes-sync/releases/latest)
|
1. [Download APK](https://github.com/inventory69/simple-notes-sync/releases/latest)
|
||||||
2. Install & open
|
2. Install & open
|
||||||
3. ⚙️ Settings → Configure server
|
3. ⚙️ Settings → Configure server:
|
||||||
4. Enable auto-sync
|
- **URL:** `http://YOUR-SERVER-IP:8080/` _(base URL only!)_
|
||||||
|
- **User:** `noteuser`
|
||||||
|
- **Password:** _(from .env)_
|
||||||
|
- **WiFi:** _(your network name)_
|
||||||
|
4. **Test connection** → Enable auto-sync
|
||||||
|
5. Done! 🎉
|
||||||
|
|
||||||
➡️ **Details:** [Complete guide](QUICKSTART.en.md)
|
➡️ **Detailed guide:** [QUICKSTART.en.md](QUICKSTART.en.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## <EFBFBD> Local Backup & Restore
|
## 📚 Documentation
|
||||||
|
|
||||||
### Create Backup
|
|
||||||
|
|
||||||
1. **Settings** → **Backup & Restore**
|
|
||||||
2. Tap **"📥 Create backup"**
|
|
||||||
3. Choose location (Downloads, SD card, cloud folder)
|
|
||||||
4. Done! All notes are saved in a `.json` file
|
|
||||||
|
|
||||||
**Filename:** `simplenotes_backup_YYYY-MM-DD_HHmmss.json`
|
|
||||||
|
|
||||||
### Restore
|
|
||||||
|
|
||||||
1. **Settings** → **"📤 Restore from file"**
|
|
||||||
2. Select backup file
|
|
||||||
3. **Choose restore mode:**
|
|
||||||
- **Merge** _(Default)_ - Add new notes, keep existing ones
|
|
||||||
- **Replace** - Delete all and import backup
|
|
||||||
- **Overwrite duplicates** - Backup wins on ID conflicts
|
|
||||||
4. Confirm - _Automatic safety backup is created!_
|
|
||||||
|
|
||||||
**💡 Tip:** Before every restore, an automatic safety backup is created - your data is safe!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🖥️ Desktop Integration (WebDAV + Markdown)
|
|
||||||
|
|
||||||
### Why Markdown?
|
|
||||||
|
|
||||||
The app automatically exports your notes as `.md` files so you can edit them on desktop:
|
|
||||||
|
|
||||||
- **JSON remains master** - Primary sync mechanism (reliable, fast)
|
|
||||||
- **Markdown is mirror** - Additional export for desktop access
|
|
||||||
- **Dual-format** - Both formats are always in sync
|
|
||||||
|
|
||||||
### Setup: WebDAV as Network Drive
|
|
||||||
|
|
||||||
**With WebDAV mount ANY Markdown editor works!**
|
|
||||||
|
|
||||||
#### Windows:
|
|
||||||
|
|
||||||
1. **Open Explorer** → Right-click on "This PC"
|
|
||||||
2. **"Map network drive"**
|
|
||||||
3. **Enter WebDAV URL:** `http://YOUR-SERVER:8080/notes-md/`
|
|
||||||
4. Enter username/password
|
|
||||||
5. **Done!** - Folder appears as drive (e.g. Z:\)
|
|
||||||
|
|
||||||
#### macOS:
|
|
||||||
|
|
||||||
1. **Finder** → Menu "Go" → "Connect to Server" (⌘K)
|
|
||||||
2. **Server Address:** `http://YOUR-SERVER:8080/notes-md/`
|
|
||||||
3. Enter username/password
|
|
||||||
4. **Done!** - Folder appears under "Network"
|
|
||||||
|
|
||||||
#### Linux:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Option 1: GNOME Files / Nautilus
|
|
||||||
Files → Other Locations → Connect to Server
|
|
||||||
Server Address: dav://YOUR-SERVER:8080/notes-md/
|
|
||||||
|
|
||||||
# Option 2: davfs2 (permanent mount)
|
|
||||||
sudo apt install davfs2
|
|
||||||
sudo mount -t davfs http://YOUR-SERVER:8080/notes-md/ /mnt/notes
|
|
||||||
```
|
|
||||||
|
|
||||||
### Workflow:
|
|
||||||
|
|
||||||
1. **Enable Markdown export** (App → Settings)
|
|
||||||
2. **Mount WebDAV** (see above)
|
|
||||||
3. **Open editor** (VS Code, Typora, Notepad++, etc.)
|
|
||||||
4. **Edit notes** - Changes are saved directly
|
|
||||||
5. **"Import Markdown Changes" in app** - Import desktop changes
|
|
||||||
|
|
||||||
**Recommended Editors:**
|
|
||||||
- **VS Code** - Free, powerful, with Markdown preview
|
|
||||||
- **Typora** - Minimalist, WYSIWYG Markdown
|
|
||||||
- **Notepad++** - Lightweight, fast
|
|
||||||
- **iA Writer** - Focused writing
|
|
||||||
|
|
||||||
- **VS Code** with WebDAV extension
|
|
||||||
- **Typora** (local copy)
|
|
||||||
- **iA Writer** (read/edit only, no auto-sync)
|
|
||||||
|
|
||||||
**⚠️ Important:**
|
|
||||||
- Markdown export is **optional** (toggle in settings)
|
|
||||||
- JSON sync **always** works - Markdown is additional
|
|
||||||
- All 6 sync triggers remain unchanged
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## <20>📚 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
|
|
||||||
|
|
||||||
|
| Document | Content |
|
||||||
|
|----------|---------|
|
||||||
|
| **[QUICKSTART.en.md](QUICKSTART.en.md)** | Step-by-step installation |
|
||||||
|
| **[FEATURES.en.md](docs/FEATURES.en.md)** | Complete feature list |
|
||||||
|
| **[BACKUP.en.md](docs/BACKUP.en.md)** | Backup & restore guide |
|
||||||
|
| **[DESKTOP.en.md](docs/DESKTOP.en.md)** | Desktop integration (Markdown) |
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ Development
|
## 🛠️ Development
|
||||||
@@ -193,24 +85,20 @@ cd android
|
|||||||
./gradlew assembleStandardRelease
|
./gradlew assembleStandardRelease
|
||||||
```
|
```
|
||||||
|
|
||||||
➡️ **Details:** [Build instructions in DOCS.en.md](DOCS.en.md)
|
➡️ **Build guide:** [DOCS.en.md](docs/DOCS.en.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🤝 Contributing
|
## 🤝 Contributing
|
||||||
|
|
||||||
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## <EFBFBD> Changelog
|
## 📄 License
|
||||||
|
|
||||||
All changes are documented in [CHANGELOG.md](CHANGELOG.md).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## <20>📄 License
|
|
||||||
|
|
||||||
MIT License - see [LICENSE](LICENSE)
|
MIT License - see [LICENSE](LICENSE)
|
||||||
|
|
||||||
**v1.2.0** · Built with Kotlin + Material Design 3
|
---
|
||||||
|
|
||||||
|
**v1.2.1** · Built with ❤️ using Kotlin + Material Design 3
|
||||||
|
|||||||
194
README.md
194
README.md
@@ -6,7 +6,9 @@
|
|||||||
[](https://m3.material.io/)
|
[](https://m3.material.io/)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
|
|
||||||
**📱 [APK Download](https://github.com/inventory69/simple-notes-sync/releases/latest)** · **📖 [Dokumentation](DOCS.md)** · **🚀 [Quick Start](QUICKSTART.md)**
|
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png" alt="Get it on IzzyOnDroid" height="80">](https://apt.izzysoft.de/fdroid/index/apk/dev.dettmer.simplenotes)
|
||||||
|
|
||||||
|
**📱 [APK Download](https://github.com/inventory69/simple-notes-sync/releases/latest)** · **📖 [Dokumentation](docs/DOCS.md)** · **🚀 [Quick Start](QUICKSTART.md)**
|
||||||
|
|
||||||
**🌍 Sprachen:** **Deutsch** · [English](README.en.md)
|
**🌍 Sprachen:** **Deutsch** · [English](README.en.md)
|
||||||
|
|
||||||
@@ -22,53 +24,27 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
## ✨ Highlights
|
||||||
|
|
||||||
### 📝 Notizen
|
- 📝 **Offline-First** - Funktioniert ohne Internet
|
||||||
* Einfache Textnotizen mit automatischem Speichern
|
- 🔄 **Auto-Sync** - Nur im Heim-WLAN (15/30/60 Min)
|
||||||
* Swipe-to-Delete mit Bestätigung
|
- 🔒 **Self-Hosted** - Deine Daten bleiben bei dir (WebDAV)
|
||||||
* Material Design 3 Editor
|
- 💾 **Lokales Backup** - Export/Import als JSON-Datei
|
||||||
|
- 🖥️ **Desktop-Integration** - Markdown-Export für VS Code, Typora, etc.
|
||||||
|
- 🔋 **Akkuschonend** - ~0.2-0.8% pro Tag
|
||||||
|
- 🎨 **Material Design 3** - Dark Mode & Dynamic Colors
|
||||||
|
|
||||||
### 💾 Backup & Wiederherstellung **NEU in v1.2.0**
|
➡️ **Vollständige Feature-Liste:** [FEATURES.md](docs/FEATURES.md)
|
||||||
* **Lokales Backup** - Exportiere alle Notizen als JSON-Datei
|
|
||||||
* **Flexible Wiederherstellung** - 3 Modi (Zusammenführen, Ersetzen, Überschreiben)
|
|
||||||
* **Automatisches Sicherheitsnetz** - Auto-Backup vor jeder Wiederherstellung
|
|
||||||
* **Unabhängig vom Server** - Funktioniert komplett offline
|
|
||||||
|
|
||||||
### 🖥️ Desktop-Integration **NEU in v1.2.0**
|
|
||||||
* **Markdown-Export** - Notizen werden automatisch als `.md` Dateien exportiert
|
|
||||||
* **WebDAV-Zugriff** - Mounte WebDAV als Netzlaufwerk für direkten Zugriff
|
|
||||||
* **Editor-Kompatibilität** - VS Code, Typora, Notepad++, oder beliebiger Markdown-Editor
|
|
||||||
* **Last-Write-Wins** - Intelligente Konfliktauflösung via Zeitstempel
|
|
||||||
* **Dual-Format** - JSON-Sync bleibt Master, Markdown ist optionaler Mirror
|
|
||||||
|
|
||||||
### 🔄 Synchronisation
|
|
||||||
* **Pull-to-Refresh** für manuellen Sync
|
|
||||||
* **Auto-Sync** (15/30/60 Min) nur im Heim-WLAN
|
|
||||||
* **Smart Server-Check** - Keine Fehler in fremden Netzwerken
|
|
||||||
* **Konfliktfreies Merging** - Deine Änderungen gehen nie verloren
|
|
||||||
* **6 Sync-Trigger** - Periodic, App-Start, WiFi, Manual, Pull-to-Refresh, Settings
|
|
||||||
|
|
||||||
### 🔒 Privacy & Self-Hosted
|
|
||||||
* **WebDAV-Server** (Nextcloud, ownCloud, etc.)
|
|
||||||
* **Docker Setup-Anleitung** in den Docs enthalten
|
|
||||||
* **Deine Daten bleiben bei dir** - Kein Tracking, keine Cloud
|
|
||||||
* **HTTP nur lokal** - HTTPS für externe Server
|
|
||||||
* **100% Open Source** (MIT Lizenz)
|
|
||||||
|
|
||||||
### 🔋 Performance
|
|
||||||
* **Akkuschonend** (~0.2-0.8% pro Tag)
|
|
||||||
* **Offline-First** - Funktioniert ohne Internet
|
|
||||||
* **Dark Mode** & Dynamic Colors
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Schnellstart
|
||||||
|
|
||||||
### 1. Server Setup
|
### 1. Server Setup (5 Minuten)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd server
|
git clone https://github.com/inventory69/simple-notes-sync.git
|
||||||
|
cd simple-notes-sync/server
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
# Passwort in .env setzen
|
# Passwort in .env setzen
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
@@ -76,118 +52,32 @@ docker compose up -d
|
|||||||
|
|
||||||
➡️ **Details:** [Server Setup Guide](server/README.md)
|
➡️ **Details:** [Server Setup Guide](server/README.md)
|
||||||
|
|
||||||
### 2. App Installation
|
### 2. App Installation (2 Minuten)
|
||||||
|
|
||||||
1. [APK herunterladen](https://github.com/inventory69/simple-notes-sync/releases/latest)
|
1. [APK herunterladen](https://github.com/inventory69/simple-notes-sync/releases/latest)
|
||||||
2. Installieren & öffnen
|
2. Installieren & öffnen
|
||||||
3. ⚙️ Einstellungen → Server konfigurieren
|
3. ⚙️ Einstellungen → Server konfigurieren:
|
||||||
4. Auto-Sync aktivieren
|
- **URL:** `http://DEINE-SERVER-IP:8080/` _(nur Base-URL!)_
|
||||||
|
- **User:** `noteuser`
|
||||||
|
- **Passwort:** _(aus .env)_
|
||||||
|
- **WLAN:** _(dein Netzwerk-Name)_
|
||||||
|
4. **Verbindung testen** → Auto-Sync aktivieren
|
||||||
|
5. Fertig! 🎉
|
||||||
|
|
||||||
➡️ **Details:** [Vollständige Anleitung](QUICKSTART.md)
|
➡️ **Ausführliche Anleitung:** [QUICKSTART.md](QUICKSTART.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## <EFBFBD> Lokales Backup & Wiederherstellung
|
## 📚 Dokumentation
|
||||||
|
|
||||||
### Backup erstellen
|
| Dokument | Inhalt |
|
||||||
|
|----------|--------|
|
||||||
1. **Einstellungen** → **Backup & Wiederherstellung**
|
| **[QUICKSTART.md](QUICKSTART.md)** | Schritt-für-Schritt Installation |
|
||||||
2. Tippe auf **"📥 Backup erstellen"**
|
| **[FEATURES.md](docs/FEATURES.md)** | Vollständige Feature-Liste |
|
||||||
3. Wähle Speicherort (Downloads, SD-Karte, Cloud-Ordner)
|
| **[BACKUP.md](docs/BACKUP.md)** | Backup & Wiederherstellung |
|
||||||
4. Fertig! Alle Notizen sind in einer `.json` Datei gesichert
|
| **[DESKTOP.md](docs/DESKTOP.md)** | Desktop-Integration (Markdown) |
|
||||||
|
| **[DOCS.md](docs/DOCS.md)** | Technische Details & Troubleshooting |
|
||||||
**Dateiname:** `simplenotes_backup_YYYY-MM-DD_HHmmss.json`
|
| **[CHANGELOG.md](CHANGELOG.md)** | Versionshistorie |
|
||||||
|
|
||||||
### Wiederherstellen
|
|
||||||
|
|
||||||
1. **Einstellungen** → **"📤 Aus Datei wiederherstellen"**
|
|
||||||
2. Wähle Backup-Datei
|
|
||||||
3. **Wiederherstellungs-Modus auswählen:**
|
|
||||||
- **Zusammenführen** _(Standard)_ - Neue Notizen hinzufügen, bestehende behalten
|
|
||||||
- **Ersetzen** - Alle löschen und Backup importieren
|
|
||||||
- **Duplikate überschreiben** - Backup gewinnt bei ID-Konflikten
|
|
||||||
4. Bestätigen - _Automatisches Sicherheits-Backup wird erstellt!_
|
|
||||||
|
|
||||||
**💡 Tipp:** Vor jeder Wiederherstellung wird automatisch ein Auto-Backup erstellt - deine Daten sind sicher!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🖥️ Desktop-Integration (WebDAV + Markdown)
|
|
||||||
|
|
||||||
### Warum Markdown?
|
|
||||||
|
|
||||||
Die App exportiert deine Notizen automatisch als `.md` Dateien, damit du sie auf dem Desktop bearbeiten kannst:
|
|
||||||
|
|
||||||
- **JSON bleibt Master** - Primärer Sync-Mechanismus (verlässlich, schnell)
|
|
||||||
- **Markdown ist Mirror** - Zusätzlicher Export für Desktop-Zugriff
|
|
||||||
- **Dual-Format** - Beide Formate sind immer synchron
|
|
||||||
|
|
||||||
### Setup: WebDAV als Netzlaufwerk
|
|
||||||
|
|
||||||
**Mit WebDAV-Mount funktioniert JEDER Markdown-Editor!**
|
|
||||||
|
|
||||||
#### Windows:
|
|
||||||
|
|
||||||
1. **Explorer öffnen** → Rechtsklick auf "Dieser PC"
|
|
||||||
2. **"Netzlaufwerk verbinden"** wählen
|
|
||||||
3. **WebDAV-URL eingeben:** `http://DEIN-SERVER:8080/notes-md/`
|
|
||||||
4. Benutzername/Passwort eingeben
|
|
||||||
5. **Fertig!** - Ordner erscheint als Laufwerk (z.B. Z:\)
|
|
||||||
|
|
||||||
#### macOS:
|
|
||||||
|
|
||||||
1. **Finder** → Menü "Gehe zu" → "Mit Server verbinden" (⌘K)
|
|
||||||
2. **Server-Adresse:** `http://DEIN-SERVER:8080/notes-md/`
|
|
||||||
3. Benutzername/Passwort eingeben
|
|
||||||
4. **Fertig!** - Ordner erscheint unter "Netzwerk"
|
|
||||||
|
|
||||||
#### Linux:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Option 1: GNOME Files / Nautilus
|
|
||||||
Dateien → Andere Orte → Mit Server verbinden
|
|
||||||
Server-Adresse: dav://DEIN-SERVER:8080/notes-md/
|
|
||||||
|
|
||||||
# Option 2: davfs2 (permanent mount)
|
|
||||||
sudo apt install davfs2
|
|
||||||
sudo mount -t davfs http://DEIN-SERVER:8080/notes-md/ /mnt/notes
|
|
||||||
```
|
|
||||||
|
|
||||||
### Workflow:
|
|
||||||
|
|
||||||
1. **Markdown-Export aktivieren** (App → Einstellungen)
|
|
||||||
2. **WebDAV mounten** (siehe oben)
|
|
||||||
3. **Editor öffnen** (VS Code, Typora, Notepad++, etc.)
|
|
||||||
4. **Notizen bearbeiten** - Änderungen werden direkt gespeichert
|
|
||||||
5. **"Import Markdown Changes" in App** - Desktop-Änderungen importieren
|
|
||||||
|
|
||||||
**Empfohlene Editoren:**
|
|
||||||
- **VS Code** - Kostenlos, mächtig, mit Markdown-Preview
|
|
||||||
- **Typora** - Minimalistisch, WYSIWYG-Markdown
|
|
||||||
- **Notepad++** - Leichtgewichtig, schnell
|
|
||||||
- **iA Writer** - Fokussiertes Schreiben
|
|
||||||
3. Notizen bearbeiten - Änderungen via "Import Markdown Changes" in die App importieren
|
|
||||||
|
|
||||||
### Alternative: Direkter Zugriff
|
|
||||||
|
|
||||||
Du kannst die `.md` Dateien auch direkt mit jedem Markdown-Editor öffnen:
|
|
||||||
|
|
||||||
- **VS Code** mit WebDAV-Extension
|
|
||||||
- **Typora** (lokale Kopie)
|
|
||||||
- **iA Writer** (nur lesen/bearbeiten, kein Auto-Sync)
|
|
||||||
|
|
||||||
**⚠️ Wichtig:**
|
|
||||||
- Markdown-Export ist **optional** (in Einstellungen ein/ausschaltbar)
|
|
||||||
- JSON-Sync funktioniert **immer** - Markdown ist zusätzlich
|
|
||||||
- Alle 6 Sync-Trigger bleiben unverändert erhalten
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## <20>📚 Dokumentation
|
|
||||||
|
|
||||||
- **[Quick Start Guide](QUICKSTART.md)** - Schritt-für-Schritt Anleitung für Endbenutzer
|
|
||||||
- **[Server Setup](server/README.md)** - WebDAV Server konfigurieren
|
|
||||||
- **[Vollständige Docs](DOCS.md)** - Features, Troubleshooting, Build-Anleitung
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -198,24 +88,20 @@ cd android
|
|||||||
./gradlew assembleStandardRelease
|
./gradlew assembleStandardRelease
|
||||||
```
|
```
|
||||||
|
|
||||||
➡️ **Details:** [Build-Anleitung in DOCS.md](DOCS.md)
|
➡️ **Build-Anleitung:** [DOCS.md](docs/DOCS.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🤝 Contributing
|
## 🤝 Contributing
|
||||||
|
|
||||||
Beiträge sind willkommen! Siehe [CONTRIBUTING.md](CONTRIBUTING.md) für Details.
|
Beiträge willkommen! Siehe [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## <EFBFBD> Changelog
|
## 📄 Lizenz
|
||||||
|
|
||||||
Alle Änderungen sind in [CHANGELOG.md](CHANGELOG.md) dokumentiert.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## <20>📄 Lizenz
|
|
||||||
|
|
||||||
MIT License - siehe [LICENSE](LICENSE)
|
MIT License - siehe [LICENSE](LICENSE)
|
||||||
|
|
||||||
**v1.2.0** · Gebaut mit Kotlin + Material Design 3
|
---
|
||||||
|
|
||||||
|
**v1.2.1** · Built with ❤️ using Kotlin + Material Design 3
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ android {
|
|||||||
applicationId = "dev.dettmer.simplenotes"
|
applicationId = "dev.dettmer.simplenotes"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
versionCode = 5 // 🔥 v1.2.0: Local Backup + Markdown Desktop Integration
|
versionCode = 8 // 🚀 v1.3.0: Multi-Device Sync with deletion tracking
|
||||||
versionName = "1.2.0" // 🔥 v1.2.0: Backup/Restore + Joplin/Obsidian Support
|
versionName = "1.3.0" // 🚀 v1.3.0: Multi-Device Sync, E-Tag caching, Markdown auto-import
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,19 @@ import com.google.android.material.color.DynamicColors
|
|||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dev.dettmer.simplenotes.adapters.NotesAdapter
|
import dev.dettmer.simplenotes.adapters.NotesAdapter
|
||||||
|
import dev.dettmer.simplenotes.models.Note
|
||||||
import dev.dettmer.simplenotes.storage.NotesStorage
|
import dev.dettmer.simplenotes.storage.NotesStorage
|
||||||
import dev.dettmer.simplenotes.sync.SyncWorker
|
import dev.dettmer.simplenotes.sync.SyncWorker
|
||||||
import dev.dettmer.simplenotes.utils.NotificationHelper
|
import dev.dettmer.simplenotes.utils.NotificationHelper
|
||||||
import dev.dettmer.simplenotes.utils.showToast
|
import dev.dettmer.simplenotes.utils.showToast
|
||||||
import dev.dettmer.simplenotes.utils.Constants
|
import dev.dettmer.simplenotes.utils.Constants
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.CheckBox
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -320,36 +326,20 @@ class MainActivity : AppCompatActivity() {
|
|||||||
): Boolean = false
|
): Boolean = false
|
||||||
|
|
||||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
val position = viewHolder.adapterPosition
|
val position = viewHolder.bindingAdapterPosition
|
||||||
val note = adapter.currentList[position]
|
val swipedNote = adapter.currentList[position]
|
||||||
val notesCopy = adapter.currentList.toMutableList()
|
|
||||||
|
|
||||||
// Track pending deletion to prevent flicker
|
// Store original list BEFORE removing note
|
||||||
pendingDeletions.add(note.id)
|
val originalList = adapter.currentList.toList()
|
||||||
|
|
||||||
// Remove from list immediately for visual feedback
|
// Remove from list for visual feedback (NOT from storage yet!)
|
||||||
notesCopy.removeAt(position)
|
val listWithoutNote = originalList.toMutableList().apply {
|
||||||
adapter.submitList(notesCopy)
|
removeAt(position)
|
||||||
|
|
||||||
// Show Snackbar with UNDO
|
|
||||||
Snackbar.make(
|
|
||||||
recyclerViewNotes,
|
|
||||||
"Notiz gelöscht",
|
|
||||||
Snackbar.LENGTH_LONG
|
|
||||||
).setAction("RÜCKGÄNGIG") {
|
|
||||||
// UNDO: Remove from pending deletions and restore
|
|
||||||
pendingDeletions.remove(note.id)
|
|
||||||
loadNotes()
|
|
||||||
}.addCallback(object : Snackbar.Callback() {
|
|
||||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
|
||||||
if (event != DISMISS_EVENT_ACTION) {
|
|
||||||
// Snackbar dismissed without UNDO → Actually delete the note
|
|
||||||
storage.deleteNote(note.id)
|
|
||||||
pendingDeletions.remove(note.id)
|
|
||||||
loadNotes()
|
|
||||||
}
|
}
|
||||||
}
|
adapter.submitList(listWithoutNote)
|
||||||
}).show()
|
|
||||||
|
// Show dialog with ability to restore
|
||||||
|
showServerDeletionDialog(swipedNote, originalList)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
|
override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
|
||||||
@@ -361,6 +351,104 @@ class MainActivity : AppCompatActivity() {
|
|||||||
itemTouchHelper.attachToRecyclerView(recyclerViewNotes)
|
itemTouchHelper.attachToRecyclerView(recyclerViewNotes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showServerDeletionDialog(note: Note, originalList: List<Note>) {
|
||||||
|
val alwaysDeleteFromServer = prefs.getBoolean(Constants.KEY_ALWAYS_DELETE_FROM_SERVER, false)
|
||||||
|
|
||||||
|
if (alwaysDeleteFromServer) {
|
||||||
|
// Auto-delete from server without asking
|
||||||
|
deleteNoteLocally(note, deleteFromServer = true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val dialogView = layoutInflater.inflate(R.layout.dialog_server_deletion, null)
|
||||||
|
val checkboxAlways = dialogView.findViewById<CheckBox>(R.id.checkboxAlwaysDeleteFromServer)
|
||||||
|
|
||||||
|
MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle("Notiz löschen")
|
||||||
|
.setMessage("\"${note.title}\" wird lokal gelöscht.\n\nAuch vom Server löschen?")
|
||||||
|
.setView(dialogView)
|
||||||
|
.setNeutralButton("Abbrechen") { _, _ ->
|
||||||
|
// RESTORE: Re-submit original list (note is NOT deleted from storage)
|
||||||
|
adapter.submitList(originalList)
|
||||||
|
}
|
||||||
|
.setOnCancelListener {
|
||||||
|
// User pressed back - also restore
|
||||||
|
adapter.submitList(originalList)
|
||||||
|
}
|
||||||
|
.setPositiveButton("Nur lokal") { _, _ ->
|
||||||
|
if (checkboxAlways.isChecked) {
|
||||||
|
prefs.edit().putBoolean(Constants.KEY_ALWAYS_DELETE_FROM_SERVER, false).apply()
|
||||||
|
}
|
||||||
|
// NOW actually delete from storage
|
||||||
|
deleteNoteLocally(note, deleteFromServer = false)
|
||||||
|
}
|
||||||
|
.setNegativeButton("Vom Server löschen") { _, _ ->
|
||||||
|
if (checkboxAlways.isChecked) {
|
||||||
|
prefs.edit().putBoolean(Constants.KEY_ALWAYS_DELETE_FROM_SERVER, true).apply()
|
||||||
|
}
|
||||||
|
deleteNoteLocally(note, deleteFromServer = true)
|
||||||
|
}
|
||||||
|
.setCancelable(true)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteNoteLocally(note: Note, deleteFromServer: Boolean) {
|
||||||
|
// Track pending deletion to prevent flicker
|
||||||
|
pendingDeletions.add(note.id)
|
||||||
|
|
||||||
|
// Delete from storage
|
||||||
|
storage.deleteNote(note.id)
|
||||||
|
|
||||||
|
// Reload to reflect changes
|
||||||
|
loadNotes()
|
||||||
|
|
||||||
|
// Show Snackbar with UNDO option
|
||||||
|
val message = if (deleteFromServer) {
|
||||||
|
"\"${note.title}\" wird lokal und vom Server gelöscht"
|
||||||
|
} else {
|
||||||
|
"\"${note.title}\" lokal gelöscht (Server bleibt)"
|
||||||
|
}
|
||||||
|
|
||||||
|
Snackbar.make(recyclerViewNotes, message, Snackbar.LENGTH_LONG)
|
||||||
|
.setAction("RÜCKGÄNGIG") {
|
||||||
|
// UNDO: Restore note
|
||||||
|
storage.saveNote(note)
|
||||||
|
pendingDeletions.remove(note.id)
|
||||||
|
loadNotes()
|
||||||
|
}
|
||||||
|
.addCallback(object : Snackbar.Callback() {
|
||||||
|
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||||
|
if (event != DISMISS_EVENT_ACTION) {
|
||||||
|
// Snackbar dismissed without UNDO
|
||||||
|
pendingDeletions.remove(note.id)
|
||||||
|
|
||||||
|
// Delete from server if requested
|
||||||
|
if (deleteFromServer) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
try {
|
||||||
|
val webdavService = WebDavSyncService(this@MainActivity)
|
||||||
|
val success = webdavService.deleteNoteFromServer(note.id)
|
||||||
|
if (success) {
|
||||||
|
runOnUiThread {
|
||||||
|
Toast.makeText(this@MainActivity, "Vom Server gelöscht", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runOnUiThread {
|
||||||
|
Toast.makeText(this@MainActivity, "Server-Löschung fehlgeschlagen", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
runOnUiThread {
|
||||||
|
Toast.makeText(this@MainActivity, "Server-Fehler: ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).show()
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupFab() {
|
private fun setupFab() {
|
||||||
fabAddNote.setOnClickListener {
|
fabAddNote.setOnClickListener {
|
||||||
openNoteEditor(null)
|
openNoteEditor(null)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.dettmer.simplenotes
|
package dev.dettmer.simplenotes
|
||||||
|
|
||||||
|
import android.app.ProgressDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@@ -8,6 +9,7 @@ import android.os.PowerManager
|
|||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.RadioButton
|
import android.widget.RadioButton
|
||||||
@@ -56,14 +58,15 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
private lateinit var editTextUsername: EditText
|
private lateinit var editTextUsername: EditText
|
||||||
private lateinit var editTextPassword: EditText
|
private lateinit var editTextPassword: EditText
|
||||||
private lateinit var switchAutoSync: SwitchCompat
|
private lateinit var switchAutoSync: SwitchCompat
|
||||||
private lateinit var switchMarkdownExport: SwitchCompat
|
private lateinit var switchMarkdownAutoSync: SwitchCompat
|
||||||
private lateinit var buttonTestConnection: Button
|
private lateinit var buttonTestConnection: Button
|
||||||
private lateinit var buttonSyncNow: Button
|
private lateinit var buttonSyncNow: Button
|
||||||
private lateinit var buttonCreateBackup: Button
|
private lateinit var buttonCreateBackup: Button
|
||||||
private lateinit var buttonRestoreFromFile: Button
|
private lateinit var buttonRestoreFromFile: Button
|
||||||
private lateinit var buttonRestoreFromServer: Button
|
private lateinit var buttonRestoreFromServer: Button
|
||||||
private lateinit var buttonImportMarkdown: Button
|
private lateinit var buttonManualMarkdownSync: Button
|
||||||
private lateinit var textViewServerStatus: TextView
|
private lateinit var textViewServerStatus: TextView
|
||||||
|
private lateinit var textViewManualSyncInfo: TextView
|
||||||
|
|
||||||
// Protocol Selection UI
|
// Protocol Selection UI
|
||||||
private lateinit var protocolRadioGroup: RadioGroup
|
private lateinit var protocolRadioGroup: RadioGroup
|
||||||
@@ -129,14 +132,15 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
editTextUsername = findViewById(R.id.editTextUsername)
|
editTextUsername = findViewById(R.id.editTextUsername)
|
||||||
editTextPassword = findViewById(R.id.editTextPassword)
|
editTextPassword = findViewById(R.id.editTextPassword)
|
||||||
switchAutoSync = findViewById(R.id.switchAutoSync)
|
switchAutoSync = findViewById(R.id.switchAutoSync)
|
||||||
switchMarkdownExport = findViewById(R.id.switchMarkdownExport)
|
switchMarkdownAutoSync = findViewById(R.id.switchMarkdownAutoSync)
|
||||||
buttonTestConnection = findViewById(R.id.buttonTestConnection)
|
buttonTestConnection = findViewById(R.id.buttonTestConnection)
|
||||||
buttonSyncNow = findViewById(R.id.buttonSyncNow)
|
buttonSyncNow = findViewById(R.id.buttonSyncNow)
|
||||||
buttonCreateBackup = findViewById(R.id.buttonCreateBackup)
|
buttonCreateBackup = findViewById(R.id.buttonCreateBackup)
|
||||||
buttonRestoreFromFile = findViewById(R.id.buttonRestoreFromFile)
|
buttonRestoreFromFile = findViewById(R.id.buttonRestoreFromFile)
|
||||||
buttonRestoreFromServer = findViewById(R.id.buttonRestoreFromServer)
|
buttonRestoreFromServer = findViewById(R.id.buttonRestoreFromServer)
|
||||||
buttonImportMarkdown = findViewById(R.id.buttonImportMarkdown)
|
buttonManualMarkdownSync = findViewById(R.id.buttonManualMarkdownSync)
|
||||||
textViewServerStatus = findViewById(R.id.textViewServerStatus)
|
textViewServerStatus = findViewById(R.id.textViewServerStatus)
|
||||||
|
textViewManualSyncInfo = findViewById(R.id.textViewManualSyncInfo)
|
||||||
|
|
||||||
// Protocol Selection UI
|
// Protocol Selection UI
|
||||||
protocolRadioGroup = findViewById(R.id.protocolRadioGroup)
|
protocolRadioGroup = findViewById(R.id.protocolRadioGroup)
|
||||||
@@ -179,7 +183,14 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
editTextUsername.setText(prefs.getString(Constants.KEY_USERNAME, ""))
|
editTextUsername.setText(prefs.getString(Constants.KEY_USERNAME, ""))
|
||||||
editTextPassword.setText(prefs.getString(Constants.KEY_PASSWORD, ""))
|
editTextPassword.setText(prefs.getString(Constants.KEY_PASSWORD, ""))
|
||||||
switchAutoSync.isChecked = prefs.getBoolean(Constants.KEY_AUTO_SYNC, false)
|
switchAutoSync.isChecked = prefs.getBoolean(Constants.KEY_AUTO_SYNC, false)
|
||||||
switchMarkdownExport.isChecked = prefs.getBoolean(Constants.KEY_MARKDOWN_EXPORT, false) // Default: disabled (offline-first)
|
|
||||||
|
// Load Markdown Auto-Sync (backward compatible)
|
||||||
|
val markdownExport = prefs.getBoolean(Constants.KEY_MARKDOWN_EXPORT, false)
|
||||||
|
val markdownAutoImport = prefs.getBoolean(Constants.KEY_MARKDOWN_AUTO_IMPORT, false)
|
||||||
|
val markdownAutoSync = markdownExport && markdownAutoImport
|
||||||
|
switchMarkdownAutoSync.isChecked = markdownAutoSync
|
||||||
|
|
||||||
|
updateMarkdownButtonVisibility()
|
||||||
|
|
||||||
// Update hint text based on selected protocol
|
// Update hint text based on selected protocol
|
||||||
updateProtocolHint()
|
updateProtocolHint()
|
||||||
@@ -268,17 +279,16 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
showRestoreDialog(RestoreSource.WEBDAV_SERVER, null)
|
showRestoreDialog(RestoreSource.WEBDAV_SERVER, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonImportMarkdown.setOnClickListener {
|
buttonManualMarkdownSync.setOnClickListener {
|
||||||
saveSettings()
|
performManualMarkdownSync()
|
||||||
importMarkdownChanges()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switchAutoSync.setOnCheckedChangeListener { _, isChecked ->
|
switchAutoSync.setOnCheckedChangeListener { _, isChecked ->
|
||||||
onAutoSyncToggled(isChecked)
|
onAutoSyncToggled(isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
switchMarkdownExport.setOnCheckedChangeListener { _, isChecked ->
|
switchMarkdownAutoSync.setOnCheckedChangeListener { _, isChecked ->
|
||||||
onMarkdownExportToggled(isChecked)
|
onMarkdownAutoSyncToggled(isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear error when user starts typing again
|
// Clear error when user starts typing again
|
||||||
@@ -547,64 +557,100 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onMarkdownExportToggled(enabled: Boolean) {
|
private fun onMarkdownAutoSyncToggled(enabled: Boolean) {
|
||||||
prefs.edit().putBoolean(Constants.KEY_MARKDOWN_EXPORT, enabled).apply()
|
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
showToast("Markdown-Export aktiviert - Notizen werden als .md-Dateien exportiert")
|
// Initial-Export wenn Feature aktiviert wird
|
||||||
} else {
|
lifecycleScope.launch {
|
||||||
showToast("Markdown-Export deaktiviert - nur JSON-Sync aktiv")
|
try {
|
||||||
}
|
val noteStorage = dev.dettmer.simplenotes.storage.NotesStorage(this@SettingsActivity)
|
||||||
|
val currentNoteCount = noteStorage.loadAllNotes().size
|
||||||
|
|
||||||
|
if (currentNoteCount > 0) {
|
||||||
|
// Zeige Progress-Dialog
|
||||||
|
val progressDialog = ProgressDialog(this@SettingsActivity).apply {
|
||||||
|
setTitle("Markdown Auto-Sync")
|
||||||
|
setMessage("Exportiere Notizen nach Markdown...")
|
||||||
|
setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
|
||||||
|
max = currentNoteCount
|
||||||
|
progress = 0
|
||||||
|
setCancelable(false)
|
||||||
|
show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun importMarkdownChanges() {
|
try {
|
||||||
// Prüfen ob Server konfiguriert ist
|
// Hole Server-Daten
|
||||||
val serverUrl = prefs.getString(Constants.KEY_SERVER_URL, "") ?: ""
|
val serverUrl = prefs.getString(Constants.KEY_SERVER_URL, "") ?: ""
|
||||||
val username = prefs.getString(Constants.KEY_USERNAME, "") ?: ""
|
val username = prefs.getString(Constants.KEY_USERNAME, "") ?: ""
|
||||||
val password = prefs.getString(Constants.KEY_PASSWORD, "") ?: ""
|
val password = prefs.getString(Constants.KEY_PASSWORD, "") ?: ""
|
||||||
|
|
||||||
if (serverUrl.isBlank() || username.isBlank() || password.isBlank()) {
|
if (serverUrl.isBlank() || username.isBlank() || password.isBlank()) {
|
||||||
showToast("Bitte zuerst WebDAV-Server konfigurieren")
|
progressDialog.dismiss()
|
||||||
return
|
showToast("⚠️ Bitte zuerst WebDAV-Server konfigurieren")
|
||||||
|
switchMarkdownAutoSync.isChecked = false
|
||||||
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import-Dialog mit Warnung
|
// Führe Initial-Export aus
|
||||||
AlertDialog.Builder(this)
|
|
||||||
.setTitle("Markdown-Import")
|
|
||||||
.setMessage(
|
|
||||||
"Importiert Änderungen aus .md-Dateien vom Server.\n\n" +
|
|
||||||
"⚠️ Bei Konflikten: Last-Write-Wins (neuere Zeitstempel gewinnen)\n\n" +
|
|
||||||
"Fortfahren?"
|
|
||||||
)
|
|
||||||
.setPositiveButton("Importieren") { _, _ ->
|
|
||||||
performMarkdownImport(serverUrl, username, password)
|
|
||||||
}
|
|
||||||
.setNegativeButton("Abbrechen", null)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun performMarkdownImport(serverUrl: String, username: String, password: String) {
|
|
||||||
showToast("Importiere Markdown-Dateien...")
|
|
||||||
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
val syncService = WebDavSyncService(this@SettingsActivity)
|
val syncService = WebDavSyncService(this@SettingsActivity)
|
||||||
val importCount = syncService.syncMarkdownFiles(serverUrl, username, password)
|
val exportedCount = syncService.exportAllNotesToMarkdown(
|
||||||
|
serverUrl = serverUrl,
|
||||||
|
username = username,
|
||||||
|
password = password,
|
||||||
|
onProgress = { current, total ->
|
||||||
|
runOnUiThread {
|
||||||
|
progressDialog.progress = current
|
||||||
|
progressDialog.setMessage("Exportiere $current/$total Notizen...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
progressDialog.dismiss()
|
||||||
|
|
||||||
|
// Speichere beide Einstellungen
|
||||||
|
prefs.edit()
|
||||||
|
.putBoolean(Constants.KEY_MARKDOWN_EXPORT, enabled)
|
||||||
|
.putBoolean(Constants.KEY_MARKDOWN_AUTO_IMPORT, enabled)
|
||||||
|
.apply()
|
||||||
|
|
||||||
|
updateMarkdownButtonVisibility()
|
||||||
|
|
||||||
|
// Erfolgs-Nachricht
|
||||||
|
showToast("✅ $exportedCount Notizen nach Markdown exportiert")
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
if (importCount > 0) {
|
|
||||||
showToast("$importCount Notizen aus Markdown importiert")
|
|
||||||
// Benachrichtige MainActivity zum Neuladen
|
|
||||||
sendBroadcast(Intent("dev.dettmer.simplenotes.NOTES_CHANGED"))
|
|
||||||
} else {
|
|
||||||
showToast("Keine Markdown-Änderungen gefunden")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
withContext(Dispatchers.Main) {
|
progressDialog.dismiss()
|
||||||
showToast("Import-Fehler: ${e.message}")
|
showToast("❌ Export fehlgeschlagen: ${e.message}")
|
||||||
|
|
||||||
|
// Deaktiviere Toggle bei Fehler
|
||||||
|
switchMarkdownAutoSync.isChecked = false
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Keine Notizen vorhanden - speichere Einstellungen direkt
|
||||||
|
prefs.edit()
|
||||||
|
.putBoolean(Constants.KEY_MARKDOWN_EXPORT, enabled)
|
||||||
|
.putBoolean(Constants.KEY_MARKDOWN_AUTO_IMPORT, enabled)
|
||||||
|
.apply()
|
||||||
|
|
||||||
|
updateMarkdownButtonVisibility()
|
||||||
|
showToast("Markdown Auto-Sync aktiviert - Notizen werden als .md-Dateien exportiert und importiert")
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.e(TAG, "Error toggling markdown auto-sync: ${e.message}")
|
||||||
|
showToast("Fehler: ${e.message}")
|
||||||
|
switchMarkdownAutoSync.isChecked = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Deaktivieren - Settings speichern
|
||||||
|
prefs.edit()
|
||||||
|
.putBoolean(Constants.KEY_MARKDOWN_EXPORT, enabled)
|
||||||
|
.putBoolean(Constants.KEY_MARKDOWN_AUTO_IMPORT, enabled)
|
||||||
|
.apply()
|
||||||
|
|
||||||
|
updateMarkdownButtonVisibility()
|
||||||
|
showToast("Markdown Auto-Sync deaktiviert - nur JSON-Sync aktiv")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -867,7 +913,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Refresh MainActivity's note list
|
// Refresh MainActivity's note list
|
||||||
setResult(RESULT_OK)
|
setResult(RESULT_OK)
|
||||||
broadcastNotesChanged()
|
broadcastNotesChanged(result.imported_notes)
|
||||||
} else {
|
} else {
|
||||||
showErrorDialog("Wiederherstellung fehlgeschlagen", result.error ?: "Unbekannter Fehler")
|
showErrorDialog("Wiederherstellung fehlgeschlagen", result.error ?: "Unbekannter Fehler")
|
||||||
}
|
}
|
||||||
@@ -880,12 +926,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Führt Restore vom Server durch (Task #1.2.0-05b)
|
* Server-Restore mit Restore-Modi (v1.3.0)
|
||||||
* Nutzt neues universelles Dialog-System mit Restore-Modi
|
|
||||||
*
|
|
||||||
* HINWEIS: Die alte WebDavSyncService.restoreFromServer() Funktion
|
|
||||||
* unterstützt noch keine Restore-Modi. Aktuell wird immer REPLACE verwendet.
|
|
||||||
* TODO: WebDavSyncService.restoreFromServer() erweitern für v1.2.1+
|
|
||||||
*/
|
*/
|
||||||
private fun performRestoreFromServer(mode: RestoreMode) {
|
private fun performRestoreFromServer(mode: RestoreMode) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
@@ -897,7 +938,6 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Logger.d(TAG, "📥 Restoring from server (mode: $mode)")
|
Logger.d(TAG, "📥 Restoring from server (mode: $mode)")
|
||||||
Logger.w(TAG, "⚠️ Server-Restore nutzt aktuell immer REPLACE Mode (TODO: v1.2.1+)")
|
|
||||||
|
|
||||||
// Auto-Backup erstellen (Sicherheitsnetz)
|
// Auto-Backup erstellen (Sicherheitsnetz)
|
||||||
val autoBackupUri = backupManager.createAutoBackup()
|
val autoBackupUri = backupManager.createAutoBackup()
|
||||||
@@ -908,8 +948,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
// Server-Restore durchführen
|
// Server-Restore durchführen
|
||||||
val webdavService = WebDavSyncService(this@SettingsActivity)
|
val webdavService = WebDavSyncService(this@SettingsActivity)
|
||||||
val result = withContext(Dispatchers.IO) {
|
val result = withContext(Dispatchers.IO) {
|
||||||
// Nutzt alte Funktion (immer REPLACE)
|
webdavService.restoreFromServer(mode) // ✅ Pass mode parameter
|
||||||
webdavService.restoreFromServer()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
progressDialog.dismiss()
|
progressDialog.dismiss()
|
||||||
@@ -917,7 +956,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
showToast("✅ Wiederhergestellt: ${result.restoredCount} Notizen")
|
showToast("✅ Wiederhergestellt: ${result.restoredCount} Notizen")
|
||||||
setResult(RESULT_OK)
|
setResult(RESULT_OK)
|
||||||
broadcastNotesChanged()
|
broadcastNotesChanged(result.restoredCount)
|
||||||
} else {
|
} else {
|
||||||
showErrorDialog("Wiederherstellung fehlgeschlagen", result.errorMessage ?: "Unbekannter Fehler")
|
showErrorDialog("Wiederherstellung fehlgeschlagen", result.errorMessage ?: "Unbekannter Fehler")
|
||||||
}
|
}
|
||||||
@@ -932,13 +971,70 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
/**
|
/**
|
||||||
* Sendet Broadcast dass Notizen geändert wurden
|
* Sendet Broadcast dass Notizen geändert wurden
|
||||||
*/
|
*/
|
||||||
private fun broadcastNotesChanged() {
|
private fun broadcastNotesChanged(count: Int = 0) {
|
||||||
val intent = Intent(dev.dettmer.simplenotes.sync.SyncWorker.ACTION_SYNC_COMPLETED)
|
val intent = Intent(dev.dettmer.simplenotes.sync.SyncWorker.ACTION_SYNC_COMPLETED)
|
||||||
intent.putExtra("success", true)
|
intent.putExtra("success", true)
|
||||||
intent.putExtra("syncedCount", 0)
|
intent.putExtra("syncedCount", count)
|
||||||
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
|
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates visibility of manual sync button based on Auto-Sync toggle state
|
||||||
|
*/
|
||||||
|
private fun updateMarkdownButtonVisibility() {
|
||||||
|
val autoSyncEnabled = switchMarkdownAutoSync.isChecked
|
||||||
|
val visibility = if (autoSyncEnabled) View.GONE else View.VISIBLE
|
||||||
|
|
||||||
|
textViewManualSyncInfo.visibility = visibility
|
||||||
|
buttonManualMarkdownSync.visibility = visibility
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs manual Markdown sync (Export + Import)
|
||||||
|
* Called when manual sync button is clicked
|
||||||
|
*/
|
||||||
|
private fun performManualMarkdownSync() {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
var progressDialog: ProgressDialog? = null
|
||||||
|
try {
|
||||||
|
// Validierung
|
||||||
|
val serverUrl = prefs.getString(Constants.KEY_SERVER_URL, "")
|
||||||
|
val username = prefs.getString(Constants.KEY_USERNAME, "")
|
||||||
|
val password = prefs.getString(Constants.KEY_PASSWORD, "")
|
||||||
|
|
||||||
|
if (serverUrl.isNullOrBlank() || username.isNullOrBlank() || password.isNullOrBlank()) {
|
||||||
|
showToast("⚠️ Bitte zuerst WebDAV-Server konfigurieren")
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress-Dialog
|
||||||
|
progressDialog = ProgressDialog(this@SettingsActivity).apply {
|
||||||
|
setTitle("Markdown-Sync")
|
||||||
|
setMessage("Synchronisiere Markdown-Dateien...")
|
||||||
|
setCancelable(false)
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync ausführen
|
||||||
|
val syncService = dev.dettmer.simplenotes.sync.WebDavSyncService(this@SettingsActivity)
|
||||||
|
val result = syncService.manualMarkdownSync()
|
||||||
|
|
||||||
|
progressDialog.dismiss()
|
||||||
|
|
||||||
|
// Erfolgs-Nachricht
|
||||||
|
val message = "✅ Sync abgeschlossen\n📤 ${result.exportedCount} exportiert\n📥 ${result.importedCount} importiert"
|
||||||
|
showToast(message)
|
||||||
|
|
||||||
|
Logger.d("SettingsActivity", "Manual markdown sync: exported=${result.exportedCount}, imported=${result.importedCount}")
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
progressDialog?.dismiss()
|
||||||
|
showToast("❌ Sync fehlgeschlagen: ${e.message}")
|
||||||
|
Logger.e("SettingsActivity", "Manual markdown sync failed", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeigt Error-Dialog an
|
* Zeigt Error-Dialog an
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package dev.dettmer.simplenotes.models
|
||||||
|
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
data class DeletionRecord(
|
||||||
|
val id: String,
|
||||||
|
val deletedAt: Long,
|
||||||
|
val deviceId: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DeletionTracker(
|
||||||
|
val version: Int = 1,
|
||||||
|
val deletedNotes: MutableList<DeletionRecord> = mutableListOf()
|
||||||
|
) {
|
||||||
|
fun addDeletion(noteId: String, deviceId: String) {
|
||||||
|
if (!deletedNotes.any { it.id == noteId }) {
|
||||||
|
deletedNotes.add(DeletionRecord(noteId, System.currentTimeMillis(), deviceId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isDeleted(noteId: String): Boolean {
|
||||||
|
return deletedNotes.any { it.id == noteId }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDeletionTimestamp(noteId: String): Long? {
|
||||||
|
return deletedNotes.find { it.id == noteId }?.deletedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeDeletion(noteId: String) {
|
||||||
|
deletedNotes.removeIf { it.id == noteId }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toJson(): String {
|
||||||
|
val jsonObject = JSONObject()
|
||||||
|
jsonObject.put("version", version)
|
||||||
|
|
||||||
|
val notesArray = JSONArray()
|
||||||
|
for (record in deletedNotes) {
|
||||||
|
val recordObj = JSONObject()
|
||||||
|
recordObj.put("id", record.id)
|
||||||
|
recordObj.put("deletedAt", record.deletedAt)
|
||||||
|
recordObj.put("deviceId", record.deviceId)
|
||||||
|
notesArray.put(recordObj)
|
||||||
|
}
|
||||||
|
jsonObject.put("deletedNotes", notesArray)
|
||||||
|
|
||||||
|
return jsonObject.toString(2) // Pretty print with 2-space indent
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromJson(json: String): DeletionTracker? {
|
||||||
|
return try {
|
||||||
|
val jsonObject = JSONObject(json)
|
||||||
|
val version = jsonObject.optInt("version", 1)
|
||||||
|
val deletedNotes = mutableListOf<DeletionRecord>()
|
||||||
|
|
||||||
|
val notesArray = jsonObject.optJSONArray("deletedNotes")
|
||||||
|
if (notesArray != null) {
|
||||||
|
for (i in 0 until notesArray.length()) {
|
||||||
|
val recordObj = notesArray.getJSONObject(i)
|
||||||
|
val record = DeletionRecord(
|
||||||
|
id = recordObj.getString("id"),
|
||||||
|
deletedAt = recordObj.getLong("deletedAt"),
|
||||||
|
deviceId = recordObj.getString("deviceId")
|
||||||
|
)
|
||||||
|
deletedNotes.add(record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DeletionTracker(version, deletedNotes)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
package dev.dettmer.simplenotes.storage
|
package dev.dettmer.simplenotes.storage
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import dev.dettmer.simplenotes.models.DeletionTracker
|
||||||
import dev.dettmer.simplenotes.models.Note
|
import dev.dettmer.simplenotes.models.Note
|
||||||
|
import dev.dettmer.simplenotes.utils.DeviceIdGenerator
|
||||||
|
import dev.dettmer.simplenotes.utils.Logger
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class NotesStorage(private val context: Context) {
|
class NotesStorage(private val context: Context) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "NotesStorage"
|
||||||
|
}
|
||||||
|
|
||||||
private val notesDir: File = File(context.filesDir, "notes").apply {
|
private val notesDir: File = File(context.filesDir, "notes").apply {
|
||||||
if (!exists()) mkdirs()
|
if (!exists()) mkdirs()
|
||||||
}
|
}
|
||||||
@@ -34,19 +41,89 @@ class NotesStorage(private val context: Context) {
|
|||||||
|
|
||||||
fun deleteNote(id: String): Boolean {
|
fun deleteNote(id: String): Boolean {
|
||||||
val file = File(notesDir, "$id.json")
|
val file = File(notesDir, "$id.json")
|
||||||
return file.delete()
|
val deleted = file.delete()
|
||||||
|
|
||||||
|
if (deleted) {
|
||||||
|
Logger.d(TAG, "🗑️ Deleted note: $id")
|
||||||
|
|
||||||
|
// Track deletion to prevent zombie notes
|
||||||
|
val deviceId = DeviceIdGenerator.getDeviceId(context)
|
||||||
|
trackDeletion(id, deviceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return deleted
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteAllNotes(): Boolean {
|
fun deleteAllNotes(): Boolean {
|
||||||
return try {
|
return try {
|
||||||
notesDir.listFiles()
|
val notes = loadAllNotes()
|
||||||
?.filter { it.extension == "json" }
|
val deviceId = DeviceIdGenerator.getDeviceId(context)
|
||||||
?.forEach { it.delete() }
|
|
||||||
|
for (note in notes) {
|
||||||
|
deleteNote(note.id) // Uses trackDeletion() automatically
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.d(TAG, "🗑️ Deleted all notes (${notes.size} notes)")
|
||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Logger.e(TAG, "Failed to delete all notes", e)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Deletion Tracking ===
|
||||||
|
|
||||||
|
private fun getDeletionTrackerFile(): File {
|
||||||
|
return File(context.filesDir, "deleted_notes.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadDeletionTracker(): DeletionTracker {
|
||||||
|
val file = getDeletionTrackerFile()
|
||||||
|
if (!file.exists()) {
|
||||||
|
return DeletionTracker()
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val json = file.readText()
|
||||||
|
DeletionTracker.fromJson(json) ?: DeletionTracker()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.e(TAG, "Failed to load deletion tracker", e)
|
||||||
|
DeletionTracker()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveDeletionTracker(tracker: DeletionTracker) {
|
||||||
|
try {
|
||||||
|
val file = getDeletionTrackerFile()
|
||||||
|
file.writeText(tracker.toJson())
|
||||||
|
|
||||||
|
if (tracker.deletedNotes.size > 1000) {
|
||||||
|
Logger.w(TAG, "⚠️ Deletion tracker large: ${tracker.deletedNotes.size} entries")
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.d(TAG, "✅ Deletion tracker saved (${tracker.deletedNotes.size} entries)")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.e(TAG, "Failed to save deletion tracker", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun trackDeletion(noteId: String, deviceId: String) {
|
||||||
|
val tracker = loadDeletionTracker()
|
||||||
|
tracker.addDeletion(noteId, deviceId)
|
||||||
|
saveDeletionTracker(tracker)
|
||||||
|
Logger.d(TAG, "📝 Tracked deletion: $noteId")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isNoteDeleted(noteId: String): Boolean {
|
||||||
|
val tracker = loadDeletionTracker()
|
||||||
|
return tracker.isDeleted(noteId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearDeletionTracker() {
|
||||||
|
saveDeletionTracker(DeletionTracker())
|
||||||
|
Logger.d(TAG, "🗑️ Deletion tracker cleared")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getNotesDir(): File = notesDir
|
fun getNotesDir(): File = notesDir
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,10 @@ object Constants {
|
|||||||
const val KEY_MARKDOWN_EXPORT = "markdown_export_enabled"
|
const val KEY_MARKDOWN_EXPORT = "markdown_export_enabled"
|
||||||
const val KEY_MARKDOWN_AUTO_IMPORT = "markdown_auto_import_enabled"
|
const val KEY_MARKDOWN_AUTO_IMPORT = "markdown_auto_import_enabled"
|
||||||
|
|
||||||
|
// 🔥 v1.3.0: Performance & Multi-Device Sync
|
||||||
|
const val KEY_ALWAYS_CHECK_SERVER = "always_check_server"
|
||||||
|
const val KEY_ALWAYS_DELETE_FROM_SERVER = "always_delete_from_server"
|
||||||
|
|
||||||
// WorkManager
|
// WorkManager
|
||||||
const val SYNC_WORK_TAG = "notes_sync"
|
const val SYNC_WORK_TAG = "notes_sync"
|
||||||
const val SYNC_DELAY_SECONDS = 5L
|
const val SYNC_DELAY_SECONDS = 5L
|
||||||
|
|||||||
@@ -109,7 +109,7 @@
|
|||||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||||
app:startIconDrawable="@android:drawable/ic_menu_compass"
|
app:startIconDrawable="@android:drawable/ic_menu_compass"
|
||||||
app:endIconMode="clear_text"
|
app:endIconMode="clear_text"
|
||||||
app:helperText="z.B. http://192.168.0.188:8080/webdav"
|
app:helperText="z.B. http://192.168.0.188:8080/notes"
|
||||||
app:helperTextEnabled="true"
|
app:helperTextEnabled="true"
|
||||||
app:boxCornerRadiusTopStart="12dp"
|
app:boxCornerRadiusTopStart="12dp"
|
||||||
app:boxCornerRadiusTopEnd="12dp"
|
app:boxCornerRadiusTopEnd="12dp"
|
||||||
@@ -429,11 +429,11 @@
|
|||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
<!-- Markdown Export Toggle -->
|
<!-- Markdown Auto-Sync Toggle (fusioniert Export + Auto-Import) -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
@@ -441,34 +441,47 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="📝 Markdown Export (Desktop-Zugriff)"
|
android:text="🔄 Markdown Auto-Sync"
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
|
android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
|
||||||
|
|
||||||
<androidx.appcompat.widget.SwitchCompat
|
<androidx.appcompat.widget.SwitchCompat
|
||||||
android:id="@+id/switchMarkdownExport"
|
android:id="@+id/switchMarkdownAutoSync"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:checked="true" />
|
android:checked="false" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Import Markdown Button -->
|
<!-- Auto-Sync Info Text -->
|
||||||
<Button
|
|
||||||
android:id="@+id/buttonImportMarkdown"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="📥 Markdown-Änderungen importieren"
|
|
||||||
style="@style/Widget.Material3.Button.TonalButton" />
|
|
||||||
|
|
||||||
<!-- Import Info Text -->
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:text="Importiert manuelle Änderungen von Desktop-Apps (.md Dateien vom Server)"
|
android:text="Synchronisiert Notizen automatisch als .md Dateien (Upload + Download bei jedem Sync)"
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
|
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
|
||||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||||
|
|
||||||
|
<!-- Manual Sync Info (nur sichtbar wenn Auto-Sync OFF) -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewManualSyncInfo"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:text="Oder synchronisiere Markdown-Dateien manuell:"
|
||||||
|
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:textColor="?attr/colorOnSurface"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<!-- Manual Sync Button (nur sichtbar wenn Auto-Sync OFF) -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonManualMarkdownSync"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Markdown synchronisieren"
|
||||||
|
android:visibility="gone"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|||||||
15
android/app/src/main/res/layout/dialog_server_deletion.xml
Normal file
15
android/app/src/main/res/layout/dialog_server_deletion.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkboxAlwaysDeleteFromServer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Diese Entscheidung merken"
|
||||||
|
android:textAppearance="@style/TextAppearance.Material3.BodyMedium" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
324
docs/BACKUP.en.md
Normal file
324
docs/BACKUP.en.md
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
# Backup & Restore 💾
|
||||||
|
|
||||||
|
**🌍 Languages:** [Deutsch](BACKUP.md) · **English**
|
||||||
|
|
||||||
|
> Secure your notes locally - independent from the server
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Overview
|
||||||
|
|
||||||
|
The backup system works **completely offline** and independent from the WebDAV server. Perfect for:
|
||||||
|
- 📥 Regular backups
|
||||||
|
- 📤 Migration to new server
|
||||||
|
- 🔄 Recovery after data loss
|
||||||
|
- 💾 Archiving old notes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📥 Create Backup
|
||||||
|
|
||||||
|
### Step-by-Step
|
||||||
|
|
||||||
|
1. **Open settings** (⚙️ icon top right)
|
||||||
|
2. **Find "Backup & Restore"** section
|
||||||
|
3. **Tap "📥 Create backup"**
|
||||||
|
4. **Choose location:**
|
||||||
|
- 📁 Downloads
|
||||||
|
- 💳 SD card
|
||||||
|
- ☁️ Cloud folder (Nextcloud, Google Drive, etc.)
|
||||||
|
- 📧 Email as attachment
|
||||||
|
5. **Done!** Backup file is saved
|
||||||
|
|
||||||
|
### File Format
|
||||||
|
|
||||||
|
**Filename:** `simplenotes_backup_YYYY-MM-DD_HHmmss.json`
|
||||||
|
|
||||||
|
**Example:** `simplenotes_backup_2026-01-05_143022.json`
|
||||||
|
|
||||||
|
**Content:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "1.2.1",
|
||||||
|
"exported_at": "2026-01-05T14:30:22Z",
|
||||||
|
"notes_count": 42,
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"id": "abc-123-def",
|
||||||
|
"title": "Shopping List",
|
||||||
|
"content": "Milk\nBread\nCheese",
|
||||||
|
"createdAt": 1704467422000,
|
||||||
|
"updatedAt": 1704467422000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Format details:**
|
||||||
|
- ✅ Human-readable (formatted JSON)
|
||||||
|
- ✅ All data included (title, content, IDs, timestamps)
|
||||||
|
- ✅ Version info for compatibility
|
||||||
|
- ✅ Note count for validation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📤 Restore Backup
|
||||||
|
|
||||||
|
### 3 Restore Modes
|
||||||
|
|
||||||
|
#### 1. Merge ⭐ _Recommended_
|
||||||
|
|
||||||
|
**What happens:**
|
||||||
|
- ✅ New notes from backup are added
|
||||||
|
- ✅ Existing notes remain unchanged
|
||||||
|
- ✅ No data loss
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Import backup from another device
|
||||||
|
- Recover old notes
|
||||||
|
- Restore accidentally deleted notes
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```
|
||||||
|
App: [Note A, Note B, Note C]
|
||||||
|
Backup: [Note A, Note D, Note E]
|
||||||
|
Result: [Note A, Note B, Note C, Note D, Note E]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Replace
|
||||||
|
|
||||||
|
**What happens:**
|
||||||
|
- ❌ ALL existing notes are deleted
|
||||||
|
- ✅ Backup notes are imported
|
||||||
|
- ⚠️ Irreversible (except through auto-backup)
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Server migration (complete restart)
|
||||||
|
- Return to old backup state
|
||||||
|
- App reinstallation
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```
|
||||||
|
App: [Note A, Note B, Note C]
|
||||||
|
Backup: [Note X, Note Y]
|
||||||
|
Result: [Note X, Note Y]
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ Warning:** Automatic safety backup is created!
|
||||||
|
|
||||||
|
#### 3. Overwrite Duplicates
|
||||||
|
|
||||||
|
**What happens:**
|
||||||
|
- ✅ New notes from backup are added
|
||||||
|
- 🔄 On ID conflicts, backup wins
|
||||||
|
- ✅ Other notes remain unchanged
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- Backup is newer than app data
|
||||||
|
- Import desktop changes
|
||||||
|
- Conflict resolution
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```
|
||||||
|
App: [Note A (v1), Note B, Note C]
|
||||||
|
Backup: [Note A (v2), Note D]
|
||||||
|
Result: [Note A (v2), Note B, Note C, Note D]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Restore Process
|
||||||
|
|
||||||
|
1. **Settings** → **"📤 Restore from file"**
|
||||||
|
2. **Select backup file** (`.json`)
|
||||||
|
3. **Choose mode:**
|
||||||
|
- 🔵 Merge _(Default)_
|
||||||
|
- 🟡 Overwrite duplicates
|
||||||
|
- 🔴 Replace _(Caution!)_
|
||||||
|
4. **Confirm** - Automatic safety backup is created
|
||||||
|
5. **Wait** - Import runs
|
||||||
|
6. **Done!** - Success message with number of imported notes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ Automatic Safety Backup
|
||||||
|
|
||||||
|
**Before every restore:**
|
||||||
|
- ✅ Automatic backup is created
|
||||||
|
- 📁 Saved in: `Android/data/dev.dettmer.simplenotes/files/`
|
||||||
|
- 🏷️ Filename: `auto_backup_before_restore_YYYY-MM-DD_HHmmss.json`
|
||||||
|
- ⏱️ Timestamp: Right before restore
|
||||||
|
|
||||||
|
**Why?**
|
||||||
|
- Protection against accidental "Replace"
|
||||||
|
- Ability to undo
|
||||||
|
- Double security
|
||||||
|
|
||||||
|
**Access via file manager:**
|
||||||
|
```
|
||||||
|
/Android/data/dev.dettmer.simplenotes/files/auto_backup_before_restore_*.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Best Practices
|
||||||
|
|
||||||
|
### Backup Strategy
|
||||||
|
|
||||||
|
#### Regular Backups
|
||||||
|
```
|
||||||
|
Daily: ❌ Too often (server sync is enough)
|
||||||
|
Weekly: ✅ Recommended for important notes
|
||||||
|
Monthly: ✅ Archiving
|
||||||
|
Before updates: ✅ Safety
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3-2-1 Rule
|
||||||
|
1. **3 copies** - Original + 2 backups
|
||||||
|
2. **2 media** - e.g., SD card + cloud
|
||||||
|
3. **1 offsite** - e.g., cloud storage
|
||||||
|
|
||||||
|
### Backup Locations
|
||||||
|
|
||||||
|
**Local (fast):**
|
||||||
|
- 📱 Internal storage / Downloads
|
||||||
|
- 💳 SD card
|
||||||
|
- 🖥️ PC (via USB)
|
||||||
|
|
||||||
|
**Cloud (secure):**
|
||||||
|
- ☁️ Nextcloud (self-hosted)
|
||||||
|
- 📧 Email to yourself
|
||||||
|
- 🗄️ Syncthing (sync between devices)
|
||||||
|
|
||||||
|
**⚠️ Avoid:**
|
||||||
|
- ❌ Google Drive / Dropbox (privacy)
|
||||||
|
- ❌ Only one copy
|
||||||
|
- ❌ Only on server (if server fails)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Advanced Usage
|
||||||
|
|
||||||
|
### Edit Backup File
|
||||||
|
|
||||||
|
The `.json` file can be edited with any text editor:
|
||||||
|
|
||||||
|
1. **Open with:** VS Code, Notepad++, nano
|
||||||
|
2. **Add/remove notes**
|
||||||
|
3. **Change title/content**
|
||||||
|
4. **Adjust IDs** (for migration)
|
||||||
|
5. **Save** and import to app
|
||||||
|
|
||||||
|
**⚠️ Important:**
|
||||||
|
- Keep valid JSON format
|
||||||
|
- IDs must be unique (UUIDs)
|
||||||
|
- Timestamps in milliseconds (Unix Epoch)
|
||||||
|
|
||||||
|
### Bulk Import
|
||||||
|
|
||||||
|
Merge multiple backups:
|
||||||
|
|
||||||
|
1. Import backup 1 (Mode: Merge)
|
||||||
|
2. Import backup 2 (Mode: Merge)
|
||||||
|
3. Import backup 3 (Mode: Merge)
|
||||||
|
4. Result: All notes combined
|
||||||
|
|
||||||
|
### Server Migration
|
||||||
|
|
||||||
|
Step-by-step:
|
||||||
|
|
||||||
|
1. **Create backup** on old server
|
||||||
|
2. **Set up new server** (see [QUICKSTART.en.md](QUICKSTART.en.md))
|
||||||
|
3. **Change server URL** in app settings
|
||||||
|
4. **Restore backup** (Mode: Replace)
|
||||||
|
5. **Test sync** - All notes on new server
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❌ Troubleshooting
|
||||||
|
|
||||||
|
### "Invalid backup file"
|
||||||
|
|
||||||
|
**Causes:**
|
||||||
|
- Corrupt JSON file
|
||||||
|
- Wrong file extension (must be `.json`)
|
||||||
|
- Incompatible app version
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Check JSON file with validator (e.g., jsonlint.com)
|
||||||
|
2. Verify file extension
|
||||||
|
3. Create backup with current app version
|
||||||
|
|
||||||
|
### "No permission to save"
|
||||||
|
|
||||||
|
**Causes:**
|
||||||
|
- Storage permission missing
|
||||||
|
- Write-protected folder
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Android: Settings → Apps → Simple Notes → Permissions
|
||||||
|
2. Activate "Storage"
|
||||||
|
3. Choose different location
|
||||||
|
|
||||||
|
### "Import failed"
|
||||||
|
|
||||||
|
**Causes:**
|
||||||
|
- Not enough storage space
|
||||||
|
- Corrupt backup file
|
||||||
|
- App crash during import
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Free up storage space
|
||||||
|
2. Create new backup file
|
||||||
|
3. Restart app and try again
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Security & Privacy
|
||||||
|
|
||||||
|
### Data Protection
|
||||||
|
- ✅ **Locally stored** - No cloud upload without your action
|
||||||
|
- ✅ **No encryption** - Plain text format for readability
|
||||||
|
- ⚠️ **Sensitive data?** - Encrypt backup file yourself (e.g., 7-Zip with password)
|
||||||
|
|
||||||
|
### Recommendations
|
||||||
|
- 🔐 Store backup files in encrypted container
|
||||||
|
- 🗑️ Regularly delete old backups
|
||||||
|
- 📧 Don't send via unencrypted email
|
||||||
|
- ☁️ Use self-hosted cloud (Nextcloud)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Technical Details
|
||||||
|
|
||||||
|
### Format Specification
|
||||||
|
|
||||||
|
**JSON structure:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "string", // App version at export
|
||||||
|
"exported_at": "ISO8601", // Export timestamp
|
||||||
|
"notes_count": number, // Number of notes
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"id": "UUID", // Unique ID
|
||||||
|
"title": "string", // Note title
|
||||||
|
"content": "string", // Note content
|
||||||
|
"createdAt": number, // Unix timestamp (ms)
|
||||||
|
"updatedAt": number // Unix timestamp (ms)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compatibility
|
||||||
|
- ✅ v1.2.0+ - Fully compatible
|
||||||
|
- ⚠️ v1.1.x - Basic functions (without auto-backup)
|
||||||
|
- ❌ v1.0.x - Not supported
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**📚 See also:**
|
||||||
|
- [QUICKSTART.en.md](../QUICKSTART.en.md) - App installation and setup
|
||||||
|
- [FEATURES.en.md](FEATURES.en.md) - Complete feature list
|
||||||
|
- [DESKTOP.en.md](DESKTOP.en.md) - Desktop integration with Markdown
|
||||||
|
|
||||||
|
**Last update:** v1.2.1 (2026-01-05)
|
||||||
324
docs/BACKUP.md
Normal file
324
docs/BACKUP.md
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
# Backup & Wiederherstellung 💾
|
||||||
|
|
||||||
|
**🌍 Languages:** **Deutsch** · [English](BACKUP.en.md)
|
||||||
|
|
||||||
|
> Sichere deine Notizen lokal - unabhängig vom Server
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Übersicht
|
||||||
|
|
||||||
|
Das Backup-System funktioniert **komplett offline** und unabhängig vom WebDAV-Server. Perfekt für:
|
||||||
|
- 📥 Regelmäßige Sicherungen
|
||||||
|
- 📤 Migration zu neuem Server
|
||||||
|
- 🔄 Wiederherstellung nach Datenverlust
|
||||||
|
- 💾 Archivierung alter Notizen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📥 Backup erstellen
|
||||||
|
|
||||||
|
### Schritt-für-Schritt
|
||||||
|
|
||||||
|
1. **Einstellungen öffnen** (⚙️ Icon oben rechts)
|
||||||
|
2. **"Backup & Wiederherstellung"** Section finden
|
||||||
|
3. **"📥 Backup erstellen"** antippen
|
||||||
|
4. **Speicherort wählen:**
|
||||||
|
- 📁 Downloads
|
||||||
|
- 💳 SD-Karte
|
||||||
|
- ☁️ Cloud-Ordner (Nextcloud, Google Drive, etc.)
|
||||||
|
- 📧 E-Mail als Anhang
|
||||||
|
5. **Fertig!** Backup-Datei ist gespeichert
|
||||||
|
|
||||||
|
### Dateiformat
|
||||||
|
|
||||||
|
**Dateiname:** `simplenotes_backup_YYYY-MM-DD_HHmmss.json`
|
||||||
|
|
||||||
|
**Beispiel:** `simplenotes_backup_2026-01-05_143022.json`
|
||||||
|
|
||||||
|
**Inhalt:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "1.2.1",
|
||||||
|
"exported_at": "2026-01-05T14:30:22Z",
|
||||||
|
"notes_count": 42,
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"id": "abc-123-def",
|
||||||
|
"title": "Einkaufsliste",
|
||||||
|
"content": "Milch\nBrot\nKäse",
|
||||||
|
"createdAt": 1704467422000,
|
||||||
|
"updatedAt": 1704467422000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Format-Details:**
|
||||||
|
- ✅ Menschenlesbar (formatiertes JSON)
|
||||||
|
- ✅ Alle Daten inklusive (Titel, Inhalt, IDs, Timestamps)
|
||||||
|
- ✅ Versions-Info für Kompatibilität
|
||||||
|
- ✅ Anzahl der Notizen für Validierung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📤 Backup wiederherstellen
|
||||||
|
|
||||||
|
### 3 Wiederherstellungs-Modi
|
||||||
|
|
||||||
|
#### 1. Zusammenführen (Merge) ⭐ _Empfohlen_
|
||||||
|
|
||||||
|
**Was passiert:**
|
||||||
|
- ✅ Neue Notizen aus Backup werden hinzugefügt
|
||||||
|
- ✅ Bestehende Notizen bleiben unverändert
|
||||||
|
- ✅ Keine Datenverluste
|
||||||
|
|
||||||
|
**Wann nutzen:**
|
||||||
|
- Backup von anderem Gerät einspielen
|
||||||
|
- Alte Notizen zurückholen
|
||||||
|
- Versehentlich gelöschte Notizen wiederherstellen
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
```
|
||||||
|
App: [Notiz A, Notiz B, Notiz C]
|
||||||
|
Backup: [Notiz A, Notiz D, Notiz E]
|
||||||
|
Ergebnis: [Notiz A, Notiz B, Notiz C, Notiz D, Notiz E]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Ersetzen (Replace)
|
||||||
|
|
||||||
|
**Was passiert:**
|
||||||
|
- ❌ ALLE bestehenden Notizen werden gelöscht
|
||||||
|
- ✅ Backup-Notizen werden importiert
|
||||||
|
- ⚠️ Unwiderruflich (außer durch Auto-Backup)
|
||||||
|
|
||||||
|
**Wann nutzen:**
|
||||||
|
- Server-Wechsel (kompletter Neustart)
|
||||||
|
- Zurück zu altem Backup-Stand
|
||||||
|
- App-Neuinstallation
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
```
|
||||||
|
App: [Notiz A, Notiz B, Notiz C]
|
||||||
|
Backup: [Notiz X, Notiz Y]
|
||||||
|
Ergebnis: [Notiz X, Notiz Y]
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ Warnung:** Automatisches Sicherheits-Backup wird erstellt!
|
||||||
|
|
||||||
|
#### 3. Duplikate überschreiben (Overwrite)
|
||||||
|
|
||||||
|
**Was passiert:**
|
||||||
|
- ✅ Neue Notizen aus Backup werden hinzugefügt
|
||||||
|
- 🔄 Bei ID-Konflikten gewinnt das Backup
|
||||||
|
- ✅ Andere Notizen bleiben unverändert
|
||||||
|
|
||||||
|
**Wann nutzen:**
|
||||||
|
- Backup ist neuer als App-Daten
|
||||||
|
- Desktop-Änderungen einspielen
|
||||||
|
- Konflikt-Auflösung
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
```
|
||||||
|
App: [Notiz A (v1), Notiz B, Notiz C]
|
||||||
|
Backup: [Notiz A (v2), Notiz D]
|
||||||
|
Ergebnis: [Notiz A (v2), Notiz B, Notiz C, Notiz D]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wiederherstellungs-Prozess
|
||||||
|
|
||||||
|
1. **Einstellungen** → **"📤 Aus Datei wiederherstellen"**
|
||||||
|
2. **Backup-Datei auswählen** (`.json`)
|
||||||
|
3. **Modus wählen:**
|
||||||
|
- 🔵 Zusammenführen _(Standard)_
|
||||||
|
- 🟡 Duplikate überschreiben
|
||||||
|
- 🔴 Ersetzen _(Vorsicht!)_
|
||||||
|
4. **Bestätigen** - Automatisches Sicherheits-Backup wird erstellt
|
||||||
|
5. **Warten** - Import läuft
|
||||||
|
6. **Fertig!** - Erfolgsmeldung mit Anzahl importierter Notizen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ Automatisches Sicherheits-Backup
|
||||||
|
|
||||||
|
**Vor jeder Wiederherstellung:**
|
||||||
|
- ✅ Automatisches Backup wird erstellt
|
||||||
|
- 📁 Gespeichert in: `Android/data/dev.dettmer.simplenotes/files/`
|
||||||
|
- 🏷️ Dateiname: `auto_backup_before_restore_YYYY-MM-DD_HHmmss.json`
|
||||||
|
- ⏱️ Zeitstempel: Direkt vor Wiederherstellung
|
||||||
|
|
||||||
|
**Warum?**
|
||||||
|
- Schutz vor versehentlichem "Ersetzen"
|
||||||
|
- Möglichkeit zum Rückgängigmachen
|
||||||
|
- Doppelte Sicherheit
|
||||||
|
|
||||||
|
**Zugriff via Dateimanager:**
|
||||||
|
```
|
||||||
|
/Android/data/dev.dettmer.simplenotes/files/auto_backup_before_restore_*.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Best Practices
|
||||||
|
|
||||||
|
### Backup-Strategie
|
||||||
|
|
||||||
|
#### Regelmäßige Backups
|
||||||
|
```
|
||||||
|
Täglich: ❌ Zu oft (Server-Sync reicht)
|
||||||
|
Wöchentlich: ✅ Empfohlen für wichtige Notizen
|
||||||
|
Monatlich: ✅ Archivierung
|
||||||
|
Vor Updates: ✅ Sicherheit
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3-2-1 Regel
|
||||||
|
1. **3 Kopien** - Original + 2 Backups
|
||||||
|
2. **2 Medien** - z.B. SD-Karte + Cloud
|
||||||
|
3. **1 Offsite** - z.B. Cloud-Speicher
|
||||||
|
|
||||||
|
### Backup-Speicherorte
|
||||||
|
|
||||||
|
**Lokal (schnell):**
|
||||||
|
- 📱 Internal Storage / Downloads
|
||||||
|
- 💳 SD-Karte
|
||||||
|
- 🖥️ PC (via USB)
|
||||||
|
|
||||||
|
**Cloud (sicher):**
|
||||||
|
- ☁️ Nextcloud (Self-Hosted)
|
||||||
|
- 📧 E-Mail an sich selbst
|
||||||
|
- 🗄️ Syncthing (Sync zwischen Geräten)
|
||||||
|
|
||||||
|
**⚠️ Vermeiden:**
|
||||||
|
- ❌ Google Drive / Dropbox (Privacy)
|
||||||
|
- ❌ Nur eine Kopie
|
||||||
|
- ❌ Nur auf Server (wenn Server ausfällt)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Erweiterte Nutzung
|
||||||
|
|
||||||
|
### Backup-Datei bearbeiten
|
||||||
|
|
||||||
|
Die `.json` Datei kann mit jedem Texteditor bearbeitet werden:
|
||||||
|
|
||||||
|
1. **Öffnen mit:** VS Code, Notepad++, nano
|
||||||
|
2. **Notizen hinzufügen/entfernen**
|
||||||
|
3. **Titel/Inhalt ändern**
|
||||||
|
4. **IDs anpassen** (für Migration)
|
||||||
|
5. **Speichern** und in App importieren
|
||||||
|
|
||||||
|
**⚠️ Wichtig:**
|
||||||
|
- Valides JSON-Format behalten
|
||||||
|
- IDs müssen eindeutig sein (UUIDs)
|
||||||
|
- Timestamps in Millisekunden (Unix Epoch)
|
||||||
|
|
||||||
|
### Bulk-Import
|
||||||
|
|
||||||
|
Mehrere Backups zusammenführen:
|
||||||
|
|
||||||
|
1. Backup 1 importieren (Modus: Zusammenführen)
|
||||||
|
2. Backup 2 importieren (Modus: Zusammenführen)
|
||||||
|
3. Backup 3 importieren (Modus: Zusammenführen)
|
||||||
|
4. Ergebnis: Alle Notizen vereint
|
||||||
|
|
||||||
|
### Server-Migration
|
||||||
|
|
||||||
|
Schritt-für-Schritt:
|
||||||
|
|
||||||
|
1. **Backup erstellen** auf altem Server
|
||||||
|
2. **Neuen Server einrichten** (siehe [QUICKSTART.md](QUICKSTART.md))
|
||||||
|
3. **Server-URL ändern** in App-Einstellungen
|
||||||
|
4. **Backup wiederherstellen** (Modus: Ersetzen)
|
||||||
|
5. **Sync testen** - Alle Notizen auf neuem Server
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❌ Fehlerbehebung
|
||||||
|
|
||||||
|
### "Backup-Datei ungültig"
|
||||||
|
|
||||||
|
**Ursachen:**
|
||||||
|
- Korrupte JSON-Datei
|
||||||
|
- Falsche Datei-Endung (muss `.json` sein)
|
||||||
|
- Inkompatible App-Version
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. JSON-Datei mit Validator prüfen (z.B. jsonlint.com)
|
||||||
|
2. Dateiendung überprüfen
|
||||||
|
3. Backup mit aktueller App-Version erstellen
|
||||||
|
|
||||||
|
### "Keine Berechtigung zum Speichern"
|
||||||
|
|
||||||
|
**Ursachen:**
|
||||||
|
- Speicher-Berechtigung fehlt
|
||||||
|
- Schreibgeschützter Ordner
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Android: Einstellungen → Apps → Simple Notes → Berechtigungen
|
||||||
|
2. "Speicher" aktivieren
|
||||||
|
3. Anderen Speicherort wählen
|
||||||
|
|
||||||
|
### "Import fehlgeschlagen"
|
||||||
|
|
||||||
|
**Ursachen:**
|
||||||
|
- Zu wenig Speicherplatz
|
||||||
|
- Korrupte Backup-Datei
|
||||||
|
- App-Crash während Import
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Speicherplatz freigeben
|
||||||
|
2. Backup-Datei neu erstellen
|
||||||
|
3. App neu starten und erneut importieren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Sicherheit & Privacy
|
||||||
|
|
||||||
|
### Daten-Schutz
|
||||||
|
- ✅ **Lokal gespeichert** - Kein Cloud-Upload ohne deine Aktion
|
||||||
|
- ✅ **Keine Verschlüsselung** - Klartextformat für Lesbarkeit
|
||||||
|
- ⚠️ **Sensible Daten?** - Backup-Datei selbst verschlüsseln (z.B. 7-Zip mit Passwort)
|
||||||
|
|
||||||
|
### Empfehlungen
|
||||||
|
- 🔐 Backup-Dateien in verschlüsseltem Container speichern
|
||||||
|
- 🗑️ Alte Backups regelmäßig löschen
|
||||||
|
- 📧 Nicht per unverschlüsselter E-Mail versenden
|
||||||
|
- ☁️ Self-Hosted Cloud nutzen (Nextcloud)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Technische Details
|
||||||
|
|
||||||
|
### Format-Spezifikation
|
||||||
|
|
||||||
|
**JSON-Struktur:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "string", // App-Version beim Export
|
||||||
|
"exported_at": "ISO8601", // Zeitstempel des Exports
|
||||||
|
"notes_count": number, // Anzahl der Notizen
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"id": "UUID", // Eindeutige ID
|
||||||
|
"title": "string", // Notiz-Titel
|
||||||
|
"content": "string", // Notiz-Inhalt
|
||||||
|
"createdAt": number, // Unix Timestamp (ms)
|
||||||
|
"updatedAt": number // Unix Timestamp (ms)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Kompatibilität
|
||||||
|
- ✅ v1.2.0+ - Vollständig kompatibel
|
||||||
|
- ⚠️ v1.1.x - Grundfunktionen (ohne Auto-Backup)
|
||||||
|
- ❌ v1.0.x - Nicht unterstützt
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**📚 Siehe auch:**
|
||||||
|
- [QUICKSTART.md](../QUICKSTART.md) - App-Installation und Einrichtung
|
||||||
|
- [FEATURES.md](FEATURES.md) - Vollständige Feature-Liste
|
||||||
|
- [DESKTOP.md](DESKTOP.md) - Desktop-Integration mit Markdown
|
||||||
|
|
||||||
|
**Letzte Aktualisierung:** v1.2.1 (2026-01-05)
|
||||||
505
docs/DESKTOP.en.md
Normal file
505
docs/DESKTOP.en.md
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
# Desktop Integration 🖥️
|
||||||
|
|
||||||
|
**🌍 Languages:** [Deutsch](DESKTOP.md) · **English**
|
||||||
|
|
||||||
|
> Edit your notes with any Markdown editor on desktop
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Overview
|
||||||
|
|
||||||
|
Desktop integration allows you to edit notes on PC/Mac:
|
||||||
|
- 📝 Any Markdown editor works
|
||||||
|
- 🔄 Automatic synchronization via WebDAV
|
||||||
|
- 💾 Dual-format: JSON (master) + Markdown (mirror)
|
||||||
|
- ⚡ Last-Write-Wins conflict resolution
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Why Markdown?
|
||||||
|
|
||||||
|
### Dual-Format Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Android App │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌─────────────┐ │
|
||||||
|
│ │ JSON │ ──→ │ Markdown │ │
|
||||||
|
│ │ (Master) │ │ (Mirror) │ │
|
||||||
|
│ └──────────┘ └─────────────┘ │
|
||||||
|
└────────┬────────────────┬───────────┘
|
||||||
|
│ │
|
||||||
|
↓ ↓
|
||||||
|
WebDAV Server
|
||||||
|
│ │
|
||||||
|
┌────┴────┐ ┌────┴──────┐
|
||||||
|
│ /notes/ │ │ /notes-md/│
|
||||||
|
│ *.json │ │ *.md │
|
||||||
|
└─────────┘ └───────────┘
|
||||||
|
↑ ↑
|
||||||
|
│ │
|
||||||
|
┌────┴────────────────┴───────────┐
|
||||||
|
│ Desktop Editor │
|
||||||
|
│ (VS Code, Typora, etc.) │
|
||||||
|
└──────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advantages
|
||||||
|
|
||||||
|
**JSON (Master):**
|
||||||
|
- ✅ Reliable and fast
|
||||||
|
- ✅ Structured data (IDs, timestamps)
|
||||||
|
- ✅ Primary sync mechanism
|
||||||
|
- ✅ Always active
|
||||||
|
|
||||||
|
**Markdown (Mirror):**
|
||||||
|
- ✅ Human-readable
|
||||||
|
- ✅ Desktop editor compatible
|
||||||
|
- ✅ Syntax highlighting
|
||||||
|
- ✅ Optionally activatable
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
### 1. First Synchronization
|
||||||
|
|
||||||
|
**Important:** Perform a sync FIRST before activating desktop integration!
|
||||||
|
|
||||||
|
1. **Set up app** (see [QUICKSTART.en.md](QUICKSTART.en.md))
|
||||||
|
2. **Test server connection**
|
||||||
|
3. **Create first note**
|
||||||
|
4. **Synchronize** (pull-to-refresh or auto-sync)
|
||||||
|
5. ✅ Server automatically creates `/notes/` and `/notes-md/` folders
|
||||||
|
|
||||||
|
### 2. Activate Desktop Integration
|
||||||
|
|
||||||
|
1. **Settings** → **Desktop Integration**
|
||||||
|
2. **Toggle ON**
|
||||||
|
3. **Initial export starts** - Shows progress (X/Y)
|
||||||
|
4. ✅ All existing notes are exported as `.md`
|
||||||
|
|
||||||
|
### 3. Mount WebDAV as Network Drive
|
||||||
|
|
||||||
|
#### Windows
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Open Explorer
|
||||||
|
2. Right-click on "This PC"
|
||||||
|
3. "Map network drive"
|
||||||
|
4. Enter URL: http://YOUR-SERVER:8080/notes-md/
|
||||||
|
5. Username: noteuser
|
||||||
|
6. Password: (your WebDAV password)
|
||||||
|
7. Drive letter: Z:\ (or any)
|
||||||
|
8. Done!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Access:** `Z:\` in Explorer
|
||||||
|
|
||||||
|
#### macOS
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Open Finder
|
||||||
|
2. Menu "Go" → "Connect to Server" (⌘K)
|
||||||
|
3. Server address: http://YOUR-SERVER:8080/notes-md/
|
||||||
|
4. Connect
|
||||||
|
5. Username: noteuser
|
||||||
|
6. Password: (your WebDAV password)
|
||||||
|
7. Done!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Access:** Finder → Network → notes-md
|
||||||
|
|
||||||
|
#### Linux (GNOME)
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Open Files / Nautilus
|
||||||
|
2. "Other Locations"
|
||||||
|
3. "Connect to Server"
|
||||||
|
4. Server address: dav://YOUR-SERVER:8080/notes-md/
|
||||||
|
5. Username: noteuser
|
||||||
|
6. Password: (your WebDAV password)
|
||||||
|
7. Done!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Access:** `/run/user/1000/gvfs/dav:host=...`
|
||||||
|
|
||||||
|
#### Linux (davfs2 - permanent)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Installation
|
||||||
|
sudo apt install davfs2
|
||||||
|
|
||||||
|
# Create mount point
|
||||||
|
sudo mkdir -p /mnt/notes-md
|
||||||
|
|
||||||
|
# Mount once
|
||||||
|
sudo mount -t davfs http://YOUR-SERVER:8080/notes-md/ /mnt/notes-md
|
||||||
|
|
||||||
|
# Permanent in /etc/fstab
|
||||||
|
echo "http://YOUR-SERVER:8080/notes-md/ /mnt/notes-md davfs rw,user,noauto 0 0" | sudo tee -a /etc/fstab
|
||||||
|
```
|
||||||
|
|
||||||
|
**Access:** `/mnt/notes-md/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Markdown Editors
|
||||||
|
|
||||||
|
### Recommended Editors
|
||||||
|
|
||||||
|
#### 1. VS Code ⭐ _Recommended_
|
||||||
|
|
||||||
|
**Advantages:**
|
||||||
|
- ✅ Free & open source
|
||||||
|
- ✅ Markdown preview (Ctrl+Shift+V)
|
||||||
|
- ✅ Syntax highlighting
|
||||||
|
- ✅ Git integration
|
||||||
|
- ✅ Extensions (spell check, etc.)
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
```
|
||||||
|
1. Install VS Code
|
||||||
|
2. Mount WebDAV drive
|
||||||
|
3. Open folder: Z:\notes-md\ (Windows) or /mnt/notes-md (Linux)
|
||||||
|
4. Done! Edit Markdown files
|
||||||
|
```
|
||||||
|
|
||||||
|
**Extensions (optional):**
|
||||||
|
- `Markdown All in One` - Shortcuts & preview
|
||||||
|
- `Markdown Preview Enhanced` - Better preview
|
||||||
|
- `Code Spell Checker` - Spell checking
|
||||||
|
|
||||||
|
#### 2. Typora
|
||||||
|
|
||||||
|
**Advantages:**
|
||||||
|
- ✅ WYSIWYG Markdown editor
|
||||||
|
- ✅ Minimalist design
|
||||||
|
- ✅ Live preview
|
||||||
|
- ⚠️ Paid (~15€)
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
```
|
||||||
|
1. Install Typora
|
||||||
|
2. Mount WebDAV
|
||||||
|
3. Open folder in Typora
|
||||||
|
4. Edit notes
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Notepad++
|
||||||
|
|
||||||
|
**Advantages:**
|
||||||
|
- ✅ Lightweight
|
||||||
|
- ✅ Fast
|
||||||
|
- ✅ Syntax highlighting
|
||||||
|
- ⚠️ No Markdown preview
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
```
|
||||||
|
1. Install Notepad++
|
||||||
|
2. Mount WebDAV
|
||||||
|
3. Open files directly
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Obsidian
|
||||||
|
|
||||||
|
**Advantages:**
|
||||||
|
- ✅ Second brain philosophy
|
||||||
|
- ✅ Graph view for links
|
||||||
|
- ✅ Many plugins
|
||||||
|
- ⚠️ Sync conflicts possible (2 masters)
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
```
|
||||||
|
1. Install Obsidian
|
||||||
|
2. Open WebDAV as vault
|
||||||
|
3. Caution: Obsidian creates own metadata!
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ Not recommended:** Can alter frontmatter
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 Markdown File Format
|
||||||
|
|
||||||
|
### Structure
|
||||||
|
|
||||||
|
Each note is exported as `.md` file with YAML frontmatter:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
id: abc-123-def-456
|
||||||
|
created: 2026-01-05T14:30:22Z
|
||||||
|
updated: 2026-01-05T14:30:22Z
|
||||||
|
tags: []
|
||||||
|
---
|
||||||
|
|
||||||
|
# Note Title
|
||||||
|
|
||||||
|
Note content here...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontmatter Fields
|
||||||
|
|
||||||
|
| Field | Type | Description | Required |
|
||||||
|
|-------|------|-------------|----------|
|
||||||
|
| `id` | UUID | Unique note ID | ✅ Yes |
|
||||||
|
| `created` | ISO8601 | Creation date | ✅ Yes |
|
||||||
|
| `updated` | ISO8601 | Modification date | ✅ Yes |
|
||||||
|
| `tags` | Array | Tags (future) | ❌ No |
|
||||||
|
|
||||||
|
### Filenames
|
||||||
|
|
||||||
|
**Sanitization rules:**
|
||||||
|
```
|
||||||
|
Title: "My Shopping List 🛒"
|
||||||
|
→ Filename: "My_Shopping_List.md"
|
||||||
|
|
||||||
|
Removed:
|
||||||
|
- Emojis: 🛒 → removed
|
||||||
|
- Special chars: / \ : * ? " < > | → removed
|
||||||
|
- Multiple spaces → single space
|
||||||
|
- Spaces → underscore _
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```
|
||||||
|
"Meeting Notes 2026" → "Meeting_Notes_2026.md"
|
||||||
|
"To-Do: Project" → "To-Do_Project.md"
|
||||||
|
"Vacation ☀️" → "Vacation.md"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Synchronization
|
||||||
|
|
||||||
|
### Workflow: Android → Desktop
|
||||||
|
|
||||||
|
1. **Create/edit note in app**
|
||||||
|
2. **Run sync** (auto or manual)
|
||||||
|
3. **JSON is uploaded** (`/notes/abc-123.json`)
|
||||||
|
4. **Markdown is exported** (`/notes-md/Note_Title.md`) _(only if Desktop Integration ON)_
|
||||||
|
5. **Desktop editor shows changes** (after refresh)
|
||||||
|
|
||||||
|
### Workflow: Desktop → Android
|
||||||
|
|
||||||
|
1. **Edit Markdown file** (in mounted folder)
|
||||||
|
2. **Save** - File is immediately on server
|
||||||
|
3. **In app: Run Markdown import**
|
||||||
|
- Settings → "Import Markdown Changes"
|
||||||
|
- Or: Auto-import on every sync (future)
|
||||||
|
4. **App adopts changes** (if desktop version is newer)
|
||||||
|
|
||||||
|
### Conflict Resolution: Last-Write-Wins
|
||||||
|
|
||||||
|
**Rule:** Newest version (by `updated` timestamp) wins
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```
|
||||||
|
App version: updated: 2026-01-05 14:00
|
||||||
|
Desktop version: updated: 2026-01-05 14:30
|
||||||
|
→ Desktop wins (newer timestamp)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Automatic:**
|
||||||
|
- ✅ On Markdown import
|
||||||
|
- ✅ On JSON sync
|
||||||
|
- ⚠️ No merge conflicts - only complete overwrite
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Settings
|
||||||
|
|
||||||
|
### Desktop Integration Toggle
|
||||||
|
|
||||||
|
**Settings → Desktop Integration**
|
||||||
|
|
||||||
|
**ON (activated):**
|
||||||
|
- ✅ New notes → automatically exported as `.md`
|
||||||
|
- ✅ Updated notes → `.md` update
|
||||||
|
- ✅ Deleted notes → `.md` remains (future: also delete)
|
||||||
|
|
||||||
|
**OFF (deactivated):**
|
||||||
|
- ❌ No Markdown export
|
||||||
|
- ✅ JSON sync continues normally
|
||||||
|
- ✅ Existing `.md` files remain
|
||||||
|
|
||||||
|
### Initial Export
|
||||||
|
|
||||||
|
**What happens on activation:**
|
||||||
|
1. All existing notes are scanned
|
||||||
|
2. Progress dialog shows progress (e.g., "23/42")
|
||||||
|
3. Each note is exported as `.md`
|
||||||
|
4. On errors: Individual note is skipped
|
||||||
|
5. Success message with number of exported notes
|
||||||
|
|
||||||
|
**Time:** ~1-2 seconds per 50 notes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Advanced Usage
|
||||||
|
|
||||||
|
### Manual Markdown Creation
|
||||||
|
|
||||||
|
You can create `.md` files manually:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
id: 00000000-0000-0000-0000-000000000001
|
||||||
|
created: 2026-01-05T12:00:00Z
|
||||||
|
updated: 2026-01-05T12:00:00Z
|
||||||
|
---
|
||||||
|
|
||||||
|
# New Desktop Note
|
||||||
|
|
||||||
|
Content here...
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ Important:**
|
||||||
|
- `id` must be valid UUID (e.g., with uuidgen.io)
|
||||||
|
- Timestamps in ISO8601 format
|
||||||
|
- Frontmatter enclosed with `---`
|
||||||
|
|
||||||
|
### Bulk Operations
|
||||||
|
|
||||||
|
**Edit multiple notes at once:**
|
||||||
|
|
||||||
|
1. Mount WebDAV
|
||||||
|
2. Open all `.md` files in VS Code
|
||||||
|
3. Find & Replace across all files (Ctrl+Shift+H)
|
||||||
|
4. Save
|
||||||
|
5. In app: "Import Markdown Changes"
|
||||||
|
|
||||||
|
### Scripting
|
||||||
|
|
||||||
|
**Example: Sort all notes by date**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
cd /mnt/notes-md/
|
||||||
|
|
||||||
|
# Sort all .md files by update date
|
||||||
|
for file in *.md; do
|
||||||
|
updated=$(grep "^updated:" "$file" | cut -d' ' -f2)
|
||||||
|
echo "$updated $file"
|
||||||
|
done | sort
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❌ Troubleshooting
|
||||||
|
|
||||||
|
### "404 Not Found" when mounting WebDAV
|
||||||
|
|
||||||
|
**Cause:** `/notes-md/` folder doesn't exist
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. **Perform first sync** - Folder is created automatically
|
||||||
|
2. OR: Create manually via terminal:
|
||||||
|
```bash
|
||||||
|
curl -X MKCOL -u noteuser:password http://server:8080/notes-md/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Markdown files don't appear
|
||||||
|
|
||||||
|
**Cause:** Desktop integration not activated
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Settings → "Desktop Integration" ON
|
||||||
|
2. Wait for initial export
|
||||||
|
3. Refresh WebDAV folder
|
||||||
|
|
||||||
|
### Changes from desktop don't appear in app
|
||||||
|
|
||||||
|
**Cause:** Markdown import not executed
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Settings → "Import Markdown Changes"
|
||||||
|
2. OR: Wait for auto-sync (future feature)
|
||||||
|
|
||||||
|
### "Frontmatter missing" error
|
||||||
|
|
||||||
|
**Cause:** `.md` file without valid YAML frontmatter
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
1. Open file in editor
|
||||||
|
2. Add frontmatter at the beginning:
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
id: NEW-UUID-HERE
|
||||||
|
created: 2026-01-05T12:00:00Z
|
||||||
|
updated: 2026-01-05T12:00:00Z
|
||||||
|
---
|
||||||
|
```
|
||||||
|
3. Save and import again
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Security & Best Practices
|
||||||
|
|
||||||
|
### Do's ✅
|
||||||
|
|
||||||
|
- ✅ **Backup before bulk edits** - Create local backup
|
||||||
|
- ✅ **One editor at a time** - Don't edit in app AND desktop in parallel
|
||||||
|
- ✅ **Wait for sync** - Run sync before desktop editing
|
||||||
|
- ✅ **Respect frontmatter** - Don't change manually (unless you know what you're doing)
|
||||||
|
|
||||||
|
### Don'ts ❌
|
||||||
|
|
||||||
|
- ❌ **Parallel editing** - App and desktop simultaneously → conflicts
|
||||||
|
- ❌ **Delete frontmatter** - Note can't be imported anymore
|
||||||
|
- ❌ **Change IDs** - Note is recognized as new
|
||||||
|
- ❌ **Manipulate timestamps** - Conflict resolution doesn't work
|
||||||
|
|
||||||
|
### Recommended Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Sync in app (pull-to-refresh)
|
||||||
|
2. Open desktop
|
||||||
|
3. Make changes
|
||||||
|
4. Save
|
||||||
|
5. In app: "Import Markdown Changes"
|
||||||
|
6. Verify
|
||||||
|
7. Run another sync
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Comparison: JSON vs Markdown
|
||||||
|
|
||||||
|
| Aspect | JSON | Markdown |
|
||||||
|
|--------|------|----------|
|
||||||
|
| **Format** | Structured | Flowing text |
|
||||||
|
| **Readability (human)** | ⚠️ Medium | ✅ Good |
|
||||||
|
| **Readability (machine)** | ✅ Perfect | ⚠️ Parsing needed |
|
||||||
|
| **Metadata** | Native | Frontmatter |
|
||||||
|
| **Editors** | Code editors | All text editors |
|
||||||
|
| **Sync speed** | ✅ Fast | ⚠️ Slower |
|
||||||
|
| **Reliability** | ✅ 100% | ⚠️ Frontmatter errors possible |
|
||||||
|
| **Mobile-first** | ✅ Yes | ❌ No |
|
||||||
|
| **Desktop-first** | ❌ No | ✅ Yes |
|
||||||
|
|
||||||
|
**Conclusion:** Using both formats = Best experience on both platforms!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔮 Future Features
|
||||||
|
|
||||||
|
Planned for v1.3.0+:
|
||||||
|
|
||||||
|
- ⏳ **Auto-Markdown-import** - Automatically on every sync
|
||||||
|
- ⏳ **Bidirectional sync** - Without manual import
|
||||||
|
- ⏳ **Markdown preview** - In the app
|
||||||
|
- ⏳ **Conflict UI** - On simultaneous changes
|
||||||
|
- ⏳ **Tags in frontmatter** - Synchronized with app
|
||||||
|
- ⏳ **Attachments** - Images/files in Markdown
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**📚 See also:**
|
||||||
|
- [QUICKSTART.en.md](../QUICKSTART.en.md) - App setup
|
||||||
|
- [FEATURES.en.md](FEATURES.en.md) - Complete feature list
|
||||||
|
- [BACKUP.en.md](BACKUP.en.md) - Backup & restore
|
||||||
|
|
||||||
|
**Last update:** v1.2.1 (2026-01-05)
|
||||||
505
docs/DESKTOP.md
Normal file
505
docs/DESKTOP.md
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
# Desktop-Integration 🖥️
|
||||||
|
|
||||||
|
**🌍 Languages:** **Deutsch** · [English](DESKTOP.en.md)
|
||||||
|
|
||||||
|
> Bearbeite deine Notizen mit jedem Markdown-Editor auf dem Desktop
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Übersicht
|
||||||
|
|
||||||
|
Die Desktop-Integration ermöglicht dir, Notizen auf dem PC/Mac zu bearbeiten:
|
||||||
|
- 📝 Jeder Markdown-Editor funktioniert
|
||||||
|
- 🔄 Automatische Synchronisation über WebDAV
|
||||||
|
- 💾 Dual-Format: JSON (Master) + Markdown (Mirror)
|
||||||
|
- ⚡ Last-Write-Wins Konfliktauflösung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Warum Markdown?
|
||||||
|
|
||||||
|
### Dual-Format Architektur
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ Android App │
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌─────────────┐ │
|
||||||
|
│ │ JSON │ ──→ │ Markdown │ │
|
||||||
|
│ │ (Master) │ │ (Mirror) │ │
|
||||||
|
│ └──────────┘ └─────────────┘ │
|
||||||
|
└────────┬────────────────┬───────────┘
|
||||||
|
│ │
|
||||||
|
↓ ↓
|
||||||
|
WebDAV Server
|
||||||
|
│ │
|
||||||
|
┌────┴────┐ ┌────┴──────┐
|
||||||
|
│ /notes/ │ │ /notes-md/│
|
||||||
|
│ *.json │ │ *.md │
|
||||||
|
└─────────┘ └───────────┘
|
||||||
|
↑ ↑
|
||||||
|
│ │
|
||||||
|
┌────┴────────────────┴───────────┐
|
||||||
|
│ Desktop Editor │
|
||||||
|
│ (VS Code, Typora, etc.) │
|
||||||
|
└──────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vorteile
|
||||||
|
|
||||||
|
**JSON (Master):**
|
||||||
|
- ✅ Zuverlässig und schnell
|
||||||
|
- ✅ Strukturierte Daten (IDs, Timestamps)
|
||||||
|
- ✅ Primärer Sync-Mechanismus
|
||||||
|
- ✅ Immer aktiv
|
||||||
|
|
||||||
|
**Markdown (Mirror):**
|
||||||
|
- ✅ Menschenlesbar
|
||||||
|
- ✅ Desktop-Editor kompatibel
|
||||||
|
- ✅ Syntax-Highlighting
|
||||||
|
- ✅ Optional aktivierbar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Schnellstart
|
||||||
|
|
||||||
|
### 1. Erste Synchronisation
|
||||||
|
|
||||||
|
**Wichtig:** Führe ZUERST einen Sync durch, bevor du Desktop-Integration aktivierst!
|
||||||
|
|
||||||
|
1. **App einrichten** (siehe [QUICKSTART.md](QUICKSTART.md))
|
||||||
|
2. **Server-Verbindung testen**
|
||||||
|
3. **Erste Notiz erstellen**
|
||||||
|
4. **Synchronisieren** (Pull-to-Refresh oder Auto-Sync)
|
||||||
|
5. ✅ Server erstellt automatisch `/notes/` und `/notes-md/` Ordner
|
||||||
|
|
||||||
|
### 2. Desktop-Integration aktivieren
|
||||||
|
|
||||||
|
1. **Einstellungen** → **Desktop-Integration**
|
||||||
|
2. **Toggle aktivieren**
|
||||||
|
3. **Initial Export startet** - Zeigt Progress (X/Y)
|
||||||
|
4. ✅ Alle bestehenden Notizen werden als `.md` exportiert
|
||||||
|
|
||||||
|
### 3. WebDAV als Netzlaufwerk mounten
|
||||||
|
|
||||||
|
#### Windows
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Explorer öffnen
|
||||||
|
2. Rechtsklick auf "Dieser PC"
|
||||||
|
3. "Netzlaufwerk verbinden"
|
||||||
|
4. URL eingeben: http://DEIN-SERVER:8080/notes-md/
|
||||||
|
5. Benutzername: noteuser
|
||||||
|
6. Passwort: (dein WebDAV-Passwort)
|
||||||
|
7. Laufwerksbuchstabe: Z:\ (oder beliebig)
|
||||||
|
8. Fertig!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Zugriff:** `Z:\` im Explorer
|
||||||
|
|
||||||
|
#### macOS
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Finder öffnen
|
||||||
|
2. Menü "Gehe zu" → "Mit Server verbinden" (⌘K)
|
||||||
|
3. Server-Adresse: http://DEIN-SERVER:8080/notes-md/
|
||||||
|
4. Verbinden
|
||||||
|
5. Benutzername: noteuser
|
||||||
|
6. Passwort: (dein WebDAV-Passwort)
|
||||||
|
7. Fertig!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Zugriff:** Finder → Netzwerk → notes-md
|
||||||
|
|
||||||
|
#### Linux (GNOME)
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Files / Nautilus öffnen
|
||||||
|
2. "Andere Orte"
|
||||||
|
3. "Mit Server verbinden"
|
||||||
|
4. Server-Adresse: dav://DEIN-SERVER:8080/notes-md/
|
||||||
|
5. Benutzername: noteuser
|
||||||
|
6. Passwort: (dein WebDAV-Passwort)
|
||||||
|
7. Fertig!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Zugriff:** `/run/user/1000/gvfs/dav:host=...`
|
||||||
|
|
||||||
|
#### Linux (davfs2 - permanent)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Installation
|
||||||
|
sudo apt install davfs2
|
||||||
|
|
||||||
|
# Mount-Point erstellen
|
||||||
|
sudo mkdir -p /mnt/notes-md
|
||||||
|
|
||||||
|
# Einmalig mounten
|
||||||
|
sudo mount -t davfs http://DEIN-SERVER:8080/notes-md/ /mnt/notes-md
|
||||||
|
|
||||||
|
# Permanent in /etc/fstab
|
||||||
|
echo "http://DEIN-SERVER:8080/notes-md/ /mnt/notes-md davfs rw,user,noauto 0 0" | sudo tee -a /etc/fstab
|
||||||
|
```
|
||||||
|
|
||||||
|
**Zugriff:** `/mnt/notes-md/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Markdown-Editoren
|
||||||
|
|
||||||
|
### Empfohlene Editoren
|
||||||
|
|
||||||
|
#### 1. VS Code ⭐ _Empfohlen_
|
||||||
|
|
||||||
|
**Vorteile:**
|
||||||
|
- ✅ Kostenlos & Open Source
|
||||||
|
- ✅ Markdown-Preview (Ctrl+Shift+V)
|
||||||
|
- ✅ Syntax-Highlighting
|
||||||
|
- ✅ Git-Integration
|
||||||
|
- ✅ Erweiterungen (Spell Check, etc.)
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
```
|
||||||
|
1. VS Code installieren
|
||||||
|
2. WebDAV-Laufwerk mounten
|
||||||
|
3. Ordner öffnen: Z:\notes-md\ (Windows) oder /mnt/notes-md (Linux)
|
||||||
|
4. Fertig! Markdown-Dateien bearbeiten
|
||||||
|
```
|
||||||
|
|
||||||
|
**Extensions (optional):**
|
||||||
|
- `Markdown All in One` - Shortcuts & Preview
|
||||||
|
- `Markdown Preview Enhanced` - Bessere Preview
|
||||||
|
- `Code Spell Checker` - Rechtschreibprüfung
|
||||||
|
|
||||||
|
#### 2. Typora
|
||||||
|
|
||||||
|
**Vorteile:**
|
||||||
|
- ✅ WYSIWYG Markdown-Editor
|
||||||
|
- ✅ Minimalistisches Design
|
||||||
|
- ✅ Live-Preview
|
||||||
|
- ⚠️ Kostenpflichtig (~15€)
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
```
|
||||||
|
1. Typora installieren
|
||||||
|
2. WebDAV mounten
|
||||||
|
3. Ordner in Typora öffnen
|
||||||
|
4. Notizen bearbeiten
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Notepad++
|
||||||
|
|
||||||
|
**Vorteile:**
|
||||||
|
- ✅ Leichtgewichtig
|
||||||
|
- ✅ Schnell
|
||||||
|
- ✅ Syntax-Highlighting
|
||||||
|
- ⚠️ Keine Markdown-Preview
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
```
|
||||||
|
1. Notepad++ installieren
|
||||||
|
2. WebDAV mounten
|
||||||
|
3. Dateien direkt öffnen
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. Obsidian
|
||||||
|
|
||||||
|
**Vorteile:**
|
||||||
|
- ✅ Zweite Gehirn-Philosophie
|
||||||
|
- ✅ Graph-View für Verlinkungen
|
||||||
|
- ✅ Viele Plugins
|
||||||
|
- ⚠️ Sync-Konflikte möglich (2 Master)
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
```
|
||||||
|
1. Obsidian installieren
|
||||||
|
2. WebDAV als Vault öffnen
|
||||||
|
3. Vorsicht: Obsidian erstellt eigene Metadaten!
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ Nicht empfohlen:** Kann Frontmatter verändern
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 Markdown-Dateiformat
|
||||||
|
|
||||||
|
### Struktur
|
||||||
|
|
||||||
|
Jede Notiz wird als `.md` Datei mit YAML-Frontmatter exportiert:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
id: abc-123-def-456
|
||||||
|
created: 2026-01-05T14:30:22Z
|
||||||
|
updated: 2026-01-05T14:30:22Z
|
||||||
|
tags: []
|
||||||
|
---
|
||||||
|
|
||||||
|
# Notiz-Titel
|
||||||
|
|
||||||
|
Notiz-Inhalt hier...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontmatter-Felder
|
||||||
|
|
||||||
|
| Feld | Typ | Beschreibung | Pflicht |
|
||||||
|
|------|-----|--------------|---------|
|
||||||
|
| `id` | UUID | Eindeutige Notiz-ID | ✅ Ja |
|
||||||
|
| `created` | ISO8601 | Erstellungsdatum | ✅ Ja |
|
||||||
|
| `updated` | ISO8601 | Änderungsdatum | ✅ Ja |
|
||||||
|
| `tags` | Array | Tags (zukünftig) | ❌ Nein |
|
||||||
|
|
||||||
|
### Dateinamen
|
||||||
|
|
||||||
|
**Sanitization-Regeln:**
|
||||||
|
```
|
||||||
|
Titel: "Meine Einkaufsliste 🛒"
|
||||||
|
→ Dateiname: "Meine_Einkaufsliste.md"
|
||||||
|
|
||||||
|
Entfernt werden:
|
||||||
|
- Emojis: 🛒 → entfernt
|
||||||
|
- Sonderzeichen: / \ : * ? " < > | → entfernt
|
||||||
|
- Mehrfache Leerzeichen → einzelnes Leerzeichen
|
||||||
|
- Leerzeichen → Unterstrich _
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiele:**
|
||||||
|
```
|
||||||
|
"Meeting Notes 2026" → "Meeting_Notes_2026.md"
|
||||||
|
"To-Do: Projekt" → "To-Do_Projekt.md"
|
||||||
|
"Urlaub ☀️" → "Urlaub.md"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Synchronisation
|
||||||
|
|
||||||
|
### Workflow: Android → Desktop
|
||||||
|
|
||||||
|
1. **Notiz in App erstellen/bearbeiten**
|
||||||
|
2. **Sync ausführen** (Auto oder manuell)
|
||||||
|
3. **JSON wird hochgeladen** (`/notes/abc-123.json`)
|
||||||
|
4. **Markdown wird exportiert** (`/notes-md/Notiz_Titel.md`) _(nur wenn Desktop-Integration AN)_
|
||||||
|
5. **Desktop-Editor zeigt Änderungen** (nach Refresh)
|
||||||
|
|
||||||
|
### Workflow: Desktop → Android
|
||||||
|
|
||||||
|
1. **Markdown-Datei bearbeiten** (im gemounteten Ordner)
|
||||||
|
2. **Speichern** - Datei liegt sofort auf Server
|
||||||
|
3. **In App: Markdown-Import ausführen**
|
||||||
|
- Einstellungen → "Import Markdown Changes"
|
||||||
|
- Oder: Auto-Import bei jedem Sync (zukünftig)
|
||||||
|
4. **App übernimmt Änderungen** (wenn Desktop-Version neuer)
|
||||||
|
|
||||||
|
### Konfliktauflösung: Last-Write-Wins
|
||||||
|
|
||||||
|
**Regel:** Neueste Version (nach `updated` Timestamp) gewinnt
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
```
|
||||||
|
App-Version: updated: 2026-01-05 14:00
|
||||||
|
Desktop-Version: updated: 2026-01-05 14:30
|
||||||
|
→ Desktop gewinnt (neuerer Timestamp)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Automatisch:**
|
||||||
|
- ✅ Beim Markdown-Import
|
||||||
|
- ✅ Beim JSON-Sync
|
||||||
|
- ⚠️ Keine Merge-Konflikte - nur komplettes Überschreiben
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Einstellungen
|
||||||
|
|
||||||
|
### Desktop-Integration Toggle
|
||||||
|
|
||||||
|
**Einstellungen → Desktop-Integration**
|
||||||
|
|
||||||
|
**AN (aktiviert):**
|
||||||
|
- ✅ Neue Notizen → automatisch als `.md` exportiert
|
||||||
|
- ✅ Aktualisierte Notizen → `.md` Update
|
||||||
|
- ✅ Gelöschte Notizen → `.md` bleibt (zukünftig: auch löschen)
|
||||||
|
|
||||||
|
**AUS (deaktiviert):**
|
||||||
|
- ❌ Kein Markdown-Export
|
||||||
|
- ✅ JSON-Sync läuft normal weiter
|
||||||
|
- ✅ Bestehende `.md` Dateien bleiben erhalten
|
||||||
|
|
||||||
|
### Initial Export
|
||||||
|
|
||||||
|
**Was passiert beim Aktivieren:**
|
||||||
|
1. Alle bestehenden Notizen werden gescannt
|
||||||
|
2. Progress-Dialog zeigt Fortschritt (z.B. "23/42")
|
||||||
|
3. Jede Notiz wird als `.md` exportiert
|
||||||
|
4. Bei Fehlern: Einzelne Notiz wird übersprungen
|
||||||
|
5. Erfolgsmeldung mit Anzahl exportierter Notizen
|
||||||
|
|
||||||
|
**Zeit:** ~1-2 Sekunden pro 50 Notizen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Erweiterte Nutzung
|
||||||
|
|
||||||
|
### Manuelle Markdown-Erstellung
|
||||||
|
|
||||||
|
Du kannst `.md` Dateien manuell erstellen:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
id: 00000000-0000-0000-0000-000000000001
|
||||||
|
created: 2026-01-05T12:00:00Z
|
||||||
|
updated: 2026-01-05T12:00:00Z
|
||||||
|
---
|
||||||
|
|
||||||
|
# Neue Desktop-Notiz
|
||||||
|
|
||||||
|
Inhalt hier...
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ Wichtig:**
|
||||||
|
- `id` muss gültige UUID sein (z.B. mit uuidgen.io)
|
||||||
|
- Timestamps in ISO8601-Format
|
||||||
|
- Frontmatter mit `---` umschließen
|
||||||
|
|
||||||
|
### Bulk-Operations
|
||||||
|
|
||||||
|
**Mehrere Notizen auf einmal bearbeiten:**
|
||||||
|
|
||||||
|
1. WebDAV mounten
|
||||||
|
2. Alle `.md` Dateien in VS Code öffnen
|
||||||
|
3. Suchen & Ersetzen über alle Dateien (Ctrl+Shift+H)
|
||||||
|
4. Speichern
|
||||||
|
5. In App: "Import Markdown Changes"
|
||||||
|
|
||||||
|
### Scripting
|
||||||
|
|
||||||
|
**Beispiel: Alle Notizen nach Datum sortieren**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
cd /mnt/notes-md/
|
||||||
|
|
||||||
|
# Alle .md Dateien nach Update-Datum sortieren
|
||||||
|
for file in *.md; do
|
||||||
|
updated=$(grep "^updated:" "$file" | cut -d' ' -f2)
|
||||||
|
echo "$updated $file"
|
||||||
|
done | sort
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❌ Fehlerbehebung
|
||||||
|
|
||||||
|
### "404 Not Found" beim WebDAV-Mount
|
||||||
|
|
||||||
|
**Ursache:** `/notes-md/` Ordner existiert nicht
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. **Erste Sync durchführen** - Ordner wird automatisch erstellt
|
||||||
|
2. ODER: Manuell erstellen via Terminal:
|
||||||
|
```bash
|
||||||
|
curl -X MKCOL -u noteuser:password http://server:8080/notes-md/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Markdown-Dateien erscheinen nicht
|
||||||
|
|
||||||
|
**Ursache:** Desktop-Integration nicht aktiviert
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Einstellungen → "Desktop-Integration" AN
|
||||||
|
2. Warten auf Initial Export
|
||||||
|
3. WebDAV-Ordner refreshen
|
||||||
|
|
||||||
|
### Änderungen vom Desktop erscheinen nicht in App
|
||||||
|
|
||||||
|
**Ursache:** Markdown-Import nicht ausgeführt
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Einstellungen → "Import Markdown Changes"
|
||||||
|
2. ODER: Auto-Sync abwarten (zukünftiges Feature)
|
||||||
|
|
||||||
|
### "Frontmatter fehlt" Fehler
|
||||||
|
|
||||||
|
**Ursache:** `.md` Datei ohne gültiges YAML-Frontmatter
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
1. Datei in Editor öffnen
|
||||||
|
2. Frontmatter am Anfang hinzufügen:
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
id: NEUE-UUID-HIER
|
||||||
|
created: 2026-01-05T12:00:00Z
|
||||||
|
updated: 2026-01-05T12:00:00Z
|
||||||
|
---
|
||||||
|
```
|
||||||
|
3. Speichern und erneut importieren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Sicherheit & Best Practices
|
||||||
|
|
||||||
|
### Do's ✅
|
||||||
|
|
||||||
|
- ✅ **Backup vor Bulk-Edits** - Lokales Backup erstellen
|
||||||
|
- ✅ **Ein Editor zur Zeit** - Nicht parallel in App UND Desktop bearbeiten
|
||||||
|
- ✅ **Sync abwarten** - Vor Desktop-Bearbeitung Sync durchführen
|
||||||
|
- ✅ **Frontmatter respektieren** - Nicht manuell ändern (außer du weißt was du tust)
|
||||||
|
|
||||||
|
### Don'ts ❌
|
||||||
|
|
||||||
|
- ❌ **Parallel bearbeiten** - App und Desktop gleichzeitig → Konflikte
|
||||||
|
- ❌ **Frontmatter löschen** - Notiz kann nicht mehr importiert werden
|
||||||
|
- ❌ **IDs ändern** - Notiz wird als neue erkannt
|
||||||
|
- ❌ **Timestamps manipulieren** - Konfliktauflösung funktioniert nicht
|
||||||
|
|
||||||
|
### Empfohlener Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Sync in App (Pull-to-Refresh)
|
||||||
|
2. Desktop öffnen
|
||||||
|
3. Änderungen machen
|
||||||
|
4. Speichern
|
||||||
|
5. In App: "Import Markdown Changes"
|
||||||
|
6. Überprüfen
|
||||||
|
7. Weiteren Sync durchführen
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Vergleich: JSON vs Markdown
|
||||||
|
|
||||||
|
| Aspekt | JSON | Markdown |
|
||||||
|
|--------|------|----------|
|
||||||
|
| **Format** | Strukturiert | Fließtext |
|
||||||
|
| **Lesbarkeit (Mensch)** | ⚠️ Mittel | ✅ Gut |
|
||||||
|
| **Lesbarkeit (Maschine)** | ✅ Perfekt | ⚠️ Parsing nötig |
|
||||||
|
| **Metadata** | Native | Frontmatter |
|
||||||
|
| **Editoren** | Code-Editoren | Alle Text-Editoren |
|
||||||
|
| **Sync-Geschwindigkeit** | ✅ Schnell | ⚠️ Langsamer |
|
||||||
|
| **Zuverlässigkeit** | ✅ 100% | ⚠️ Frontmatter-Fehler möglich |
|
||||||
|
| **Mobile-First** | ✅ Ja | ❌ Nein |
|
||||||
|
| **Desktop-First** | ❌ Nein | ✅ Ja |
|
||||||
|
|
||||||
|
**Fazit:** Beide Formate nutzen = Beste Erfahrung auf beiden Plattformen!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔮 Zukünftige Features
|
||||||
|
|
||||||
|
Geplant für v1.3.0+:
|
||||||
|
|
||||||
|
- ⏳ **Auto-Markdown-Import** - Bei jedem Sync automatisch
|
||||||
|
- ⏳ **Bidirektionaler Sync** - Ohne manuellen Import
|
||||||
|
- ⏳ **Markdown-Vorschau** - In der App
|
||||||
|
- ⏳ **Konflikts-UI** - Bei gleichzeitigen Änderungen
|
||||||
|
- ⏳ **Tags in Frontmatter** - Synchronisiert mit App
|
||||||
|
- ⏳ **Attachments** - Bilder/Dateien in Markdown
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**📚 Siehe auch:**
|
||||||
|
- [QUICKSTART.md](../QUICKSTART.md) - App-Einrichtung
|
||||||
|
- [FEATURES.md](FEATURES.md) - Vollständige Feature-Liste
|
||||||
|
- [BACKUP.md](BACKUP.md) - Backup & Wiederherstellung
|
||||||
|
|
||||||
|
**Letzte Aktualisierung:** v1.2.1 (2026-01-05)
|
||||||
274
docs/FEATURES.en.md
Normal file
274
docs/FEATURES.en.md
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
# Complete Feature List 📋
|
||||||
|
|
||||||
|
**🌍 Languages:** [Deutsch](FEATURES.md) · **English**
|
||||||
|
|
||||||
|
> All features of Simple Notes Sync in detail
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Note Management
|
||||||
|
|
||||||
|
### Basic Features
|
||||||
|
- ✅ **Simple text notes** - Focus on content, no distractions
|
||||||
|
- ✅ **Auto-save** - No manual saving needed
|
||||||
|
- ✅ **Title + content** - Clear structure for each note
|
||||||
|
- ✅ **Timestamps** - Creation and modification date automatically
|
||||||
|
- ✅ **Swipe-to-delete** - Intuitive gesture for deletion
|
||||||
|
- ✅ **Confirmation dialog** - Protection against accidental deletion
|
||||||
|
- ✅ **Material Design 3** - Modern, clean UI
|
||||||
|
- ✅ **Dark mode** - Automatically based on system settings
|
||||||
|
- ✅ **Dynamic colors** - Adapts to your Android theme
|
||||||
|
|
||||||
|
### Editor
|
||||||
|
- ✅ **Minimalist editor** - No bells and whistles
|
||||||
|
- ✅ **Auto-focus** - Start writing immediately
|
||||||
|
- ✅ **Fullscreen mode** - Maximum writing space
|
||||||
|
- ✅ **Save button** - Manual confirmation possible
|
||||||
|
- ✅ **Back navigation** - Saves automatically
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💾 Backup & Restore
|
||||||
|
|
||||||
|
### Local Backup System
|
||||||
|
- ✅ **JSON export** - All notes in one file
|
||||||
|
- ✅ **Free location choice** - Downloads, SD card, cloud folder
|
||||||
|
- ✅ **Filenames with timestamp** - `simplenotes_backup_YYYY-MM-DD_HHmmss.json`
|
||||||
|
- ✅ **Complete export** - Title, content, timestamps, IDs
|
||||||
|
- ✅ **Human-readable format** - JSON with formatting
|
||||||
|
- ✅ **Independent from server** - Works completely offline
|
||||||
|
|
||||||
|
### Restore Modes
|
||||||
|
- ✅ **Merge** - Add new notes, keep existing ones _(Default)_
|
||||||
|
- ✅ **Replace** - Delete all and import backup
|
||||||
|
- ✅ **Overwrite duplicates** - Backup wins on ID conflicts
|
||||||
|
- ✅ **Automatic safety backup** - Before every restore
|
||||||
|
- ✅ **Backup validation** - Checks format and version
|
||||||
|
- ✅ **Error handling** - Clear error messages on issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🖥️ Desktop Integration
|
||||||
|
|
||||||
|
### Markdown Export
|
||||||
|
- ✅ **Automatic export** - Each note → `.md` file
|
||||||
|
- ✅ **Dual-format** - JSON (master) + Markdown (mirror)
|
||||||
|
- ✅ **Filename sanitization** - Safe filenames from titles
|
||||||
|
- ✅ **Frontmatter metadata** - YAML with ID, timestamps, tags
|
||||||
|
- ✅ **WebDAV sync** - Parallel to JSON sync
|
||||||
|
- ✅ **Optional** - Toggle in settings
|
||||||
|
- ✅ **Initial export** - All existing notes when activated
|
||||||
|
- ✅ **Progress indicator** - Shows X/Y during export
|
||||||
|
|
||||||
|
### Markdown Import
|
||||||
|
- ✅ **Desktop → App** - Import changes from desktop
|
||||||
|
- ✅ **Last-Write-Wins** - Conflict resolution via timestamp
|
||||||
|
- ✅ **Frontmatter parsing** - Reads metadata from `.md` files
|
||||||
|
- ✅ **Detect new notes** - Automatically adopt to app
|
||||||
|
- ✅ **Detect updates** - Only if desktop version is newer
|
||||||
|
- ✅ **Error tolerance** - Individual errors don't abort import
|
||||||
|
|
||||||
|
### WebDAV Access
|
||||||
|
- ✅ **Network drive mount** - Windows, macOS, Linux
|
||||||
|
- ✅ **Any Markdown editor** - VS Code, Typora, Notepad++, iA Writer
|
||||||
|
- ✅ **Live editing** - Direct access to `.md` files
|
||||||
|
- ✅ **Folder structure** - `/notes/` for JSON, `/notes-md/` for Markdown
|
||||||
|
- ✅ **Automatic folder creation** - On first sync
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Synchronization
|
||||||
|
|
||||||
|
### Auto-Sync
|
||||||
|
- ✅ **Interval selection** - 15, 30 or 60 minutes
|
||||||
|
- ✅ **WiFi binding** - Only in configured home WiFi
|
||||||
|
- ✅ **Battery-friendly** - ~0.2-0.8% per day
|
||||||
|
- ✅ **Smart server check** - No errors on foreign networks
|
||||||
|
- ✅ **WorkManager** - Reliable background execution
|
||||||
|
- ✅ **Battery optimization compatible** - Works even with Doze mode
|
||||||
|
|
||||||
|
### Sync Triggers (6 total)
|
||||||
|
1. ✅ **Periodic sync** - Automatically after interval
|
||||||
|
2. ✅ **App-start sync** - When opening the app
|
||||||
|
3. ✅ **WiFi-connect sync** - When home WiFi connects
|
||||||
|
4. ✅ **Manual sync** - Button in settings
|
||||||
|
5. ✅ **Pull-to-refresh** - Swipe gesture in notes list
|
||||||
|
6. ✅ **Settings-save sync** - After server configuration
|
||||||
|
|
||||||
|
### Sync Mechanism
|
||||||
|
- ✅ **Upload** - Local changes to server
|
||||||
|
- ✅ **Download** - Server changes to app
|
||||||
|
- ✅ **Conflict detection** - On simultaneous changes
|
||||||
|
- ✅ **Conflict-free merging** - Last-Write-Wins via timestamp
|
||||||
|
- ✅ **Sync status tracking** - LOCAL_ONLY, PENDING, SYNCED, CONFLICT
|
||||||
|
- ✅ **Error handling** - Retry on network issues
|
||||||
|
- ✅ **Offline-first** - App works without server
|
||||||
|
|
||||||
|
### Server Connection
|
||||||
|
- ✅ **WebDAV protocol** - Standard protocol
|
||||||
|
- ✅ **HTTP/HTTPS** - HTTP only local, HTTPS for external
|
||||||
|
- ✅ **Username/password** - Basic authentication
|
||||||
|
- ✅ **Connection test** - Test in settings
|
||||||
|
- ✅ **Gateway SSID** - WiFi name for auto-sync
|
||||||
|
- ✅ **Server URL normalization** - Automatic `/notes/` and `/notes-md/` _(NEW in v1.2.1)_
|
||||||
|
- ✅ **Flexible URL input** - Both variants work: `http://server/` and `http://server/notes/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Privacy & Security
|
||||||
|
|
||||||
|
### Self-Hosted
|
||||||
|
- ✅ **Own server** - Full control over data
|
||||||
|
- ✅ **No cloud** - No third parties
|
||||||
|
- ✅ **No tracking** - No analytics, no telemetry
|
||||||
|
- ✅ **No account** - Only server credentials
|
||||||
|
- ✅ **100% open source** - MIT License
|
||||||
|
|
||||||
|
### Data Security
|
||||||
|
- ✅ **Local storage** - App-private storage (Android)
|
||||||
|
- ✅ **WebDAV encryption** - HTTPS for external servers
|
||||||
|
- ✅ **Password storage** - Android SharedPreferences (encrypted)
|
||||||
|
- ✅ **No third-party libs** - Only Android SDK + Sardine (WebDAV)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔋 Performance & Optimization
|
||||||
|
|
||||||
|
### Battery Efficiency
|
||||||
|
- ✅ **Optimized sync intervals** - 15/30/60 min
|
||||||
|
- ✅ **WiFi-only** - No mobile data sync
|
||||||
|
- ✅ **Smart server check** - Only in home WiFi
|
||||||
|
- ✅ **WorkManager** - System-optimized execution
|
||||||
|
- ✅ **Doze mode compatible** - Sync runs even in standby
|
||||||
|
- ✅ **Measured consumption:**
|
||||||
|
- 15 min: ~0.8% / day (~23 mAh)
|
||||||
|
- 30 min: ~0.4% / day (~12 mAh) ⭐ _Recommended_
|
||||||
|
- 60 min: ~0.2% / day (~6 mAh)
|
||||||
|
|
||||||
|
### App Performance
|
||||||
|
- ✅ **Offline-first** - Works without internet
|
||||||
|
- ✅ **Instant-load** - Notes load in <100ms
|
||||||
|
- ✅ **Smooth scrolling** - RecyclerView with ViewHolder
|
||||||
|
- ✅ **Material Design 3** - Native Android UI
|
||||||
|
- ✅ **Kotlin Coroutines** - Asynchronous operations
|
||||||
|
- ✅ **Minimal APK size** - ~2 MB
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Technical Details
|
||||||
|
|
||||||
|
### Platform
|
||||||
|
- ✅ **Android 8.0+** (API 26+)
|
||||||
|
- ✅ **Target SDK 36** (Android 15)
|
||||||
|
- ✅ **Kotlin** - Modern programming language
|
||||||
|
- ✅ **Material Design 3** - Latest design guidelines
|
||||||
|
- ✅ **ViewBinding** - Type-safe view references
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
- ✅ **MVVM-Light** - Simple architecture
|
||||||
|
- ✅ **Single Activity** - Modern navigation
|
||||||
|
- ✅ **Kotlin Coroutines** - Async/Await pattern
|
||||||
|
- ✅ **Dispatchers.IO** - Background operations
|
||||||
|
- ✅ **SharedPreferences** - Settings storage
|
||||||
|
- ✅ **File-based storage** - JSON files locally
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
- ✅ **AndroidX** - Jetpack libraries
|
||||||
|
- ✅ **Material Components** - Material Design 3
|
||||||
|
- ✅ **Sardine** - WebDAV client (com.thegrizzlylabs)
|
||||||
|
- ✅ **Gson** - JSON serialization
|
||||||
|
- ✅ **WorkManager** - Background tasks
|
||||||
|
- ✅ **OkHttp** - HTTP client (via Sardine)
|
||||||
|
|
||||||
|
### Build Variants
|
||||||
|
- ✅ **Standard** - Universal APK (100% FOSS, no Google dependencies)
|
||||||
|
- ✅ **F-Droid** - Identical to Standard (100% FOSS)
|
||||||
|
- ✅ **Debug/Release** - Development and production
|
||||||
|
- ✅ **No Google Services** - Completely FOSS, no proprietary libraries
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Server Compatibility
|
||||||
|
|
||||||
|
### Tested WebDAV Servers
|
||||||
|
- ✅ **Docker WebDAV** (recommended for self-hosting)
|
||||||
|
- ✅ **Nextcloud** - Fully compatible
|
||||||
|
- ✅ **ownCloud** - Works perfectly
|
||||||
|
- ✅ **Apache mod_dav** - Standard WebDAV
|
||||||
|
- ✅ **nginx + WebDAV** - With correct configuration
|
||||||
|
|
||||||
|
### Server Features
|
||||||
|
- ✅ **Basic Auth** - Username/password
|
||||||
|
- ✅ **Directory listing** - For download
|
||||||
|
- ✅ **PUT/GET** - Upload/download
|
||||||
|
- ✅ **MKCOL** - Create folders
|
||||||
|
- ✅ **DELETE** - Delete notes (future)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔮 Future Features
|
||||||
|
|
||||||
|
Planned for upcoming versions (see [TODO.md](project-docs/simple-notes-sync/planning/TODO.md)):
|
||||||
|
|
||||||
|
### v1.3.0 - Web Editor & Organization
|
||||||
|
- ⏳ **Browser-based editor** - Edit notes in web browser
|
||||||
|
- ⏳ **WebDAV access via browser** - No mount needed
|
||||||
|
- ⏳ **Mobile-optimized** - Responsive design
|
||||||
|
- ⏳ **Offline-capable** - Progressive Web App (PWA)
|
||||||
|
- ⏳ **Tags/labels** - Categorize notes
|
||||||
|
- ⏳ **Search** - Full-text search in all notes
|
||||||
|
- ⏳ **Sorting** - By date, title, tags
|
||||||
|
- ⏳ **Filter** - Filter by tags
|
||||||
|
|
||||||
|
### v1.4.0 - Sharing & Export
|
||||||
|
- ⏳ **Share note** - Via share intent
|
||||||
|
- ⏳ **Export single note** - As .txt or .md
|
||||||
|
- ⏳ **Import from text** - Via share intent
|
||||||
|
|
||||||
|
### v1.5.0 - Advanced Editor Features
|
||||||
|
- ⏳ **Markdown preview** - In-app rendering
|
||||||
|
- ⏳ **Checklists** - TODO lists in notes
|
||||||
|
- ⏳ **Syntax highlighting** - For code snippets
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Comparison with Other Apps
|
||||||
|
|
||||||
|
| Feature | Simple Notes Sync | Google Keep | Nextcloud Notes |
|
||||||
|
|---------|------------------|-------------|-----------------|
|
||||||
|
| Offline-first | ✅ | ⚠️ Limited | ⚠️ Limited |
|
||||||
|
| Self-hosted | ✅ | ❌ | ✅ |
|
||||||
|
| Auto-sync | ✅ | ✅ | ✅ |
|
||||||
|
| Markdown export | ✅ | ❌ | ✅ |
|
||||||
|
| Desktop access | ✅ (WebDAV) | ✅ (Web) | ✅ (Web + WebDAV) |
|
||||||
|
| Local backup | ✅ | ❌ | ⚠️ Server backup |
|
||||||
|
| No Google account | ✅ | ❌ | ✅ |
|
||||||
|
| Open source | ✅ MIT | ❌ | ✅ AGPL |
|
||||||
|
| APK size | ~2 MB | ~50 MB | ~8 MB |
|
||||||
|
| Battery usage | ~0.4%/day | ~1-2%/day | ~0.5%/day |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❓ FAQ
|
||||||
|
|
||||||
|
**Q: Do I need a server?**
|
||||||
|
A: No! The app works completely offline. The server is optional for sync.
|
||||||
|
|
||||||
|
**Q: Which server is best?**
|
||||||
|
A: For beginners: Docker WebDAV (simple, easy). For pros: Nextcloud (many features).
|
||||||
|
|
||||||
|
**Q: Does Markdown export work without Desktop Integration?**
|
||||||
|
A: No, you need to activate the feature in settings.
|
||||||
|
|
||||||
|
**Q: Will my data be lost if I switch servers?**
|
||||||
|
A: No! Create a local backup, switch servers, restore.
|
||||||
|
|
||||||
|
**Q: Why JSON + Markdown?**
|
||||||
|
A: JSON is reliable and fast (master). Markdown is human-readable (mirror for desktop).
|
||||||
|
|
||||||
|
**Q: Can I use the app without Google Play?**
|
||||||
|
A: Yes! Download the APK directly from GitHub or use F-Droid.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last update:** v1.2.1 (2026-01-05)
|
||||||
274
docs/FEATURES.md
Normal file
274
docs/FEATURES.md
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
# Vollständige Feature-Liste 📋
|
||||||
|
|
||||||
|
**🌍 Languages:** **Deutsch** · [English](FEATURES.en.md)
|
||||||
|
|
||||||
|
> Alle Features von Simple Notes Sync im Detail
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Notiz-Verwaltung
|
||||||
|
|
||||||
|
### Basis-Funktionen
|
||||||
|
- ✅ **Einfache Textnotizen** - Fokus auf Inhalt, keine Ablenkung
|
||||||
|
- ✅ **Automatisches Speichern** - Kein manuelles Speichern nötig
|
||||||
|
- ✅ **Titel + Inhalt** - Klare Struktur für jede Notiz
|
||||||
|
- ✅ **Zeitstempel** - Erstellungs- und Änderungsdatum automatisch
|
||||||
|
- ✅ **Swipe-to-Delete** - Intuitive Geste zum Löschen
|
||||||
|
- ✅ **Bestätigungs-Dialog** - Schutz vor versehentlichem Löschen
|
||||||
|
- ✅ **Material Design 3** - Moderne, saubere UI
|
||||||
|
- ✅ **Dark Mode** - Automatisch je nach System-Einstellung
|
||||||
|
- ✅ **Dynamic Colors** - Passt sich deinem Android-Theme an
|
||||||
|
|
||||||
|
### Editor
|
||||||
|
- ✅ **Minimalistischer Editor** - Kein Schnickschnack
|
||||||
|
- ✅ **Auto-Fokus** - Direkt losschreiben
|
||||||
|
- ✅ **Vollbild-Modus** - Maximale Schreibfläche
|
||||||
|
- ✅ **Speichern-Button** - Manuelle Bestätigung möglich
|
||||||
|
- ✅ **Zurück-Navigation** - Speichert automatisch
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💾 Backup & Wiederherstellung
|
||||||
|
|
||||||
|
### Lokales Backup System
|
||||||
|
- ✅ **JSON-Export** - Alle Notizen in einer Datei
|
||||||
|
- ✅ **Freie Speicherort-Wahl** - Downloads, SD-Karte, Cloud-Ordner
|
||||||
|
- ✅ **Dateinamen mit Zeitstempel** - `simplenotes_backup_YYYY-MM-DD_HHmmss.json`
|
||||||
|
- ✅ **Vollständiger Export** - Titel, Inhalt, Timestamps, IDs
|
||||||
|
- ✅ **Menschenlesbares Format** - JSON mit Formatierung
|
||||||
|
- ✅ **Unabhängig vom Server** - Funktioniert komplett offline
|
||||||
|
|
||||||
|
### Wiederherstellungs-Modi
|
||||||
|
- ✅ **Zusammenführen (Merge)** - Neue Notizen hinzufügen, bestehende behalten _(Standard)_
|
||||||
|
- ✅ **Ersetzen (Replace)** - Alle löschen und Backup importieren
|
||||||
|
- ✅ **Duplikate überschreiben (Overwrite)** - Backup gewinnt bei ID-Konflikten
|
||||||
|
- ✅ **Automatisches Sicherheits-Backup** - Vor jeder Wiederherstellung
|
||||||
|
- ✅ **Backup-Validierung** - Prüft Format und Version
|
||||||
|
- ✅ **Fehlerbehandlung** - Klare Fehlermeldungen bei Problemen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🖥️ Desktop-Integration
|
||||||
|
|
||||||
|
### Markdown-Export
|
||||||
|
- ✅ **Automatischer Export** - Jede Notiz → `.md` Datei
|
||||||
|
- ✅ **Dual-Format** - JSON (Master) + Markdown (Mirror)
|
||||||
|
- ✅ **Dateinamen-Sanitization** - Sichere Dateinamen aus Titeln
|
||||||
|
- ✅ **Frontmatter-Metadata** - YAML mit ID, Timestamps, Tags
|
||||||
|
- ✅ **WebDAV-Sync** - Parallel zum JSON-Sync
|
||||||
|
- ✅ **Optional** - In Einstellungen ein/ausschaltbar
|
||||||
|
- ✅ **Initial Export** - Alle bestehenden Notizen beim Aktivieren
|
||||||
|
- ✅ **Progress-Anzeige** - Zeigt X/Y beim Export
|
||||||
|
|
||||||
|
### Markdown-Import
|
||||||
|
- ✅ **Desktop → App** - Änderungen vom Desktop importieren
|
||||||
|
- ✅ **Last-Write-Wins** - Konfliktauflösung via Timestamp
|
||||||
|
- ✅ **Frontmatter-Parsing** - Liest Metadata aus `.md` Dateien
|
||||||
|
- ✅ **Neue Notizen erkennen** - Automatisch in App übernehmen
|
||||||
|
- ✅ **Updates erkennen** - Nur wenn Desktop-Version neuer ist
|
||||||
|
- ✅ **Fehlertoleranz** - Einzelne Fehler brechen Import nicht ab
|
||||||
|
|
||||||
|
### WebDAV-Zugriff
|
||||||
|
- ✅ **Network Drive Mount** - Windows, macOS, Linux
|
||||||
|
- ✅ **Jeder Markdown-Editor** - VS Code, Typora, Notepad++, iA Writer
|
||||||
|
- ✅ **Live-Bearbeitung** - Direkter Zugriff auf `.md` Dateien
|
||||||
|
- ✅ **Ordner-Struktur** - `/notes/` für JSON, `/notes-md/` für Markdown
|
||||||
|
- ✅ **Automatische Ordner-Erstellung** - Beim ersten Sync
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Synchronisation
|
||||||
|
|
||||||
|
### Auto-Sync
|
||||||
|
- ✅ **Intervall-Auswahl** - 15, 30 oder 60 Minuten
|
||||||
|
- ✅ **WLAN-Bindung** - Nur im konfigurierten Heim-WLAN
|
||||||
|
- ✅ **Akkuschonend** - ~0.2-0.8% pro Tag
|
||||||
|
- ✅ **Smart Server-Check** - Keine Fehler in fremden Netzwerken
|
||||||
|
- ✅ **WorkManager** - Zuverlässige Background-Ausführung
|
||||||
|
- ✅ **Battery-Optimierung kompatibel** - Funktioniert auch mit Doze Mode
|
||||||
|
|
||||||
|
### Sync-Trigger (6 Stück)
|
||||||
|
1. ✅ **Periodic Sync** - Automatisch nach Intervall
|
||||||
|
2. ✅ **App-Start Sync** - Beim Öffnen der App
|
||||||
|
3. ✅ **WiFi-Connect Sync** - Wenn Heim-WLAN verbindet
|
||||||
|
4. ✅ **Manual Sync** - Button in Einstellungen
|
||||||
|
5. ✅ **Pull-to-Refresh** - Wisch-Geste in Notizliste
|
||||||
|
6. ✅ **Settings-Save Sync** - Nach Server-Konfiguration
|
||||||
|
|
||||||
|
### Sync-Mechanismus
|
||||||
|
- ✅ **Upload** - Lokale Änderungen zum Server
|
||||||
|
- ✅ **Download** - Server-Änderungen in App
|
||||||
|
- ✅ **Konflikt-Erkennung** - Bei gleichzeitigen Änderungen
|
||||||
|
- ✅ **Konfliktfreies Merging** - Last-Write-Wins via Timestamp
|
||||||
|
- ✅ **Sync-Status Tracking** - LOCAL_ONLY, PENDING, SYNCED, CONFLICT
|
||||||
|
- ✅ **Fehlerbehandlung** - Retry bei Netzwerkproblemen
|
||||||
|
- ✅ **Offline-First** - App funktioniert ohne Server
|
||||||
|
|
||||||
|
### Server-Verbindung
|
||||||
|
- ✅ **WebDAV-Protokoll** - Standard-Protokoll
|
||||||
|
- ✅ **HTTP/HTTPS** - HTTP nur lokal, HTTPS für extern
|
||||||
|
- ✅ **Username/Password** - Basic Authentication
|
||||||
|
- ✅ **Connection Test** - In Einstellungen testen
|
||||||
|
- ✅ **Gateway SSID** - WLAN-Name für Auto-Sync
|
||||||
|
- ✅ **Server-URL Normalisierung** - Automatisches `/notes/` und `/notes-md/` _(NEU in v1.2.1)_
|
||||||
|
- ✅ **Flexible URL-Eingabe** - Beide Varianten funktionieren: `http://server/` und `http://server/notes/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Privacy & Sicherheit
|
||||||
|
|
||||||
|
### Self-Hosted
|
||||||
|
- ✅ **Eigener Server** - Volle Kontrolle über Daten
|
||||||
|
- ✅ **Keine Cloud** - Keine Drittanbieter
|
||||||
|
- ✅ **Kein Tracking** - Keine Analytik, keine Telemetrie
|
||||||
|
- ✅ **Kein Account** - Nur Server-Zugangsdaten
|
||||||
|
- ✅ **100% Open Source** - MIT Lizenz
|
||||||
|
|
||||||
|
### Daten-Sicherheit
|
||||||
|
- ✅ **Lokale Speicherung** - App-Private Storage (Android)
|
||||||
|
- ✅ **WebDAV-Verschlüsselung** - HTTPS für externe Server
|
||||||
|
- ✅ **Passwort-Speicherung** - Android SharedPreferences (verschlüsselt)
|
||||||
|
- ✅ **Keine Drittanbieter-Libs** - Nur Android SDK + Sardine (WebDAV)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔋 Performance & Optimierung
|
||||||
|
|
||||||
|
### Akku-Effizienz
|
||||||
|
- ✅ **Optimierte Sync-Intervalle** - 15/30/60 Min
|
||||||
|
- ✅ **WLAN-Only** - Kein Mobile Data Sync
|
||||||
|
- ✅ **Smart Server-Check** - Nur im Heim-WLAN
|
||||||
|
- ✅ **WorkManager** - System-optimierte Ausführung
|
||||||
|
- ✅ **Doze Mode kompatibel** - Sync läuft auch im Standby
|
||||||
|
- ✅ **Gemessener Verbrauch:**
|
||||||
|
- 15 Min: ~0.8% / Tag (~23 mAh)
|
||||||
|
- 30 Min: ~0.4% / Tag (~12 mAh) ⭐ _Empfohlen_
|
||||||
|
- 60 Min: ~0.2% / Tag (~6 mAh)
|
||||||
|
|
||||||
|
### App-Performance
|
||||||
|
- ✅ **Offline-First** - Funktioniert ohne Internet
|
||||||
|
- ✅ **Instant-Load** - Notizen laden in <100ms
|
||||||
|
- ✅ **Smooth Scrolling** - RecyclerView mit ViewHolder
|
||||||
|
- ✅ **Material Design 3** - Native Android UI
|
||||||
|
- ✅ **Kotlin Coroutines** - Asynchrone Operationen
|
||||||
|
- ✅ **Minimale APK-Größe** - ~2 MB
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Technische Details
|
||||||
|
|
||||||
|
### Plattform
|
||||||
|
- ✅ **Android 8.0+** (API 26+)
|
||||||
|
- ✅ **Target SDK 36** (Android 15)
|
||||||
|
- ✅ **Kotlin** - Moderne Programmiersprache
|
||||||
|
- ✅ **Material Design 3** - Neueste Design-Richtlinien
|
||||||
|
- ✅ **ViewBinding** - Typ-sichere View-Referenzen
|
||||||
|
|
||||||
|
### Architektur
|
||||||
|
- ✅ **MVVM-Light** - Einfache Architektur
|
||||||
|
- ✅ **Single Activity** - Moderne Navigation
|
||||||
|
- ✅ **Kotlin Coroutines** - Async/Await Pattern
|
||||||
|
- ✅ **Dispatchers.IO** - Background-Operationen
|
||||||
|
- ✅ **SharedPreferences** - Settings-Speicherung
|
||||||
|
- ✅ **File-Based Storage** - JSON-Dateien lokal
|
||||||
|
|
||||||
|
### Abhängigkeiten
|
||||||
|
- ✅ **AndroidX** - Jetpack Libraries
|
||||||
|
- ✅ **Material Components** - Material Design 3
|
||||||
|
- ✅ **Sardine** - WebDAV Client (com.thegrizzlylabs)
|
||||||
|
- ✅ **Gson** - JSON Serialization
|
||||||
|
- ✅ **WorkManager** - Background Tasks
|
||||||
|
- ✅ **OkHttp** - HTTP Client (via Sardine)
|
||||||
|
|
||||||
|
### Build-Varianten
|
||||||
|
- ✅ **Standard** - Universal APK (100% FOSS, keine Google-Dependencies)
|
||||||
|
- ✅ **F-Droid** - Identisch mit Standard (100% FOSS)
|
||||||
|
- ✅ **Debug/Release** - Entwicklung und Production
|
||||||
|
- ✅ **Keine Google Services** - Komplett FOSS, keine proprietären Bibliotheken
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Server-Kompatibilität
|
||||||
|
|
||||||
|
### Getestete WebDAV-Server
|
||||||
|
- ✅ **Docker WebDAV** (empfohlen für Self-Hosting)
|
||||||
|
- ✅ **Nextcloud** - Vollständig kompatibel
|
||||||
|
- ✅ **ownCloud** - Funktioniert einwandfrei
|
||||||
|
- ✅ **Apache mod_dav** - Standard WebDAV
|
||||||
|
- ✅ **nginx + WebDAV** - Mit korrekter Konfiguration
|
||||||
|
|
||||||
|
### Server-Features
|
||||||
|
- ✅ **Basic Auth** - Username/Password
|
||||||
|
- ✅ **Directory Listing** - Für Download
|
||||||
|
- ✅ **PUT/GET** - Upload/Download
|
||||||
|
- ✅ **MKCOL** - Ordner erstellen
|
||||||
|
- ✅ **DELETE** - Notizen löschen (zukünftig)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔮 Zukünftige Features
|
||||||
|
|
||||||
|
Geplant für kommende Versionen (siehe [TODO.md](project-docs/simple-notes-sync/planning/TODO.md)):
|
||||||
|
|
||||||
|
### v1.3.0 - Web Editor & Organisation
|
||||||
|
- ⏳ **Browser-basierter Editor** - Notizen im Webbrowser bearbeiten
|
||||||
|
- ⏳ **WebDAV-Zugriff via Browser** - Kein Mount nötig
|
||||||
|
- ⏳ **Mobile-optimiert** - Responsive Design
|
||||||
|
- ⏳ **Offline-fähig** - Progressive Web App (PWA)
|
||||||
|
- ⏳ **Tags/Labels** - Kategorisierung von Notizen
|
||||||
|
- ⏳ **Suche** - Volltextsuche in allen Notizen
|
||||||
|
- ⏳ **Sortierung** - Nach Datum, Titel, Tags
|
||||||
|
- ⏳ **Filter** - Nach Tags filtern
|
||||||
|
|
||||||
|
### v1.4.0 - Sharing & Export
|
||||||
|
- ⏳ **Notiz teilen** - Via Share-Intent
|
||||||
|
- ⏳ **Einzelne Notiz exportieren** - Als .txt oder .md
|
||||||
|
- ⏳ **Import von Text** - Via Share-Intent
|
||||||
|
|
||||||
|
### v1.5.0 - Erweiterte Editor-Features
|
||||||
|
- ⏳ **Markdown-Vorschau** - In-App Rendering
|
||||||
|
- ⏳ **Checklisten** - TODO-Listen in Notizen
|
||||||
|
- ⏳ **Syntax-Highlighting** - Für Code-Snippets
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Vergleich mit anderen Apps
|
||||||
|
|
||||||
|
| Feature | Simple Notes Sync | Google Keep | Nextcloud Notes |
|
||||||
|
|---------|------------------|-------------|-----------------|
|
||||||
|
| Offline-First | ✅ | ⚠️ Eingeschränkt | ⚠️ Eingeschränkt |
|
||||||
|
| Self-Hosted | ✅ | ❌ | ✅ |
|
||||||
|
| Auto-Sync | ✅ | ✅ | ✅ |
|
||||||
|
| Markdown-Export | ✅ | ❌ | ✅ |
|
||||||
|
| Desktop-Zugriff | ✅ (WebDAV) | ✅ (Web) | ✅ (Web + WebDAV) |
|
||||||
|
| Lokales Backup | ✅ | ❌ | ⚠️ Server-Backup |
|
||||||
|
| Kein Google-Account | ✅ | ❌ | ✅ |
|
||||||
|
| Open Source | ✅ MIT | ❌ | ✅ AGPL |
|
||||||
|
| APK-Größe | ~2 MB | ~50 MB | ~8 MB |
|
||||||
|
| Akku-Verbrauch | ~0.4%/Tag | ~1-2%/Tag | ~0.5%/Tag |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❓ FAQ
|
||||||
|
|
||||||
|
**Q: Brauche ich einen Server?**
|
||||||
|
A: Nein! Die App funktioniert auch komplett offline. Der Server ist optional für Sync.
|
||||||
|
|
||||||
|
**Q: Welcher Server ist am besten?**
|
||||||
|
A: Für Einstieg: Docker WebDAV (einfach, leicht). Für Profis: Nextcloud (viele Features).
|
||||||
|
|
||||||
|
**Q: Funktioniert Markdown-Export ohne Desktop-Integration?**
|
||||||
|
A: Nein, du musst das Feature in den Einstellungen aktivieren.
|
||||||
|
|
||||||
|
**Q: Gehen meine Daten verloren wenn ich den Server wechsle?**
|
||||||
|
A: Nein! Erstelle ein lokales Backup, wechsle Server, stelle wieder her.
|
||||||
|
|
||||||
|
**Q: Warum JSON + Markdown?**
|
||||||
|
A: JSON ist zuverlässig und schnell (Master). Markdown ist menschenlesbar (Mirror für Desktop).
|
||||||
|
|
||||||
|
**Q: Kann ich die App ohne Google Play nutzen?**
|
||||||
|
A: Ja! Lade die APK direkt von GitHub oder nutze F-Droid.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Letzte Aktualisierung:** v1.2.1 (2026-01-05)
|
||||||
12
fastlane/metadata/android/de-DE/changelogs/6.txt
Normal file
12
fastlane/metadata/android/de-DE/changelogs/6.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
v1.2.1 - Initial Export + URL Normalization
|
||||||
|
|
||||||
|
Fehlerbehebung
|
||||||
|
• Bestehende Notizen werden beim Aktivieren der Desktop-Integration exportiert
|
||||||
|
• Markdown-Dateien landen korrekt im /notes-md/ Ordner
|
||||||
|
• Vereinfachte Server-Konfiguration: Nur Base-URL eingeben (z.B. http://server:8080/)
|
||||||
|
• App erstellt automatisch /notes/ und /notes-md/
|
||||||
|
• Beide URL-Varianten funktionieren: mit und ohne /notes
|
||||||
|
|
||||||
|
Verbesserungen
|
||||||
|
• Beispiel-URL zeigt jetzt /notes statt /webdav
|
||||||
|
• Progress-Dialog beim Export
|
||||||
12
fastlane/metadata/android/de-DE/changelogs/7.txt
Normal file
12
fastlane/metadata/android/de-DE/changelogs/7.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
v1.2.2 - Rückwärtskompatibilität für v1.2.0 User
|
||||||
|
|
||||||
|
Kritische Fehlerbehebung
|
||||||
|
• Server-Wiederherstellung findet jetzt ALLE Notizen (Root + /notes/)
|
||||||
|
• User die von v1.2.0 upgraden verlieren keine Daten mehr
|
||||||
|
• Alte Notizen aus Root-Ordner werden beim Restore gefunden
|
||||||
|
|
||||||
|
Technische Details
|
||||||
|
• Dual-Mode Download nur bei Server-Restore aktiv
|
||||||
|
• Normale Syncs bleiben schnell (scannen nur /notes/)
|
||||||
|
• Automatische Deduplication verhindert Duplikate
|
||||||
|
• Sanfte Migration: Neue Uploads gehen in /notes/, alte bleiben lesbar
|
||||||
12
fastlane/metadata/android/de-DE/changelogs/8.txt
Normal file
12
fastlane/metadata/android/de-DE/changelogs/8.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
v1.3.0 - Multi-Device Sync
|
||||||
|
|
||||||
|
NEUE FEATURES:
|
||||||
|
• Multi-Device Sync mit Deletion Tracking (keine Zombie-Notizen)
|
||||||
|
• Wisch-Geste zum Server-Löschen (verhindert Duplikate auf anderen Geräten)
|
||||||
|
• E-Tag Performance-Optimierung (~150ms statt 3s)
|
||||||
|
• Markdown Auto-Sync Toggle (Export + Import vereint)
|
||||||
|
• Manueller Markdown-Sync Button
|
||||||
|
• Server-Wiederherstellung Modi (Merge/Replace/Overwrite)
|
||||||
|
|
||||||
|
Dank an Thomas aus Bielefeld!
|
||||||
|
Kompatibel: v1.2.0-v1.3.0
|
||||||
12
fastlane/metadata/android/en-US/changelogs/6.txt
Normal file
12
fastlane/metadata/android/en-US/changelogs/6.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
v1.2.1 - Initial Export + URL Normalization
|
||||||
|
|
||||||
|
Bugfixes
|
||||||
|
• Existing notes are now exported when Desktop Integration is enabled
|
||||||
|
• Markdown files now correctly land in /notes-md/ folder
|
||||||
|
• Simplified server config: Enter only base URL (e.g. http://server:8080/)
|
||||||
|
• App automatically creates /notes/ and /notes-md/
|
||||||
|
• Both URL variants work: with and without /notes
|
||||||
|
|
||||||
|
Improvements
|
||||||
|
• Example URL now shows /notes instead of /webdav
|
||||||
|
• Progress dialog during export
|
||||||
12
fastlane/metadata/android/en-US/changelogs/7.txt
Normal file
12
fastlane/metadata/android/en-US/changelogs/7.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
v1.2.2 - Backward Compatibility for v1.2.0 Users
|
||||||
|
|
||||||
|
Critical Bugfix
|
||||||
|
• Server restore now finds ALL notes (Root + /notes/)
|
||||||
|
• Users upgrading from v1.2.0 no longer lose data
|
||||||
|
• Old notes from Root folder are found during restore
|
||||||
|
|
||||||
|
Technical Details
|
||||||
|
• Dual-mode download only active for server restore
|
||||||
|
• Normal syncs remain fast (scan only /notes/)
|
||||||
|
• Automatic deduplication prevents duplicates
|
||||||
|
• Smooth migration: New uploads go to /notes/, old ones remain readable
|
||||||
12
fastlane/metadata/android/en-US/changelogs/8.txt
Normal file
12
fastlane/metadata/android/en-US/changelogs/8.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
v1.3.0 - Multi-Device Sync
|
||||||
|
|
||||||
|
NEW FEATURES:
|
||||||
|
• Multi-Device Sync with deletion tracking (no zombie notes)
|
||||||
|
• Swipe gesture for server deletion (prevents duplicates on other devices)
|
||||||
|
• E-Tag performance optimization (~150ms vs 3s)
|
||||||
|
• Markdown Auto-Sync toggle (unified Export + Import)
|
||||||
|
• Manual Markdown sync button
|
||||||
|
• Server restore modes (Merge/Replace/Overwrite)
|
||||||
|
|
||||||
|
Thanks to Thomas from Bielefeld!
|
||||||
|
Compatible: v1.2.0-v1.3.0
|
||||||
Reference in New Issue
Block a user