Compare commits
15 Commits
5f8e13f06f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7131087cb | ||
| dfb0a694de | |||
| 79970b5c65 | |||
|
|
5b31067e43 | ||
|
|
c69ff049a0 | ||
|
|
81220d53d1 | ||
| b3091a5aa3 | |||
|
|
b1bde2fab5 | ||
| abc381ca24 | |||
|
|
1fbd91fe82 | ||
| c2087de639 | |||
|
|
c47653c9cb | ||
| e639e250b7 | |||
|
|
58a50512bf | ||
|
|
1add1fcadb |
@@ -7,7 +7,14 @@ All notable changes to this project are documented here. The format follows
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.4.0] - 2026-08-31
|
||||
|
||||
### Added
|
||||
- Agendula now speaks **German** and **Brazilian Portuguese**, the first
|
||||
community translations. Pick one under **Settings → App language**, or in
|
||||
Android's own per-app language settings; leave it on *System default* and
|
||||
Agendula follows your phone. Anything not yet translated falls back to
|
||||
English.
|
||||
- Agendula can now be translated. Pick or request a language on Weblate and
|
||||
translate in the browser — the link sits at the top of the language picker in
|
||||
**Settings → App language**. Partial translations are fine; anything
|
||||
|
||||
@@ -29,8 +29,8 @@ android {
|
||||
// release itself (versionCode is pinned to MAJOR*10000 + MINOR*100 +
|
||||
// PATCH from versionName, e.g. 0.2.0 -> 200). The Gitea release is marked
|
||||
// as a pre-release while MAJOR is 0. See docs/RELEASING.md.
|
||||
versionCode = 302
|
||||
versionName = "0.3.2"
|
||||
versionCode = 400
|
||||
versionName = "0.4.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
190
app/src/main/res/values-de/strings.xml
Normal file
190
app/src/main/res/values-de/strings.xml
Normal file
@@ -0,0 +1,190 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="back">Zurück</string>
|
||||
<string name="save">Speichern</string>
|
||||
<string name="delete">Löschen</string>
|
||||
<string name="edit">Bearbeiten</string>
|
||||
<string name="field_description">Beschreibung</string>
|
||||
<string name="task_deleted">Gelöscht</string>
|
||||
<string name="app_name">Agendula</string>
|
||||
<string name="task_untitled">Unbenannte Aufgabe</string>
|
||||
<string name="tasks_title">Aufgaben</string>
|
||||
<string name="task_list_empty">Keine Aufgaben bisher.</string>
|
||||
<string name="task_list_failure">Aufgaben konnten nicht geladen werden.</string>
|
||||
<string name="smart_no_date">Kein Datum</string>
|
||||
<string name="smart_completed">Abgeschlossen</string>
|
||||
<string name="task_detail_title">Aufgabe</string>
|
||||
<string name="task_detail_not_found">Diese Aufgabe ist nicht mehr verfügbar.</string>
|
||||
<string name="edit_task_title">Aufgabe bearbeiten</string>
|
||||
<string name="new_task_title">Neue Aufgabe</string>
|
||||
<string name="field_title">Titel</string>
|
||||
<string name="edit_save_failed">Konnte nicht gespeichert werden. Versuche es erneut.</string>
|
||||
<string name="add_task_hint">Aufgabe hinzufügen</string>
|
||||
<string name="add_subtask_hint">Unteraufgabe hinzufügen</string>
|
||||
<string name="cd_add_task">Aufgabe hinzufügen</string>
|
||||
<string name="cd_complete">Abgeschlossen</string>
|
||||
<string name="cd_reopen">Erneut öffnen</string>
|
||||
<string name="undo">Rückgängig</string>
|
||||
<string name="section_today">Heute</string>
|
||||
<string name="cd_expand_subtasks">Unteraufgaben anzeigen</string>
|
||||
<string name="cd_collapse_subtasks">Unteraufgaben ausblenden</string>
|
||||
<string name="section_overdue">Überfällig</string>
|
||||
<string name="section_upcoming">Anstehend</string>
|
||||
<string name="section_no_date">Kein Datum</string>
|
||||
<string name="section_completed">Abgeschlossen</string>
|
||||
<string name="priority_none">Keine</string>
|
||||
<string name="priority_low">Niedrig</string>
|
||||
<string name="priority_medium">Mittel</string>
|
||||
<string name="priority_high">Hoch</string>
|
||||
<string name="detail_mark_complete">Als abgeschlossen markieren</string>
|
||||
<string name="detail_mark_incomplete">Als nicht abgeschlossen markieren</string>
|
||||
<string name="detail_list">Liste</string>
|
||||
<string name="detail_due">Fällig</string>
|
||||
<string name="detail_start">Beginn</string>
|
||||
<string name="detail_priority">Priorität</string>
|
||||
<string name="detail_progress">Fortschritt</string>
|
||||
<string name="detail_subtasks">Unteraufgaben</string>
|
||||
<string name="detail_parent">Hauptaufgabe</string>
|
||||
<string name="detail_part_of">Teil von</string>
|
||||
<string name="edit_list_label">Liste</string>
|
||||
<string name="edit_start_label">Beginn</string>
|
||||
<string name="edit_due_label">Fällig</string>
|
||||
<string name="edit_all_day">Ganzer Tag</string>
|
||||
<string name="edit_priority_label">Priorität</string>
|
||||
<string name="edit_progress_label">Fortschritt</string>
|
||||
<string name="edit_parent_label">Hauptaufgabe</string>
|
||||
<string name="edit_parent_none">Keine (Hauptebene)</string>
|
||||
<string name="edit_parent_search">Aufgaben suchen</string>
|
||||
<string name="edit_parent_empty">Keine passenden Aufgaben in der Liste gefunden.</string>
|
||||
<string name="edit_reminder_label">Erinnerungen</string>
|
||||
<string name="edit_set">Übernehmen</string>
|
||||
<string name="edit_clear">Leeren</string>
|
||||
<string name="edit_title_hint">Aufgaben-Titel</string>
|
||||
<string name="edit_when">Wann</string>
|
||||
<string name="edit_more_fields">Weitere Felder</string>
|
||||
<string name="close">Schließen</string>
|
||||
<string name="dialog_cancel">Abbrechen</string>
|
||||
<string name="edit_conflict_title">Termin wurde extern geändert</string>
|
||||
<string name="edit_conflict_body">Die Aufgabe wurde seit der Öffnung wo anders geändert (über Sync oder einer anderen App). Möchtest die Änderungen überschreiben?</string>
|
||||
<string name="edit_conflict_overwrite">Überschreiben</string>
|
||||
<string name="reminder_none">Keine</string>
|
||||
<string name="reminder_at_due">Zum Start der Aufgabe</string>
|
||||
<string name="reminder_5_min">5 Minuten vorher</string>
|
||||
<string name="reminder_10_min">10 Minuten vorher</string>
|
||||
<string name="reminder_30_min">30 Minuten vorher</string>
|
||||
<string name="reminder_1_hour">1 Stunde vorher</string>
|
||||
<string name="reminder_1_day">1 Tag vorher</string>
|
||||
<string name="reminder_custom">Benutzerdefiniert</string>
|
||||
<string name="reminder_custom_with_value">Benutzerdefiniert (%1$s)</string>
|
||||
<string name="reminder_custom_set">Übernehmen</string>
|
||||
<string name="reminder_custom_amount">Anzahl eingeben</string>
|
||||
<string name="reminder_use_default">Standard verwenden</string>
|
||||
<string name="reminder_unit_minutes">Minuten</string>
|
||||
<string name="reminder_unit_hours">Stunden</string>
|
||||
<string name="reminder_unit_days">Tage</string>
|
||||
<string name="reminder_unit_weeks">Woche</string>
|
||||
<string name="settings_list_reminders_title">Erinnerungen (Pro Liste)</string>
|
||||
<string name="settings_list_reminders_hint">Überschreibe die Standard-Erinnerungen für individuelle Listen</string>
|
||||
<string name="settings_list_reminder_inherits">Standard (%1$s)</string>
|
||||
<string name="error_blank_title">Gebe einen Titel ein.</string>
|
||||
<string name="error_no_list">Wähle eine Liste.</string>
|
||||
<string name="error_due_before_start">Fälligkeit darf nicht vor dem Start liegen.</string>
|
||||
<string name="error_reminder_without_due">Setze ein Fälligkeits-Datum für Erinnerungen.</string>
|
||||
<string name="lists_header">Listen</string>
|
||||
<string name="new_task">Neue Aufgabe</string>
|
||||
<string name="lists_failure">Deine Aufgaben konnten nicht gelesen werden.</string>
|
||||
<string name="lists_empty">Keine Aufgaben-Listen bisher. Füge eine in deiner Aufgaben-App hinzu oder mit der + Schaltfläche.</string>
|
||||
<string name="smart_today">Heute</string>
|
||||
<string name="smart_overdue">Überfällig</string>
|
||||
<string name="smart_upcoming">Anstehend</string>
|
||||
<string name="smart_all">Alle</string>
|
||||
<string name="open_count">%1$d offen</string>
|
||||
<string name="home_today_progress">%1$d von %2$d erledigt</string>
|
||||
<string name="home_today_remaining">%1$d übrig</string>
|
||||
<string name="home_today_all_done">Alles erledigt 🎉</string>
|
||||
<string name="home_today_empty">Heute keine Fälligkeiten 🎉</string>
|
||||
<string name="home_upcoming_view_all">Alle anzeigen</string>
|
||||
<string name="home_due_today">Heute</string>
|
||||
<string name="home_due_tomorrow">Morgen</string>
|
||||
<string name="home_search_hint">Aufgaben suchen</string>
|
||||
<string name="home_search_clear">Suche zurücksetzen</string>
|
||||
<string name="home_search_close">Suche schließen</string>
|
||||
<string name="home_search_empty">Keine Aufgaben passen zu „%1$s“</string>
|
||||
<string name="reminder_due_at">Fälligkeiten %1$s</string>
|
||||
<string name="reminder_channel_name">Aufgaben Erinnerungen</string>
|
||||
<string name="reminder_channel_desc">Benachrichtigungen für fällige Aufgaben.</string>
|
||||
<string name="onboarding_no_provider_title">Eine Task-App wird benötigt</string>
|
||||
<string name="onboarding_no_provider_body">Agendula zeigt und bearbeitet die Aufgaben, die in OpenTasks oder tasks.org gespeichert sind und per DAVx5 synchronisiert werden. Installiere eine davon, um zu starten.</string>
|
||||
<string name="onboarding_permission_title">Erlaube Zugriff auf deine Aufgaben</string>
|
||||
<string name="onboarding_permission_body">Agendula benötigt die Berechtigung, deine Aufgaben zu lesen und zu schreiben. Das ist die einzige Berechtigung, die es jemals anfragt.</string>
|
||||
<string name="onboarding_permission_button">Zugriff gewähren</string>
|
||||
<string name="onboarding_install_opentasks">Installiere OpenTasks</string>
|
||||
<string name="onboarding_install_tasksorg">Installiere tasks.org</string>
|
||||
<string name="reminder_onboarding_title">Verpasse nichts, was fällig ist</string>
|
||||
<string name="reminder_onboarding_body">Aufgaben-Apps senden selbst keine Erinnerungen, daher liefert Agendula sie für dich. Schalte sie ein, um eine Benachrichtigung zu erhalten, wenn eine Aufgabe fällig ist.</string>
|
||||
<string name="reminder_onboarding_enable_button">Erinnerungen aktivieren</string>
|
||||
<string name="reminder_onboarding_skip_button">Nicht jetzt</string>
|
||||
<string name="reminder_benefit_delivery_title">Agendula erinnert dich</string>
|
||||
<string name="reminder_benefit_delivery_body">Es plant eine Benachrichtigung für jede Aufgabe mit einem Fälligkeitsdatum.</string>
|
||||
<string name="reminder_benefit_timing_title">Auf deinem Zeitplan</string>
|
||||
<string name="reminder_benefit_timing_body">Wähle in den Einstellungen, wie weit im Voraus du erinnert werden möchtest.</string>
|
||||
<string name="reminder_benefit_reversible_title">Jederzeit änderbar</string>
|
||||
<string name="reminder_benefit_reversible_body">Schalte Erinnerungen aus, wann immer du willst — es ist nur ein Schalter.</string>
|
||||
<string name="settings_title">Einstellungen</string>
|
||||
<string name="settings_section_appearance">Darstellung</string>
|
||||
<string name="settings_appearance_subtitle">Theme und Farbe</string>
|
||||
<string name="settings_section_task_form">Aufgaben-Formular</string>
|
||||
<string name="settings_task_form_subtitle">Standard Felder, Liste und Unter-Aufgaben</string>
|
||||
<string name="settings_reminders_subtitle">Benachrichtigungen für fällige Aufgaben</string>
|
||||
<string name="settings_form_fields_hint">Standardmäßig angezeigte Felder — alles Weitere liegt hinter „Weitere Felder“.</string>
|
||||
<string name="settings_dynamic_color_unavailable">Verfügbar für Android 12 und höher</string>
|
||||
<string name="settings_about_author">von Jean-Luc Makiola</string>
|
||||
<string name="settings_about_source">Quellcode</string>
|
||||
<string name="settings_license">Lizenz</string>
|
||||
<string name="settings_about_support">Entwicklung unterstützen</string>
|
||||
<string name="settings_about_version">Version %1$s</string>
|
||||
<string name="settings_about_logo_desc">Calendula-App-Symbol</string>
|
||||
<string name="settings_language">App-Sprache</string>
|
||||
<string name="settings_language_auto">Systemstandard</string>
|
||||
<string name="settings_report_problem">Problem melden</string>
|
||||
<string name="settings_report_problem_hint">Öffne den Issue-Tracker</string>
|
||||
<string name="crash_report_issue_title">Absturzbericht</string>
|
||||
<string name="settings_theme">Design</string>
|
||||
<string name="settings_theme_system">System folgen</string>
|
||||
<string name="settings_theme_light">Hell</string>
|
||||
<string name="settings_theme_dark">Dunkel</string>
|
||||
<string name="settings_dynamic_color">Dynamische Farben</string>
|
||||
<string name="settings_dynamic_color_hint">Verwende Farben aus dem Hintergrundbild</string>
|
||||
<string name="settings_section_reminders">Erinnerungen</string>
|
||||
<string name="settings_reminders">Fälligkeits-Erinnerungen</string>
|
||||
<string name="settings_reminders_hint">Benachrichtige mich wenn eine Aufgabe fällig ist</string>
|
||||
<string name="settings_default_reminder">Wann erinnern</string>
|
||||
<string name="settings_exact_alarms">Exakter Zeitpunkt</string>
|
||||
<string name="settings_exact_alarms_allowed">Die Erinnerungen erfolgen zum exakten Zeitpunkt</string>
|
||||
<string name="settings_exact_alarms_blocked">Blockiert – tippe, um exakte Erinnerungen zu erlauben</string>
|
||||
<string name="settings_section_tasks">Aufgaben</string>
|
||||
<string name="settings_default_list">Standard-Liste</string>
|
||||
<string name="settings_default_list_first">Erste verfügbare Felder</string>
|
||||
<string name="settings_add_subtask_row">Zeile \"Unteraufgaben hinzufügen\" anzeigen</string>
|
||||
<string name="settings_add_subtask_row_hint">Ein Eingabefeld am Ende der Unteraufgaben einer aufgeklappten Aufgabe</string>
|
||||
<string name="settings_bottom_add_bar">Untere Schnellhinzufüge-Leiste</string>
|
||||
<string name="settings_bottom_add_bar_hint">Füge Aufgaben hinzu, über eine Leiste, die unten an einer Liste angepinnt ist, statt über die schwebende Schaltfläche</string>
|
||||
<plurals name="reminder_minutes">
|
||||
<item quantity="one">%1$d Minute vorher</item>
|
||||
<item quantity="other">%1$d Minuten vorher</item>
|
||||
</plurals>
|
||||
<plurals name="reminder_hours">
|
||||
<item quantity="one">%1$d Stunde vorher</item>
|
||||
<item quantity="other">%1$d Stunden vorher</item>
|
||||
</plurals>
|
||||
<plurals name="reminder_days">
|
||||
<item quantity="one">%1$d Tag vorher</item>
|
||||
<item quantity="other">%1$d Tage vorher</item>
|
||||
</plurals>
|
||||
<plurals name="reminder_weeks">
|
||||
<item quantity="one">%1$d Woche vorher</item>
|
||||
<item quantity="other">%1$d Wochen vorher</item>
|
||||
</plurals>
|
||||
<string name="settings_translate">Hilf beim Übersetzen</string>
|
||||
<string name="settings_translate_hint">Füge eine Sprache auf Weblate hinzu oder verbessere sie</string>
|
||||
</resources>
|
||||
196
app/src/main/res/values-pt-rBR/strings.xml
Normal file
196
app/src/main/res/values-pt-rBR/strings.xml
Normal file
@@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Agendula</string>
|
||||
<string name="app_tagline">Um app de tarefas em Material 3 Expressive. \nFlorescendo.</string>
|
||||
<string name="task_untitled">Tarefa sem título</string>
|
||||
<string name="back">Voltar</string>
|
||||
<string name="save">Salvar</string>
|
||||
<string name="delete">Deletar</string>
|
||||
<string name="tasks_title">Tarefas</string>
|
||||
<string name="task_list_empty">Nenhuma tarefa aqui ainda.</string>
|
||||
<string name="task_list_failure">Não foi possível carregar estas tarefas.</string>
|
||||
<string name="smart_no_date">Sem data</string>
|
||||
<string name="smart_completed">Completo</string>
|
||||
<string name="task_detail_title">Tarefa</string>
|
||||
<string name="task_detail_not_found">Esta tarefa não existe mais.</string>
|
||||
<string name="edit">Editar</string>
|
||||
<string name="edit_task_title">Editar tarefa</string>
|
||||
<string name="new_task_title">Nova tarefa</string>
|
||||
<string name="field_title">Título</string>
|
||||
<string name="field_description">Descrição</string>
|
||||
<string name="edit_save_failed">Não foi possível salvar. Tente novamente.</string>
|
||||
<string name="add_task_hint">Adicionar tarefa</string>
|
||||
<string name="add_subtask_hint">Adicionar subtarefa</string>
|
||||
<string name="cd_add_task">Adicionar tarefa</string>
|
||||
<string name="cd_complete">Concluído</string>
|
||||
<string name="cd_reopen">Reabrir</string>
|
||||
<string name="task_deleted">Deletado</string>
|
||||
<string name="undo">Desfazer</string>
|
||||
<string name="cd_expand_subtasks">Mostrar subtarefas</string>
|
||||
<string name="cd_collapse_subtasks">Esconder subtarefas</string>
|
||||
<string name="section_overdue">Prazo expirado</string>
|
||||
<string name="section_today">Hoje</string>
|
||||
<string name="section_upcoming">A seguir</string>
|
||||
<string name="section_no_date">Sem data</string>
|
||||
<string name="section_completed">Concluído</string>
|
||||
<string name="priority_none">Nenhuma</string>
|
||||
<string name="priority_low">Baixa</string>
|
||||
<string name="priority_medium">Média</string>
|
||||
<string name="priority_high">Alta</string>
|
||||
<string name="detail_mark_complete">Marcar como concluído</string>
|
||||
<string name="detail_mark_incomplete">Marcar como não concluído</string>
|
||||
<string name="detail_list">Lista</string>
|
||||
<string name="detail_due">Prazo</string>
|
||||
<string name="detail_start">Inicia</string>
|
||||
<string name="detail_priority">Prioridade</string>
|
||||
<string name="detail_progress">Progresso</string>
|
||||
<string name="detail_subtasks">Subtarefas</string>
|
||||
<string name="detail_parent">Tarefa pai</string>
|
||||
<string name="detail_part_of">Parte de</string>
|
||||
<string name="percent_complete">%1$d%%</string>
|
||||
<string name="edit_list_label">Lista</string>
|
||||
<string name="edit_start_label">Inicia</string>
|
||||
<string name="edit_due_label">Prazo</string>
|
||||
<string name="edit_all_day">O dia todo</string>
|
||||
<string name="edit_priority_label">Prioridade</string>
|
||||
<string name="edit_progress_label">Progresso</string>
|
||||
<string name="edit_parent_label">Tarefa pai</string>
|
||||
<string name="edit_parent_none">Nenhum (primeiro nível)</string>
|
||||
<string name="edit_parent_search">Buscar tarefas</string>
|
||||
<string name="edit_parent_empty">Nenhuma tarefa correspondente nesta lista.</string>
|
||||
<string name="edit_reminder_label">Lembrete</string>
|
||||
<string name="edit_set">Ajustar</string>
|
||||
<string name="edit_clear">Limpar</string>
|
||||
<string name="edit_title_hint">Título da tarefa</string>
|
||||
<string name="edit_when">Quando</string>
|
||||
<string name="edit_more_fields">Mais campos</string>
|
||||
<string name="close">Fechar</string>
|
||||
<string name="dialog_cancel">Cancelar</string>
|
||||
<string name="edit_conflict_title">A tarefa foi alterada em outro lugar</string>
|
||||
<string name="edit_conflict_body">Esta tarefa foi atualizada em outro local (uma sincronização ou outro app) desde que você a abriu. Sobrescrever estas mudanças com suas edições?</string>
|
||||
<string name="edit_conflict_overwrite">Sobrescrever</string>
|
||||
<string name="reminder_none">Nenhum</string>
|
||||
<string name="reminder_at_due">Na hora da tarefa</string>
|
||||
<string name="reminder_5_min">5 minutos antes</string>
|
||||
<string name="reminder_10_min">10 minutos antes</string>
|
||||
<string name="reminder_30_min">30 minutos antes</string>
|
||||
<string name="reminder_1_hour">1 hora antes</string>
|
||||
<string name="reminder_1_day">1 dia antes</string>
|
||||
<string name="reminder_custom">Personalizado</string>
|
||||
<string name="reminder_custom_with_value">Personalizado (%1$s)</string>
|
||||
<string name="reminder_custom_set">Ajustar</string>
|
||||
<string name="reminder_custom_amount">Insira uma quantidade</string>
|
||||
<string name="reminder_use_default">Usar padrão</string>
|
||||
<string name="reminder_unit_minutes">Minutos</string>
|
||||
<string name="reminder_unit_hours">Horas</string>
|
||||
<string name="reminder_unit_days">Dias</string>
|
||||
<string name="reminder_unit_weeks">Semanas</string>
|
||||
<string name="settings_list_reminders_title">Lembretes por lista</string>
|
||||
<string name="settings_list_reminders_hint">Alterar o lembrete padrão para listas individuais</string>
|
||||
<string name="settings_list_reminder_inherits">Padrão (%1$s)</string>
|
||||
<string name="error_blank_title">Insira um título.</string>
|
||||
<string name="error_no_list">Escolha uma lista.</string>
|
||||
<string name="error_due_before_start">Prazo não pode ser antes do início.</string>
|
||||
<string name="error_reminder_without_due">Ajuste um prazo para usar um lembrete.</string>
|
||||
<string name="lists_header">Listas</string>
|
||||
<string name="new_task">Nova tarefa</string>
|
||||
<string name="lists_failure">Não foi possível ler suas tarefas.</string>
|
||||
<string name="lists_empty">Nenhuma lista de tarefas ainda. Adicione uma no seu app de tarefas ou com o botão +.</string>
|
||||
<string name="smart_today">Hoje</string>
|
||||
<string name="smart_overdue">Prazo expirado</string>
|
||||
<string name="smart_upcoming">A seguir</string>
|
||||
<string name="smart_all">Tudo</string>
|
||||
<string name="open_count">%1$d abertos</string>
|
||||
<string name="home_today_progress">%1$d de %2$d concluídos</string>
|
||||
<string name="home_today_remaining">%1$d restantes</string>
|
||||
<string name="home_today_all_done">Tudo pronto 🎉</string>
|
||||
<string name="home_today_empty">Nada terminando hoje 🎉</string>
|
||||
<string name="home_upcoming_view_all">Ver tudo</string>
|
||||
<string name="home_due_today">Hoje</string>
|
||||
<string name="home_due_tomorrow">Amanhã</string>
|
||||
<string name="home_search_hint">Buscar tarefas</string>
|
||||
<string name="home_search_clear">Limpar buscas</string>
|
||||
<string name="home_search_close">Fechar busca</string>
|
||||
<string name="home_search_empty">Nenhuma tarefa corresponde a \"%1$s\"</string>
|
||||
<string name="reminder_due_at">Termina em %1$s</string>
|
||||
<string name="reminder_channel_name">Lembretes de tarefas</string>
|
||||
<string name="reminder_channel_desc">Notificações para tarefas com prazo.</string>
|
||||
<string name="onboarding_no_provider_title">Um app de tarefas é necessário</string>
|
||||
<string name="onboarding_no_provider_body">O Agendula mostra e edita as tarefas armazenadas no OpenTasks ou tasks.org, sincronizadas pelo DAVx5. Instale um para começar.</string>
|
||||
<string name="onboarding_permission_title">Permitir acesso às suas tarefas</string>
|
||||
<string name="onboarding_permission_body">O Agendula precisa de permissão para ler e salvar suas tarefas. É tudo o que ele pede.</string>
|
||||
<string name="onboarding_permission_button">Conceder acesso às tarefas</string>
|
||||
<string name="onboarding_install_opentasks">Instalar OpenTasks</string>
|
||||
<string name="onboarding_install_tasksorg">Instalar tasks.org</string>
|
||||
<string name="reminder_onboarding_title">Nunca perca seus prazos</string>
|
||||
<string name="reminder_onboarding_body">Apps de tarefas não enviam lembretes sozinhos, então o Agendula os entrega para você. Ative para receber uma notificação de quando uma tarefa estiver no prazo.</string>
|
||||
<string name="reminder_onboarding_enable_button">Ativar lembretes</string>
|
||||
<string name="reminder_onboarding_skip_button">Agora não</string>
|
||||
<string name="reminder_benefit_delivery_title">O Agendula lembra você</string>
|
||||
<string name="reminder_benefit_delivery_body">Ele agenda uma notificação para cada tarefa com prazo.</string>
|
||||
<string name="reminder_benefit_timing_title">Na sua rotina</string>
|
||||
<string name="reminder_benefit_timing_body">Escolha o quão à frente quer ser lembrado em Ajustes.</string>
|
||||
<string name="reminder_benefit_reversible_title">Altere a qualquer momento</string>
|
||||
<string name="reminder_benefit_reversible_body">Desative os lembretes quando quiser — é só um botão.</string>
|
||||
<string name="settings_title">Ajustes</string>
|
||||
<string name="settings_section_appearance">Aparência</string>
|
||||
<string name="settings_appearance_subtitle">Tema e cor</string>
|
||||
<string name="settings_section_task_form">Formulário de tarefa</string>
|
||||
<string name="settings_task_form_subtitle">Campos, listas e subtarefas padrão</string>
|
||||
<string name="settings_reminders_subtitle">Notificações para tarefas com prazo acabando</string>
|
||||
<string name="settings_form_fields_hint">Campos mostrados por padrão em uma nova tarefa. O resto está atrás de \"Mais campos\".</string>
|
||||
<string name="settings_dynamic_color_unavailable">Disponível no Android 12 e mais recente</string>
|
||||
<string name="settings_about_author">por Jean-Luc Makiola</string>
|
||||
<string name="settings_about_source">Código-fonte</string>
|
||||
<string name="settings_license">Licença</string>
|
||||
<string name="settings_about_support">Apoie o desenvolvimento</string>
|
||||
<string name="settings_about_version">Versão %1$s</string>
|
||||
<string name="settings_about_logo_desc">Ícone de app do Agendula</string>
|
||||
<string name="settings_language">Idioma do app</string>
|
||||
<string name="settings_language_auto">Padrão do sistema</string>
|
||||
<string name="settings_translate">Ajude a traduzir</string>
|
||||
<string name="settings_translate_hint">Adicione ou melhore um idioma no Weblate</string>
|
||||
<string name="settings_report_problem">Relate um problema</string>
|
||||
<string name="settings_report_problem_hint">Abrir o rastreador de problemas</string>
|
||||
<string name="crash_report_issue_title">Relatório de quebra</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_theme_system">Seguir o sistema</string>
|
||||
<string name="settings_theme_light">Claro</string>
|
||||
<string name="settings_theme_dark">Escuro</string>
|
||||
<string name="settings_dynamic_color">Cor dinâmica</string>
|
||||
<string name="settings_dynamic_color_hint">Usar cores do papel de parede</string>
|
||||
<string name="settings_section_reminders">Lembretes</string>
|
||||
<string name="settings_reminders">Lembretes no prazo</string>
|
||||
<string name="settings_reminders_hint">Me notifique quando uma tarefa estiver no prazo</string>
|
||||
<string name="settings_default_reminder">Quando lembrar</string>
|
||||
<string name="settings_exact_alarms">Na hora</string>
|
||||
<string name="settings_exact_alarms_allowed">Lembretes disparam na mesma hora</string>
|
||||
<string name="settings_exact_alarms_blocked">Bloqueado — toque para permitir lembretes na hora</string>
|
||||
<string name="settings_section_tasks">Tarefas</string>
|
||||
<string name="settings_default_list">Lista padrão</string>
|
||||
<string name="settings_default_list_first">Primeira lista disponível</string>
|
||||
<string name="settings_add_subtask_row">Mostrar uma linha \"adicionar subtarefa\"</string>
|
||||
<string name="settings_add_subtask_row_hint">Um campo Adicionar no fim das subtarefas de uma tarefa expandida</string>
|
||||
<string name="settings_bottom_add_bar">Barra adicionar-rápido abaixo</string>
|
||||
<string name="settings_bottom_add_bar_hint">Adicione tarefas de uma barra presa abaixo de uma lista, em vez do botão flutuante</string>
|
||||
<plurals name="reminder_minutes">
|
||||
<item quantity="one">%1$d minuto antes</item>
|
||||
<item quantity="many">%1$d minutos antes</item>
|
||||
<item quantity="other">%1$d minutos antes</item>
|
||||
</plurals>
|
||||
<plurals name="reminder_hours">
|
||||
<item quantity="one">%1$d hora antes</item>
|
||||
<item quantity="many">%1$d horas antes</item>
|
||||
<item quantity="other">%1$d horas antes</item>
|
||||
</plurals>
|
||||
<plurals name="reminder_days">
|
||||
<item quantity="one">%1$d dia antes</item>
|
||||
<item quantity="many">%1$d dias antes</item>
|
||||
<item quantity="other">%1$d dias antes</item>
|
||||
</plurals>
|
||||
<plurals name="reminder_weeks">
|
||||
<item quantity="one">%1$d semana antes</item>
|
||||
<item quantity="many">%1$d semanas antes</item>
|
||||
<item quantity="other">%1$d semanas antes</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -8,4 +8,6 @@
|
||||
-->
|
||||
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<locale android:name="en" />
|
||||
<locale android:name="de" />
|
||||
<locale android:name="pt-BR" />
|
||||
</locale-config>
|
||||
|
||||
357
docs/STORAGE-AND-SYNC.md
Normal file
357
docs/STORAGE-AND-SYNC.md
Normal file
@@ -0,0 +1,357 @@
|
||||
# Agendula — storage and sync
|
||||
|
||||
> Decided direction, captured 2026-08-01. Supersedes the earlier "Posture B =
|
||||
> bundle OpenTasks" working notes, which are withdrawn (see
|
||||
> [Dead ends](#dead-ends--do-not-revisit)). This is the detailed companion to
|
||||
> `ARCHITECTURE.md` §7 and the `ProviderResolver` comments, **and it redefines
|
||||
> what Posture B means** — those two need a follow-up edit.
|
||||
> `ROADMAP.md` / `PLAN.md` remain known-stale and are due a deliberate pass;
|
||||
> this document does not attempt it.
|
||||
|
||||
## The plan, in short
|
||||
|
||||
**What we're building**
|
||||
|
||||
1. **Our own provider.** Vendor the Apache-2.0 dmfs task provider in-tree as
|
||||
`:provider`, renamed to our own authority and permission namespace. Our
|
||||
database, our namespace — coexists with everything, replaces nothing.
|
||||
2. **Our own sync.** An Agendula sync adapter, so remote storage never depends
|
||||
on another app's roadmap.
|
||||
3. **The user picks the mode.** Local-only · Synced · External provider.
|
||||
4. **Least permission.** Ask only for what the chosen mode needs, when it needs
|
||||
it.
|
||||
5. **Kit-first.** Anything that isn't task-domain goes to floret-kit.
|
||||
|
||||
**In what order**
|
||||
|
||||
| # | Step | Why now |
|
||||
|---|---|---|
|
||||
| 1 | Merge `fix/provider-interaction-review` | unmerged and rotting; touches the same permission flow as step 2 |
|
||||
| 2 | Vendor `:provider` under our own authority | the identity, done once — and it ships a complete local-first app |
|
||||
| 3 | Export / backup | our data now lives only in our app's private storage |
|
||||
| 4 | File the DAVx5 issue | cheap, non-blocking, serves F-Droid users |
|
||||
| 5 | Sync adapter | the 1.x arc; design discussion pending |
|
||||
|
||||
Everything below is the reasoning behind those choices, the alternatives that
|
||||
were rejected, and the constraints they have to survive.
|
||||
|
||||
---
|
||||
|
||||
## The decision
|
||||
|
||||
Agendula gets its **own identity all the way down** — its own task database
|
||||
under its own authority, its own sync, and a storage mode the user picks. It
|
||||
does not adopt, replace, or impersonate another project's provider.
|
||||
|
||||
Four parts:
|
||||
|
||||
1. **Own DB, bundled in-process.** Vendor the Apache-2.0 dmfs
|
||||
`opentasks-provider` as an in-tree `:provider` Gradle module, renamed to
|
||||
authority `de.jeanlucmakiola.agendula.tasks` with permissions
|
||||
`de.jeanlucmakiola.agendula.permission.READ_TASKS` / `…WRITE_TASKS`. Not a
|
||||
separate provider *app*; not a schema written from scratch. We keep the dmfs
|
||||
`TaskContract` shape — it's proven, it's what our whole data layer already
|
||||
speaks, and it's what every CalDAV engine already understands — we just own
|
||||
the namespace it lives in.
|
||||
2. **Own sync adapter**, so remote storage never depends on another app's
|
||||
roadmap. Protocol coverage is deliberately open — separate discussion.
|
||||
3. **The user chooses the backend**: local-only, synced, or an external provider
|
||||
that's already on the device.
|
||||
4. **Ask for only what the chosen mode actually needs**, at the moment it needs
|
||||
it.
|
||||
|
||||
Plus a standing rule: **anything that isn't task-domain goes to floret-kit.**
|
||||
|
||||
### The vocabulary, redefined
|
||||
|
||||
`ARCHITECTURE.md` §7 and `ProviderResolver`'s KDoc still describe Posture B as
|
||||
"bundle OpenTasks and find `org.dmfs.tasks` first." Replace with:
|
||||
|
||||
- **Posture A** — front-end over an *external* provider (OpenTasks, tasks.org).
|
||||
Still fully supported; it stops being the default and becomes a **user
|
||||
choice**.
|
||||
- **Posture B** — our own bundled provider under **our own** authority.
|
||||
Coexists with everything; replaces nothing.
|
||||
|
||||
The A/B seam itself is unchanged and still earns its keep: `ProviderResolver` is
|
||||
the only thing that knows an authority, `AndroidTasksDataSource` the only thing
|
||||
that touches a resolver. UI, ViewModels, domain and repository are untouched by
|
||||
all of this.
|
||||
|
||||
---
|
||||
|
||||
## Why not squat `org.dmfs.tasks`
|
||||
|
||||
The rejected plan was to bundle the provider under dmfs's own authority so
|
||||
DAVx5 would sync into it unwittingly. Reasons it's out, in order of how badly
|
||||
each one bites:
|
||||
|
||||
1. **It is a structural identity mismatch, and the resulting bug is invisible to
|
||||
both sides.** Content-provider *authorities* are how a sync engine finds a
|
||||
provider, but Android **account visibility is keyed by package name**: since
|
||||
API 26 an app only sees accounts whose authenticator has made them visible to
|
||||
*its package*, and `GET_ACCOUNTS` alone no longer suffices. A sync engine's
|
||||
allowlist would name the package `org.dmfs.tasks`, not
|
||||
`de.jeanlucmakiola.agendula`. So the bundled provider could find **zero**
|
||||
accounts — and the dmfs provider uses account enumeration to prune task lists
|
||||
whose account has gone away. The failure mode isn't "no sync", it's "our
|
||||
provider quietly purges synced lists." *(Reasoned from the platform rules,
|
||||
not from having read DAVx5's source — but the class of bug is structural, and
|
||||
every future place anything keys on package rather than authority is a fresh
|
||||
instance of it.)* An explicit integration under our own name makes this bug
|
||||
impossible by construction.
|
||||
2. **Play Store install-time landmine.** Two apps cannot declare the same
|
||||
authority (`INSTALL_FAILED_CONFLICTING_PROVIDER`) or the same `<permission>`
|
||||
name (`INSTALL_FAILED_DUPLICATE_PERMISSION`, waived only for identical
|
||||
signing certs). Anyone with OpenTasks installed gets a failed install,
|
||||
surfacing as one-star reviews we can't usefully answer.
|
||||
3. **Migration data loss.** "Uninstall OpenTasks first" takes its DB with it.
|
||||
CalDAV-synced tasks reconcile back; local-only tasks are simply gone, and
|
||||
OpenTasks has no export (dmfs/opentasks #170, #204, #71 — years-old,
|
||||
unimplemented; their wiki punts to a desktop client).
|
||||
4. **Squatting another project's namespace doesn't scale.** At low install
|
||||
counts nobody notices. At scale we'd be generating issues on dmfs's tracker
|
||||
that aren't dmfs's fault, and silently maintaining a schema fork under their
|
||||
name.
|
||||
|
||||
---
|
||||
|
||||
## The `:provider` module
|
||||
|
||||
**Source.** dmfs `opentasks-provider`, Apache-2.0. Target the **1.4.2** source
|
||||
(DB version 23 — the version that actually carries `is_recurring`; tasks.org's
|
||||
fork is DB 22 and lacks it, which is why `TaskMapper.task` reads `rrule`/`rdate`
|
||||
for recurrence detection rather than trusting the column).
|
||||
|
||||
**Layout: in-tree module, not a git submodule.** floret-kit is a submodule
|
||||
because we co-develop it. This is a fork we will sync from upstream
|
||||
approximately never, so in-tree is simpler for both stores and honest about what
|
||||
it is. Ship a `provider/PROVENANCE.md`: upstream commit, and every change we
|
||||
made.
|
||||
|
||||
**License hygiene, on day one.** The app is MIT, the provider is Apache-2.0 —
|
||||
permissive into permissive, fine — but the module keeps its Apache-2.0 headers,
|
||||
`LICENSE`, and `NOTICE`. Ten minutes now; embarrassing to retrofit once it's in
|
||||
two store listings.
|
||||
|
||||
**What we change:**
|
||||
|
||||
- Authority → `de.jeanlucmakiola.agendula.tasks` (it's already a string
|
||||
resource, `opentasks_authority`).
|
||||
- Permission names → `de.jeanlucmakiola.agendula.permission.*`. These are
|
||||
**hardcoded in the AAR manifest**, which is the single clearest reason
|
||||
vendoring is mandatory rather than merely preferable — you cannot rename them
|
||||
in a prebuilt artifact without `tools:` node surgery we'd rather not ship.
|
||||
- Drop `<uses-permission android:name="android.permission.GET_ACCOUNTS" />`.
|
||||
We own our own accounts, so we don't need it — but note the provider's
|
||||
account-cleanup path is written *assuming* it, so this is a review-and-rework
|
||||
item, not a free deletion. **Verify** the provider's local-list/local-account
|
||||
path works with no account present at all; that's the entire local-only mode.
|
||||
- Drop the exported `BOOT_COMPLETED` / `TIME_SET` / `TIMEZONE_CHANGED` receiver,
|
||||
or keep it deliberately and give it an explicit `android:exported`. The AAR is
|
||||
from the `targetSdk 29` era; AGP hard-errors on a merged manifest with an
|
||||
intent-filtered component and no explicit `exported` once targetSdk ≥ 31, and
|
||||
we're on 36. Fixed at source instead of patched around.
|
||||
- Modernize the build: it ships `minSdk 21` / `targetSdk 29`, Robolectric 3.5.1,
|
||||
JUnit 4.12. We're `minSdk 29` / `targetSdk 36` and Play raises its target-API
|
||||
floor annually, so this isn't optional upkeep.
|
||||
|
||||
**Our data now lives in our app's private storage.** Uninstall means deletion.
|
||||
That single fact is what promotes export/backup from "nice to have" to a v1
|
||||
feature — see [Storage modes](#storage-modes--the-users-choice).
|
||||
|
||||
---
|
||||
|
||||
## Storage modes — the user's choice
|
||||
|
||||
| Mode | Backing store | Sync | Needs |
|
||||
|---|---|---|---|
|
||||
| **Local** | our bundled provider | none | no permissions at all — same-uid provider access needs no grant |
|
||||
| **Synced** | our bundled provider | our sync adapter | network + an account the user configures |
|
||||
| **External** | OpenTasks / tasks.org | whatever that provider's engine does (DAVx5 …) | that provider's `READ`/`WRITE_TASKS`, granted at runtime |
|
||||
|
||||
Local and Synced are the same store — Synced is Local with an account attached,
|
||||
so switching on sync is not a migration.
|
||||
|
||||
**Resolver ordering needs deciding.** Today `ProviderResolver.CANDIDATES` is a
|
||||
fixed priority list and the first hit wins. Once we bundle our own provider,
|
||||
"first hit" is the wrong rule: someone who used Agendula locally and *later*
|
||||
installs DAVx5 + OpenTasks would see an external candidate outrank the provider
|
||||
that actually holds their data. Options: rank ours first whenever it's
|
||||
non-empty, or make the mode an explicit Settings choice (it's user-visible
|
||||
either way, so probably both — auto-pick a sane default, let Settings override).
|
||||
|
||||
**Export/backup is a v1 feature.** Not, as previously framed, a migration safety
|
||||
net for "uninstall OpenTasks" — that scenario no longer exists. It's data
|
||||
portability for Local-mode users, whose tasks otherwise exist in exactly one
|
||||
place with no second copy. On Play, where most users won't have a sync engine,
|
||||
that's the majority.
|
||||
|
||||
---
|
||||
|
||||
## Sync — own adapter
|
||||
|
||||
**Decided:** Agendula ships its own sync. Not because DAVx5 is bad, but because
|
||||
depending on it makes one external maintainer's roadmap the gate on our core
|
||||
feature — the same shape of dependency the whole identity decision exists to
|
||||
escape.
|
||||
|
||||
The two precedents diverge and the choice between them is the whole point:
|
||||
tasks.org has its own authority **and its own sync** (sovereign); jtx Board has
|
||||
its own authority and **depends on DAVx5** (and got added, though a working
|
||||
relationship with bitfire is part of that story). We're taking the tasks.org
|
||||
shape.
|
||||
|
||||
**Play sharpens this.** DAVx5 is a paid app on Google Play and free only on
|
||||
F-Droid — *worth confirming, since it's load-bearing* — which means most Play
|
||||
users will never have it. Lobbying bitfire is therefore an **F-Droid-audience
|
||||
feature, not a sync strategy**.
|
||||
|
||||
**Still file the DAVx5 issue** — cheap, non-blocking, real value for F-Droid
|
||||
users. And make it the strongest possible version of the ask: our provider *is*
|
||||
the dmfs provider with renamed strings, so it's byte-identical contract
|
||||
compliance and a small enum-shaped addition with near-zero ongoing maintenance
|
||||
for them. Say that explicitly. "Here's a change that can't break anything" lands
|
||||
very differently from "please support my app."
|
||||
|
||||
**Open — the next discussion.** Protocol coverage ("support as much as
|
||||
possible"), the account model, conflict resolution, and where the DAV/iCalendar
|
||||
work lives. One constraint to settle early: we're MIT; `dav4jvm` is Apache-2.0
|
||||
and fine, but **verify `ical4android`'s license** before assuming it's usable.
|
||||
|
||||
---
|
||||
|
||||
## Permissions — only what the mode needs
|
||||
|
||||
The manifest is static, so "request only what we need" is really two different
|
||||
mechanisms, and conflating them is how apps end up over-permissioned:
|
||||
|
||||
- **Runtime (dangerous) permissions** — genuinely stageable. Ask at the moment
|
||||
the feature is used, never up front.
|
||||
- **Install-time (normal) permissions** — declared unconditionally; the only
|
||||
lever is **not declaring them until the feature ships**, and not letting a
|
||||
bundled dependency drag in ones we don't use.
|
||||
|
||||
| Permission | When |
|
||||
|---|---|
|
||||
| *(none)* for our own provider | same-uid access needs no grant — `ProviderStatus.NEEDS_PERMISSION` must never fire in Local/Synced mode |
|
||||
| `org.dmfs.permission.*`, `org.tasks.permission.*` | requested **only** when the user selects External mode; declared always (static manifest) |
|
||||
| `POST_NOTIFICATIONS` | when reminders are first enabled |
|
||||
| `USE_EXACT_ALARM` / `SCHEDULE_EXACT_ALARM` | when exact due-time reminders are used. Note Play reviews `USE_EXACT_ALARM` and requires the app to be a calendar/alarm/task app — we qualify, but it needs a justification in the listing |
|
||||
| `INTERNET` | **don't declare it until sync ships** |
|
||||
| `GET_ACCOUNTS` | never — stripped from the vendored provider; our own account type doesn't need it to see its own accounts |
|
||||
|
||||
**Work item:** the permission gate in `RootScreen` / `PermissionViewModel` /
|
||||
`ProviderResolver.hasPermission` currently assumes an external provider always
|
||||
needs a grant. It needs a bypass for our own provider. Modest, but it's the
|
||||
exact flow `fix/provider-interaction-review` just touched — merge that first.
|
||||
|
||||
---
|
||||
|
||||
## What lands in floret-kit
|
||||
|
||||
Standing rule, matching the kit's own thesis (*share the mechanics, keep the
|
||||
look — the kit never knows about a specific app's domain*): if it isn't
|
||||
task-domain, it goes to the kit.
|
||||
|
||||
| Candidate | Kit module | Note |
|
||||
|---|---|---|
|
||||
| ContentProvider seam — `ColumnReader`, failures, observer→Flow | `core-provider` | **already on the kit's deferred list**, blocked on migrating Calendula to the name-based reader. Bundling our own provider is the forcing function that makes this worth doing. |
|
||||
| Runtime-permission staging — request/state machine, rationale plumbing, "ask at point of use" | new, e.g. `core-permissions` | pure mechanics, and Calendula has the identical problem |
|
||||
| DAV client + iCalendar parse/serialize | new, e.g. `core-dav` | **the big one.** Calendula is a calendar app; it needs the same primitives. Worth designing for two consumers from the start rather than extracting later |
|
||||
| Export/backup plumbing — SAF, file writing, share-out | kit | the *serialization* of tasks is domain; the plumbing isn't |
|
||||
| Sync-adapter/account scaffolding | kit, probably | the `AbstractThreadedSyncAdapter` + authenticator boilerplate is identical everywhere; the delta logic is domain |
|
||||
|
||||
**Stays app-local:** the vendored `:provider` module (task-specific, and
|
||||
Apache-2.0 against the kit's MIT), `TaskContract` and the mappers, domain models
|
||||
and smart lists, all screens, and the reminder *scheduler* (per the kit's
|
||||
existing "not shared" call — Agendula pulls, Calendula pushes).
|
||||
|
||||
---
|
||||
|
||||
## Distribution — F-Droid and Play from day one
|
||||
|
||||
Not on either yet; both are targets, so build *for* them rather than retrofitting.
|
||||
|
||||
- **F-Droid** requires from-source. The in-tree `:provider` module satisfies it;
|
||||
a JitPack artifact would not. floret-kit's composite build already keeps the
|
||||
kit from-source, and the reproducibility guard (`vcsInfo { include = false }`
|
||||
in `app/build.gradle.kts`) is already in place.
|
||||
- **Play** requires a rising target-API floor, a data-safety declaration, and
|
||||
justification for `USE_EXACT_ALARM`. It also means dangerous permissions we
|
||||
don't use are a liability, not just clutter — which is most of why
|
||||
`GET_ACCOUNTS` and the stray receiver come out of the vendored provider.
|
||||
- **Parked, not solved:** dual-distribution signing. F-Droid reproducible builds
|
||||
verify against *our* signed APK; Play App Signing re-signs with Google's key.
|
||||
Both can coexist, but it needs a deliberate pass before the first Play upload.
|
||||
See `RELEASING.md`.
|
||||
|
||||
---
|
||||
|
||||
## Dead ends — do not revisit
|
||||
|
||||
- **One APK that detects at install time and adapts.** Impossible.
|
||||
`<provider>` authorities and `<permission>` declarations are frozen at build
|
||||
time and read by the OS at install; there is no install-time hook where our
|
||||
code runs. And unlike a component, a `<permission>` cannot be runtime-toggled
|
||||
— no `setComponentEnabledSetting` equivalent.
|
||||
- **Dynamic feature modules** to deliver the provider conditionally. Conditions
|
||||
are limited to hardware features / SDK / country — there is no "only if app X
|
||||
is absent" — and they require Play, so they're dead for F-Droid regardless.
|
||||
- **`frontend` / `standalone` build flavors.** Two flavors means two
|
||||
`applicationId`s (two listings, two signing lines, and switching costs a user
|
||||
their local data), or one `applicationId` and they can't coexist in a repo
|
||||
anyway. Obsolete now that our provider coexists with everything instead of
|
||||
replacing anything.
|
||||
- **Maven Central for the provider.** `org.dmfs:opentasks-provider` *is* there —
|
||||
but only up to `1.1.8.1` (2016, `jar` packaging, 3 versions). No DB 23.
|
||||
Verified.
|
||||
- **JitPack (`com.github.dmfs.opentasks:opentasks-provider:1.4.2`).** Has the
|
||||
right version, but it's a prebuilt artifact (fails F-Droid from-source), it
|
||||
can't have its hardcoded permission names renamed, and adding JitPack widens
|
||||
the dependency trust surface — `settings.gradle.kts` is currently `google()` +
|
||||
`mavenCentral()` only, under `FAIL_ON_PROJECT_REPOS`. *Still usable for a
|
||||
throwaway spike* (a library string resource can be overridden from the app
|
||||
module, so the authority rename works), but not for anything we ship.
|
||||
|
||||
---
|
||||
|
||||
## Sequencing
|
||||
|
||||
1. **Merge `fix/provider-interaction-review`** (`47cf99a`, currently unmerged
|
||||
into `main`). It's blocking nothing and rotting, and it touches the exact
|
||||
permission flow step 2 changes.
|
||||
2. **Vendor `:provider`** under our own authority and permission namespace, with
|
||||
the permission-gate bypass. This is the identity, done once, done right —
|
||||
and it ships a complete local-first app to both stores.
|
||||
3. **Export/backup.** Now a v1 feature, not a migration hack.
|
||||
4. **File the DAVx5 issue.** Non-blocking, cheap, serves F-Droid users.
|
||||
5. **Sync adapter.** The 1.x arc; design discussion pending.
|
||||
|
||||
**Scope honesty:** the withdrawn notes costed this at "2–4 days shippable, +1
|
||||
week for F-Droid." Steps 2–5 are a substantially larger program than that, and
|
||||
the roadmap should say so rather than inheriting the old estimate.
|
||||
|
||||
---
|
||||
|
||||
## Open questions
|
||||
|
||||
1. **Sync protocol coverage**, account model, conflict resolution — the next
|
||||
discussion.
|
||||
2. **Resolver ordering / mode selection UX** once our provider coexists with
|
||||
external ones (see [Storage modes](#storage-modes--the-users-choice)).
|
||||
3. **Does the vendored provider work with no account at all?** Local-only mode
|
||||
depends on it entirely. First thing the vendoring work should prove.
|
||||
4. **`ical4android` licensing** vs our MIT.
|
||||
5. **jtx Board** as an additional External-mode candidate — richer contract,
|
||||
later. (`PLAN.md` decision #3, still open.)
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
The provider-interaction review on `fix/provider-interaction-review` fixed,
|
||||
among others: recurrence-aware editing (routes through the instances URI),
|
||||
all-day UTC handling, the `DUE`/`DURATION` collision, per-task reminders (Alarm
|
||||
property rows), and flow-recovery robustness. That makes `ROADMAP.md` open
|
||||
decision #5 ("recurrence-aware editing out of scope for v1") **stale**.
|
||||
17
fastlane/metadata/android/en-US/changelogs/400.txt
Normal file
17
fastlane/metadata/android/en-US/changelogs/400.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
### Added
|
||||
- Agendula now speaks **German** and **Brazilian Portuguese**, the first
|
||||
community translations. Pick one under **Settings → App language**, or in
|
||||
Android's own per-app language settings; leave it on *System default* and
|
||||
Agendula follows your phone. Anything not yet translated falls back to
|
||||
English.
|
||||
- Agendula can now be translated. Pick or request a language on Weblate and
|
||||
translate in the browser — the link sits at the top of the language picker in
|
||||
**Settings → App language**. Partial translations are fine; anything
|
||||
untranslated falls back to English.
|
||||
|
||||
### Changed
|
||||
- Agendula's home is now **Codeberg** (`jlmakiola/agendula`) — that's where the
|
||||
source, issues, pull requests and releases live. The Source and License links
|
||||
in Settings, the issue-reporting link and the F-Droid metadata all point there
|
||||
now. The self-hosted Gitea instance stays as build infrastructure.
|
||||
|
||||
Submodule floret-kit updated: b08fb69843...e047a2bd48
Reference in New Issue
Block a user