diff --git a/docs/.gitignore b/docs/.gitignore
index 2f81dc77..79961b20 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -2,3 +2,4 @@ node_modules
dist
.vitepress/cache
.vitepress/build
+public/reference
\ No newline at end of file
diff --git a/docs/.vitepress/theme/Footer.vue b/docs/.vitepress/theme/Footer.vue
index dd8da80c..b29f2509 100644
--- a/docs/.vitepress/theme/Footer.vue
+++ b/docs/.vitepress/theme/Footer.vue
@@ -6,6 +6,7 @@
User Guide
Developer Guide
Contributor Guide
+ SDK Reference
Legal
@@ -14,8 +15,8 @@
@@ -36,10 +37,13 @@
.column {
display: flex;
flex-direction: column;
+ row-gap: 0.25rem;
h4 {
font-weight: 600;
+ margin-bottom: 0.5rem;
}
}
+ gap: 2rem;
}
.copyright {
margin-top: 2rem;
diff --git a/docs/docs/developer-guide/plugins/_category_.yml b/docs/docs/developer-guide/plugins/_category_.yml
deleted file mode 100644
index 64b73c6f..00000000
--- a/docs/docs/developer-guide/plugins/_category_.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-label: Plugins
-position: 4
-link:
- type: generated-index
- title: Plugins
- description: In this chapter you will learn how external apps can integrate with Kvaesitso.
\ No newline at end of file
diff --git a/docs/docs/developer-guide/plugins/access-control.md b/docs/docs/developer-guide/plugins/access-control.md
new file mode 100644
index 00000000..5a16d6ff
--- /dev/null
+++ b/docs/docs/developer-guide/plugins/access-control.md
@@ -0,0 +1,7 @@
+# Access Control
+
+Plugins potentially deal with sensitive user data. It is important to control, which apps can access a plugin's APIs in order to avoid any data breaches.
+
+For that reason, the plugin SDK has a built-in permission system. When a user first tries to enable a plugin in Kvaesitso, the launcher sends a permission request to the plugin. The plugin then shows a dialog which allows the user to either grant or deny the request. Once the request is granted, the package name of the requesting app is added to an internally stored allowlist. When an app tries to use a plugin, the plugin checks whether the calling app has been allowlisted before. If it hasn't, it throws a `SecurityException`.
+
+All the plugin SDK's plugin base classes already implement this system so there are no additional steps needed and your plugin is protected by default. However, if you wish to add or remove apps to or from the allowlist manually, you can use the `PluginPermissionManager` class.
diff --git a/docs/docs/developer-guide/plugins/get-started.md b/docs/docs/developer-guide/plugins/get-started.md
index 686c0a1e..bd597b24 100644
--- a/docs/docs/developer-guide/plugins/get-started.md
+++ b/docs/docs/developer-guide/plugins/get-started.md
@@ -1,5 +1,50 @@
-# Get Started
-
-First, create a new project in Android Studio.
+# Get started
## 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.
+
+Add the following dependency to your project:
+
+```
+de.mm20.launcher2:plugin-sdk:$version
+```
+
+The current version is: [](https://central.sonatype.com/artifact/de.mm20.launcher2/plugin-sdk)
+
+## Create your first plugin
+
+Start by creating a new class and giving it a meaningful name.
+
+```kt
+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`.
+
+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
+
+
+
+
+
+
+```
+
+- `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.
+ > [!WARNING]
+ > You must not change this later or things will break.
+- The `` lets Kvaesitso know that this content provider is a plugin.
+
+## Next steps
+
+Your next steps depend on the type of plugin that you want to develop:
+
+- Weather provider plugin: please refer to
diff --git a/docs/docs/developer-guide/plugins/metadata.md b/docs/docs/developer-guide/plugins/metadata.md
new file mode 100644
index 00000000..6fa23248
--- /dev/null
+++ b/docs/docs/developer-guide/plugins/metadata.md
@@ -0,0 +1,83 @@
+# Metadata
+
+You can customize how your plugin appears in Kvaesitso's UI.
+
+## Plugin package
+
+These properties apply to your whole plugin package. They are shown in the list of plugins and at the top section of the plugin detail page. `` elements must be added as a direct child of the `` element in your `AndroidManifest.xml`.
+
+### Label
+
+```xml
+
+```
+
+The label that is used for the plugin package. If none is set, the application label is used instead.
+
+### Description
+
+```xml
+
+```
+
+A description for your plugin package.
+
+### Author
+
+```xml
+
+```
+
+The name of the plugin author.
+
+### Icon
+
+```xml
+
+
+```
+
+The icon for the plugin package. If none is set, the app icon is used instead.
+
+## Plugin
+
+These properties apply to a single plugin. One plugin package can contain multiple plugins, but all plugins of one plugin package are grouped on the same settings screen.
+
+Plugin metadata are either set as attributes on the `` element, or as `` elements that are direct children of the `` element in the `AndroidManifest.xml`:
+
+### Label
+
+```xml
+
+```
+
+This is used in several places in the launcher UI, depending on the type of plugin. For example, if your plugin is a weather plugin, this is shown in the weather provider settings. If your plugin is a file search plugin, this is shown on the file search settings screen. If none is set, the application label is used instead.
+
+### Icon
+
+```xml
+
+
+```
+
+For search plugins, this is used on the little badge that indicates from which plugin a search result originated. If none is set, the application icon is used instead.
+
+### Description
+
+```xml
+
+```
+
+A static description what your plugin does. This can be overridden by the plugin itself to display dynamic information instead.
diff --git a/docs/docs/developer-guide/plugins/plugin-types/common/_plugin_state.md b/docs/docs/developer-guide/plugins/plugin-types/common/_plugin_state.md
new file mode 100644
index 00000000..4c0812cd
--- /dev/null
+++ b/docs/docs/developer-guide/plugins/plugin-types/common/_plugin_state.md
@@ -0,0 +1,15 @@
+Some plugins need to be configured before they can be used. For example, users might need to connect an account, or provide an API key.
+
+If your plugin has such requirements, you can override
+
+```kt
+suspend fun getPluginState(): PluginState
+```
+
+This method can either return `PluginState.Ready`, or `PluginState.SetupRequired`.
+
+- `PluginState.Ready` can have a status `text` to describe what the plugin does in its current configuration. For example _"Search {username}'s files on {service}_". This overrides the plugin's [description](/docs/developer-guide/plugins/metadata.html#description-1).
+- `PluginState.SetupRequired` needs to a `setupActivity` Intent that starts the setup. You can also provide a `message` to describe what kind of setup needs to be performed. For example _"Sign in with {service} to search files on {service}"_
+
+> [!IMPORTANT]
+> This method is only meant to provide hints to the launcher's user interface. You should not rely on it as a safeguard for your other plugin methods. There is still a chance that your other plugin methods are called regardless of the return value of this method. Make sure to check your requirements in the other plugin methods as well.
diff --git a/docs/docs/developer-guide/plugins/plugin-types/file-search.md b/docs/docs/developer-guide/plugins/plugin-types/file-search.md
new file mode 100644
index 00000000..73038940
--- /dev/null
+++ b/docs/docs/developer-guide/plugins/plugin-types/file-search.md
@@ -0,0 +1,11 @@
+# File Search
+
+TODO
+
+## Plugin state
+
+
+
+## Examples
+
+- [OneDrive plugin](https://github.com/Kvaesitso/Plugin-OneDrive)
diff --git a/docs/docs/developer-guide/plugins/plugin-types/weather.md b/docs/docs/developer-guide/plugins/plugin-types/weather.md
new file mode 100644
index 00000000..ed2f751c
--- /dev/null
+++ b/docs/docs/developer-guide/plugins/plugin-types/weather.md
@@ -0,0 +1,79 @@
+# Weather Provider Plugins
+
+Weather provider plugins need to extend the `WeatherProvider` class:
+
+```kt
+class MyWeatherProviderPlugin : WeatherProvider(
+ WeatherPluginConfig()
+)
+
+```
+
+In the super constructor call, pass a `WeatherPluginConfig` object.
+
+## Location search
+
+If your weather provider service provides an API to lookup locations, you should override
+
+```kt
+suspend fun findLocations(query: String, lang: String): List
+```
+
+This method is called when a user has _Auto location_ disabled and they are trying to set a new location.
+
+The default implementation uses the Android Geocoder, but this API has the limitation that it relies on Google Play Services so you should use your own implementation whenever feasable.
+
+`findLocations` returns a list of `WeatherLocation`s. Return an empty list if no location has been found.
+
+### Location types
+
+There are two types of locations:
+
+- `WeatherLocation.LatLon`: use this when your weather service identifies locations by their geo coordinates.
+- `WeatherLocation.Id`: use this when your weather service has an internal ID system to identify locations.
+
+## Featch weather data
+
+Implement both `getWeatherData` methods:
+
+```kt
+suspend fun getWeatherData(lat: Double, lon: Double, lang: String?): List?`
+```
+
+```kt
+suspend fun getWeatherData(location: WeatherLocation, lang: String?): List?
+```
+
+The first method is called when the user has _Auto location_ enabled. Are the last known coordinates of the user.
+
+The second method is called when the user has set their location to a fixed location. `location` is guaranteed to be a value that has been returned by `findLocations` before. If you haven't overriden `findLocations`, this will always be a `WeatherLocation.LatLon`.
+
+Both methods return a list of `Forecast`s. If an error occurs, you can throw an instruction or return `null`, in this case the launcher will keep the old data and start another attempt at a later time.
+
+`Forecast` objects need at least a `timestamp` (unix time in millis), a `temperature`, a `condition`, an `icon`, a `location` name, and a `provider` name.
+
+- The `condition` should preferably be localized in the user's language, which is provided by the `lang` parameter.
+- To construct a `Temperature`, you can use the `Double.C`, `Double.F`, or `Double.K` helper functions, depending on whether the numeric value returned by your weather service API is in degrees celsius, degrees fahrenheit, or kelvin:
+
+ ```kt
+ val temp = tempValueInCelcius.C
+ val temp2 = tempValueInFahrenheit.F
+ val temp3 = tempValueInKelvin.K
+ ```
+
+ Similar helper functions are available to construct
+
+ - `Pressure` (`Double.hPa`, and `Double.mbar`), and
+ - `WindSpeed` values (`Double.m_s`, `Double.km_h`, and `Double.mph`)
+
+- `location` is the name of the location.
+ - In fixed location mode, you should read this value from the `location` parameter, to ensure that the name in the weather widget matches the name that the user has set in preferences.
+ - In auto location mode, if your weather service does not give you a location name, you can use the `getLocationName` method to reverse geocode the location name using Android's Geocoder API.
+
+## Plugin state
+
+
+
+## Examples
+
+- [OpenWeatherMap plugin](https://github.com/Kvaesitso/Plugin-OpenWeatherMap)
diff --git a/docs/docs/developer-guide/plugins/search-provider/file-search.md b/docs/docs/developer-guide/plugins/search-provider/file-search.md
deleted file mode 100644
index e790b25f..00000000
--- a/docs/docs/developer-guide/plugins/search-provider/file-search.md
+++ /dev/null
@@ -1 +0,0 @@
-# File Search
\ No newline at end of file
diff --git a/docs/docs/developer-guide/plugins/search-provider/index.md b/docs/docs/developer-guide/plugins/search-provider/index.md
deleted file mode 100644
index f1c1788b..00000000
--- a/docs/docs/developer-guide/plugins/search-provider/index.md
+++ /dev/null
@@ -1 +0,0 @@
-# Search provider
\ No newline at end of file
diff --git a/docs/docs/developer-guide/sidebar.ts b/docs/docs/developer-guide/sidebar.ts
index adbbbc5c..dc6cdb72 100644
--- a/docs/docs/developer-guide/sidebar.ts
+++ b/docs/docs/developer-guide/sidebar.ts
@@ -53,4 +53,39 @@ export const DeveloperGuideSidebar: DefaultTheme.SidebarItem[] = [
},
],
},
+ {
+ text: 'Plugin Development',
+ items: [
+ {
+ text: 'Get Started',
+ link: '/docs/developer-guide/plugins/get-started',
+ },
+ {
+ text: 'Plugin Types',
+ items: [
+ {
+ text: 'Weather Provider',
+ link: '/docs/developer-guide/plugins/plugin-types/weather',
+ },
+ {
+ text: 'File Search Provider',
+ link: '/docs/developer-guide/plugins/plugin-types/file-search',
+ },
+ ],
+ },
+ {
+ text: 'Metadata',
+ link: '/docs/developer-guide/plugins/metadata',
+ },
+ {
+ text: 'Access Control',
+ link: '/docs/developer-guide/plugins/access-control',
+ },
+ {
+ text: 'Reference',
+ link: '/reference/index.html',
+ target: '_blank',
+ },
+ ],
+ },
]