Branch 1 of 2 for v2.7 (the .ics topic). Adds the write side of a hand-rolled RFC 5545 engine (zero deps, stays on kotlinx-datetime): - domain/ics: IcsText (escape + 75-octet folding), IcsEvent model, IcsWriter.writeCalendar. Timezone rule: all-day VALUE=DATE, one-off timed UTC Z, recurring timed TZID-labelled from EVENT_TIMEZONE (no VTIMEZONE — import resolves TZID against the OS tz db). - Single-event share from the detail screen (FileProvider + ACTION_SEND). - Whole-calendar backup of the writable local calendars to a SAF file (Settings -> Calendars -> Export as .ics), one combined VCALENDAR. - insertEvent now writes Events.UID_2445; legacy rows fall back to a stable synthesised UID at export time so a later restore won't dupe. - EXDATE / RECURRENCE-ID overrides are deliberately skipped this pass (documented v1 limit; import will skip them too). Engine + mapper unit-tested. Import (Branch 2, feat/ics-import) ships in the same v2.7 release; no tag until both land + on-device review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
141 lines
6.1 KiB
XML
141 lines
6.1 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
xmlns:tools="http://schemas.android.com/tools">
|
|
|
|
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
|
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
|
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
<!--
|
|
Lets the "Reliable delivery" setting open the direct system dialog to
|
|
exempt Calendula from battery optimisation (so reminder broadcasts aren't
|
|
delayed by Doze). Used only to launch that dialog; falls back to the
|
|
battery-optimisation list if the OS declines the direct intent.
|
|
-->
|
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
|
|
|
<!-- Package visibility (Android 11+): without this, getLaunchIntentForPackage
|
|
returns null and the calendar manager's per-account "manage" button can't
|
|
open the source sync app (DAVx5, ICSx5, Google Calendar, …). The LAUNCHER
|
|
intent makes launchable apps visible so we can launch whichever app owns a
|
|
calendar account's authenticator. -->
|
|
<queries>
|
|
<intent>
|
|
<action android:name="android.intent.action.MAIN" />
|
|
<category android:name="android.intent.category.LAUNCHER" />
|
|
</intent>
|
|
</queries>
|
|
|
|
<application
|
|
android:name=".CalendulaApp"
|
|
android:allowBackup="true"
|
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
|
android:fullBackupContent="@xml/backup_rules"
|
|
android:icon="@mipmap/ic_launcher"
|
|
android:label="@string/app_name"
|
|
android:localeConfig="@xml/locales_config"
|
|
android:roundIcon="@mipmap/ic_launcher_round"
|
|
android:supportsRtl="true"
|
|
android:theme="@style/Theme.Calendula"
|
|
tools:targetApi="35">
|
|
<activity
|
|
android:name=".MainActivity"
|
|
android:exported="true"
|
|
android:launchMode="singleTop"
|
|
android:windowSoftInputMode="adjustResize">
|
|
<intent-filter>
|
|
<action android:name="android.intent.action.MAIN" />
|
|
<category android:name="android.intent.category.LAUNCHER" />
|
|
</intent-filter>
|
|
|
|
<!-- Launcher long-press shortcuts (e.g. "New event"). -->
|
|
<meta-data
|
|
android:name="android.app.shortcuts"
|
|
android:resource="@xml/shortcuts" />
|
|
</activity>
|
|
|
|
<!-- The provider broadcasts EVENT_REMINDER at reminder time but posts
|
|
no notification itself — a calendar app must (v1.4, Etar model).
|
|
Exported: the broadcast arrives from the provider's process. -->
|
|
<receiver
|
|
android:name=".data.reminders.EventReminderReceiver"
|
|
android:exported="true">
|
|
<intent-filter>
|
|
<action android:name="android.intent.action.EVENT_REMINDER" />
|
|
<data
|
|
android:host="com.android.calendar"
|
|
android:scheme="content" />
|
|
</intent-filter>
|
|
</receiver>
|
|
|
|
<!-- Home-screen widgets (Glance). Exported: the launcher/host binds them. -->
|
|
<receiver
|
|
android:name=".widget.agenda.AgendaWidgetReceiver"
|
|
android:label="@string/widget_agenda_label"
|
|
android:exported="true">
|
|
<intent-filter>
|
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
|
</intent-filter>
|
|
<meta-data
|
|
android:name="android.appwidget.provider"
|
|
android:resource="@xml/appwidget_info_agenda" />
|
|
</receiver>
|
|
|
|
<receiver
|
|
android:name=".widget.month.MonthWidgetReceiver"
|
|
android:label="@string/widget_month_label"
|
|
android:exported="true">
|
|
<intent-filter>
|
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
|
</intent-filter>
|
|
<meta-data
|
|
android:name="android.appwidget.provider"
|
|
android:resource="@xml/appwidget_info_month" />
|
|
</receiver>
|
|
|
|
<!-- Keeps both widgets fresh: the calendar provider broadcasts
|
|
PROVIDER_CHANGED on any data change (our writes and external sync),
|
|
and the system broadcasts the date/time ones at midnight / clock
|
|
changes so "today" highlighting rolls over. -->
|
|
<receiver
|
|
android:name=".widget.WidgetUpdateReceiver"
|
|
android:exported="true">
|
|
<intent-filter>
|
|
<action android:name="android.intent.action.PROVIDER_CHANGED" />
|
|
<data
|
|
android:host="com.android.calendar"
|
|
android:scheme="content" />
|
|
</intent-filter>
|
|
<intent-filter>
|
|
<action android:name="android.intent.action.DATE_CHANGED" />
|
|
<action android:name="android.intent.action.TIME_SET" />
|
|
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
|
|
</intent-filter>
|
|
</receiver>
|
|
|
|
<!-- Hands .ics files we stage in the cache to other apps via a content
|
|
Uri (single-event share). Authority tracks applicationId so the
|
|
debug suffix doesn't break getUriForFile. -->
|
|
<provider
|
|
android:name="androidx.core.content.FileProvider"
|
|
android:authorities="${applicationId}.fileprovider"
|
|
android:exported="false"
|
|
android:grantUriPermissions="true">
|
|
<meta-data
|
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
android:resource="@xml/file_paths" />
|
|
</provider>
|
|
|
|
<!-- Persists the per-app language (M4) on API < 33, where the platform
|
|
per-app-languages API is unavailable. On 33+ this is a no-op. -->
|
|
<service
|
|
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
|
android:enabled="false"
|
|
android:exported="false">
|
|
<meta-data
|
|
android:name="autoStoreLocales"
|
|
android:value="true" />
|
|
</service>
|
|
</application>
|
|
|
|
</manifest>
|