feat: Add geolocation and haptics plugins (#1599)

* init geolocation plugin

* ios impl (w/o js api)

* generate ts api

* use newer tauri commit

* add temporary postinstall

* include src in files

* guest-js

* just ship dist-js for now

* fix watcher

* fix android compile error

* fix android build for real

* fix heading type

* initial getCurrentPosition android impl (wip)

* prevent panics if errors (strings) are sent over the channel

* Add android watchPosition implementation

* init haptics plugin (android)

* ios and new apis (ANDROID IS LIKELY BROKEN - MAY NOT EVEN COMPILE)

* use tauri-specta that accounts for raw fn arg idents

* add complete android support (it's not working great due to random soft-/hardware support)

* fix(haptics): Fix the NotificationFeedbackType::Success and Version (#1)

* Fix success feedback and version

* Apply suggestions from code review

* Update package.json

---------

Co-authored-by: Fabian-Lars <118197967+FabianLars-crabnebula@users.noreply.github.com>

* android: improve permission callback handling

* keep track of ongoing perms requests

* rebuild

* license headers

* rm sqlite feat

* fmt

* what diff u talkin bout?

* ignore dist-js again

* fix audits

* dedupe api.js

* clippy

* changefiles

* readmes

* clean up todos

* rm dsstore

* rm wrong feats

* mirror

* covector

* rebuild

* ios requires the wry feature

* lint

* update lock

---------

Co-authored-by: fabianlars <fabianlars@fabianlars.de>
Co-authored-by: Brendan Allan <brendonovich@outlook.com>
Co-authored-by: Naman Garg <155433377+naman-crabnebula@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@crabnebula.dev>
This commit is contained in:
Fabian-Lars
2024-08-02 15:45:47 +02:00
committed by GitHub
parent 34df132fb1
commit 9606089b2a
102 changed files with 4896 additions and 52 deletions
@@ -0,0 +1,148 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.geolocation
import android.annotation.SuppressLint
import android.content.Context
import android.location.Location
import android.location.LocationManager
import android.os.SystemClock
import androidx.core.location.LocationManagerCompat
import app.tauri.Logger
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
public class Geolocation(private val context: Context) {
private var fusedLocationClient: FusedLocationProviderClient? = null
private var locationCallback: LocationCallback? = null
fun isLocationServicesEnabled(): Boolean {
val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
return LocationManagerCompat.isLocationEnabled(lm)
}
@SuppressWarnings("MissingPermission")
fun sendLocation(enableHighAccuracy: Boolean, successCallback: (location: Location) -> Unit, errorCallback: (error: String) -> Unit) {
val resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
if (resultCode == ConnectionResult.SUCCESS) {
val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (this.isLocationServicesEnabled()) {
var networkEnabled = false
try {
networkEnabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
} catch (_: Exception) {
Logger.error("isProviderEnabled failed")
}
val lowPrio = if (networkEnabled) Priority.PRIORITY_BALANCED_POWER_ACCURACY else Priority.PRIORITY_LOW_POWER
val prio = if (enableHighAccuracy) Priority.PRIORITY_HIGH_ACCURACY else lowPrio
Logger.error(prio.toString())
LocationServices
.getFusedLocationProviderClient(context)
.getCurrentLocation(prio, null)
.addOnFailureListener { e -> e.message?.let { errorCallback(it) } }
.addOnSuccessListener { location ->
if (location == null) {
errorCallback("Location unavailable.")
} else {
successCallback(location)
}
}
} else {
errorCallback("Location disabled.")
}
} else {
errorCallback("Google Play Services unavailable.")
}
}
@SuppressLint("MissingPermission")
fun requestLocationUpdates(enableHighAccuracy: Boolean, timeout: Long, successCallback: (location: Location) -> Unit, errorCallback: (error: String) -> Unit) {
val resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
if (resultCode == ConnectionResult.SUCCESS) {
clearLocationUpdates()
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (this.isLocationServicesEnabled()) {
var networkEnabled = false
try {
networkEnabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
} catch (_: Exception) {
Logger.error("isProviderEnabled failed")
}
val lowPrio = if (networkEnabled) Priority.PRIORITY_BALANCED_POWER_ACCURACY else Priority.PRIORITY_LOW_POWER
val prio = if (enableHighAccuracy) Priority.PRIORITY_HIGH_ACCURACY else lowPrio
Logger.error(prio.toString())
val locationRequest = LocationRequest.Builder(10000)
.setMaxUpdateDelayMillis(timeout)
.setMinUpdateIntervalMillis(5000)
.setPriority(prio)
.build()
locationCallback =
object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
val lastLocation = locationResult.lastLocation
if (lastLocation == null) {
errorCallback("Location unavailable.")
} else {
successCallback(lastLocation)
}
}
}
fusedLocationClient?.requestLocationUpdates(locationRequest, locationCallback!!, null)
} else {
errorCallback("Location disabled.")
}
} else {
errorCallback("Google Play Services not available.")
}
}
fun clearLocationUpdates() {
if (locationCallback != null) {
fusedLocationClient?.removeLocationUpdates(locationCallback!!)
locationCallback = null
}
}
@SuppressLint("MissingPermission")
fun getLastLocation(maximumAge: Long): Location? {
var lastLoc: Location? = null
val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
for (provider in lm.allProviders) {
val tmpLoc = lm.getLastKnownLocation(provider)
if (tmpLoc != null) {
val locationAge = SystemClock.elapsedRealtimeNanos() - tmpLoc.elapsedRealtimeNanos
val maxAgeNano = maximumAge * 1000000L
if (locationAge <= maxAgeNano && (lastLoc == null || lastLoc.elapsedRealtimeNanos > tmpLoc.elapsedRealtimeNanos)) {
lastLoc = tmpLoc
}
}
}
return lastLoc
}
}