feat(android): integrate web api & native AI chat button (#10239)

This commit is contained in:
Aki Chang
2025-02-18 16:23:25 +08:00
committed by GitHub
parent 892fd16f52
commit 3f4b7ec51e
15 changed files with 302 additions and 23 deletions

View File

@@ -1,26 +1,80 @@
package app.affine.pro
import android.os.Build
import android.os.Bundle
import androidx.annotation.RequiresApi
import android.content.res.ColorStateList
import android.view.Gravity
import android.view.View
import android.widget.Toast
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
import androidx.core.view.updateMargins
import androidx.lifecycle.lifecycleScope
import app.affine.pro.plugin.AIButtonPlugin
import app.affine.pro.plugin.AffineThemePlugin
import app.affine.pro.utils.dp
import com.getcapacitor.BridgeActivity
import com.getcapacitor.plugin.CapacitorCookies
import com.getcapacitor.plugin.CapacitorHttp
import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.coroutines.launch
class MainActivity : BridgeActivity() {
class MainActivity : BridgeActivity(), AIButtonPlugin.Callback, AffineThemePlugin.Callback,
View.OnClickListener {
init {
registerPlugins(
listOf(
AffineThemePlugin::class.java,
AIButtonPlugin::class.java,
CapacitorHttp::class.java,
CapacitorCookies::class.java,
)
)
}
@RequiresApi(Build.VERSION_CODES.R)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
private val fab: FloatingActionButton by lazy {
FloatingActionButton(this).apply {
visibility = View.GONE
layoutParams = CoordinatorLayout.LayoutParams(dp(52), dp(52)).apply {
gravity = Gravity.END or Gravity.BOTTOM
updateMargins(0, 0, dp(24), dp(86))
}
customSize = dp(52)
setImageResource(R.drawable.ic_ai)
setOnClickListener(this@MainActivity)
val parent = bridge.webView.parent as CoordinatorLayout
parent.addView(this)
}
}
override fun present() {
lifecycleScope.launch {
fab.show()
}
}
override fun dismiss() {
lifecycleScope.launch {
fab.hide()
}
}
override fun onThemeChanged(darkMode: Boolean) {
lifecycleScope.launch {
fab.backgroundTintList = ColorStateList.valueOf(
ContextCompat.getColor(
this@MainActivity,
if (darkMode) {
R.color.layer_background_primary_dark
} else {
R.color.layer_background_primary
}
)
)
}
}
override fun onClick(v: View) {
Toast.makeText(this, "TODO: Start AI chat~", Toast.LENGTH_SHORT).show()
}
}

View File

@@ -0,0 +1,27 @@
package app.affine.pro.plugin
import com.getcapacitor.Plugin
import com.getcapacitor.PluginCall
import com.getcapacitor.PluginMethod
import com.getcapacitor.annotation.CapacitorPlugin
@CapacitorPlugin(name = "AIButton")
class AIButtonPlugin : Plugin() {
interface Callback {
fun present()
fun dismiss()
}
@PluginMethod
fun present(call: PluginCall) {
(activity as? Callback)?.present()
call.resolve()
}
@PluginMethod
fun dismiss(call: PluginCall) {
(activity as? Callback)?.dismiss()
call.resolve()
}
}

View File

@@ -0,0 +1,20 @@
package app.affine.pro.plugin
import com.getcapacitor.Plugin
import com.getcapacitor.PluginCall
import com.getcapacitor.PluginMethod
import com.getcapacitor.annotation.CapacitorPlugin
@CapacitorPlugin(name = "AffineTheme")
class AffineThemePlugin : Plugin() {
interface Callback {
fun onThemeChanged(darkMode: Boolean)
}
@PluginMethod
fun onThemeChanged(call: PluginCall) {
(bridge.activity as? Callback)?.onThemeChanged(call.data.optBoolean("darkMode"))
call.resolve()
}
}

View File

@@ -0,0 +1,10 @@
package app.affine.pro.utils
import android.content.Context
import android.util.TypedValue
fun Context.dp(dp: Int) = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp.toFloat(),
resources.displayMetrics
).toInt()