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
@@ -0,0 +1,538 @@
package de.mm20.launcher2.icons
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.materialIcon
import androidx.compose.material.icons.materialPath
import androidx.compose.ui.graphics.vector.group
val Icons.Rounded.Battery0Bar
get() = materialIcon("Icons.Rounded.Battery0Bar") {
materialPath {
moveTo(17f, 5f)
verticalLineToRelative(16f)
curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
horizontalLineTo(8f)
curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
verticalLineTo(5f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
verticalLineTo(3f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
verticalLineToRelative(1f)
horizontalLineToRelative(2f)
curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
close()
moveTo(15f, 6f)
horizontalLineTo(9f)
verticalLineToRelative(14f)
horizontalLineToRelative(6f)
verticalLineTo(6f)
close()
}
}
val Icons.Rounded.Battery1Bar
get() = materialIcon("Icons.Rounded.Battery1Bar") {
materialPath {
moveTo(17f, 5f)
verticalLineToRelative(16f)
curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
horizontalLineTo(8f)
curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
verticalLineTo(5f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
verticalLineTo(3f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
verticalLineToRelative(1f)
horizontalLineToRelative(2f)
curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
close()
moveTo(15f, 6f)
horizontalLineTo(9f)
verticalLineToRelative(12f)
horizontalLineToRelative(6f)
verticalLineTo(6f)
close()
}
}
val Icons.Rounded.Battery2Bar
get() = materialIcon("Icons.Rounded.Battery2Bar") {
materialPath {
moveTo(17f, 5f)
verticalLineToRelative(16f)
curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
horizontalLineTo(8f)
curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
verticalLineTo(5f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
verticalLineTo(3f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
verticalLineToRelative(1f)
horizontalLineToRelative(2f)
curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
close()
moveTo(15f, 6f)
horizontalLineTo(9f)
verticalLineToRelative(10f)
horizontalLineToRelative(6f)
verticalLineTo(6f)
close()
}
}
val Icons.Rounded.Battery3Bar
get() = materialIcon("Icons.Rounded.Battery3Bar") {
materialPath {
moveTo(17f, 5f)
verticalLineToRelative(16f)
curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
horizontalLineTo(8f)
curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
verticalLineTo(5f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
verticalLineTo(3f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
verticalLineToRelative(1f)
horizontalLineToRelative(2f)
curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
close()
moveTo(15f, 6f)
horizontalLineTo(9f)
verticalLineToRelative(8f)
horizontalLineToRelative(6f)
verticalLineTo(6f)
close()
}
}
val Icons.Rounded.Battery4Bar
get() = materialIcon("Icons.Rounded.Battery4Bar") {
materialPath {
moveTo(17f, 5f)
verticalLineToRelative(16f)
curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
horizontalLineTo(8f)
curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
verticalLineTo(5f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
verticalLineTo(3f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
verticalLineToRelative(1f)
horizontalLineToRelative(2f)
curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
close()
moveTo(15f, 6f)
horizontalLineTo(9f)
verticalLineToRelative(6f)
horizontalLineToRelative(6f)
verticalLineTo(6f)
close()
}
}
val Icons.Rounded.Battery5Bar
get() = materialIcon("Icons.Rounded.Battery5Bar") {
materialPath {
moveTo(17f, 5f)
verticalLineToRelative(16f)
curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
horizontalLineTo(8f)
curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
verticalLineTo(5f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
verticalLineTo(3f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
verticalLineToRelative(1f)
horizontalLineToRelative(2f)
curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
close()
moveTo(15f, 6f)
horizontalLineTo(9f)
verticalLineToRelative(4f)
horizontalLineToRelative(6f)
verticalLineTo(6f)
close()
}
}
val Icons.Rounded.Battery6Bar
get() = materialIcon("Icons.Rounded.Battery6Bar") {
materialPath {
moveTo(17f, 5f)
verticalLineToRelative(16f)
curveToRelative(0f, 0.55f, -0.45f, 1f, -1f, 1f)
horizontalLineTo(8f)
curveToRelative(-0.55f, 0f, -1f, -0.45f, -1f, -1f)
verticalLineTo(5f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
verticalLineTo(3f)
curveToRelative(0f, -0.55f, 0.45f, -1f, 1f, -1f)
horizontalLineToRelative(2f)
curveToRelative(0.55f, 0f, 1f, 0.45f, 1f, 1f)
verticalLineToRelative(1f)
horizontalLineToRelative(2f)
curveTo(16.55f, 4f, 17f, 4.45f, 17f, 5f)
close()
moveTo(15f, 6f)
horizontalLineTo(9f)
verticalLineToRelative(2f)
horizontalLineToRelative(6f)
verticalLineTo(6f)
close()
}
}
val Icons.Rounded.BatteryCharging0Bar
get() = materialIcon("Icons.Rounded.BatteryCharging0Bar") {
group(
scaleX = 0.26458333f,
scaleY = 0.26458333f,
) {
materialPath {
moveTo(41.574219f, 7.5585938f)
curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
lineTo(37.794922f, 15.117188f)
lineTo(30.236328f, 15.117188f)
curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
lineTo(26.457031f, 79.369141f)
curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
lineTo(60.472656f, 83.150391f)
curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
lineTo(64.251953f, 18.898438f)
curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
lineTo(52.914062f, 15.117188f)
lineTo(52.914062f, 11.337891f)
curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
lineTo(41.574219f, 7.5585938f)
close()
moveTo(34.015625f, 22.677734f)
lineTo(56.693359f, 22.677734f)
lineTo(56.693359f, 75.589844f)
lineTo(34.015625f, 75.589844f)
lineTo(34.015625f, 22.677734f)
close()
moveTo(47.253906f, 32.126953f)
curveTo(46.605486f, 32.112843f, 45.950535f, 32.41843f, 45.582031f, 33.109375f)
lineTo(35.490234f, 52.005859f)
curveTo(34.809919f, 53.290899f, 35.716123f, 54.802734f, 37.152344f, 54.802734f)
lineTo(41.574219f, 54.802734f)
lineTo(41.574219f, 68.03125f)
curveTo(41.574219f, 69.996604f, 44.219867f, 70.640241f, 45.126953f, 68.939453f)
lineTo(55.21875f, 50.041016f)
curveTo(55.899065f, 48.755976f, 54.955066f, 47.244141f, 53.556641f, 47.244141f)
lineTo(49.134766f, 47.244141f)
lineTo(49.134766f, 34.015625f)
curveTo(49.134766f, 32.848696f, 48.201598f, 32.147575f, 47.253906f, 32.126953f)
close()
}
}
}
val Icons.Rounded.BatteryCharging1Bar
get() = materialIcon("Icons.Rounded.BatteryCharging1Bar") {
group(
scaleX = 0.26458333f,
scaleY = 0.26458333f,
) {
materialPath {
moveTo(41.574219f, 7.5585938f)
curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
lineTo(37.794922f, 15.117188f)
lineTo(30.236328f, 15.117188f)
curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
lineTo(26.457031f, 79.369141f)
curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
lineTo(60.472656f, 83.150391f)
curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
lineTo(64.251953f, 18.898438f)
curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
lineTo(52.914062f, 15.117188f)
lineTo(52.914062f, 11.337891f)
curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
lineTo(41.574219f, 7.5585938f)
close()
moveTo(34.015625f, 22.677734f)
lineTo(56.693359f, 22.677734f)
lineTo(56.693359f, 68.03125f)
lineTo(45.611328f, 68.03125f)
lineTo(45.126953f, 68.939453f)
curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
lineTo(34.015625f, 68.03125f)
lineTo(34.015625f, 22.677734f)
close()
moveTo(41.574219f, 68.03125f)
lineTo(45.611328f, 68.03125f)
lineTo(55.21875f, 50.041016f)
curveTo(55.899065f, 48.755976f, 54.955066f, 47.244141f, 53.556641f, 47.244141f)
lineTo(49.134766f, 47.244141f)
lineTo(49.134766f, 34.015625f)
curveTo(49.134766f, 32.050271f, 46.489118f, 31.408588f, 45.582031f, 33.109375f)
lineTo(35.490234f, 52.005859f)
curveTo(34.809919f, 53.290899f, 35.716123f, 54.802734f, 37.152344f, 54.802734f)
lineTo(41.574219f, 54.802734f)
lineTo(41.574219f, 68.03125f)
close()
}
}
}
val Icons.Rounded.BatteryCharging2Bar
get() = materialIcon("Icons.Rounded.BatteryCharging2Bar") {
group(
scaleX = 0.26458333f,
scaleY = 0.26458333f,
) {
materialPath {
moveTo(41.574219f, 7.5585938f)
curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
lineTo(37.794922f, 15.117188f)
lineTo(30.236328f, 15.117188f)
curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
lineTo(26.457031f, 79.369141f)
curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
lineTo(60.472656f, 83.150391f)
curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
lineTo(64.251953f, 18.898438f)
curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
lineTo(52.914062f, 15.117188f)
lineTo(52.914062f, 11.337891f)
curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
lineTo(41.574219f, 7.5585938f)
close()
moveTo(34.015625f, 22.677734f)
lineTo(56.693359f, 22.677734f)
lineTo(56.693359f, 60.472656f)
lineTo(49.648438f, 60.472656f)
lineTo(45.126953f, 68.939453f)
curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
lineTo(41.574219f, 60.472656f)
lineTo(34.015625f, 60.472656f)
lineTo(34.015625f, 22.677734f)
close()
moveTo(41.574219f, 60.472656f)
lineTo(49.648438f, 60.472656f)
lineTo(55.21875f, 50.041016f)
curveTo(55.899065f, 48.755976f, 54.955066f, 47.244141f, 53.556641f, 47.244141f)
lineTo(49.134766f, 47.244141f)
lineTo(49.134766f, 34.015625f)
curveTo(49.134766f, 32.050271f, 46.489118f, 31.408588f, 45.582031f, 33.109375f)
lineTo(35.490234f, 52.005859f)
curveTo(34.809919f, 53.290899f, 35.716123f, 54.802734f, 37.152344f, 54.802734f)
lineTo(41.574219f, 54.802734f)
lineTo(41.574219f, 60.472656f)
close()
}
}
}
val Icons.Rounded.BatteryCharging3Bar
get() = materialIcon("Icons.Rounded.BatteryCharging3Bar") {
group(
scaleX = 0.26458333f,
scaleY = 0.26458333f,
) {
materialPath {
moveTo(41.574219f, 7.5585938f)
curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
lineTo(37.794922f, 15.117188f)
lineTo(30.236328f, 15.117188f)
curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
lineTo(26.457031f, 79.369141f)
curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
lineTo(60.472656f, 83.150391f)
curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
lineTo(64.251953f, 18.898438f)
curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
lineTo(52.914062f, 15.117188f)
lineTo(52.914062f, 11.337891f)
curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
lineTo(41.574219f, 7.5585938f)
close()
moveTo(34.015625f, 22.677734f)
lineTo(56.693359f, 22.677734f)
lineTo(56.693359f, 52.914062f)
lineTo(53.683594f, 52.914062f)
lineTo(45.126953f, 68.939453f)
curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
lineTo(41.574219f, 54.802734f)
lineTo(37.152344f, 54.802734f)
curveTo(36.048819f, 54.802734f, 35.258483f, 53.910469f, 35.259766f, 52.914062f)
lineTo(34.015625f, 52.914062f)
lineTo(34.015625f, 22.677734f)
close()
moveTo(35.259766f, 52.914062f)
lineTo(53.683594f, 52.914062f)
lineTo(55.21875f, 50.041016f)
curveTo(55.899065f, 48.755976f, 54.955066f, 47.244141f, 53.556641f, 47.244141f)
lineTo(49.134766f, 47.244141f)
lineTo(49.134766f, 34.015625f)
curveTo(49.134766f, 32.050271f, 46.489118f, 31.408588f, 45.582031f, 33.109375f)
lineTo(35.490234f, 52.005859f)
curveTo(35.332642f, 52.303535f, 35.260152f, 52.613661f, 35.259766f, 52.914062f)
close()
}
}
}
val Icons.Rounded.BatteryCharging4Bar
get() = materialIcon("Icons.Rounded.BatteryCharging4Bar") {
group(
scaleX = 0.26458333f,
scaleY = 0.26458333f,
) {
materialPath {
moveTo(41.574219f, 7.5585938f)
curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
lineTo(37.794922f, 15.117188f)
lineTo(30.236328f, 15.117188f)
curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
lineTo(26.457031f, 79.369141f)
curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
lineTo(60.472656f, 83.150391f)
curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
lineTo(64.251953f, 18.898438f)
curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
lineTo(52.914062f, 15.117188f)
lineTo(52.914062f, 11.337891f)
curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
lineTo(41.574219f, 7.5585938f)
close()
moveTo(34.015625f, 22.677734f)
lineTo(56.693359f, 22.677734f)
lineTo(56.693359f, 45.353516f)
lineTo(49.134766f, 45.353516f)
lineTo(49.134766f, 47.244141f)
lineTo(53.556641f, 47.244141f)
curveTo(54.955066f, 47.244141f, 55.899065f, 48.755976f, 55.21875f, 50.041016f)
lineTo(45.126953f, 68.939453f)
curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
lineTo(41.574219f, 54.802734f)
lineTo(37.152344f, 54.802734f)
curveTo(35.716123f, 54.802734f, 34.809919f, 53.290899f, 35.490234f, 52.005859f)
lineTo(39.042969f, 45.353516f)
lineTo(34.015625f, 45.353516f)
lineTo(34.015625f, 22.677734f)
close()
moveTo(39.042969f, 45.353516f)
lineTo(49.134766f, 45.353516f)
lineTo(49.134766f, 34.015625f)
curveTo(49.134766f, 32.050271f, 46.489118f, 31.408588f, 45.582031f, 33.109375f)
lineTo(39.042969f, 45.353516f)
close()
}
}
}
val Icons.Rounded.BatteryCharging5Bar
get() = materialIcon("Icons.Rounded.BatteryCharging5Bar") {
group(
scaleX = 0.26458333f,
scaleY = 0.26458333f,
) {
materialPath {
moveTo(41.574219f, 7.5585938f)
curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
lineTo(37.794922f, 15.117188f)
lineTo(30.236328f, 15.117188f)
curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
lineTo(26.457031f, 79.369141f)
curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
lineTo(60.472656f, 83.150391f)
curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
lineTo(64.251953f, 18.898438f)
curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
lineTo(52.914062f, 15.117188f)
lineTo(52.914062f, 11.337891f)
curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
lineTo(41.574219f, 7.5585938f)
close()
moveTo(34.015625f, 22.677734f)
lineTo(56.693359f, 22.677734f)
lineTo(56.693359f, 37.794922f)
lineTo(49.134766f, 37.794922f)
lineTo(49.134766f, 47.244141f)
lineTo(53.556641f, 47.244141f)
curveTo(54.955066f, 47.244141f, 55.899065f, 48.755976f, 55.21875f, 50.041016f)
lineTo(45.126953f, 68.939453f)
curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
lineTo(41.574219f, 54.802734f)
lineTo(37.152344f, 54.802734f)
curveTo(35.716123f, 54.802734f, 34.809919f, 53.290899f, 35.490234f, 52.005859f)
lineTo(43.080078f, 37.794922f)
lineTo(34.015625f, 37.794922f)
lineTo(34.015625f, 22.677734f)
close()
moveTo(43.080078f, 37.794922f)
lineTo(49.134766f, 37.794922f)
lineTo(49.134766f, 34.015625f)
curveTo(49.134766f, 32.050271f, 46.489118f, 31.408588f, 45.582031f, 33.109375f)
lineTo(43.080078f, 37.794922f)
close()
}
}
}
val Icons.Rounded.BatteryCharging6Bar
get() = materialIcon("Icons.Rounded.BatteryCharging6Bar") {
group(
scaleX = 0.26458333f,
scaleY = 0.26458333f,
) {
materialPath {
moveTo(41.574219f, 7.5585938f)
curveTo(39.495479f, 7.5585937f, 37.794922f, 9.2591505f, 37.794922f, 11.337891f)
lineTo(37.794922f, 15.117188f)
lineTo(30.236328f, 15.117188f)
curveTo(28.157588f, 15.117187f, 26.457031f, 16.819697f, 26.457031f, 18.898438f)
lineTo(26.457031f, 79.369141f)
curveTo(26.457031f, 81.447881f, 28.157588f, 83.150391f, 30.236328f, 83.150391f)
lineTo(60.472656f, 83.150391f)
curveTo(62.551396f, 83.150391f, 64.251953f, 81.447881f, 64.251953f, 79.369141f)
lineTo(64.251953f, 18.898438f)
curveTo(64.251953f, 16.819697f, 62.551396f, 15.117188f, 60.472656f, 15.117188f)
lineTo(52.914062f, 15.117188f)
lineTo(52.914062f, 11.337891f)
curveTo(52.914063f, 9.2591505f, 51.213506f, 7.5585938f, 49.134766f, 7.5585938f)
lineTo(41.574219f, 7.5585938f)
close()
moveTo(34.015625f, 22.677734f)
lineTo(56.693359f, 22.677734f)
lineTo(56.693359f, 30.236328f)
lineTo(34.015625f, 30.236328f)
lineTo(34.015625f, 22.677734f)
close()
moveTo(47.253906f, 32.126953f)
curveTo(48.201598f, 32.147575f, 49.134766f, 32.848696f, 49.134766f, 34.015625f)
lineTo(49.134766f, 47.244141f)
lineTo(53.556641f, 47.244141f)
curveTo(54.955066f, 47.244141f, 55.899065f, 48.755976f, 55.21875f, 50.041016f)
lineTo(45.126953f, 68.939453f)
curveTo(44.219867f, 70.640241f, 41.574219f, 69.996604f, 41.574219f, 68.03125f)
lineTo(41.574219f, 54.802734f)
lineTo(37.152344f, 54.802734f)
curveTo(35.716123f, 54.802734f, 34.809919f, 53.290899f, 35.490234f, 52.005859f)
lineTo(45.582031f, 33.109375f)
curveTo(45.950535f, 32.41843f, 46.605486f, 32.112843f, 47.253906f, 32.126953f)
close()
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,7 @@
package de.mm20.launcher2.icons
import android.graphics.drawable.Drawable
import androidx.compose.ui.graphics.vector.ImageVector
sealed interface LauncherIconLayer
@@ -53,4 +54,9 @@ data class TextLayer(
val color: Int = 0,
) : LauncherIconLayer
data class VectorLayer(
val vector: ImageVector,
val color: Int = 0,
) : LauncherIconLayer
object TransparentLayer: LauncherIconLayer
@@ -1,9 +1,11 @@
package de.mm20.launcher2.plugin
import android.content.ContentResolver
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.util.Log
import de.mm20.launcher2.plugin.config.SearchPluginConfig
import de.mm20.launcher2.plugin.config.QueryPluginConfig
import de.mm20.launcher2.plugin.config.WeatherPluginConfig
import de.mm20.launcher2.plugin.contracts.PluginContract
@@ -11,7 +13,25 @@ class PluginApi(
private val pluginAuthority: String,
private val contentResolver: ContentResolver,
) {
fun getSearchPluginConfig(): SearchPluginConfig? {
fun getConfig(): Bundle? {
val configBundle = try {
contentResolver.call(
Uri.Builder()
.scheme("content")
.authority(pluginAuthority)
.build(),
PluginContract.Methods.GetConfig,
null,
null
) ?: return null
} catch (e: Exception) {
Log.e("MM20", "Plugin $pluginAuthority threw exception", e)
return null
}
return configBundle
}
fun getSearchPluginConfig(): QueryPluginConfig? {
val configBundle = try {
contentResolver.call(
Uri.Builder()
@@ -27,7 +47,7 @@ class PluginApi(
return null
}
return SearchPluginConfig(configBundle)
return QueryPluginConfig(configBundle)
}
fun getWeatherPluginConfig(): WeatherPluginConfig? {
@@ -10,7 +10,7 @@ data class PluginPackage(
val author: String? = null,
val settings: Intent? = null,
val plugins: List<Plugin>,
val isOfficial: Boolean = false,
val isVerified: Boolean = false,
) {
val enabled: Boolean = plugins.all { it.enabled }
}
@@ -0,0 +1,190 @@
package de.mm20.launcher2.plugin
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.os.CancellationSignal
import android.util.Log
import de.mm20.launcher2.plugin.config.QueryPluginConfig
import de.mm20.launcher2.plugin.contracts.LocationPluginContract
import de.mm20.launcher2.plugin.contracts.PluginContract
import de.mm20.launcher2.plugin.contracts.SearchPluginContract
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import kotlin.coroutines.resume
private class NotUpdated : Exception("Not updated")
abstract class QueryPluginApi<TQuery, TResult>(
private val context: Context,
private val pluginAuthority: String,
) {
private fun getLanguage() = context.resources.configuration.locales[0].language
fun getConfig(): QueryPluginConfig? {
val configBundle = try {
context.contentResolver.call(
Uri.Builder()
.scheme("content")
.authority(pluginAuthority)
.build(),
PluginContract.Methods.GetConfig,
null,
null
) ?: return null
} catch (e: Exception) {
Log.e("MM20", "Plugin $pluginAuthority threw exception", e)
return null
}
return QueryPluginConfig(configBundle)
}
suspend fun search(query: TQuery, allowNetwork: Boolean): List<TResult> = withContext(Dispatchers.IO) {
val lang = getLanguage()
val uri = Uri.Builder()
.scheme("content")
.authority(pluginAuthority)
.path(LocationPluginContract.Paths.Search)
.appendQueryParameters(query)
.appendQueryParameter(
SearchPluginContract.Params.AllowNetwork,
allowNetwork.toString()
)
.appendQueryParameter(
SearchPluginContract.Params.Lang,
lang
)
.build()
val cancellationSignal = CancellationSignal()
return@withContext suspendCancellableCoroutine {
it.invokeOnCancellation {
cancellationSignal.cancel()
}
val cursor = try {
context.contentResolver.query(
uri,
null,
null,
cancellationSignal
)
} catch (e: Exception) {
Log.e("MM20", "Plugin $pluginAuthority threw exception", e)
it.resume(emptyList())
return@suspendCancellableCoroutine
}
if (cursor == null) {
Log.e("MM20", "Plugin $pluginAuthority returned null cursor")
it.resume(emptyList())
return@suspendCancellableCoroutine
}
val results = cursor.getData() ?: emptyList()
it.resume(results)
}
}
suspend fun get(id: String): Result<TResult?> = withContext(Dispatchers.IO) {
val uri = Uri.Builder()
.scheme("content")
.authority(pluginAuthority)
.path(SearchPluginContract.Paths.Root)
.appendPath(id)
.appendQueryParameter(SearchPluginContract.Params.Lang, getLanguage())
.build()
val cancellationSignal = CancellationSignal()
return@withContext suspendCancellableCoroutine {
it.invokeOnCancellation {
cancellationSignal.cancel()
}
val cursor = try {
context.contentResolver.query(
uri,
null,
null,
cancellationSignal
)
} catch (e: Exception) {
Log.e("MM20", "Plugin $pluginAuthority threw exception", e)
it.resume(Result.failure(e))
return@suspendCancellableCoroutine
}
if (cursor == null) {
Log.e("MM20", "Plugin $pluginAuthority returned null cursor")
it.resume(Result.success(null))
return@suspendCancellableCoroutine
}
val result = cursor.getData()?.firstOrNull()
if (result == null) {
it.resume(Result.success(null))
return@suspendCancellableCoroutine
}
it.resume(Result.success(result))
}
}
suspend fun refresh(item: TResult, lastUpdate: Long): Result<TResult?> = withContext(Dispatchers.IO) {
val uri = Uri.Builder()
.scheme("content")
.authority(pluginAuthority)
.path(SearchPluginContract.Paths.Refresh)
.appendQueryParameter(SearchPluginContract.Params.Lang, getLanguage())
.appendQueryParameter(SearchPluginContract.Params.UpdatedAt, lastUpdate.toString())
.build()
val cancellationSignal = CancellationSignal()
return@withContext suspendCancellableCoroutine {
it.invokeOnCancellation {
cancellationSignal.cancel()
}
val cursor = try {
context.contentResolver.query(
uri,
null,
item.toBundle(),
cancellationSignal
)
} catch (e: Exception) {
Log.e("MM20", "Plugin $pluginAuthority threw exception", e)
it.resume(Result.failure(e))
return@suspendCancellableCoroutine
}
if (cursor == null) {
Log.e("MM20", "Plugin $pluginAuthority returned null cursor")
it.resume(Result.failure(IllegalArgumentException()))
return@suspendCancellableCoroutine
}
if (cursor.extras?.getBoolean(SearchPluginContract.Extras.NotUpdated) == true) {
it.resume(Result.failure(NotUpdated()))
return@suspendCancellableCoroutine
}
val result = cursor.getData()?.firstOrNull()
if (result == null) {
it.resume(Result.success(null))
return@suspendCancellableCoroutine
}
it.resume(Result.success(result))
}
}
protected abstract fun Uri.Builder.appendQueryParameters(query: TQuery): Uri.Builder
protected abstract fun Cursor.getData(): List<TResult>?
protected abstract fun TResult.toBundle(): Bundle
}
@@ -2,8 +2,8 @@ package de.mm20.launcher2.plugin.config
import android.os.Bundle
fun SearchPluginConfig(bundle: Bundle): SearchPluginConfig? {
return SearchPluginConfig(
fun QueryPluginConfig(bundle: Bundle): QueryPluginConfig {
return QueryPluginConfig(
storageStrategy = valueOfOrElse(
bundle.getString(
"storageStrategy",
@@ -1,16 +1,135 @@
package de.mm20.launcher2.search
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.DirectionsBike
import androidx.compose.material.icons.rounded.AccountBalance
import androidx.compose.material.icons.rounded.AttachMoney
import androidx.compose.material.icons.rounded.Attractions
import androidx.compose.material.icons.rounded.BakeryDining
import androidx.compose.material.icons.rounded.Bed
import androidx.compose.material.icons.rounded.Bento
import androidx.compose.material.icons.rounded.Book
import androidx.compose.material.icons.rounded.BreakfastDining
import androidx.compose.material.icons.rounded.BrunchDining
import androidx.compose.material.icons.rounded.CarRental
import androidx.compose.material.icons.rounded.CarRepair
import androidx.compose.material.icons.rounded.Casino
import androidx.compose.material.icons.rounded.Checkroom
import androidx.compose.material.icons.rounded.Church
import androidx.compose.material.icons.rounded.ContentCut
import androidx.compose.material.icons.rounded.Diamond
import androidx.compose.material.icons.rounded.DirectionsBoat
import androidx.compose.material.icons.rounded.DirectionsBus
import androidx.compose.material.icons.rounded.DirectionsCar
import androidx.compose.material.icons.rounded.DirectionsTransit
import androidx.compose.material.icons.rounded.Discount
import androidx.compose.material.icons.rounded.DownhillSkiing
import androidx.compose.material.icons.rounded.ElectricScooter
import androidx.compose.material.icons.rounded.EvStation
import androidx.compose.material.icons.rounded.Fastfood
import androidx.compose.material.icons.rounded.Festival
import androidx.compose.material.icons.rounded.FitnessCenter
import androidx.compose.material.icons.rounded.Flight
import androidx.compose.material.icons.rounded.Forest
import androidx.compose.material.icons.rounded.Gavel
import androidx.compose.material.icons.rounded.Hiking
import androidx.compose.material.icons.rounded.Hotel
import androidx.compose.material.icons.rounded.Icecream
import androidx.compose.material.icons.rounded.Kayaking
import androidx.compose.material.icons.rounded.KebabDining
import androidx.compose.material.icons.rounded.Liquor
import androidx.compose.material.icons.rounded.LocalAtm
import androidx.compose.material.icons.rounded.LocalBar
import androidx.compose.material.icons.rounded.LocalCafe
import androidx.compose.material.icons.rounded.LocalCarWash
import androidx.compose.material.icons.rounded.LocalConvenienceStore
import androidx.compose.material.icons.rounded.LocalFireDepartment
import androidx.compose.material.icons.rounded.LocalFlorist
import androidx.compose.material.icons.rounded.LocalGasStation
import androidx.compose.material.icons.rounded.LocalGroceryStore
import androidx.compose.material.icons.rounded.LocalHospital
import androidx.compose.material.icons.rounded.LocalLaundryService
import androidx.compose.material.icons.rounded.LocalMall
import androidx.compose.material.icons.rounded.LocalParking
import androidx.compose.material.icons.rounded.LocalPharmacy
import androidx.compose.material.icons.rounded.LocalPizza
import androidx.compose.material.icons.rounded.LocalPolice
import androidx.compose.material.icons.rounded.LocalPostOffice
import androidx.compose.material.icons.rounded.LocalTaxi
import androidx.compose.material.icons.rounded.LunchDining
import androidx.compose.material.icons.rounded.Moped
import androidx.compose.material.icons.rounded.Mosque
import androidx.compose.material.icons.rounded.Motorcycle
import androidx.compose.material.icons.rounded.Museum
import androidx.compose.material.icons.rounded.MusicNote
import androidx.compose.material.icons.rounded.Newspaper
import androidx.compose.material.icons.rounded.Nightlife
import androidx.compose.material.icons.rounded.NordicWalking
import androidx.compose.material.icons.rounded.Palette
import androidx.compose.material.icons.rounded.Paragliding
import androidx.compose.material.icons.rounded.Park
import androidx.compose.material.icons.rounded.Pets
import androidx.compose.material.icons.rounded.Phone
import androidx.compose.material.icons.rounded.Place
import androidx.compose.material.icons.rounded.Pool
import androidx.compose.material.icons.rounded.RamenDining
import androidx.compose.material.icons.rounded.Restaurant
import androidx.compose.material.icons.rounded.School
import androidx.compose.material.icons.rounded.ShoppingBag
import androidx.compose.material.icons.rounded.Skateboarding
import androidx.compose.material.icons.rounded.Snowboarding
import androidx.compose.material.icons.rounded.SoupKitchen
import androidx.compose.material.icons.rounded.Sports
import androidx.compose.material.icons.rounded.SportsBar
import androidx.compose.material.icons.rounded.SportsBaseball
import androidx.compose.material.icons.rounded.SportsBasketball
import androidx.compose.material.icons.rounded.SportsCricket
import androidx.compose.material.icons.rounded.SportsFootball
import androidx.compose.material.icons.rounded.SportsGolf
import androidx.compose.material.icons.rounded.SportsGymnastics
import androidx.compose.material.icons.rounded.SportsHandball
import androidx.compose.material.icons.rounded.SportsHockey
import androidx.compose.material.icons.rounded.SportsMartialArts
import androidx.compose.material.icons.rounded.SportsMotorsports
import androidx.compose.material.icons.rounded.SportsRugby
import androidx.compose.material.icons.rounded.SportsSoccer
import androidx.compose.material.icons.rounded.SportsTennis
import androidx.compose.material.icons.rounded.SportsVolleyball
import androidx.compose.material.icons.rounded.Stadium
import androidx.compose.material.icons.rounded.Subway
import androidx.compose.material.icons.rounded.Surfing
import androidx.compose.material.icons.rounded.Synagogue
import androidx.compose.material.icons.rounded.TakeoutDining
import androidx.compose.material.icons.rounded.TempleBuddhist
import androidx.compose.material.icons.rounded.TempleHindu
import androidx.compose.material.icons.rounded.TheaterComedy
import androidx.compose.material.icons.rounded.Theaters
import androidx.compose.material.icons.rounded.Train
import androidx.compose.material.icons.rounded.Tram
import androidx.compose.material.icons.rounded.Wc
import androidx.core.content.ContextCompat
import de.mm20.launcher2.base.R
import de.mm20.launcher2.icons.CableCar
import de.mm20.launcher2.icons.Candle
import de.mm20.launcher2.icons.ColorLayer
import de.mm20.launcher2.icons.Dentistry
import de.mm20.launcher2.icons.Eyeglasses
import de.mm20.launcher2.icons.Monument
import de.mm20.launcher2.icons.StaticLauncherIcon
import de.mm20.launcher2.icons.TintedIconLayer
import kotlinx.collections.immutable.ImmutableList
import java.time.DayOfWeek
import java.time.Duration
import de.mm20.launcher2.icons.Stethoscope
import de.mm20.launcher2.icons.VectorLayer
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.search.location.Address
import de.mm20.launcher2.search.location.Attribution
import de.mm20.launcher2.search.location.Departure
import de.mm20.launcher2.search.location.LocationIcon
import de.mm20.launcher2.search.location.OpeningHours
import de.mm20.launcher2.search.location.OpeningSchedule
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.temporal.TemporalAdjusters
import android.location.Location as AndroidLocation
@@ -20,128 +139,182 @@ interface Location : SavableSearchable {
val longitude: Double
val fixMeUrl: String?
val category: LocationCategory?
val icon: LocationIcon?
val category: String?
val address: Address?
val street: String?
val houseNumber: String?
val openingSchedule: OpeningSchedule?
val websiteUrl: String?
val phoneNumber: String?
val emailAddress: String?
val userRating: Float?
val userRatingCount: Int?
val openingSchedule: OpeningSchedule?
val departures: List<Departure>?
val attribution: Attribution?
get() = null
override val preferDetailsOverLaunch: Boolean
get() = true
override fun launch(context: Context, options: Bundle?): Boolean {
return context.tryStartActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse("geo:$latitude,$longitude?q=${Uri.encode(label)}")
),
options
)
}
override fun getPlaceholderIcon(context: Context): StaticLauncherIcon {
val (resId, bgColor) = when (category) {
LocationCategory.FAST_FOOD, LocationCategory.RESTAURANT -> with(
labelOverride ?: label
) {
when {
contains(
"pizza",
ignoreCase = true
) -> R.drawable.ic_location_pizza to R.color.red
val (vector, bgColor) = when (icon) {
// blue: transportation
LocationIcon.Car -> Icons.Rounded.DirectionsCar to R.color.blue
LocationIcon.CarRental -> Icons.Rounded.CarRental to R.color.blue
LocationIcon.CarRepair -> Icons.Rounded.CarRepair to R.color.blue
LocationIcon.CarWash -> Icons.Rounded.LocalCarWash to R.color.blue
LocationIcon.ChargingStation -> Icons.Rounded.EvStation to R.color.blue
LocationIcon.GasStation -> Icons.Rounded.LocalGasStation to R.color.blue
LocationIcon.Parking -> Icons.Rounded.LocalParking to R.color.blue
LocationIcon.Bus -> Icons.Rounded.DirectionsBus to R.color.blue
LocationIcon.Tram -> Icons.Rounded.Tram to R.color.blue
LocationIcon.Subway -> Icons.Rounded.Subway to R.color.blue
LocationIcon.Train -> Icons.Rounded.Train to R.color.blue
LocationIcon.CableCar -> Icons.Rounded.CableCar to R.color.blue
LocationIcon.Airport -> Icons.Rounded.Flight to R.color.blue
LocationIcon.Boat -> Icons.Rounded.DirectionsBoat to R.color.blue
LocationIcon.Moped -> Icons.Rounded.Moped to R.color.blue
LocationIcon.Bike -> Icons.AutoMirrored.Rounded.DirectionsBike to R.color.blue
LocationIcon.Motorcycle -> Icons.Rounded.Motorcycle to R.color.blue
LocationIcon.ElectricScooter -> Icons.Rounded.ElectricScooter to R.color.blue
LocationIcon.Taxi -> Icons.Rounded.LocalTaxi to R.color.blue
LocationIcon.GenericTransit -> Icons.Rounded.DirectionsTransit to R.color.blue
contains(
"ramen",
ignoreCase = true
) -> R.drawable.ic_location_ramen to R.color.orange
// cyan: art, culture, entertainment
LocationIcon.ArtGallery -> Icons.Rounded.Palette to R.color.cyan
LocationIcon.Museum -> Icons.Rounded.Museum to R.color.cyan
LocationIcon.Theater -> Icons.Rounded.TheaterComedy to R.color.cyan
LocationIcon.MovieTheater -> Icons.Rounded.Theaters to R.color.cyan
LocationIcon.AmusementPark -> Icons.Rounded.Attractions to R.color.cyan
LocationIcon.NightClub -> Icons.Rounded.Nightlife to R.color.cyan
LocationIcon.ConcertHall -> Icons.Rounded.MusicNote to R.color.cyan
LocationIcon.Stadium -> Icons.Rounded.Stadium to R.color.cyan
LocationIcon.Casino -> Icons.Rounded.Casino to R.color.cyan
LocationIcon.Circus -> Icons.Rounded.Festival to R.color.cyan
// pink: hotels
LocationIcon.Hotel -> Icons.Rounded.Hotel to R.color.pink
// orange: food and drink
LocationIcon.Restaurant -> Icons.Rounded.Restaurant to R.color.orange
LocationIcon.Cafe -> Icons.Rounded.LocalCafe to R.color.orange
LocationIcon.FastFood -> Icons.Rounded.Fastfood to R.color.orange
LocationIcon.Pizza -> Icons.Rounded.LocalPizza to R.color.orange
LocationIcon.Burger -> Icons.Rounded.LunchDining to R.color.orange
LocationIcon.Kebab -> Icons.Rounded.KebabDining to R.color.orange
LocationIcon.IceCream -> Icons.Rounded.Icecream to R.color.orange
LocationIcon.Ramen -> Icons.Rounded.RamenDining to R.color.orange
LocationIcon.Soup -> Icons.Rounded.SoupKitchen to R.color.orange
LocationIcon.Bar -> Icons.Rounded.LocalBar to R.color.orange
LocationIcon.Brunch -> Icons.Rounded.BrunchDining to R.color.orange
LocationIcon.Breakfast -> Icons.Rounded.BreakfastDining to R.color.orange
LocationIcon.Pub -> Icons.Rounded.SportsBar to R.color.orange
LocationIcon.JapaneseCuisine -> Icons.Rounded.Bento to R.color.orange
LocationIcon.AsianCuisine -> Icons.Rounded.TakeoutDining to R.color.orange
// indigo: business and shopping
LocationIcon.Shopping -> Icons.Rounded.ShoppingBag to R.color.indigo
LocationIcon.Supermarket -> Icons.Rounded.LocalGroceryStore to R.color.indigo
LocationIcon.Florist -> Icons.Rounded.LocalFlorist to R.color.indigo
LocationIcon.Kiosk -> Icons.Rounded.Newspaper to R.color.indigo
LocationIcon.FurnitureStore -> Icons.Rounded.Bed to R.color.indigo
LocationIcon.CellPhoneStore -> Icons.Rounded.Phone to R.color.indigo
LocationIcon.BookStore -> Icons.Rounded.Book to R.color.indigo
LocationIcon.ClothingStore -> Icons.Rounded.Checkroom to R.color.indigo
LocationIcon.ConvenienceStore -> Icons.Rounded.LocalConvenienceStore to R.color.indigo
LocationIcon.DiscountStore -> Icons.Rounded.Discount to R.color.indigo
LocationIcon.JewelryStore -> Icons.Rounded.Diamond to R.color.indigo
LocationIcon.ShoppingMall -> Icons.Rounded.LocalMall to R.color.indigo
LocationIcon.LiquorStore -> Icons.Rounded.Liquor to R.color.indigo
LocationIcon.PetStore -> Icons.Rounded.Pets to R.color.indigo
LocationIcon.Bakery -> Icons.Rounded.BakeryDining to R.color.indigo
LocationIcon.Optician -> Icons.Rounded.Eyeglasses to R.color.indigo
LocationIcon.Pharmacy -> Icons.Rounded.LocalPharmacy to R.color.indigo
LocationIcon.HairSalon -> Icons.Rounded.ContentCut to R.color.indigo
LocationIcon.Laundromat -> Icons.Rounded.LocalLaundryService to R.color.indigo
// purple: sports and recreation
LocationIcon.Sports -> Icons.Rounded.Sports to R.color.purple
LocationIcon.FitnessCenter -> Icons.Rounded.FitnessCenter to R.color.purple
LocationIcon.Soccer -> Icons.Rounded.SportsSoccer to R.color.purple
LocationIcon.Basketball -> Icons.Rounded.SportsBasketball to R.color.purple
LocationIcon.Golf -> Icons.Rounded.SportsGolf to R.color.purple
LocationIcon.Tennis -> Icons.Rounded.SportsTennis to R.color.purple
LocationIcon.Baseball -> Icons.Rounded.SportsBaseball to R.color.purple
LocationIcon.Rugby -> Icons.Rounded.SportsRugby to R.color.purple
LocationIcon.AmericanFootball -> Icons.Rounded.SportsFootball to R.color.purple
LocationIcon.Hiking -> Icons.Rounded.Hiking to R.color.purple
LocationIcon.Swimming -> Icons.Rounded.Pool to R.color.purple
LocationIcon.Surfing -> Icons.Rounded.Surfing to R.color.purple
LocationIcon.Motorsports -> Icons.Rounded.SportsMotorsports to R.color.purple
LocationIcon.Handball -> Icons.Rounded.SportsHandball to R.color.purple
LocationIcon.Volleyball -> Icons.Rounded.SportsVolleyball to R.color.purple
LocationIcon.Skiing -> Icons.Rounded.DownhillSkiing to R.color.purple
LocationIcon.Kayaking -> Icons.Rounded.Kayaking to R.color.purple
LocationIcon.Skateboarding -> Icons.Rounded.Skateboarding to R.color.purple
LocationIcon.Cricket -> Icons.Rounded.SportsCricket to R.color.purple
LocationIcon.MartialArts -> Icons.Rounded.SportsMartialArts to R.color.purple
LocationIcon.NordicWalking -> Icons.Rounded.NordicWalking to R.color.purple
LocationIcon.Paragliding -> Icons.Rounded.Paragliding to R.color.purple
LocationIcon.Gymnastics -> Icons.Rounded.SportsGymnastics to R.color.purple
LocationIcon.Snowboarding -> Icons.Rounded.Snowboarding to R.color.purple
LocationIcon.Hockey -> Icons.Rounded.SportsHockey to R.color.purple
// green: finances
LocationIcon.Bank -> Icons.Rounded.AttachMoney to R.color.green
LocationIcon.Atm -> Icons.Rounded.LocalAtm to R.color.green
// red: health
LocationIcon.Hospital -> Icons.Rounded.LocalHospital to R.color.red
LocationIcon.Clinic -> Icons.Rounded.LocalHospital to R.color.red
LocationIcon.Dentist -> Icons.Rounded.Dentistry to R.color.red
LocationIcon.Physician -> Icons.Rounded.Stethoscope to R.color.red
// light green: nature
LocationIcon.Park -> Icons.Rounded.Park to R.color.lightgreen
LocationIcon.Forest -> Icons.Rounded.Forest to R.color.lightgreen
// brown: places of worship and remembrance
LocationIcon.Monument -> Icons.Rounded.Monument to R.color.brown
LocationIcon.Church -> Icons.Rounded.Church to R.color.brown
LocationIcon.Mosque -> Icons.Rounded.Mosque to R.color.brown
LocationIcon.Synagogue -> Icons.Rounded.Synagogue to R.color.brown
LocationIcon.HinduTemple -> Icons.Rounded.TempleHindu to R.color.brown
LocationIcon.BuddhistTemple -> Icons.Rounded.TempleBuddhist to R.color.brown
LocationIcon.Candle -> Icons.Rounded.Candle to R.color.brown
// bluegrey: public services
LocationIcon.GovernmentBuilding -> Icons.Rounded.AccountBalance to R.color.bluegrey
LocationIcon.Police -> Icons.Rounded.LocalPolice to R.color.bluegrey
LocationIcon.FireDepartment -> Icons.Rounded.LocalFireDepartment to R.color.bluegrey
LocationIcon.Courthouse -> Icons.Rounded.Gavel to R.color.bluegrey
LocationIcon.PostOffice -> Icons.Rounded.LocalPostOffice to R.color.bluegrey
LocationIcon.Library -> Icons.Rounded.Book to R.color.bluegrey
LocationIcon.School -> Icons.Rounded.School to R.color.bluegrey
LocationIcon.University -> Icons.Rounded.School to R.color.bluegrey
LocationIcon.PublicBathroom -> Icons.Rounded.Wc to R.color.bluegrey
contains(
"tapas",
ignoreCase = true
) -> R.drawable.ic_location_tapas to R.color.orange
contains(
"keba" /* b or p, depending on locale */,
ignoreCase = true
) -> R.drawable.ic_location_kebab to R.color.orange
category == LocationCategory.FAST_FOOD -> R.drawable.ic_location_fastfood to R.color.orange
else -> R.drawable.ic_location_restaurant to R.color.red
}
}
LocationCategory.BAR -> R.drawable.ic_location_bar to R.color.amber
LocationCategory.CAFE, LocationCategory.COFFEE_SHOP -> R.drawable.ic_location_cafe to R.color.brown
LocationCategory.HOTEL -> R.drawable.ic_location_hotel to R.color.green
LocationCategory.SUPERMARKET -> R.drawable.ic_location_supermarket to R.color.lightblue
LocationCategory.SCHOOL -> R.drawable.ic_location_school to R.color.purple
LocationCategory.PARKING -> R.drawable.ic_location_parking to R.color.blue
LocationCategory.FUEL -> R.drawable.ic_location_fuel to R.color.teal
LocationCategory.TOILETS -> R.drawable.ic_location_toilets to R.color.blue
LocationCategory.PHARMACY -> R.drawable.ic_location_pharmacy to R.color.pink
LocationCategory.HOSPITAL, LocationCategory.CLINIC -> R.drawable.ic_location_hospital to R.color.red
LocationCategory.POST_OFFICE -> R.drawable.ic_location_post_office to R.color.yellow
LocationCategory.PUB, LocationCategory.BIERGARTEN -> R.drawable.ic_location_pub to R.color.amber
LocationCategory.GRAVE_YARD -> R.drawable.ic_location_grave_yard to R.color.grey
LocationCategory.DOCTORS -> R.drawable.ic_location_doctors to R.color.red
LocationCategory.POLICE -> R.drawable.ic_location_police to R.color.blue
LocationCategory.DENTIST -> R.drawable.ic_location_dentist to R.color.lightblue
LocationCategory.LIBRARY, LocationCategory.BOOKS -> R.drawable.ic_location_library to R.color.brown
LocationCategory.COLLEGE, LocationCategory.UNIVERSITY -> R.drawable.ic_location_college to R.color.purple
LocationCategory.ICE_CREAM -> R.drawable.ic_location_ice_cream to R.color.pink
LocationCategory.THEATRE -> R.drawable.ic_location_theatre to R.color.purple
LocationCategory.PUBLIC_BUILDING -> R.drawable.ic_location_public_building to R.color.bluegrey
LocationCategory.CINEMA -> R.drawable.ic_location_cinema to R.color.purple
LocationCategory.NIGHTCLUB -> R.drawable.ic_location_nightclub to R.color.purple
LocationCategory.CONVENIENCE -> R.drawable.ic_location_convenience to R.color.lightblue
LocationCategory.CLOTHES -> R.drawable.ic_location_clothes to R.color.pink
LocationCategory.HAIRDRESSER, LocationCategory.BEAUTY -> R.drawable.ic_location_hairdresser to R.color.pink
LocationCategory.CAR_REPAIR -> R.drawable.ic_location_car_repair to R.color.blue
LocationCategory.BAKERY -> R.drawable.ic_location_bakery to R.color.brown
LocationCategory.CAR -> R.drawable.ic_location_car to R.color.blue
LocationCategory.MOBILE_PHONE -> R.drawable.ic_location_mobile_phone to R.color.blue
LocationCategory.FURNITURE -> R.drawable.ic_location_furniture to R.color.brown
LocationCategory.ALCOHOL -> R.drawable.ic_location_alcohol to R.color.amber
LocationCategory.FLORIST -> R.drawable.ic_location_florist to R.color.green
LocationCategory.HARDWARE -> R.drawable.ic_location_hardware to R.color.brown
LocationCategory.ELECTRONICS -> R.drawable.ic_location_electronics to R.color.blue
LocationCategory.SHOES -> R.drawable.ic_location_shoes to R.color.pink
LocationCategory.MALL, LocationCategory.DEPARTMENT_STORE, LocationCategory.CHEMIST -> R.drawable.ic_location_mall to R.color.blue
LocationCategory.OPTICIAN -> R.drawable.ic_location_optician to R.color.blue
LocationCategory.JEWELRY -> R.drawable.ic_location_jewelry to R.color.pink
LocationCategory.GIFT -> R.drawable.ic_location_gift to R.color.pink
LocationCategory.BICYCLE -> R.drawable.ic_location_bicycle to R.color.blue
LocationCategory.LAUNDRY -> R.drawable.ic_location_laundry to R.color.blue
LocationCategory.COMPUTER -> R.drawable.ic_location_computer to R.color.blue
LocationCategory.TOBACCO -> R.drawable.ic_location_tobacco to R.color.amber
LocationCategory.WINE -> R.drawable.ic_location_wine to R.color.amber
LocationCategory.PHOTO -> R.drawable.ic_location_photo to R.color.blue
LocationCategory.BANK -> R.drawable.ic_location_bank to R.color.blue
LocationCategory.SOCCER -> R.drawable.ic_location_soccer to R.color.green
LocationCategory.BASKETBALL -> R.drawable.ic_location_basketball to R.color.orange
LocationCategory.TENNIS -> R.drawable.ic_location_tennis to R.color.orange
LocationCategory.FITNESS, LocationCategory.FITNESS_CENTRE -> R.drawable.ic_location_fitness to R.color.orange
LocationCategory.TRAM_STOP -> R.drawable.ic_location_tram_stop to R.color.blue
LocationCategory.RAILWAY_STATION -> R.drawable.ic_location_railway_stop to R.color.lightblue
LocationCategory.BUS_STATION, LocationCategory.BUS_STOP -> R.drawable.ic_location_bus_station to R.color.blue
LocationCategory.ATM -> R.drawable.ic_location_atm to R.color.green
LocationCategory.ART -> R.drawable.ic_location_art to R.color.deeporange
LocationCategory.KIOSK -> R.drawable.ic_location_kiosk to R.color.bluegrey
LocationCategory.MUSEUM -> R.drawable.ic_location_museum to R.color.deeporange
LocationCategory.PARCEL_LOCKER -> R.drawable.ic_location_parcel_locker to R.color.bluegrey
LocationCategory.TRAVEL_AGENCY -> R.drawable.ic_location_travel_agency to R.color.lightblue
else -> R.drawable.ic_location_place to R.color.bluegrey
null -> Icons.Rounded.Place to R.color.bluegrey
}
return StaticLauncherIcon(
foregroundLayer = TintedIconLayer(
icon = ContextCompat.getDrawable(context, resId)!!,
scale = 0.5f,
foregroundLayer = VectorLayer(
vector = vector,
color = ContextCompat.getColor(context, bgColor)
),
backgroundLayer = ColorLayer(ContextCompat.getColor(context, bgColor))
)
}
fun toAndroidLocation(): AndroidLocation {
val location = AndroidLocation("KvaesitsoLocationProvider")
location.latitude = latitude
location.longitude = longitude
return location
}
fun toAndroidLocation(): AndroidLocation =
AndroidLocation("KvaesitsoLocationProvider").apply {
this.latitude = this@Location.latitude
this.longitude = this@Location.longitude
}
fun distanceTo(androidLocation: AndroidLocation): Float {
return androidLocation.distanceTo(this.toAndroidLocation())
@@ -149,107 +322,25 @@ interface Location : SavableSearchable {
fun distanceTo(otherLocation: Location): Float =
this.distanceTo(otherLocation.toAndroidLocation())
fun distanceTo(
latitude: Double,
longitude: Double,
locationProvider: String = "KvaesitsoLocationProvider"
): Float =
this.distanceTo(AndroidLocation(locationProvider).apply {
this.latitude = latitude
this.longitude = longitude
})
}
// https://taginfo.openstreetmap.org/tags
// 'amenity', 'shop', 'sport' of which the most important
enum class LocationCategory {
RESTAURANT,
FAST_FOOD,
BAR,
CAFE,
HOTEL,
SUPERMARKET,
OTHER,
SCHOOL,
PARKING,
FUEL,
TOILETS,
PHARMACY,
HOSPITAL,
POST_OFFICE,
PUB,
GRAVE_YARD,
DOCTORS,
POLICE,
DENTIST,
LIBRARY,
COLLEGE,
ICE_CREAM,
THEATRE,
PUBLIC_BUILDING,
CINEMA,
NIGHTCLUB,
BIERGARTEN,
CLINIC,
UNIVERSITY,
DEPARTMENT_STORE,
CLOTHES,
CONVENIENCE,
HAIRDRESSER,
CAR_REPAIR,
BEAUTY,
BOOKS,
BAKERY,
CAR,
MOBILE_PHONE,
FURNITURE,
ALCOHOL,
FLORIST,
HARDWARE,
ELECTRONICS,
SHOES,
MALL,
OPTICIAN,
JEWELRY,
GIFT,
BICYCLE,
LAUNDRY,
COMPUTER,
TOBACCO,
WINE,
PHOTO,
COFFEE_SHOP,
BANK,
SOCCER,
BASKETBALL,
TENNIS,
FITNESS,
TRAM_STOP,
RAILWAY_STATION,
RAILWAY_STOP,
BUS_STATION,
ATM,
ART,
KIOSK,
BUS_STOP,
MUSEUM,
PARCEL_LOCKER,
CHEMIST,
TRAVEL_AGENCY,
FITNESS_CENTRE
fun OpeningSchedule.isOpen(date: LocalDateTime = LocalDateTime.now()): Boolean = when (this) {
is OpeningSchedule.TwentyFourSeven -> true
is OpeningSchedule.Hours -> openingHours.any { it.isOpen(date) }
}
data class OpeningHours(
val dayOfWeek: DayOfWeek,
val startTime: LocalTime,
val duration: Duration
) {
fun isOpen(date: LocalDateTime = LocalDateTime.now()): Boolean {
val startTime = date.with(TemporalAdjusters.previousOrSame(dayOfWeek)).with(startTime)
val endTime = startTime.plus(duration)
return date in startTime..<endTime
}
override fun toString(): String = "$dayOfWeek $startTime-${startTime.plus(duration)}"
}
data class OpeningSchedule(
val isTwentyFourSeven: Boolean,
val openingHours: ImmutableList<OpeningHours>
) {
fun isOpen(date: LocalDateTime = LocalDateTime.now()): Boolean {
return isTwentyFourSeven || openingHours.any { it.isOpen(date) }
}
fun OpeningHours.isOpen(date: LocalDateTime = LocalDateTime.now()): Boolean {
val startTime = date.with(TemporalAdjusters.previousOrSame(dayOfWeek)).with(startTime)
val endTime = startTime.plus(duration)
return date in startTime..<endTime
}
@@ -8,7 +8,7 @@ package de.mm20.launcher2.search
*/
interface UpdatableSearchable<T : SavableSearchable> {
val timestamp: Long
val updatedSelf: (suspend () -> UpdateResult<T>)?
val updatedSelf: (suspend (SavableSearchable) -> UpdateResult<T>)?
}
sealed class UpdateResult<out T> {
@@ -16,3 +16,16 @@ sealed class UpdateResult<out T> {
data class TemporarilyUnavailable<T>(val cause: Throwable? = null) : UpdateResult<T>()
data class PermanentlyUnavailable<T>(val cause: Throwable? = null) : UpdateResult<T>()
}
fun <T>Result<T?>.asUpdateResult(): UpdateResult<T> {
return if (isSuccess) {
val refreshed = getOrNull()
if (refreshed == null) {
UpdateResult.PermanentlyUnavailable()
} else {
UpdateResult.Success(refreshed)
}
} else {
UpdateResult.TemporarilyUnavailable(exceptionOrNull())
}
}
@@ -1,5 +1,7 @@
package de.mm20.launcher2.search
import de.mm20.launcher2.search.location.OpeningHours
import de.mm20.launcher2.search.location.OpeningSchedule
import kotlinx.collections.immutable.persistentListOf
import org.junit.Assert
import org.junit.Test