diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/SettingsActivity.kt b/android/app/src/main/java/dev/dettmer/simplenotes/SettingsActivity.kt
index 71b04b6..1fa509d 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/SettingsActivity.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/SettingsActivity.kt
@@ -2,6 +2,7 @@
package dev.dettmer.simplenotes
+import android.annotation.SuppressLint
import android.app.ProgressDialog
import android.content.Context
import android.content.Intent
@@ -186,10 +187,12 @@ class SettingsActivity : AppCompatActivity() {
}
// Set URL with protocol prefix in the text field
+ @Suppress("SetTextI18n") // Technical URL, not UI text
editTextServerUrl.setText("$protocol://$hostPath")
} else {
// Default: HTTP selected (lokale Server sind häufiger), empty URL with prefix
radioHttp.isChecked = true
+ @Suppress("SetTextI18n") // Technical URL, not UI text
editTextServerUrl.setText("http://")
}
@@ -252,6 +255,7 @@ class SettingsActivity : AppCompatActivity() {
}
// Set new URL with correct protocol
+ @Suppress("SetTextI18n") // Technical URL, not UI text
editTextServerUrl.setText("$newProtocol://$hostPath")
// Move cursor to end
@@ -379,7 +383,7 @@ class SettingsActivity : AppCompatActivity() {
val versionName = BuildConfig.VERSION_NAME
val versionCode = BuildConfig.VERSION_CODE
- textViewAppVersion.text = "Version $versionName ($versionCode)"
+ textViewAppVersion.text = getString(R.string.about_version, versionName, versionCode)
} catch (e: Exception) {
Logger.e(TAG, "Failed to load version info", e)
textViewAppVersion.text = getString(R.string.version_not_available)
@@ -644,7 +648,7 @@ class SettingsActivity : AppCompatActivity() {
val serverUrl = prefs.getString(Constants.KEY_SERVER_URL, null)
if (serverUrl.isNullOrEmpty()) {
- textViewServerStatus.text = "❌ Nicht konfiguriert"
+ textViewServerStatus.text = getString(R.string.server_status_not_configured)
textViewServerStatus.setTextColor(getColor(android.R.color.holo_red_dark))
return
}
@@ -669,10 +673,10 @@ class SettingsActivity : AppCompatActivity() {
}
if (isReachable) {
- textViewServerStatus.text = "✅ Erreichbar"
+ textViewServerStatus.text = getString(R.string.server_status_reachable)
textViewServerStatus.setTextColor(getColor(android.R.color.holo_green_dark))
} else {
- textViewServerStatus.text = "❌ Nicht erreichbar"
+ textViewServerStatus.text = getString(R.string.server_status_unreachable)
textViewServerStatus.setTextColor(getColor(android.R.color.holo_red_dark))
}
}
@@ -818,6 +822,12 @@ class SettingsActivity : AppCompatActivity() {
.show()
}
+ /**
+ * Note: REQUEST_IGNORE_BATTERY_OPTIMIZATIONS is acceptable for F-Droid builds.
+ * For Play Store builds, this would need to be changed to
+ * ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS (shows list, doesn't request directly).
+ */
+ @SuppressLint("BatteryLife")
private fun openBatteryOptimizationSettings() {
try {
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
@@ -947,6 +957,7 @@ class SettingsActivity : AppCompatActivity() {
}
// Info Text
+ @Suppress("SetTextI18n") // Programmatically generated dialog text
val infoText = android.widget.TextView(this).apply {
text = "Quelle: $sourceText\n\nWiederherstellungs-Modus:"
textSize = 16f
@@ -955,7 +966,7 @@ class SettingsActivity : AppCompatActivity() {
// Hinweis Text
val hintText = android.widget.TextView(this).apply {
- text = "\nℹ️ Ein Sicherheits-Backup wird vor dem Wiederherstellen automatisch erstellt."
+ text = getString(R.string.backup_restore_info)
textSize = 14f
setTypeface(null, android.graphics.Typeface.ITALIC)
setPadding(0, 20, 0, 0)
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/sync/SafeSardineWrapper.kt b/android/app/src/main/java/dev/dettmer/simplenotes/sync/SafeSardineWrapper.kt
index 8826943..d9e7305 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/sync/SafeSardineWrapper.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/sync/SafeSardineWrapper.kt
@@ -12,6 +12,8 @@ import okhttp3.RequestBody.Companion.toRequestBody
import java.io.Closeable
import java.io.InputStream
+private const val HTTP_METHOD_NOT_ALLOWED = 405
+
/**
* 🔧 v1.7.1: Wrapper für Sardine der Connection Leaks verhindert
* 🔧 v1.7.2 (IMPL_003): Implementiert Closeable für explizites Resource-Management
@@ -171,7 +173,7 @@ class SafeSardineWrapper private constructor(
.build()
okHttpClient.newCall(request).execute().use { response ->
- if (!response.isSuccessful && response.code != 405) { // 405 = already exists
+ if (!response.isSuccessful && response.code != HTTP_METHOD_NOT_ALLOWED) { // 405 = already exists
throw java.io.IOException("MKCOL failed: ${response.code} ${response.message}")
}
Logger.d(TAG, "createDirectory($url) → ${response.code}")
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/sync/WebDavSyncService.kt b/android/app/src/main/java/dev/dettmer/simplenotes/sync/WebDavSyncService.kt
index a4136cb..eab9726 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/sync/WebDavSyncService.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/sync/WebDavSyncService.kt
@@ -1164,8 +1164,14 @@ class WebDavSyncService(private val context: Context) {
return deletedCount
}
- @Suppress("NestedBlockDepth", "LoopWithTooManyJumpStatements")
+ @Suppress(
+ "NestedBlockDepth",
+ "LoopWithTooManyJumpStatements",
+ "LongMethod",
+ "ComplexMethod"
+ )
// Sync logic requires nested conditions for comprehensive error handling and conflict resolution
+ // TODO: Refactor into smaller functions in v1.9.0/v2.0.0 (see LINT_DETEKT_FEHLER_BEHEBUNG_PLAN.md)
private fun downloadRemoteNotes(
sardine: Sardine,
serverUrl: String,
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/DragDropListState.kt b/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/DragDropListState.kt
index 6804d1d..96be346 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/DragDropListState.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/DragDropListState.kt
@@ -121,6 +121,7 @@ class DragDropListState(
}
}
+ @Suppress("UnusedPrivateProperty")
private val LazyListItemInfo.offsetEnd: Int
get() = this.offset + this.size
}
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/NoteEditorScreen.kt b/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/NoteEditorScreen.kt
index 7f366e3..83c49d6 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/NoteEditorScreen.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/NoteEditorScreen.kt
@@ -69,6 +69,10 @@ import kotlinx.coroutines.delay
import dev.dettmer.simplenotes.utils.showToast
import kotlin.math.roundToInt
+private const val LAYOUT_DELAY_MS = 100L
+private const val ITEM_CORNER_RADIUS_DP = 8
+private const val DRAGGING_ITEM_Z_INDEX = 10f
+
/**
* Main Composable for the Note Editor screen.
*
@@ -108,7 +112,7 @@ fun NoteEditorScreen(
// v1.5.0: Auto-focus and show keyboard
LaunchedEffect(uiState.isNewNote, uiState.noteType) {
- delay(100) // Wait for layout
+ delay(LAYOUT_DELAY_MS) // Wait for layout
when {
uiState.isNewNote -> {
// New note: focus title
@@ -398,9 +402,12 @@ private fun ChecklistEditor(
onDelete = { onDelete(item.id) },
onAddNewItem = { onAddNewItemAfter(item.id) },
requestFocus = shouldFocus,
- isDragging = isDragging, // 🆕 v1.8.0: IMPL_023 - Drag state übergeben
- isAnyItemDragging = dragDropState.draggingItemIndex != null, // 🆕 v1.8.0: IMPL_023 - Gradient während Drag ausblenden
- dragModifier = Modifier.dragContainer(dragDropState, index), // 🆕 v1.8.0: IMPL_023 - Drag nur auf Handle
+ // 🆕 v1.8.0: IMPL_023 - Drag state übergeben
+ isDragging = isDragging,
+ // 🆕 v1.8.0: IMPL_023 - Gradient während Drag ausblenden
+ isAnyItemDragging = dragDropState.draggingItemIndex != null,
+ // 🆕 v1.8.0: IMPL_023 - Drag nur auf Handle
+ dragModifier = Modifier.dragContainer(dragDropState, index),
modifier = Modifier
.animateItem() // 🆕 v1.8.0 (IMPL_017): LazyColumn Item-Animation
.offset {
@@ -409,11 +416,12 @@ private fun ChecklistEditor(
if (isDragging) dragDropState.draggingItemOffset.roundToInt() else 0
)
}
- .zIndex(if (isDragging) 10f else 0f) // 🆕 v1.8.0: IMPL_023 - Gedraggtes Item liegt über anderen
- .shadow(elevation, shape = RoundedCornerShape(8.dp))
+ // 🆕 v1.8.0: IMPL_023 - Gedraggtes Item liegt über anderen
+ .zIndex(if (isDragging) DRAGGING_ITEM_Z_INDEX else 0f)
+ .shadow(elevation, shape = RoundedCornerShape(ITEM_CORNER_RADIUS_DP.dp))
.background(
color = MaterialTheme.colorScheme.surface,
- shape = RoundedCornerShape(8.dp)
+ shape = RoundedCornerShape(ITEM_CORNER_RADIUS_DP.dp)
)
)
}
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/components/ChecklistItemRow.kt b/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/components/ChecklistItemRow.kt
index 59eea25..8076448 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/components/ChecklistItemRow.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/ui/editor/components/ChecklistItemRow.kt
@@ -24,6 +24,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -54,7 +55,11 @@ import dev.dettmer.simplenotes.ui.editor.ChecklistItemState
* v1.5.0: Jetpack Compose NoteEditor Redesign
* v1.8.0: Long text UX improvements (gradient fade, auto-expand on focus)
* v1.8.0: IMPL_023 - Enlarged drag handle (48dp touch target) + drag modifier
+ *
+ * Note: Using 10 parameters for Composable is acceptable for complex UI components.
+ * @suppress LongParameterList - Composables naturally have many parameters
*/
+@Suppress("LongParameterList")
@Composable
fun ChecklistItemRow(
item: ChecklistItemState,
@@ -92,8 +97,12 @@ fun ChecklistItemRow(
// 🆕 v1.8.0: Dynamische Gradient-Sichtbarkeit basierend auf Scroll-Position
val showGradient = useScrollClipping && !isFocused && !isAnyItemDragging
- val showTopGradient = showGradient && scrollState.value > 0
- val showBottomGradient = showGradient && scrollState.value < scrollState.maxValue
+ val showTopGradient by remember {
+ derivedStateOf { showGradient && scrollState.value > 0 }
+ }
+ val showBottomGradient by remember {
+ derivedStateOf { showGradient && scrollState.value < scrollState.maxValue }
+ }
// v1.5.0: Auto-focus AND show keyboard when requestFocus is true (new items)
LaunchedEffect(requestFocus) {
@@ -283,6 +292,7 @@ private const val COLLAPSED_MAX_LINES = 5
// 🆕 v1.8.0: Preview Composables for Manual Testing
// ════════════════════════════════════════════════════════════════
+@Suppress("UnusedPrivateMember")
@Preview(showBackground = true)
@Composable
private fun ChecklistItemRowShortTextPreview() {
@@ -301,6 +311,7 @@ private fun ChecklistItemRowShortTextPreview() {
)
}
+@Suppress("UnusedPrivateMember")
@Preview(showBackground = true)
@Composable
private fun ChecklistItemRowLongTextPreview() {
@@ -324,6 +335,7 @@ private fun ChecklistItemRowLongTextPreview() {
)
}
+@Suppress("UnusedPrivateMember")
@Preview(showBackground = true)
@Composable
private fun ChecklistItemRowCheckedPreview() {
@@ -343,6 +355,7 @@ private fun ChecklistItemRowCheckedPreview() {
}
// 🆕 v1.8.0: IMPL_023 - Preview for dragging state
+@Suppress("UnusedPrivateMember")
@Preview(showBackground = true)
@Composable
private fun ChecklistItemRowDraggingPreview() {
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/ui/main/components/SyncStatusBanner.kt b/android/app/src/main/java/dev/dettmer/simplenotes/ui/main/components/SyncStatusBanner.kt
index 8eae250..a5ccc99 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/ui/main/components/SyncStatusBanner.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/ui/main/components/SyncStatusBanner.kt
@@ -5,12 +5,8 @@ import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/ui/settings/ComposeSettingsActivity.kt b/android/app/src/main/java/dev/dettmer/simplenotes/ui/settings/ComposeSettingsActivity.kt
index ec9ddbd..cb13ed2 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/ui/settings/ComposeSettingsActivity.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/ui/settings/ComposeSettingsActivity.kt
@@ -1,5 +1,6 @@
package dev.dettmer.simplenotes.ui.settings
+import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
@@ -149,7 +150,12 @@ class ComposeSettingsActivity : AppCompatActivity() {
/**
* Open system battery optimization settings
* v1.5.0: Ported from old SettingsActivity
+ *
+ * Note: REQUEST_IGNORE_BATTERY_OPTIMIZATIONS is acceptable for F-Droid builds.
+ * For Play Store builds, this would need to be changed to
+ * ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS (shows list, doesn't request directly).
*/
+ @SuppressLint("BatteryLife")
private fun openBatteryOptimizationSettings() {
try {
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/ui/settings/screens/SyncSettingsScreen.kt b/android/app/src/main/java/dev/dettmer/simplenotes/ui/settings/screens/SyncSettingsScreen.kt
index beea6a5..efc2167 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/ui/settings/screens/SyncSettingsScreen.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/ui/settings/screens/SyncSettingsScreen.kt
@@ -12,7 +12,6 @@ import androidx.compose.material.icons.filled.PhonelinkRing
import androidx.compose.material.icons.filled.Save
import androidx.compose.material.icons.filled.Schedule
import androidx.compose.material.icons.filled.SettingsInputAntenna
-import androidx.compose.material.icons.filled.Speed
import androidx.compose.material.icons.filled.Wifi
import androidx.compose.material3.Button
import androidx.compose.material3.Text
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetConfigActivity.kt b/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetConfigActivity.kt
index 7c357f7..33c790a 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetConfigActivity.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetConfigActivity.kt
@@ -6,7 +6,6 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.activity.compose.setContent
-import androidx.datastore.preferences.core.Preferences
import androidx.glance.appwidget.GlanceAppWidgetManager
import androidx.glance.appwidget.state.getAppWidgetState
import androidx.glance.appwidget.state.updateAppWidgetState
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetConfigScreen.kt b/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetConfigScreen.kt
index 8aee4a8..4447515 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetConfigScreen.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetConfigScreen.kt
@@ -53,6 +53,9 @@ import kotlin.math.roundToInt
*
* 🆕 v1.8.0 (IMPL_025): Save-FAB + onSettingsChanged für Reconfigure-Flow
*/
+
+private const val NOTE_PREVIEW_MAX_LENGTH = 50
+
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NoteWidgetConfigScreen(
@@ -63,7 +66,7 @@ fun NoteWidgetConfigScreen(
onNoteSelected: (noteId: String, isLocked: Boolean, opacity: Float) -> Unit,
onSave: ((noteId: String, isLocked: Boolean, opacity: Float) -> Unit)? = null,
onSettingsChanged: ((noteId: String?, isLocked: Boolean, opacity: Float) -> Unit)? = null,
- onCancel: () -> Unit
+ @Suppress("UNUSED_PARAMETER") onCancel: () -> Unit // Reserved for future use
) {
val allNotes = remember { storage.loadAllNotes().sortedByDescending { it.updatedAt } }
var lockWidget by remember { mutableStateOf(initialLock) }
@@ -248,7 +251,7 @@ private fun NoteSelectionCard(
)
Text(
text = when (note.noteType) {
- NoteType.TEXT -> note.content.take(50).replace("\n", " ")
+ NoteType.TEXT -> note.content.take(NOTE_PREVIEW_MAX_LENGTH).replace("\n", " ")
NoteType.CHECKLIST -> {
val items = note.checklistItems ?: emptyList()
val checked = items.count { it.isChecked }
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetContent.kt b/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetContent.kt
index a7bdcf5..8c9a017 100644
--- a/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetContent.kt
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetContent.kt
@@ -35,7 +35,6 @@ import androidx.glance.layout.height
import androidx.glance.layout.padding
import androidx.glance.layout.size
import androidx.glance.layout.width
-import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import dev.dettmer.simplenotes.R
@@ -52,20 +51,18 @@ import dev.dettmer.simplenotes.ui.editor.ComposeNoteEditorActivity
// ── Size Classification ──
-enum class WidgetSizeClass {
- SMALL, // Nur Titel
- NARROW_MED, // Schmal, Vorschau
- NARROW_TALL, // Schmal, voller Inhalt
- WIDE_MED, // Breit, Vorschau
- WIDE_TALL // Breit, voller Inhalt
-}
+private val WIDGET_HEIGHT_SMALL_THRESHOLD = 110.dp
+private val WIDGET_SIZE_MEDIUM_THRESHOLD = 250.dp
+
+private const val TEXT_PREVIEW_COMPACT_LENGTH = 100
+private const val TEXT_PREVIEW_FULL_LENGTH = 200
private fun DpSize.toSizeClass(): WidgetSizeClass = when {
- height < 110.dp -> WidgetSizeClass.SMALL
- width < 250.dp && height < 250.dp -> WidgetSizeClass.NARROW_MED
- width < 250.dp -> WidgetSizeClass.NARROW_TALL
- height < 250.dp -> WidgetSizeClass.WIDE_MED
- else -> WidgetSizeClass.WIDE_TALL
+ height < WIDGET_HEIGHT_SMALL_THRESHOLD -> WidgetSizeClass.SMALL
+ width < WIDGET_SIZE_MEDIUM_THRESHOLD && height < WIDGET_SIZE_MEDIUM_THRESHOLD -> WidgetSizeClass.NARROW_MED
+ width < WIDGET_SIZE_MEDIUM_THRESHOLD -> WidgetSizeClass.NARROW_TALL
+ height < WIDGET_SIZE_MEDIUM_THRESHOLD -> WidgetSizeClass.WIDE_MED
+ else -> WidgetSizeClass.WIDE_TALL
}
@Composable
@@ -316,7 +313,9 @@ private fun OptionsBar(
@Composable
private fun TextNotePreview(note: Note, compact: Boolean) {
Text(
- text = note.content.take(if (compact) 100 else 200),
+ text = note.content.take(
+ if (compact) TEXT_PREVIEW_COMPACT_LENGTH else TEXT_PREVIEW_FULL_LENGTH
+ ),
style = TextStyle(
color = GlanceTheme.colors.onSurface,
fontSize = if (compact) 13.sp else 14.sp
diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/widget/WidgetSizeClass.kt b/android/app/src/main/java/dev/dettmer/simplenotes/widget/WidgetSizeClass.kt
new file mode 100644
index 0000000..872040d
--- /dev/null
+++ b/android/app/src/main/java/dev/dettmer/simplenotes/widget/WidgetSizeClass.kt
@@ -0,0 +1,14 @@
+package dev.dettmer.simplenotes.widget
+
+/**
+ * 🆕 v1.8.0: Size classification for responsive Note Widget layouts
+ *
+ * Determines which layout variant to use based on widget dimensions.
+ */
+enum class WidgetSizeClass {
+ SMALL, // Nur Titel
+ NARROW_MED, // Schmal, Vorschau
+ NARROW_TALL, // Schmal, voller Inhalt
+ WIDE_MED, // Breit, Vorschau
+ WIDE_TALL // Breit, voller Inhalt
+}
diff --git a/android/app/src/main/res/layout/activity_settings.xml b/android/app/src/main/res/layout/activity_settings.xml
index 77ca364..3cc599e 100644
--- a/android/app/src/main/res/layout/activity_settings.xml
+++ b/android/app/src/main/res/layout/activity_settings.xml
@@ -56,7 +56,7 @@
@@ -72,7 +72,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:text="🏠 Intern (HTTP)"
+ android:text="@string/server_connection_http"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
android:checked="false" />
@@ -81,7 +81,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:text="🌐 Extern (HTTPS)"
+ android:text="@string/server_connection_https"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
android:checked="true" />
@@ -92,7 +92,7 @@
android:id="@+id/protocolHintText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="HTTP nur für lokale Netzwerke (z.B. 192.168.x.x, 10.x.x.x)"
+ android:text="@string/server_connection_http_hint"
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
android:textColor="?attr/colorOnSurfaceVariant"
android:layout_marginBottom="16dp"
@@ -104,12 +104,12 @@
android:id="@+id/textInputLayoutServerUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:hint="Server-Adresse"
+ android:hint="@string/server_address"
android:layout_marginBottom="12dp"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
app:startIconDrawable="@android:drawable/ic_menu_compass"
app:endIconMode="clear_text"
- app:helperText="z.B. http://192.168.0.188:8080/notes"
+ app:helperText="@string/server_address_hint"
app:helperTextEnabled="true"
app:boxCornerRadiusTopStart="12dp"
app:boxCornerRadiusTopEnd="12dp"
@@ -298,7 +298,7 @@
@@ -315,7 +315,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
- android:text="Legt fest, wie oft die App im Hintergrund synchronisiert. Kürzere Intervalle bedeuten aktuellere Daten, verbrauchen aber etwas mehr Akku.\n\n⏱️ Hinweis: Wenn dein Smartphone im Standby ist, kann Android die Synchronisation verzögern (bis zu 60 Min.), um Akku zu sparen. Das ist normal und betrifft alle Hintergrund-Apps."
+ android:text="@string/sync_interval_info"
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
android:lineSpacingMultiplier="1.3" />
@@ -333,14 +333,14 @@
android:id="@+id/radioInterval15"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="⚡ Alle 15 Minuten"
+ android:text="@string/sync_interval_15min_title"
android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
android:paddingVertical="8dp" />
@@ -405,7 +405,7 @@
@@ -422,7 +422,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
- android:text="ℹ️ Exportiert Notizen zusätzlich als .md Dateien. Mounte WebDAV als Netzlaufwerk um mit VS Code, Typora oder jedem Markdown-Editor zu bearbeiten. JSON-Sync bleibt primäres Format."
+ android:text="@string/markdown_info"
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
android:textColor="?attr/colorOnPrimaryContainer"
android:lineSpacingMultiplier="1.3" />
@@ -441,7 +441,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:text="🔄 Markdown Auto-Sync"
+ android:text="@string/markdown_auto_sync_title"
android:textAppearance="@style/TextAppearance.Material3.BodyLarge" />
@@ -468,7 +468,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
- android:text="Oder synchronisiere Markdown-Dateien manuell:"
+ android:text="@string/settings_markdown_manual_hint"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
android:textColor="?attr/colorOnSurface"
android:visibility="gone" />
@@ -478,7 +478,7 @@
android:id="@+id/buttonManualMarkdownSync"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Markdown synchronisieren"
+ android:text="@string/settings_markdown_manual_button"
android:visibility="gone"
style="@style/Widget.Material3.Button.TonalButton" />
@@ -521,7 +521,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
- android:text="ℹ️ Bei jeder Wiederherstellung wird automatisch ein Sicherheits-Backup erstellt."
+ android:text="@string/settings_backup_info"
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
android:textColor="?attr/colorOnPrimaryContainer"
android:lineSpacingMultiplier="1.3" />
@@ -532,7 +532,7 @@
@@ -541,7 +541,7 @@
android:id="@+id/buttonCreateBackup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="📥 Backup erstellen"
+ android:text="@string/backup_create"
android:layout_marginBottom="8dp"
style="@style/Widget.Material3.Button.TonalButton" />
@@ -550,7 +550,7 @@
android:id="@+id/buttonRestoreFromFile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="📤 Aus Datei wiederherstellen"
+ android:text="@string/backup_restore_file"
android:layout_marginBottom="16dp"
style="@style/Widget.Material3.Button.TonalButton" />
@@ -566,7 +566,7 @@
@@ -575,7 +575,7 @@
android:id="@+id/buttonRestoreFromServer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="🔄 Vom Server wiederherstellen"
+ android:text="@string/backup_restore_server"
style="@style/Widget.Material3.Button.TonalButton" />
@@ -600,7 +600,7 @@
@@ -622,7 +622,7 @@
@@ -631,7 +631,7 @@
android:id="@+id/textViewAppVersion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="Version wird geladen..."
+ android:text="@string/settings_about_app_version_loading"
android:textAppearance="@style/TextAppearance.Material3.BodyMedium" />
@@ -659,7 +659,7 @@
@@ -667,7 +667,7 @@
@@ -695,7 +695,7 @@
@@ -703,7 +703,7 @@
@@ -730,7 +730,7 @@
@@ -738,7 +738,7 @@
@@ -767,7 +767,7 @@
@@ -796,7 +796,7 @@
@@ -804,7 +804,7 @@
@@ -834,7 +834,7 @@
android:id="@+id/buttonExportLogs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="📤 Logs exportieren & teilen"
+ android:text="@string/settings_debug_export_logs"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_marginBottom="8dp" />
@@ -843,7 +843,7 @@
android:id="@+id/buttonClearLogs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="🗑️ Logs löschen"
+ android:text="@string/settings_debug_delete_logs"
style="@style/Widget.Material3.Button.OutlinedButton" />
diff --git a/android/app/src/main/res/values-de/strings.xml b/android/app/src/main/res/values-de/strings.xml
index d5f2823..c47d215 100644
--- a/android/app/src/main/res/values-de/strings.xml
+++ b/android/app/src/main/res/values-de/strings.xml
@@ -58,6 +58,13 @@
Synchronisierung fehlgeschlagen
Synchronisierung läuft bereits
+
+ Mit Server synchronisiert
+ Warte auf Synchronisierung
+ Synchronisierungskonflikt erkannt
+ Noch nicht synchronisiert
+ Auf Server gelöscht
+
Sync-Status Hilfe
Sync-Status Icons
@@ -209,12 +216,26 @@
Markdown Desktop-Integration
Auto-Sync: An
Auto-Sync: Aus
+ Oder synchronisiere Markdown-Dateien manuell:
+ Markdown synchronisieren
Backup & Wiederherstellung
Lokales oder Server-Backup
+ 📦 Bei jeder Wiederherstellung wird automatisch ein Sicherheits-Backup erstellt.
+ Lokales Backup
+ Server-Backup
Über diese App
+ 📱 App-Version
+ Version wird geladen…
+ 🌐 GitHub Repository
+ 👤 Entwickler
+ ⚖️ Lizenz
Debug & Diagnose
Logging: An
Logging: Aus
+ 📝 Datei-Logging
+ Sync-Logs in Datei speichern
+ 📤 Logs exportieren & teilen
+ 🗑️ Logs löschen
@@ -531,6 +552,17 @@
- %d erledigt
+
+
+
+ Parallele Downloads
+ parallel
+ Sequentiell (langsam, sicher)
+ Ausgewogen (3x schneller)
+ Empfohlen (5x schneller)
+ Schnell (7x schneller)
+ Maximum (10x schneller, kann Server belasten)
+
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index d7190f8..a099149 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -216,12 +216,26 @@
Markdown Desktop Integration
Auto-Sync: On
Auto-Sync: Off
+ Or sync markdown files manually:
+ Sync Markdown
Backup & Restore
Local or server backup
+ 📦 A safety backup is automatically created before each restore.
+ Local Backup
+ Server Backup
About this App
+ 📱 App Version
+ Loading version…
+ 🌐 GitHub Repository
+ 👤 Developer
+ ⚖️ License
Debug & Diagnostics
Logging: On
Logging: Off
+ 📝 File Logging
+ Save sync logs to file
+ 📤 Export & share logs
+ 🗑️ Delete logs
diff --git a/android/app/src/main/res/xml/network_security_config.xml b/android/app/src/main/res/xml/network_security_config.xml
index b69e5da..05a66a9 100644
--- a/android/app/src/main/res/xml/network_security_config.xml
+++ b/android/app/src/main/res/xml/network_security_config.xml
@@ -1,18 +1,31 @@
-
-
-
-
+ Android's Network Security Config doesn't support IP-based domain rules,
+ so we must allow cleartext globally but validate URLs in the app.
+ Public servers MUST use HTTPS. -->
+
-
-
+
+
+
+
+
+
+
+