Merge remote-tracking branch 'upstream/main' into android/debug-package-name

This commit is contained in:
Gannon
2024-01-12 21:27:14 -05:00
53 changed files with 1194 additions and 1133 deletions

View File

@ -19,8 +19,8 @@ android {
applicationId "app.omnivore.omnivore"
minSdk 26
targetSdk 33
versionCode 178
versionName "0.0.178"
versionCode 186
versionName "0.0.186"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {

File diff suppressed because one or more lines are too long

View File

@ -114,14 +114,15 @@ suspend fun DataService.mergeWebHighlights(jsonString: String) {
highlightPositionAnchorIndex = mergeHighlightInput.highlightPositionAnchorIndex.getOrNull() ?: 0
)
highlight.serverSyncStatus = ServerSyncStatus.NEEDS_CREATION.rawValue
highlight.serverSyncStatus = ServerSyncStatus.NEEDS_MERGE.rawValue
saveHighlightChange(db.highlightChangesDao(), mergeHighlightInput.articleId, highlight)
Log.d("sync", "overlapHighlightIdList: " + mergeHighlightInput.overlapHighlightIdList)
for (highlightID in mergeHighlightInput.overlapHighlightIdList) {
deleteHighlight(mergeHighlightInput.articleId, highlightID)
}
val highlightChange = saveHighlightChange(
db.highlightChangesDao(),
mergeHighlightInput.articleId,
highlight,
html = mergeHighlightInput.html.getOrNull(),
overlappingIDs = mergeHighlightInput.overlapHighlightIdList
)
val crossRef = SavedItemAndHighlightCrossRef(
highlightId = mergeHighlightInput.id,
@ -131,11 +132,8 @@ suspend fun DataService.mergeWebHighlights(jsonString: String) {
db.highlightDao().insertAll(listOf(highlight))
db.savedItemAndHighlightCrossRefDao().insertAll(listOf(crossRef))
val isUpdatedOnServer = networker.mergeHighlights(mergeHighlightInput)
if (isUpdatedOnServer) {
highlight.serverSyncStatus = ServerSyncStatus.IS_SYNCED.rawValue
db.highlightDao().update(highlight)
}
Log.d("sync", "Setting up highlight merge")
performHighlightChange(highlightChange)
}
}

View File

@ -1,7 +1,10 @@
package app.omnivore.omnivore.dataService
import android.util.Log
import androidx.room.PrimaryKey
import app.omnivore.omnivore.graphql.generated.type.CreateHighlightInput
import app.omnivore.omnivore.graphql.generated.type.HighlightType
import app.omnivore.omnivore.graphql.generated.type.MergeHighlightInput
import app.omnivore.omnivore.graphql.generated.type.UpdateHighlightInput
import app.omnivore.omnivore.models.ServerSyncStatus
import app.omnivore.omnivore.networking.*
@ -9,6 +12,7 @@ import app.omnivore.omnivore.persistence.entities.Highlight
import app.omnivore.omnivore.persistence.entities.HighlightChange
import app.omnivore.omnivore.persistence.entities.SavedItem
import app.omnivore.omnivore.persistence.entities.highlightChangeToHighlight
import app.omnivore.omnivore.persistence.entities.saveHighlightChange
import com.apollographql.apollo3.api.Optional
import kotlinx.coroutines.delay
import kotlin.math.log
@ -112,8 +116,6 @@ private suspend fun DataService.syncHighlightChange(highlightChange: HighlightCh
}
ServerSyncStatus.NEEDS_UPDATE.rawValue -> {
Log.d("sync", "creating highlight update change: ${highlightChange}")
updateSyncStatus(ServerSyncStatus.IS_SYNCING)
val isUpdatedOnServer = networker.updateHighlight(
@ -123,7 +125,6 @@ private suspend fun DataService.syncHighlightChange(highlightChange: HighlightCh
sharedAt = Optional.absent()
)
)
Log.d("sync", "sycn.updateHighlight result: ${isUpdatedOnServer}")
if (isUpdatedOnServer) {
updateSyncStatus(ServerSyncStatus.IS_SYNCED)
@ -134,21 +135,21 @@ private suspend fun DataService.syncHighlightChange(highlightChange: HighlightCh
}
ServerSyncStatus.NEEDS_CREATION.rawValue -> {
Log.d("sync", "creating highlight create change: ${highlightChange}")
updateSyncStatus(ServerSyncStatus.IS_SYNCING)
val createResult = networker.createHighlight(
CreateHighlightInput(
annotation = Optional.presentIfNotNull(highlight.annotation),
articleId = highlightChange.savedItemId,
id = highlight.highlightId,
patch = Optional.presentIfNotNull(highlight.patch),
quote = Optional.presentIfNotNull(highlight.quote),
shortId = highlight.shortId
)
val input = CreateHighlightInput(
id = highlight.highlightId,
shortId = highlight.shortId,
articleId = highlightChange.savedItemId,
type = Optional.presentIfNotNull(HighlightType.safeValueOf(highlight.type)),
annotation = Optional.presentIfNotNull(highlight.annotation),
patch = Optional.presentIfNotNull(highlight.patch),
quote = Optional.presentIfNotNull(highlight.quote),
)
Log.d("sync", "Creating highlight from input: ${input}")
val createResult = networker.createHighlight(
input
)
Log.d("sync", "sycn.createResult: " + createResult)
if (createResult.newHighlight != null || createResult.alreadyExists) {
updateSyncStatus(ServerSyncStatus.IS_SYNCED)
return true
@ -157,6 +158,58 @@ private suspend fun DataService.syncHighlightChange(highlightChange: HighlightCh
return false
}
}
ServerSyncStatus.NEEDS_MERGE.rawValue -> {
Log.d("sync", "NEEDS MERGE: ${highlightChange}")
val mergeHighlightInput = MergeHighlightInput(
id = highlight.highlightId,
shortId = highlight.shortId,
articleId = highlightChange.savedItemId,
annotation = Optional.presentIfNotNull(highlight.annotation),
color = Optional.presentIfNotNull(highlight.color),
highlightPositionAnchorIndex = Optional.presentIfNotNull(highlight.highlightPositionAnchorIndex),
highlightPositionPercent = Optional.presentIfNotNull(highlight.highlightPositionPercent),
html = Optional.presentIfNotNull(highlightChange.html),
overlapHighlightIdList = highlightChange.overlappingIDs ?: emptyList(),
patch = highlight.patch ?: "",
prefix = Optional.presentIfNotNull(highlight.prefix),
quote = highlight.quote ?: "",
suffix = Optional.presentIfNotNull(highlight.suffix)
)
val isUpdatedOnServer = networker.mergeHighlights(mergeHighlightInput)
if (!isUpdatedOnServer) {
Log.d("sync", "FAILED TO MERGE HIGHLIGHT")
highlight.serverSyncStatus = ServerSyncStatus.NEEDS_MERGE.rawValue
return false
}
for (highlightID in mergeHighlightInput.overlapHighlightIdList) {
Log.d("sync", "DELETING MERGED HIGHLIGHT: ${highlightID}")
val deleteChange = HighlightChange(
highlightId = highlightID,
savedItemId = highlightChange.savedItemId,
type = "",
shortId = "",
annotation = null,
createdAt = null,
patch = null,
prefix = null,
quote = null,
serverSyncStatus = ServerSyncStatus.NEEDS_DELETION.rawValue,
html = null,
suffix = null,
updatedAt = null,
color = null,
highlightPositionPercent = null,
highlightPositionAnchorIndex = null,
overlappingIDs = null
)
performHighlightChange(deleteChange)
}
return true
}
else -> return false
}
}

View File

@ -5,5 +5,6 @@ enum class ServerSyncStatus(val rawValue: Int) {
IS_SYNCING(1),
NEEDS_DELETION(2),
NEEDS_CREATION(3),
NEEDS_UPDATE(4)
NEEDS_UPDATE(4),
NEEDS_MERGE(5)
}

View File

@ -110,7 +110,6 @@ suspend fun Networker.updateWebHighlight(jsonString: String): Boolean {
suspend fun Networker.updateHighlight(input: UpdateHighlightInput): Boolean {
return try {
val result = authenticatedApolloClient().mutation(UpdateHighlightMutation(input)).execute()
Log.d("Network", "update highlight result: $result")
result.data?.updateHighlight?.onUpdateHighlightSuccess?.highlight != null
} catch (e: java.lang.Exception) {
false
@ -144,12 +143,8 @@ data class CreateHighlightResult(
)
suspend fun Networker.createHighlight(input: CreateHighlightInput): CreateHighlightResult {
Log.d("sync", "creating highlight with input: ${input}")
try {
val result = authenticatedApolloClient().mutation(CreateHighlightMutation(input)).execute()
Log.d("sync", "result: ${result.data}")
val createdHighlight = result.data?.createHighlight?.onCreateHighlightSuccess?.highlight
if (createdHighlight != null) {

View File

@ -14,7 +14,7 @@ import app.omnivore.omnivore.persistence.entities.*
SavedItemAndSavedItemLabelCrossRef::class,
SavedItemAndHighlightCrossRef::class
],
version = 20
version = 24
)
abstract class AppDatabase : RoomDatabase() {
abstract fun viewerDao(): ViewerDao

View File

@ -7,10 +7,16 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey
import androidx.room.Query
import androidx.room.TypeConverter
import androidx.room.TypeConverters
import app.omnivore.omnivore.models.ServerSyncStatus
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import kotlinx.serialization.json.Json
@Entity
@TypeConverters(StringListTypeConverter::class)
data class HighlightChange(
@PrimaryKey val highlightId: String,
val savedItemId: String,
@ -24,16 +30,45 @@ data class HighlightChange(
var prefix: String?,
var quote: String?,
var serverSyncStatus: Int = ServerSyncStatus.IS_SYNCED.rawValue,
val html: String?,
var shortId: String,
val suffix: String?,
val updatedAt: String?,
val color: String?,
val highlightPositionPercent: Double?,
val highlightPositionAnchorIndex: Int?
val highlightPositionAnchorIndex: Int?,
val overlappingIDs: List<String>?
)
fun saveHighlightChange(dao: HighlightChangesDao, savedItemId: String, highlight: Highlight): HighlightChange {
Log.d("sync", "saving highlight change: " + savedItemId + ", " + highlight)
class StringListTypeConverter {
@TypeConverter
fun listToString(data: List<String>?): String? {
data?.let {
return Gson().toJson(data)
}
return null
}
@TypeConverter
fun stringToList(jsonString: String?): List<String>? {
return if (jsonString.isNullOrEmpty()) {
null
} else {
val itemType = object : TypeToken<List<String>>() {}.type
return Gson().fromJson<List<String>>(jsonString, itemType)
}
}
}
fun saveHighlightChange(
dao: HighlightChangesDao,
savedItemId: String,
highlight: Highlight,
html: String? = null,
overlappingIDs: List<String>? = null): HighlightChange {
Log.d("sync", "saving highlight change: " + highlight.serverSyncStatus + ", " + highlight.type)
val change = HighlightChange(
savedItemId = savedItemId,
highlightId = highlight.highlightId,
@ -43,6 +78,7 @@ fun saveHighlightChange(dao: HighlightChangesDao, savedItemId: String, highlight
prefix = highlight.prefix,
suffix = highlight.suffix,
patch = highlight.patch,
html = html,
annotation = highlight.annotation,
createdAt = highlight.createdAt,
updatedAt = highlight.updatedAt,
@ -50,7 +86,8 @@ fun saveHighlightChange(dao: HighlightChangesDao, savedItemId: String, highlight
color =highlight.color,
highlightPositionPercent = highlight.highlightPositionPercent,
highlightPositionAnchorIndex = highlight.highlightPositionAnchorIndex,
serverSyncStatus = highlight.serverSyncStatus
serverSyncStatus = highlight.serverSyncStatus,
overlappingIDs = overlappingIDs
)
dao.insertAll(listOf(change))
return change

View File

@ -0,0 +1,39 @@
package app.omnivore.omnivore.ui.auth
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.AutofillNode
import androidx.compose.ui.autofill.AutofillType
import androidx.compose.ui.composed
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalAutofill
import androidx.compose.ui.platform.LocalAutofillTree
object AuthUtils {
@OptIn(ExperimentalComposeUiApi::class)
fun Modifier.autofill(
autofillTypes: List<AutofillType>,
onFill: ((String) -> Unit),
) = composed {
val autofill = LocalAutofill.current
val autofillNode = AutofillNode(onFill = onFill, autofillTypes = autofillTypes)
LocalAutofillTree.current += autofillNode
this
.onGloballyPositioned {
autofillNode.boundingBox = it.boundsInWindow()
}
.onFocusChanged { focusState ->
autofill?.run {
if (focusState.isFocused) {
requestAutofillForNode(autofillNode)
} else {
cancelAutofillForNode(autofillNode)
}
}
}
}
}

View File

@ -10,7 +10,9 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.AutofillType
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
@ -25,6 +27,7 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import app.omnivore.omnivore.BuildConfig
import app.omnivore.omnivore.R
import app.omnivore.omnivore.ui.auth.AuthUtils.autofill
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
@ -86,6 +89,7 @@ fun EmailLoginView(viewModel: LoginViewModel) {
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun LoginFields(
email: String,
@ -105,6 +109,12 @@ fun LoginFields(
horizontalAlignment = Alignment.CenterHorizontally
) {
OutlinedTextField(
modifier = Modifier.autofill(
autofillTypes = listOf(
AutofillType.EmailAddress,
),
onFill = { onEmailChange(it) }
),
value = email,
placeholder = { Text(stringResource(R.string.email_login_field_placeholder_email)) },
label = { Text(stringResource(R.string.email_login_field_label_email)) },
@ -112,11 +122,17 @@ fun LoginFields(
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Done,
keyboardType = KeyboardType.Email,
),
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
)
OutlinedTextField(
modifier = Modifier.autofill(
autofillTypes = listOf(
AutofillType.Password,
),
onFill = { onPasswordChange(it) }
),
value = password,
placeholder = { Text(stringResource(R.string.email_login_field_placeholder_password)) },
label = { Text(stringResource(R.string.email_login_field_label_password)) },
@ -129,21 +145,22 @@ fun LoginFields(
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
)
Button(onClick = {
if (email.isNotBlank() && password.isNotBlank()) {
onLoginClick()
focusManager.clearFocus()
} else {
Toast.makeText(
context,
context.getString(R.string.email_login_error_msg),
Toast.LENGTH_SHORT
).show()
}
}, colors = ButtonDefaults.buttonColors(
contentColor = Color(0xFF3D3D3D),
containerColor = Color(0xffffd234)
)
Button(
onClick = {
if (email.isNotBlank() && password.isNotBlank()) {
onLoginClick()
focusManager.clearFocus()
} else {
Toast.makeText(
context,
context.getString(R.string.email_login_error_msg),
Toast.LENGTH_SHORT
).show()
}
}, colors = ButtonDefaults.buttonColors(
contentColor = Color(0xFF3D3D3D),
containerColor = Color(0xffffd234)
)
) {
Text(
text = stringResource(R.string.email_login_action_login),

View File

@ -14,7 +14,9 @@ import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.AutofillType
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
@ -27,6 +29,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import app.omnivore.omnivore.R
import app.omnivore.omnivore.ui.auth.AuthUtils.autofill
@Composable
fun EmailSignUpView(viewModel: LoginViewModel) {
@ -140,6 +143,7 @@ fun EmailSignUpForm(viewModel: LoginViewModel) {
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun EmailSignUpFields(
email: String,
@ -165,6 +169,12 @@ fun EmailSignUpFields(
horizontalAlignment = Alignment.CenterHorizontally
) {
OutlinedTextField(
modifier = Modifier.autofill(
autofillTypes = listOf(
AutofillType.EmailAddress,
),
onFill = { onEmailChange(it) }
),
value = email,
placeholder = { Text(stringResource(R.string.email_signup_field_placeholder_email)) },
label = { Text(stringResource(R.string.email_signup_field_label_email)) },
@ -174,6 +184,12 @@ fun EmailSignUpFields(
)
OutlinedTextField(
modifier = Modifier.autofill(
autofillTypes = listOf(
AutofillType.Password,
),
onFill = { onPasswordChange(it) }
),
value = password,
placeholder = { Text(stringResource(R.string.email_signup_field_placeholder_password)) },
label = { Text(stringResource(R.string.email_signup_field_label_password)) },

View File

@ -310,7 +310,7 @@ fun LibraryViewContent(libraryViewModel: LibraryViewModel, modifier: Modifier) {
items = cardsData,
key = { item -> item.savedItem.savedItemId }
) { cardDataWithLabels ->
val swipeThreshold = 0.40f
val swipeThreshold = 0.45f
val currentThresholdFraction = remember { mutableStateOf(0f) }
val currentItem by rememberUpdatedState(cardDataWithLabels.savedItem)
@ -320,7 +320,7 @@ fun LibraryViewContent(libraryViewModel: LibraryViewModel, modifier: Modifier) {
currentThresholdFraction.value < swipeThreshold ||
currentThresholdFraction.value > 1.0f
) {
false
return@rememberDismissState false
}
if (it == DismissValue.DismissedToEnd) { // Archiving/UnArchiving.

View File

@ -61,6 +61,8 @@ data class WebReaderContent(
Log.d("theme", "current theme is: ${preferences.themeKey}")
Log.d("sync", "HIGHLIGHTS JSON: ${articleContent.highlightsJSONString()}")
return """
<!DOCTYPE html>
<html>

View File

@ -324,6 +324,7 @@ class WebReaderViewModel @Inject constructor(
// }
fun handleIncomingWebMessage(actionID: String, jsonString: String) {
Log.d("sync", "incoming change: ${actionID}: ${jsonString}")
when (actionID) {
"createHighlight" -> {
viewModelScope.launch {
@ -331,13 +332,11 @@ class WebReaderViewModel @Inject constructor(
}
}
"deleteHighlight" -> {
Log.d("Loggo", "receive delete highlight action: $jsonString")
viewModelScope.launch {
dataService.deleteHighlightFromJSON(jsonString)
}
}
"updateHighlight" -> {
Log.d("Loggo", "receive update highlight action: $jsonString")
viewModelScope.launch {
dataService.updateWebHighlight(jsonString)
}

View File

@ -0,0 +1,233 @@
<resources>
<string name="app_name">Omnivore</string>
<string name="welcome_title">Verpasse nie wieder eine großartige Lektüre</string>
<string name="learn_more">Mehr erfahren</string>
<string name="welcome_subtitle">Speichere Artikel und lies sie später in unserem ablenkungsfreien Reader.</string>
<string name="highlight_menu_action">Markieren</string>
<string name="copy_menu_action">Kopieren</string>
<string name="annotate_menu_action">Anmerken</string>
<string name="pdf_remove_highlight">Entfernen</string>
<string name="pdf_highlight_menu_action">Markieren</string>
<string name="pdf_highlight_copy">Kopieren</string>
<string name="highlight_note">Notiz</string>
<string name="copyTextSelection">Kopieren</string>
<string name="pdf_highlight_menu_note">Notiz</string>
<!-- Apple Auth -->
<string name="apple_auth_text">Mit Apple fortfahren</string>
<string name="apple_auth_loading">Anmeldung...</string>
<!-- Create User Profile -->
<string name="create_user_profile_title">Erstelle dein Profil</string>
<string name="create_user_profile_loading">Laden...</string>
<string name="create_user_profile_action_cancel">Anmeldung abbrechen</string>
<string name="create_user_profile_action_submit">Absenden</string>
<string name="create_user_profile_field_placeholder_name">Name</string>
<string name="create_user_profile_field_label_name">Name</string>
<string name="create_user_profile_field_placeholder_username">Benutzername</string>
<string name="create_user_profile_field_label_username">Benutzername</string>
<string name="create_user_profile_error_msg">Bitte gib einen gültigen Namen und Benutzernamen ein.</string>
<!-- Email Login -->
<string name="email_login_loading">Laden...</string>
<string name="email_login_action_back">Zurück zum Social Login</string>
<string name="email_login_action_no_account">Du hast noch kein Konto?</string>
<string name="email_login_action_forgot_password">Passwort vergessen?</string>
<string name="email_login_action_login">Anmelden</string>
<string name="email_login_field_placeholder_email">benutzer@email.com</string>
<string name="email_login_field_label_email">E-Mail</string>
<string name="email_login_field_placeholder_password">Passwort</string>
<string name="email_login_field_label_password">Passwort</string>
<string name="email_login_error_msg">Bitte gib eine E-Mail-Adresse und ein Passwort ein.</string>
<!-- Email Sign Up -->
<string name="email_signup_verification_message">Wir haben eine Verifizierungs-E-Mail an %1$s gesendet. Bitte bestätige deine E-Mail und tippe dann auf den unten stehenden Knopf.</string>
<string name="email_signup_check_status">Status prüfen</string>
<string name="email_signup_action_use_different_email">Eine andere E-Mail verwenden?</string>
<string name="email_signup_loading">Laden...</string>
<string name="email_signup_action_back">Zurück zu Social Login</string>
<string name="email_signup_action_already_have_account">Du hast bereits ein Konto?</string>
<string name="email_signup_action_sign_up">Registrieren</string>
<string name="email_signup_field_placeholder_email">benutzer@email.com</string>
<string name="email_signup_field_label_email">E-Mail</string>
<string name="email_signup_field_placeholder_password">Passwort</string>
<string name="email_signup_field_label_password">Passwort</string>
<string name="email_signup_field_placeholder_name">Name</string>
<string name="email_signup_field_label_name">Name</string>
<string name="email_signup_field_placeholder_username">Name</string>
<string name="email_signup_field_label_username">Name</string>
<string name="email_signup_error_msg">Bitte fülle alle Felder aus.</string>
<!-- Google Auth -->
<string name="google_auth_text">Mit Google fortfahren</string>
<string name="google_auth_loading">Anmeldung...</string>
<!-- LoginViewModel -->
<string name="login_view_model_self_hosting_settings_updated">Einstellungen für Self-Hosting aktualisiert.</string>
<string name="login_view_model_self_hosting_settings_reset">Einstellungen für Self-Hosting zurückgesetzt.</string>
<string name="login_view_model_username_validation_length_error_msg">Benutzername muss zwischen 4 und 15 Zeichen lang sein.</string>
<string name="login_view_model_username_validation_alphanumeric_error_msg">Benutzername darf nur Buchstaben und Zahlen enthalten.</string>
<string name="login_view_model_username_not_available_error_msg">Dieser Benutzername ist nicht verfügbar.</string>
<string name="login_view_model_connection_error_msg">Entschuldigung, wir haben Probleme, eine Verbindung zum Server herzustellen.</string>
<string name="login_view_model_something_went_wrong_error_msg">Etwas ist schiefgelaufen. Bitte überprüfe deine E-Mail und dein Passwort und versuche es erneut.</string>
<string name="login_view_model_something_went_wrong_two_error_msg">Etwas ist schiefgelaufen. Bitte überprüfe deine Anmeldeinformationen und versuche es erneut.</string>
<string name="login_view_model_google_auth_error_msg">Authentifizierung mit Google fehlgeschlagen.</string>
<string name="login_view_model_missing_auth_token_error_msg">Kein Authentifizierungstoken gefunden.</string>
<!-- SelfHostedView -->
<string name="self_hosted_view_loading">Laden...</string>
<string name="self_hosted_view_action_reset">Zurücksetzen</string>
<string name="self_hosted_view_action_back">Zurück</string>
<string name="self_hosted_view_action_save">Speichern</string>
<string name="self_hosted_view_action_learn_more">Mehr über Self-Hosting von Omnivore erfahren</string>
<string name="self_hosted_view_field_api_url_label">API-Server</string>
<string name="self_hosted_view_field_web_url_label">Webserver</string>
<string name="self_hosted_view_error_msg">Bitte gib die Adressen des API-Servers und des Webservers ein.</string>
<!-- WelcomeScreen -->
<string name="welcome_screen_action_dismiss">Schließen</string>
<string name="welcome_screen_action_continue_with_email">Mit E-Mail fortfahren</string>
<string name="welcome_screen_action_self_hosting_options">Self-Hosting Optionen</string>
<!-- LabelCreationDialog -->
<string name="label_creation_title">Neues Label erstellen</string>
<string name="label_creation_content">Weise einen Namen und eine Farbe zu.</string>
<string name="label_creation_action_create">Erstellen</string>
<string name="label_creation_action_cancel">Abbrechen</string>
<string name="label_creation_label_placeholder">Label-Name</string>
<!-- LabelSelectionSheet -->
<string name="label_selection_sheet_title">Nach Label filtern</string>
<string name="label_selection_sheet_title_alt">Labels setzen</string>
<string name="label_selection_sheet_action_cancel">Abbrechen</string>
<string name="label_selection_sheet_action_search">Suchen</string>
<string name="label_selection_sheet_action_save">Speichern</string>
<string name="label_selection_sheet_text_create">Erstelle ein neues Label mit dem Namen \"%1$s\"</string>
<string name="label_selection_sheet_label_too_long_error_msg">Der angegebene Name ist zu lang (muss %1$d Zeichen oder weniger sein)</string>
<!-- LibraryFilterBar -->
<string name="library_filter_bar_label_labels">Labels</string>
<!-- LibraryNavigationBar -->
<string name="library_nav_bar_title">Bibliothek</string>
<string name="library_nav_bar_title_alt"></string>
<string name="library_nav_bar_field_placeholder_search">Suchen</string>
<!-- LibraryViewModel -->
<string name="library_view_model_snackbar_success">Labels aktualisiert</string>
<string name="library_view_model_snackbar_error">Labels konnten nicht gesetzt werden</string>
<!-- NotebookView -->
<string name="notebook_view_title">Notizbuch</string>
<string name="notebook_view_action_copy">Kopieren</string>
<string name="notebook_view_snackbar_msg">Notizbuch kopiert</string>
<!-- EditNoteModal -->
<string name="edit_note_modal_title">Notiz</string>
<string name="edit_note_modal_action_save">Speichern</string>
<string name="edit_note_modal_action_cancel">Abbrechen</string>
<!-- ArticleNotes -->
<string name="article_notes_title">Artikelnotizen</string>
<string name="article_notes_action_add_notes">Notizen hinzufügen...</string>
<!-- HighlightsList -->
<string name="highlights_list_title">Hervorhebungen</string>
<string name="highlights_list_action_copy">Kopieren</string>
<string name="highlights_list_snackbar_msg">Hervorhebung kopiert</string>
<string name="highlights_list_action_add_note">Notiz hinzufügen...</string>
<string name="highlights_list_error_msg_no_highlights">Du hast dieser Seite keine Hervorhebungen hinzugefügt.</string>
<!-- ReaderPreferencesView -->
<string name="reader_preferences_view_font_size">Schriftgröße:</string>
<string name="reader_preferences_view_margin">Rand</string>
<string name="reader_preferences_view_line_spacing">Zeilenabstand</string>
<string name="reader_preferences_view_theme">Thema:</string>
<string name="reader_preferences_view_auto">Automatisch</string>
<string name="reader_preferences_view_high_constrast_text">Hoher Textkontrast</string>
<string name="reader_preferences_view_justify_text">Text ausrichten</string>
<!-- WebReaderLoadingContainer -->
<string name="web_reader_loading_container_error_msg">Wir konnten deinen Inhalt nicht abrufen.</string>
<string name="web_reader_loading_container_bottom_sheet_reader_preferences">Lese-Einstellungen</string>
<string name="web_reader_loading_container_bottom_sheet_notebook">Notizbuch</string>
<string name="web_reader_loading_container_bottom_sheet_edit_info">Info bearbeiten</string>
<string name="web_reader_loading_container_bottom_sheet_e">Notizbuch</string>
<string name="web_reader_loading_container_bottom_sheet_open_link">Link öffnen</string>
<!-- OpenLinkView -->
<string name="open_link_view_action_open_in_browser">Im Browser öffnen</string>
<string name="open_link_view_action_save_to_omnivore">In Omnivore speichern</string>
<string name="open_link_view_action_copy_link">Link kopieren</string>
<string name="open_link_view_action_cancel">Abbrechen</string>
<!-- WebReaderViewModel -->
<string name="web_reader_view_model_save_link_success">Link gespeichert</string>
<string name="web_reader_view_model_save_link_error">Fehler beim Speichern des Links</string>
<string name="web_reader_view_model_copy_link_success">Link kopiert</string>
<!-- SaveContent -->
<string name="save_content_msg">Speichern</string>
<string name="save_content_action_read_now">Jetzt lesen</string>
<string name="save_content_action_read_later">Später lesen</string>
<string name="save_content_action_dismiss">Schließen</string>
<!-- SaveViewModel -->
<string name="save_view_model_msg">Speichern in Omnivore...</string>
<string name="save_view_model_error_not_logged_in">Du bist nicht angemeldet. Bitte melde dich an, bevor du speicherst.</string>
<string name="save_view_model_page_saved_success">Seite gespeichert</string>
<string name="save_view_model_page_saved_error">Fehler beim Speichern deiner Seite</string>
<!-- SavedItemContextMenu -->
<string name="saved_item_context_menu_action_edit_info">Info bearbeiten</string>
<string name="saved_item_context_menu_action_edit_labels">Labels bearbeiten</string>
<string name="saved_item_context_menu_action_archive">Archivieren</string>
<string name="saved_item_context_menu_action_unarchive">Aus dem Archiv wiederherstellen</string>
<string name="saved_item_context_menu_action_share_original">Original teilen</string>
<string name="saved_item_context_menu_action_remove_item">Element entfernen</string>
<!-- LogoutDialog -->
<string name="logout_dialog_title">Abmelden</string>
<string name="logout_dialog_confirm_msg">Bist du sicher, dass du dich abmelden möchtest?</string>
<string name="logout_dialog_action_confirm">Bestätigen</string>
<string name="logout_dialog_action_cancel">Abbrechen</string>
<!-- ManageAccount -->
<string name="manage_account_title">Konto verwalten</string>
<string name="manage_account_action_reset_data_cache">Cache zurücksetzen</string>
<!-- PolicyWebView -->
<string name="policy_webview_title">Einstellungen</string>
<!-- SettingsView -->
<string name="settings_view_title">Einstellungen</string>
<string name="settings_view_setting_row_documentation">Dokumentation</string>
<string name="settings_view_setting_row_feedback">Feedback</string>
<string name="settings_view_setting_row_privacy_policy">Datenschutzerklärung</string>
<string name="settings_view_setting_row_terms_and_conditions">Nutzungsbedingungen</string>
<string name="settings_view_setting_row_manage_account">Konto verwalten</string>
<string name="settings_view_setting_row_logout">Abmelden</string>
<!-- AddLinkSheet -->
<string name="add_link_sheet_title">Link hinzufügen</string>
<string name="add_link_sheet_text_field_placeholder">Link hinzufügen</string>
<string name="add_link_sheet_action_add_link">Hinzufügen</string>
<string name="add_link_sheet_action_cancel">Abbrechen</string>
<string name="add_link_sheet_action_paste_from_clipboard">Aus Zwischenablage holen</string>
<string name="add_link_sheet_invalid_url_error">Ungültiger Link</string>
<string name="add_link_sheet_save_url_error">Fehler beim Speichern des Links!</string>
<string name="add_link_sheet_save_url_success">Link erfolgreich gespeichert!</string>
<!-- EditInfoViewModel -->
<string name="edit_info_view_model_error_not_logged_in">Du bist nicht angemeldet. Bitte melde dich an, bevor du speicherst.</string>
<!-- EditInfoSheet -->
<string name="edit_info_sheet_title">Info bearbeiten</string>
<string name="edit_info_sheet_text_field_label_title">Titel</string>
<string name="edit_info_sheet_text_field_label_author">Autor</string>
<string name="edit_info_sheet_text_field_label_description">Beschreibung</string>
<string name="edit_info_sheet_action_save">Speichern</string>
<string name="edit_info_sheet_action_cancel">Abbrechen</string>
<string name="edit_info_sheet_error">Fehler beim Bearbeiten des Artikels!</string>
<string name="edit_info_sheet_success">Artikelinformationen erfolgreich aktualisiert!</string>
</resources>

View File

@ -54,8 +54,8 @@
<string name="email_signup_field_label_password">Password</string>
<string name="email_signup_field_placeholder_name">Name</string>
<string name="email_signup_field_label_name">Name</string>
<string name="email_signup_field_placeholder_username">Name</string>
<string name="email_signup_field_label_username">Name</string>
<string name="email_signup_field_placeholder_username">Username</string>
<string name="email_signup_field_label_username">Username</string>
<string name="email_signup_error_msg">Please complete all fields.</string>
<!-- Google Auth -->