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
+2
View File
@@ -0,0 +1,2 @@
/build
/.tauri
+46
View File
@@ -0,0 +1,46 @@
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "app.tauri.haptics"
compileSdk = 34
defaultConfig {
minSdk = 24
targetSdk = 34
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.0")
implementation("com.google.android.material:material:1.7.0")
implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
implementation(project(":tauri-android"))
}
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
+2
View File
@@ -0,0 +1,2 @@
include ':tauri-android'
project(':tauri-android').projectDir = new File('./.tauri/tauri-api')
@@ -0,0 +1,28 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.haptics
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("app.tauri.haptics", appContext.packageName)
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.VIBRATE" />
</manifest>
@@ -0,0 +1,143 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.haptics
import android.app.Activity
import android.content.Context
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.os.VibratorManager
import app.tauri.Logger
import app.tauri.annotation.Command
import app.tauri.annotation.InvokeArg
import app.tauri.annotation.TauriPlugin
import app.tauri.haptics.patterns.ImpactPatternHeavy
import app.tauri.haptics.patterns.ImpactPatternLight
import app.tauri.haptics.patterns.ImpactPatternMedium
import app.tauri.haptics.patterns.ImpactPatternRigid
import app.tauri.haptics.patterns.ImpactPatternSoft
import app.tauri.haptics.patterns.NotificationPatternError
import app.tauri.haptics.patterns.NotificationPatternSuccess
import app.tauri.haptics.patterns.NotificationPatternWarning
import app.tauri.haptics.patterns.Pattern
import app.tauri.haptics.patterns.SelectionPattern
import app.tauri.plugin.Invoke
import app.tauri.plugin.Plugin
import com.fasterxml.jackson.annotation.JsonProperty
@InvokeArg
class HapticsOptions {
var duration: Long = 300
}
@InvokeArg
class NotificationFeedbackArgs {
val type: NotificationFeedbackType = NotificationFeedbackType.Success
}
@InvokeArg
enum class NotificationFeedbackType {
@JsonProperty("success")
Success,
@JsonProperty("warning")
Warning,
@JsonProperty("error")
Error;
fun into(): Pattern {
return when(this) {
Success -> NotificationPatternSuccess
Warning -> NotificationPatternWarning
Error -> NotificationPatternError
}
}
}
@InvokeArg
class ImpactFeedbackArgs {
val style: ImpactFeedbackStyle = ImpactFeedbackStyle.Medium
}
@InvokeArg
enum class ImpactFeedbackStyle {
@JsonProperty("light")
Light,
@JsonProperty("medium")
Medium,
@JsonProperty("heavy")
Heavy,
@JsonProperty("soft")
Soft,
@JsonProperty("rigid")
Rigid;
fun into(): Pattern {
return when(this) {
Light -> ImpactPatternLight
Medium -> ImpactPatternMedium
Heavy -> ImpactPatternHeavy
Soft -> ImpactPatternSoft
Rigid -> ImpactPatternRigid
}
}
}
@TauriPlugin
class HapticsPlugin(private val activity: Activity): Plugin(activity) {
private val vibrator: Vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibManager = activity.applicationContext.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibManager.defaultVibrator
} else {
@Suppress("DEPRECATION")
activity.applicationContext.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}
//
// TAURI COMMANDS
//
@Command
fun vibrate(invoke: Invoke) {
val args = invoke.parseArgs(HapticsOptions::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(args.duration, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
vibrator.vibrate(args.duration)
}
invoke.resolve()
}
@Command
fun impactFeedback(invoke: Invoke) {
val args = invoke.parseArgs(ImpactFeedbackArgs::class.java)
vibratePattern(args.style.into())
invoke.resolve()
}
@Command
fun notificationFeedback(invoke: Invoke) {
val args = invoke.parseArgs(NotificationFeedbackArgs::class.java)
vibratePattern(args.type.into())
invoke.resolve()
}
// TODO: Consider breaking this up into Start,Change,End like capacitor
@Command
fun selectionFeedback(invoke: Invoke) {
vibratePattern(SelectionPattern)
invoke.resolve()
}
// INTERNAL FUNCTIONS
private fun vibratePattern(pattern: Pattern) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(pattern.timings, pattern.amplitudes, -1))
} else {
vibrator.vibrate(pattern.oldSDKPattern, -1)
}
}
}
@@ -0,0 +1,35 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.haptics.patterns
val ImpactPatternLight = Pattern(
longArrayOf(0, 50),
intArrayOf(0, 30),
longArrayOf(0, 20)
)
val ImpactPatternMedium = Pattern(
longArrayOf(0, 43),
intArrayOf(0, 50),
longArrayOf(0, 43)
)
val ImpactPatternHeavy = Pattern(
longArrayOf(0, 60),
intArrayOf(0, 70),
longArrayOf(0, 61)
)
val ImpactPatternSoft = Pattern(
longArrayOf(0, 50),
intArrayOf(0, 30),
longArrayOf(0, 20)
)
val ImpactPatternRigid = Pattern(
longArrayOf(0, 43),
intArrayOf(0, 50),
longArrayOf(0, 43)
)
@@ -0,0 +1,23 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.haptics.patterns
val NotificationPatternSuccess = Pattern(
longArrayOf(0, 40, 100, 40),
intArrayOf(0, 50, 0, 60),
longArrayOf(0, 40, 100, 40)
)
val NotificationPatternWarning = Pattern(
longArrayOf(0, 40, 120, 60),
intArrayOf(0, 40, 0, 60),
longArrayOf(0, 40, 120, 60)
)
val NotificationPatternError = Pattern(
longArrayOf(0, 60, 100, 40, 80, 50),
intArrayOf(0, 50, 0, 40, 0, 50),
longArrayOf(0, 60, 100, 40, 80, 50)
)
@@ -0,0 +1,11 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.haptics.patterns
class Pattern (
val timings: LongArray,
val amplitudes: IntArray,
val oldSDKPattern: LongArray
) {}
@@ -0,0 +1,11 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.haptics.patterns
val SelectionPattern = Pattern (
timings = longArrayOf(0, 50),
amplitudes = intArrayOf(0, 30),
oldSDKPattern = longArrayOf(0, 70)
)
@@ -0,0 +1,21 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.haptics
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}