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:
Christoph
2024-06-14 11:57:03 +02:00
committed by GitHub
co-authored by MM20
parent cfe80ff3e5
commit 65a9c8c1fe
145 changed files with 5804 additions and 2199 deletions
@@ -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)
+14
View File
@@ -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',
},
,
],
},
],
},
]