diff --git a/docs/docs/developer-guide/plugins/get-started.md b/docs/docs/developer-guide/plugins/get-started.md index fc27ada75..62d0d8996 100644 --- a/docs/docs/developer-guide/plugins/get-started.md +++ b/docs/docs/developer-guide/plugins/get-started.md @@ -2,7 +2,8 @@ ## Get the plugin SDK -Kvaesitso comes with a plugin SDK that abstracts away the low level details of inter-app communication and streamlines the process of creating plugins. +Kvaesitso comes with a plugin SDK that abstracts away the low level details of inter-app +communication and streamlines the process of creating plugins. Add the following dependency to your project: @@ -10,7 +11,8 @@ Add the following dependency to your project: de.mm20.launcher2:plugin-sdk:$version ``` -The current version is: [![](https://img.shields.io/maven-central/v/de.mm20.launcher2/plugin-sdk?style=flat-square)](https://central.sonatype.com/artifact/de.mm20.launcher2/plugin-sdk) +The current version +is: [![](https://img.shields.io/maven-central/v/de.mm20.launcher2/plugin-sdk?style=flat-square)](https://central.sonatype.com/artifact/de.mm20.launcher2/plugin-sdk) ## Create your first plugin @@ -21,14 +23,18 @@ class MyFirstPlugin ``` -This class needs to extends one of the plugin base classes. Which class to extend depends on the kind of plugin that you want to develop. Please refer to the article of the respective plugin type to continue. But first, regardless of plugin type, you need to register your plugin in the `AndroidManifest.xml`. +This class needs to extends one of the plugin base classes. Which class to extend depends on the +kind of plugin that you want to develop. Please refer to the article of the respective plugin type +to continue. But first, regardless of plugin type, you need to register your plugin in the +`AndroidManifest.xml`. -Under the hood, plugins are implemented using Android's content provider APIs. While the plugin SDK abstracts most of that away from you, you still need to register the plugin class as a content provider in the `AndroidManifest.xml`: +Under the hood, plugins are implemented using Android's content provider APIs. While the plugin SDK +abstracts most of that away from you, you still need to register the plugin class as a content +provider in the `AndroidManifest.xml`: ```xml - @@ -38,7 +44,8 @@ Under the hood, plugins are implemented using Android's content provider APIs. W ``` - `android:name` is the class name of your plugin class. -- `android:authorities` must be a globally unique name. It is a good practice to prefix it with your app's package name and add a unique suffix. +- `android:authorities` must be a globally unique name. It is a good practice to prefix it with your + app's package name and add a unique suffix. > [!WARNING] > You must not change this later or things will break. - The `` lets Kvaesitso know that this content provider is a plugin. @@ -47,6 +54,13 @@ Under the hood, plugins are implemented using Android's content provider APIs. W Your next steps depend on the type of plugin that you want to develop: -- Weather provider plugin: [Weather Provider](/docs/developer-guide/plugins/plugin-types/weather.html) -- File search plugin: [File Search Provider](/docs/developer-guide/plugins/plugin-types/file-search.html) -- Places search plugin: [Places Search Provider](/docs/developer-guide/plugins/plugin-types/places-search.html) +- Weather provider + plugin: [Weather Provider](/docs/developer-guide/plugins/plugin-types/weather.html) +- File search + plugin: [File Search Provider](/docs/developer-guide/plugins/plugin-types/file-search.html) +- Places search + plugin: [Places Search Provider](/docs/developer-guide/plugins/plugin-types/places-search.html) +- Contact search + plugin: [Contact Search Provider](/docs/developer-guide/plugins/plugin-types/contact-search.html) +- Calendar provider + plugin: [Calendar Provider](/docs/developer-guide/plugins/plugin-types/calendar.html) diff --git a/docs/docs/developer-guide/plugins/plugin-types/calendar.md b/docs/docs/developer-guide/plugins/plugin-types/calendar.md new file mode 100644 index 000000000..c4543c4d8 --- /dev/null +++ b/docs/docs/developer-guide/plugins/plugin-types/calendar.md @@ -0,0 +1,144 @@ +# Calendar Provider + +Calendar provider plugins need to extend +the +`CalendarProvider` +class: + +```kt +class MyCalendarPlugin() : CalendarProvider( + QueryPluginConfig() +) +``` + +In the super constructor call, pass +a +`QueryPluginConfig` +object. + +## Plugin config + + + +## Calendar lists + +Calendar lists are collections of calendar entries (i.e. "Private", "Work", "Family"). Users can +choose, which lists to include in search results and the calendar widget. To implement calendar +lists, override + +```kt +suspend fun getCalendarLists(): List +``` + +This method should return a list +of +`CalendarList`s. At least one list should be returned. + +### The `CalendarList` object + +The `CalendarEvent` has the following properties: + +- `id`: A unique ID for this list. +- `name`: A human-readable name for this list. +- `contentTypes`: A list + of + `CalendarListType`s (`Calendar`, `Tasks`) that this list includes. +- `accountName` (optional): The name of the account this list belongs to. Lists that belong to the + same account are grouped together in the launcher UI. +- `color` (optional): The color of this list, in `0xAARRGGBB` format. + +## Search calendar events + +Calendar search is used by both search and calendar widget. +To implement calendar search, override + +```kt +suspend fun search(query: CalendarQuery, params: SearchParams): List +``` + +- `query` includes the query parameters: + - `query`: The search term. Can be null if the query was started by the calendar widget. + - `start`: The timestamp (ms since epoch) of the start of the search window. + - `end`: The timestamp (ms since epoch) of the end of the search window. + - `excludedCalendars`: List of calendar list IDs that should be excluded from the search + results. + + + +`search` returns a list of `CalendarEvent`s. The list can be empty if no results were found. + +### The `CalendarEvent` object + +A `CalendarEvent` has the following properties: + +- `id`: A unique ID for this event. +- `title`: The title of the event. +- `calendarName`: The name of the calendar the event belongs to. +- `description` (optional): A description of the event. +- `location` (optional): The location of the event. +- `color` (optional): The color of the event, in `0xAARRGGBB` format. +- `startTime`: Start time of the event in milliseconds since epoch. For tasks, this can be null. +- `endTime`: End time of the event in milliseconds since epoch. For tasks, this is the due date. +- `includeTime`: If false, only the date will be shown for the event. +- `attendees`: A list of human-readable names, representing the attendees. +- `uri`: A URI that opens the event. Can be a URI that your app can handle, or a https link. +- `isCompleted` (optional): If this is not null, the event is treated as a task, indicated by a + checkmark in the UI. + +## Refresh an event + +If you have set `config.storageStrategy` to `StorageStrategy.StoreCopy`, the launcher will +periodically +try to refresh the stored copy. This happens for example when a user long-presses an event to view +its details. To update the event, you can override + +```kt +suspend fun refresh(item: CalendarEvent, params: RefreshParams): CalendarEvent? +``` + +The stored event will be replaced with the return value of this method. If the event is no +longer available, it should return `null`. In this case, the launcher will remove it from its +database. If the event is temporarily unavailable, an exception should be thrown. + +- `item` is the version that the launcher has currently stored + + + +The default implementation returns `item` without any changes. + +## Get an event + +If you have set `config.storageStrategy` to `StorageStrategy.StoreReference`, you must override + +```kt +suspend fun get(id: String, params: GetParams): CalendarEvent? +``` + +This method is used to look up a event by its `id`. If the event is no longer available, it should +return `null`. In this case, the launcher will remove it from its database. + +- `id` is the ID of the event that is being requested + + + +## Plugin state + + + +## Additional notes + +### Calendar widget + +The calendar widget uses the same `search` method that the search uses. The only difference is that +`query.query` is always `null` and that `params.allowNetwork` is always `false`. + +### Tasks + +Tasks are a special type of calendar event, because they can be completed or uncompleted. A +`CalendarEvent` is treated as a task, when its `isCompleted` property is not `null`. For tasks, the +`endTime` is the due date. The `startTime` is the time from which the task should be displayed, and +it can be `null` to always display the task. + +## Examples + +- **[Tasks.org plugin (deprecated)](https://github.com/Kvaesitso/Plugin-TasksOrg)** \ No newline at end of file diff --git a/docs/docs/developer-guide/plugins/plugin-types/contact-search.md b/docs/docs/developer-guide/plugins/plugin-types/contact-search.md index 035a10142..14c1cd9c2 100644 --- a/docs/docs/developer-guide/plugins/plugin-types/contact-search.md +++ b/docs/docs/developer-guide/plugins/plugin-types/contact-search.md @@ -1,4 +1,4 @@ -# Contact search +# Contact Search Contact search provider plugins need to extend the `ContactProvider` @@ -86,7 +86,7 @@ If you have set `config.storageStrategy` to `StorageStrategy.StoreReference`, yo suspend fun get(id: String, params: GetParams): Contact? ``` -This method is used to lookup a contact by its `id`. If the contact is no longer available, it should +This method is used to look up a contact by its `id`. If the contact is no longer available, it should return `null`. In this case, the launcher will remove it from its database. - `id` is the ID of the contact that is being requested diff --git a/docs/docs/developer-guide/plugins/plugin-types/weather.md b/docs/docs/developer-guide/plugins/plugin-types/weather.md index 3f7e195a5..e0222444e 100644 --- a/docs/docs/developer-guide/plugins/plugin-types/weather.md +++ b/docs/docs/developer-guide/plugins/plugin-types/weather.md @@ -1,4 +1,4 @@ -# Weather Provider Plugins +# Weather Provider Weather provider plugins need to extend the `WeatherProvider` diff --git a/docs/docs/developer-guide/sidebar.ts b/docs/docs/developer-guide/sidebar.ts index 010ba7140..93a529b2c 100644 --- a/docs/docs/developer-guide/sidebar.ts +++ b/docs/docs/developer-guide/sidebar.ts @@ -79,6 +79,10 @@ export const DeveloperGuideSidebar: DefaultTheme.SidebarItem[] = [ text: 'Places Search Provider', link: '/docs/developer-guide/plugins/plugin-types/places-search', }, + { + text: 'Calendar Provider', + link: '/docs/developer-guide/plugins/plugin-types/calendar', + }, ], }, { diff --git a/plugins/sdk/src/main/java/de/mm20/launcher2/sdk/calendar/CalendarList.kt b/plugins/sdk/src/main/java/de/mm20/launcher2/sdk/calendar/CalendarList.kt index 1e31a8937..ff88c0bf4 100644 --- a/plugins/sdk/src/main/java/de/mm20/launcher2/sdk/calendar/CalendarList.kt +++ b/plugins/sdk/src/main/java/de/mm20/launcher2/sdk/calendar/CalendarList.kt @@ -12,10 +12,7 @@ data class CalendarList( */ val name: String, /** - * The main content type of this list. This has mainly cosmetic purposes (labels, icons). - * It doesn't need to be 100% accurate since the actual type is determined on a per-item basis. - * However, the launcher will hide some settings if it doesn't find any lists that would be - * affected by them. + * The content type of this list. */ val contentTypes: List, /** @@ -23,7 +20,7 @@ data class CalendarList( */ val accountName: String? = null, /** - * The color of this list, in 0xFFAARRGGBB format. + * The color of this list, in 0xAARRGGBB format. * If null, the launcher will use a default theme color. * The color is corrected to match the launcher's theme (i.e. for dark mode). */