Add support for location plugins (#772)
* Cherry pick location refactor * Refactor :data:openstreetmaps to :data:locations * contract, plugin sdk * Implement serialization, module tweaks * Include check for out-of-date departures in SearchableItemVM.requestUpdatedSearchable() * settings for location plugins * Try not to be too lazy * more fiddling with the plugin SDK * add departures in MapView with mock data for debug builds * change icons * add boats * animate departure lazycolumn * Add MarqueeText for text overflow handling * Define height for Departures in case there is no map to display * Don't inclure railway / highway tags for OSM since there will be location plugins for that * sort by time * - Apply pre-merge changes to LocationSettings - Add banner warning about slowed down location search for large search radii * ditch `showLocationOnMap` * LocationItem: make `showOpeningSchedule` toggleable * LocationItem: make Navigation AssistChip work for people that don't have google maps installed * LocationItem: resolve TODOs * MapTiles: ditch unused code, animate userIndicator * Reintroduce departure list * Add LineColor * Add osm tag `stars` as `userRating` https://taginfo.openstreetmap.org/keys/stars#overview * typealias -> import * Don't add Navigation Chip when there is no way to resolve navigation intents * Add settings migration * Set plugin SDK version to 1.2.0-SNAPSHOT * Deduplicate shared plugin classes, use kotlinx.serialization * Fix imports * Use ZonedDateTime for depature times * Add more line types * Rewrite location serialization * Replace street/houseNumber with address * Add attribution field * Add plugin config * Reject location search requests without lat lon parameters * Add default values to plugin location class * Don't crash if column value is null * Add docs comments to LocationCategory values * Refactor OpeningSchedule as polymorphic * remove dead corpse *ahem* code * Split LocationCategory into category and icon (Also update to Kotlin 2.0, please don't do this at home) * Add more location icons * Fix (?) location deserializer * Add more location icons * More icons * Meh * Add Pub * Disable Github Maven repo if credentials are missing * Add location search specific settings to plugin details screen * Add language parameter * Unbreak the build * Refactor plugin SDK (with breaking changes) * Set plugin SDK version to 2.0.0-SNAPSHOT * Document SDK breaking changes * Implement LocationProvider.getQuery * Add a typesafe cursor API * Oops I did it again next time maybe check if the code is actually compiling before pushing * Add missing return statement * Fix list serialization * Departure time UI adjustment * Use typesafe cursor for weather plugins * Add userRatingCount and emailAddress fields * grrr * Rename and extend LineTypes * Add default lineType to Departure to fix serialization errors * Fix refreshing stored plugin locations * Adapt line name column width to available departures * Fix plugin settings screen category overlap * add LocationItem.GenericTransit * Fix crash during deserialization of locations * Update SDK docs * Replace plugin "official" mark with "verified developer" mark before anyone gets sued * show 'now' when departure is in less than one minute * Add typesafe Bundle API * Implement plugin API changes * Plugin SDK: Fix refresh result not being returned * Update docs * apply alpha to departures that have departured * better (maybe): reduce saturation instead of alpha * Add default values for Attribution * Display attribution * Rearrange location result layout * Reduce searchable update interval to 1 minute * Pass last update time to refresh function * Change refresh path and ensure that timestamp is only update when the item was updated * categorize osm location * Update docs * Optimize location search - run providers in parallel - flatten code * add experimental address parsing for OSM * add poi_category_townhall * Fix popup closing when favorites items are updated * Revert "Fix popup closing when favorites items are updated" This reverts commit fc517fd066c7f8109b6d6df2d4f536af66398207. * Fork AndroidAddressFormatter to `:libs:address-formatter` * migrate `:libs:address-formatter` dependencies to version catalog and update them * also consider addr:{suburb,hamlet} for `Address.city` if city tag is missing * Move poi strings back to strings.xml * Update Jetpack Compose * Move address-formatter back to its original package, add license and readme * Move address-formatter back to its original package --------- Co-authored-by: MM20 <15646950+MM2-0@users.noreply.github.com>
This commit is contained in:
@@ -49,3 +49,4 @@ 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)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Migrate from v1.x to v2.x
|
||||
|
||||
Version 2.0.0 of the plugin SDK introduces some breaking changes:
|
||||
|
||||
## Name changes
|
||||
|
||||
- The `SearchPluginConfig` class has been renamed to `QueryPluginConfig`
|
||||
|
||||
## Signature changes
|
||||
|
||||
### `FileProvider`
|
||||
|
||||
### `search`
|
||||
|
||||
```kt
|
||||
suspend fun search(query: String, allowNetwork: Boolean): List<File>
|
||||
```
|
||||
|
||||
has been changed to
|
||||
|
||||
```kt
|
||||
suspend fun search(query: String, params: SearchParams): List<File>
|
||||
```
|
||||
|
||||
`params` provides additional properties to configure how the search is performed:
|
||||
- `allowNetwork` - whether to allow network requests
|
||||
- `lang` - the language to use for the search
|
||||
|
||||
### `get`
|
||||
|
||||
```kt
|
||||
suspend fun get(id: String): File?
|
||||
```
|
||||
|
||||
has been changed to
|
||||
|
||||
```kt
|
||||
suspend fun get(id: String, params: GetParams): File?
|
||||
```
|
||||
@@ -0,0 +1,4 @@
|
||||
- `params` provides additional parameters:
|
||||
- `lang` is the current language of the launcher. This can differ from the system language, as
|
||||
the user can set a different language per app. This value should be used to localize the
|
||||
result.
|
||||
@@ -0,0 +1,18 @@
|
||||
Search plugins have the following configuration properties:
|
||||
|
||||
- `storageStrategy`: Describes how the launcher should store a search result in its internal
|
||||
database. This is relevant when a user pins a search result to favorites, or when they assign a
|
||||
tag or custom label. In these situations, the launcher needs to be able to restore the search
|
||||
result from its database. There are two different strategies:
|
||||
- **`StorageStrategy.StoreCopy`** (default): The launcher stores all relevant information about
|
||||
this search result in its own internal database. The result can be restored without querying
|
||||
the plugin again. The launcher will try refresh the search result at its own discretion (e.g.
|
||||
when a user long-presses a restored search result to view its details). This strategy is the
|
||||
default and should be used whenever the plugin can't restore a search result immediately. It
|
||||
is best suited for online search plugins.
|
||||
- **`StorageStrategy.StoreReference`**: The launcher only stores the ID of the search result,
|
||||
and the plugin that created it. To restore a result, the plugin is queried again. This
|
||||
allows the plugin to update key fields (i.e. the label) immediately. However, plugins that
|
||||
use this strategy must guarantee, that they can restore a search result at any time, in a
|
||||
timely manner. In particular, the plugin must be able to restore a search result without
|
||||
any network requests. This strategy is best suited for on-device search plugins.
|
||||
@@ -0,0 +1,7 @@
|
||||
- `params` provides additional parameters:
|
||||
- `lang` is the current language of the launcher. This can differ from the system language, as
|
||||
the user can set a different language per app. This value should be used to localize the
|
||||
result.
|
||||
- `lastUpdated` the timestamp (in milliseconds) when `item` was last updated. This should be
|
||||
used to determine if the item needs to be refreshed again. If you decide not to refresh the
|
||||
item, you should return the original `item` parameter.
|
||||
@@ -0,0 +1,11 @@
|
||||
- `params` provides additional parameters for the search:
|
||||
- `allowNetwork` is a flag that indicates whether the user has enabled online search for this
|
||||
query. Plugins are generally advised to respect this request. This flag exists mainly for
|
||||
privacy reasons: the majority of searches target offline results (like apps, or contacts).
|
||||
Sending every single search request to external servers is overkill and can be a privacy
|
||||
issue. (Besides, it's not very nice to overload servers with unnecessary requests.) To reduce
|
||||
the amount of data that is sent to external servers, users can control, whether a search
|
||||
should include online results or not.
|
||||
- `lang` is the current language of the launcher. This can differ from the system language, as
|
||||
the user can set a different language per app. This value should be used for any localization
|
||||
in the search results.
|
||||
@@ -1,20 +0,0 @@
|
||||
Search plugins have the following configuration properties:
|
||||
|
||||
- `storageStrategy`: Describes how the launcher should store a search result in its internal
|
||||
database. This is relevant when a user pins a search result to favorites, or when they assign a
|
||||
tag or custom label. In these situations, the launcher needs to be able to restore the search
|
||||
result from its database. There are two different strategies:
|
||||
- **`StorageStrategy.StoreReference`**: The launcher only stores the ID of the search result,
|
||||
and the plugin that created it. To restore a result, the plugin is queried again. This
|
||||
strategy allows the plugin provider to update a search result at a later point in time.
|
||||
However, plugins that use this strategy must guarantee that a search result can be restored in
|
||||
a timely manner. In particular, the plugin provider must be able to restore a search result
|
||||
without any network requests.
|
||||
- **`StorageStrategy.StoreCopy`** (default): The launcher stores all relevant information about
|
||||
this search result in its own internal database. The result can be restored without querying
|
||||
the plugin again. This strategy is very easy to implement. The downside is, that results
|
||||
cannot be updated at a later point in time.
|
||||
- **`StorageStrategy.Deferred`** (default): The launcher stores all relevant information in its
|
||||
own internal database, like [StoreCopy]. A fresh copy is fetched from the plugin provider when
|
||||
the user opens the search result's detail view. This allows the plugin provider to update the
|
||||
search result at a later point in time, without the time constraints of [StoreReference].
|
||||
@@ -6,35 +6,30 @@ class:
|
||||
|
||||
```kt
|
||||
class MyFileSearchPlugin : FileProvider(
|
||||
SearchPluginConfig()
|
||||
QueryPluginConfig()
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
In the super constructor call, pass
|
||||
a <a href="/reference/core/shared/de.mm20.launcher2.plugin.config/-search-plugin-config/index.html" target="_blank">`SearchPluginConfig`</a>
|
||||
a <a href="/reference/core/shared/de.mm20.launcher2.plugin.config/-query-plugin-config/index.html" target="_blank">`QueryPluginConfig`</a>
|
||||
object.
|
||||
|
||||
## Plugin config
|
||||
|
||||
<!--@include: ./common/_search_plugin_config.md-->
|
||||
<!--@include: ./common/_query_plugin_config.md-->
|
||||
|
||||
## Search files
|
||||
|
||||
To implement file search, override
|
||||
|
||||
```kt
|
||||
suspend fun search(query: String, allowNetwork: Boolean): List<File>
|
||||
suspend fun search(query: String, params: SearchParams): List<File>
|
||||
```
|
||||
|
||||
- `query` is the search term
|
||||
- `allowNetwork` is a flag that indicates whether the user has enabled online search for this query.
|
||||
Plugins are generally advised to respect this request. This flag exists mainly for privacy
|
||||
reasons: the majority of searches target offline results (like apps, or contacts). Sending every
|
||||
single search request to external servers is overkill and can be a privacy issue. (Besides, it's
|
||||
not very nice to overload servers with unnecessary requests.) To reduce the amount of data that is
|
||||
leaked to external servers, users can control, whether a search should include online results or
|
||||
not.
|
||||
|
||||
<!--@include: ./common/_search_params.md-->
|
||||
|
||||
`search` returns a list of `File`s. The list can be empty if no results were found.
|
||||
|
||||
@@ -60,18 +55,41 @@ A `File` has the following properties:
|
||||
cloud drive and are not owned by the user themselves, but shared with them.
|
||||
- `metadata`: Additional file metadata.
|
||||
|
||||
## Get a file
|
||||
## Refresh a file
|
||||
|
||||
If you have set `config.storageStrategy` to `StorageStrategy.StoreReference`
|
||||
or `StorageStrategy.Deferred`, you must override
|
||||
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 a file to view its
|
||||
details. To update the file, you can override
|
||||
|
||||
```kt
|
||||
suspend fun get(id: String): File?
|
||||
suspend fun refresh(item: File, params: RefreshParams): File?
|
||||
```
|
||||
|
||||
The stored file will be replaced with the return value of this method. If the file is no longer
|
||||
available, it should return `null`. In this case, the launcher will remove it from its database. If
|
||||
the file is temporarily unavailable, an exception should be thrown.
|
||||
|
||||
- `item` is the version that the launcher has currently stored
|
||||
|
||||
<!--@include: ./common/_refresh_params.md-->
|
||||
|
||||
The default implementation returns `item` without any changes.
|
||||
|
||||
## Get a file
|
||||
|
||||
If you have set `config.storageStrategy` to `StorageStrategy.StoreReference`, you must override
|
||||
|
||||
```kt
|
||||
suspend fun get(id: String, params: GetParams): File?
|
||||
```
|
||||
|
||||
This method is used to lookup a file by its `id`. If the file is no longer available, it should
|
||||
return `null`. In this case, the launcher will remove it from its database. If the file is
|
||||
temporarily unavailable, an exception should be thrown.
|
||||
return `null`. In this case, the launcher will remove it from its database.
|
||||
|
||||
- `id` is the ID of the file that is being requested
|
||||
|
||||
<!--@include: ./common/_get_params.md-->
|
||||
|
||||
## Plugin state
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
# Places Search
|
||||
|
||||
Places search provider plugins need to extend
|
||||
the <a href="/reference/plugins/sdk/de.mm20.launcher2.sdk.locations/-location-provider/index.html" target="_blank">`LocationProvider`</a>
|
||||
class:
|
||||
|
||||
```kt
|
||||
class MyplaceSearchPlugin : LocationProvider(
|
||||
QueryPluginConfig()
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
In the super constructor call, pass
|
||||
a <a href="/reference/core/shared/de.mm20.launcher2.plugin.config/-query-plugin-config/index.html" target="_blank">`QueryPluginConfig`</a>
|
||||
object.
|
||||
|
||||
## Plugin config
|
||||
|
||||
<!--@include: ./common/_query_plugin_config.md-->
|
||||
|
||||
## Search places
|
||||
|
||||
To implement place search, override
|
||||
|
||||
```kt
|
||||
suspend fun search(query: LocationQuery, params: SearchParams): List<Location>
|
||||
```
|
||||
|
||||
- `query` includes the query parameters:
|
||||
- `query`: The search term
|
||||
- `userLatitude`: The latitude of the user's current location
|
||||
- `userLongitude`: The longitude of the user's current location
|
||||
- `radius`: The search radius in meters
|
||||
|
||||
<!--@include: ./common/_search_params.md-->
|
||||
|
||||
`search` returns a list of `Location`s. The list can be empty if no results were found.
|
||||
|
||||
### The `Location` object
|
||||
|
||||
A `Location` has the following properties:
|
||||
|
||||
- `id`: A unique and stable identifier for this location. This is used to track usage stats so if
|
||||
two
|
||||
places are identical, they must have the same ID, and if they are different, they need to have
|
||||
different IDs.
|
||||
- `label`: The name that is shown to the user
|
||||
- `latitude`: The latitude of the location
|
||||
- `longitude`: The longitude of the location
|
||||
- `icon`: An enum value of `LocationIcon` that determines the icon that is shown for this location
|
||||
- `category`: A human readable category of the location. For example, _"Restaurant"_, _"Hotel"_, or
|
||||
_"Museum"_. This should be localized with the `params.lang` parameter.
|
||||
- `address`: The address of the location
|
||||
- `websiteUrl`: The URL of the location's website
|
||||
- `phoneNumber`: The phone number of the location
|
||||
- `emailAddress`: The email address of the location
|
||||
- `openingSchedule`: Either
|
||||
- `OpeningSchedule.TwentyFourSeven` if the location is open at all times
|
||||
- `OpeningSchedule.Hours(openingHours: List<OpeningHours>)` if the location has specific opening
|
||||
hours, with each `OpeningHours` object containing:
|
||||
- `dayOfWeek`: The day of the week, as `DayOfWeek` enum value. If a location is open past
|
||||
midnight, `dayOfWeek` should refer to the day when the opening hours start.
|
||||
- `startTime`: The time the location opens, as a `LocalTime` object
|
||||
- `duration`: The duration the location is open, as a `Duration` object
|
||||
- `departures`: If the place is a public transport station, this field contains the next departures
|
||||
from this station. This is a list of `Departure` objects, each containing:
|
||||
- `time`: The scheduled departure time as `ZonedDateTime`
|
||||
- `delay`: The delay as `Duration`. If the departure is on time, this must be `Duration.ZERO`.
|
||||
If no real-time data is available, this should be `null`.
|
||||
- `line`: The line name (e.g. _"S1"_, _"U2"_, or _"73"_)
|
||||
- `lastStop`: The destination of the line
|
||||
- `type`: The type of the line, as `LineType` enum value
|
||||
- `lineColor`: The color of the line, as a `Color`
|
||||
- `userRating`: A user rating of this location, on a scale from 0 to 1. This is multiplied by 5
|
||||
and shown as a star rating bar in the launcher.
|
||||
- `userRatingCount`: The number of user ratings that were used to calculate the `userRating`
|
||||
- `fixMeUrl`: A URL where users can report incorrect data for this location.
|
||||
- `attribution`: Attribution that should be shown alongside the search result (read the data
|
||||
provider's terms of service to find out if this is required).
|
||||
|
||||
## Refresh a place
|
||||
|
||||
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 a
|
||||
place to view its details. To update the place, you can override
|
||||
|
||||
```kt
|
||||
suspend fun refresh(item: Location, params: RefreshParams): Location?
|
||||
```
|
||||
|
||||
The stored place will be replaced with the return value of this method. If the place is no longer
|
||||
available, it should return `null`. In this case, the launcher will remove it from its database. If
|
||||
the place is temporarily unavailable, an exception should be thrown.
|
||||
|
||||
- `item` is the version that the launcher has currently stored
|
||||
|
||||
<!--@include: ./common/_refresh_params.md-->
|
||||
|
||||
The default implementation returns `item` without any changes.
|
||||
|
||||
## Get a place
|
||||
|
||||
If you have set `config.storageStrategy` to `StorageStrategy.StoreReference`, you must override
|
||||
|
||||
```kt
|
||||
suspend fun get(id: String, params: GetParams): Location?
|
||||
```
|
||||
|
||||
This method is used to lookup a place by its `id`. If the place 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 place that is being requested
|
||||
|
||||
<!--@include: ./common/_get_params.md-->
|
||||
|
||||
## Plugin state
|
||||
|
||||
<!--@include: ./common/_plugin_state.md-->
|
||||
|
||||
## Examples
|
||||
|
||||
- [Foursquare plugin](https://github.com/Kvaesitso/Plugin-Foursquare)
|
||||
- [HERE plugin](https://github.com/Kvaesitso/Plugin-HERE)
|
||||
@@ -117,3 +117,4 @@ a `condition`, an `icon`, a `location` name, and a `provider` name.
|
||||
## Examples
|
||||
|
||||
- [OpenWeatherMap plugin](https://github.com/Kvaesitso/Plugin-OpenWeatherMap)
|
||||
- [Breezy Weather plugin](https://github.com/Kvaesitso/Plugin-BreezyWeather)
|
||||
|
||||
@@ -71,6 +71,10 @@ export const DeveloperGuideSidebar: DefaultTheme.SidebarItem[] = [
|
||||
text: 'File Search Provider',
|
||||
link: '/docs/developer-guide/plugins/plugin-types/file-search',
|
||||
},
|
||||
{
|
||||
text: 'Places Search Provider',
|
||||
link: '/docs/developer-guide/plugins/plugin-types/places-search',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -90,6 +94,16 @@ export const DeveloperGuideSidebar: DefaultTheme.SidebarItem[] = [
|
||||
link: '/reference/index.html',
|
||||
target: '_blank',
|
||||
},
|
||||
{
|
||||
text: 'Migrations',
|
||||
items: [
|
||||
{
|
||||
text: 'Migrate to plugin SDK v2.x',
|
||||
link: '/docs/developer-guide/plugins/migrations/v2',
|
||||
},
|
||||
,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user