Replace manual native bindings with UniFFI

This commit is contained in:
Lonami Exo 2022-10-27 17:38:46 +02:00
parent fd1dac1045
commit 812597f027
12 changed files with 121 additions and 208 deletions

View File

@ -57,6 +57,7 @@ dependencies {
implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
implementation 'androidx.compose.material:material:1.2.1' implementation 'androidx.compose.material:material:1.2.1'
implementation "androidx.navigation:navigation-compose:2.5.2" implementation "androidx.navigation:navigation-compose:2.5.2"
implementation "net.java.dev.jna:jna:5.12.0@aar"
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
@ -68,7 +69,7 @@ dependencies {
// See https://github.com/mozilla/rust-android-gradle for other targets and required toolchains // See https://github.com/mozilla/rust-android-gradle for other targets and required toolchains
cargo { cargo {
module = "../native" module = "../native"
libname = "talaria" libname = "uniffi_talaria"
targets = ["arm64"] targets = ["arm64"]
profile = 'release' profile = 'release'
} }
@ -78,3 +79,14 @@ tasks.whenTaskAdded { task ->
task.dependsOn 'cargoBuild' task.dependsOn 'cargoBuild'
} }
} }
// See https://mozilla.github.io/uniffi-rs/kotlin/gradle.html for more details on this snippet
android.applicationVariants.all { variant ->
def t = tasks.register("generate${variant.name.capitalize()}UniFFIBindings", Exec) {
workingDir "${project.projectDir}"
commandLine 'uniffi-bindgen', 'generate', '../native/src/talaria.udl', '--language', 'kotlin', '--no-format', '--out-dir', "${buildDir}/generated/source/uniffi/${variant.name}/java"
}
variant.javaCompileProvider.get().dependsOn(t)
def sourceSet = variant.sourceSets.find { it.name == variant.name }
sourceSet.java.srcDir new File(buildDir, "generated/source/uniffi/${variant.name}/java")
}

View File

@ -7,8 +7,9 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface import androidx.compose.material.Surface
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import dev.lonami.talaria.bindings.Native
import dev.lonami.talaria.ui.theme.TalariaTheme import dev.lonami.talaria.ui.theme.TalariaTheme
import uniffi.talaria.initClient
import uniffi.talaria.initDatabase
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -24,7 +25,7 @@ class MainActivity : ComponentActivity() {
} }
} }
Native.initDatabase(getDatabasePath("talaria.db").path) initDatabase(getDatabasePath("talaria.db").path)
Native.initClient() initClient()
} }
} }

View File

@ -18,11 +18,11 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import dev.lonami.talaria.bindings.Native
import dev.lonami.talaria.ui.screens.ChatScreen import dev.lonami.talaria.ui.screens.ChatScreen
import dev.lonami.talaria.ui.screens.DialogScreen import dev.lonami.talaria.ui.screens.DialogScreen
import dev.lonami.talaria.ui.screens.LoginScreen import dev.lonami.talaria.ui.screens.LoginScreen
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import uniffi.talaria.needLogin
enum class TalariaScreen(@StringRes val title: Int) { enum class TalariaScreen(@StringRes val title: Int) {
Login(title = R.string.app_name), Login(title = R.string.app_name),
@ -148,7 +148,7 @@ fun TalariaApp(modifier: Modifier = Modifier) {
val currentScreen = val currentScreen =
TalariaScreen.valueOf(backStackEntry?.destination?.route ?: TalariaScreen.Login.name) TalariaScreen.valueOf(backStackEntry?.destination?.route ?: TalariaScreen.Login.name)
val loggedIn by remember { mutableStateOf(!Native.needLogin()) } val loggedIn by remember { mutableStateOf(!needLogin()) }
var selectedDialog by remember { mutableStateOf("") } var selectedDialog by remember { mutableStateOf("") }
val drawerState = rememberDrawerState(DrawerValue.Closed) val drawerState = rememberDrawerState(DrawerValue.Closed)

View File

@ -1,24 +0,0 @@
package dev.lonami.talaria.bindings
object Native {
init {
System.loadLibrary("talaria")
}
external fun initDatabase(path: String)
external fun initClient()
external fun needLogin(): Boolean
external fun requestLoginCode(phone: String): Long
external fun signIn(tokenPtr: Long, code: String)
external fun getDialogs(): Long
external fun dialogCount(dialogsPtr: Long): Int
external fun dialogPacked(dialogsPtr: Long, index: Int): String
external fun dialogTitle(dialogsPtr: Long, index: Int): String
external fun dialogSender(dialogsPtr: Long, index: Int): String
external fun dialogText(dialogsPtr: Long, index: Int): String
external fun dialogTime(dialogsPtr: Long, index: Int): String
external fun dialogAck(dialogsPtr: Long, index: Int): Int
external fun dialogPin(dialogsPtr: Long, index: Int): Int
external fun freeDialogs(dialogsPtr: Long)
external fun sendMessage(packed: String, text: String)
}

View File

@ -1,9 +1,9 @@
package dev.lonami.talaria.data package dev.lonami.talaria.data
import dev.lonami.talaria.bindings.Native
import dev.lonami.talaria.models.Dialog import dev.lonami.talaria.models.Dialog
import dev.lonami.talaria.models.MessageAck import dev.lonami.talaria.models.MessageAck
import dev.lonami.talaria.models.MessagePreview import dev.lonami.talaria.models.MessagePreview
import uniffi.talaria.*
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@ -15,34 +15,34 @@ class NativeDialogRepository : DialogRepository {
override fun loadDialogs(): List<Dialog> { override fun loadDialogs(): List<Dialog> {
val dialogs = mutableListOf<Dialog>() val dialogs = mutableListOf<Dialog>()
val dialogPtr = Native.getDialogs() val dialogPtr = getDialogs()
try { try {
val dialogCount = Native.dialogCount(dialogPtr) val dialogCount = dialogCount(dialogPtr)
for (i in 0 until dialogCount) { for (i in 0U until dialogCount) {
dialogs.add( dialogs.add(
Dialog( Dialog(
id = Native.dialogPacked(dialogPtr, i), id = dialogPacked(dialogPtr, i),
title = Native.dialogTitle(dialogPtr, i), title = dialogTitle(dialogPtr, i),
lastMessage = MessagePreview( lastMessage = MessagePreview(
sender = Native.dialogSender(dialogPtr, i), sender = dialogSender(dialogPtr, i),
text = Native.dialogText(dialogPtr, i), text = dialogText(dialogPtr, i),
date = LocalDateTime.parse( date = LocalDateTime.parse(
Native.dialogTime(dialogPtr, i), dialogTime(dialogPtr, i),
DateTimeFormatter.ISO_OFFSET_DATE_TIME DateTimeFormatter.ISO_OFFSET_DATE_TIME
), ),
ack = when (Native.dialogAck(dialogPtr, i)) { ack = when (dialogAck(dialogPtr, i)) {
0 -> MessageAck.RECEIVED 0U -> MessageAck.RECEIVED
1 -> MessageAck.SENT 1U -> MessageAck.SENT
2 -> MessageAck.SEEN 2U -> MessageAck.SEEN
else -> MessageAck.RECEIVED else -> MessageAck.RECEIVED
} }
), ),
pinned = Native.dialogPin(dialogPtr, i) != 0 pinned = dialogPin(dialogPtr, i) != 0U
) )
) )
} }
} finally { } finally {
Native.freeDialogs(dialogPtr) freeDialogs(dialogPtr)
} }
return dialogs return dialogs

View File

@ -18,8 +18,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import dev.lonami.talaria.R import dev.lonami.talaria.R
import dev.lonami.talaria.bindings.Native
import dev.lonami.talaria.ui.theme.TalariaTheme import dev.lonami.talaria.ui.theme.TalariaTheme
import uniffi.talaria.requestLoginCode
import uniffi.talaria.signIn
enum class LoginStage { enum class LoginStage {
ASK_PHONE, ASK_PHONE,
@ -105,7 +106,7 @@ fun LoginScreen(onConfirmOtp: () -> Unit, modifier: Modifier = Modifier) {
var phone by remember { mutableStateOf("") } var phone by remember { mutableStateOf("") }
var otp by remember { mutableStateOf("") } var otp by remember { mutableStateOf("") }
var tokenPtr by remember { mutableStateOf(0L) } var tokenPtr by remember { mutableStateOf(0UL) }
Column( Column(
modifier = modifier modifier = modifier
@ -127,7 +128,7 @@ fun LoginScreen(onConfirmOtp: () -> Unit, modifier: Modifier = Modifier) {
phone, phone,
onPhoneChanged = { phone = it }, onPhoneChanged = { phone = it },
onSendCode = { onSendCode = {
tokenPtr = Native.requestLoginCode(phone) tokenPtr = requestLoginCode(phone)
stage = LoginStage.ASK_CODE stage = LoginStage.ASK_CODE
} }
) )
@ -135,7 +136,7 @@ fun LoginScreen(onConfirmOtp: () -> Unit, modifier: Modifier = Modifier) {
otp, otp,
onOtpChanged = { otp = it }, onOtpChanged = { otp = it },
onConfirmOtp = { onConfirmOtp = {
Native.signIn(tokenPtr, otp) signIn(tokenPtr, otp)
onConfirmOtp() onConfirmOtp()
} }
) )

View File

@ -1,7 +1,6 @@
package dev.lonami.talaria.ui.state package dev.lonami.talaria.ui.state
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import dev.lonami.talaria.bindings.Native
import dev.lonami.talaria.data.MessageRepository import dev.lonami.talaria.data.MessageRepository
import dev.lonami.talaria.models.Message import dev.lonami.talaria.models.Message
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -18,7 +17,7 @@ class ChatViewModel : ViewModel() {
} }
fun sendMessage(dialog: String, message: String) { fun sendMessage(dialog: String, message: String) {
Native.sendMessage(dialog, message) sendMessage(dialog, message)
_uiState.update { state -> _uiState.update { state ->
state.messages.add(Message("You", message)) state.messages.add(Message("You", message))
state state

View File

@ -4,8 +4,10 @@ buildscript {
} }
}// Top-level build file where you can add configuration options common to all sub-projects/modules. }// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id 'com.android.application' version '7.3.0' apply false // uniffi-recommended Gradle script fails with Android plugin 7.3.1.
id 'com.android.library' version '7.3.0' apply false // See https://github.com/mozilla/uniffi-rs/issues/1386 for details.
id 'com.android.application' version '7.2.2' apply false
id 'com.android.library' version '7.2.2' apply false
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
id "org.mozilla.rust-android-gradle.rust-android" version "0.9.3" id "org.mozilla.rust-android-gradle.rust-android" version "0.9.3"
} }

View File

@ -4,11 +4,12 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[lib] [lib]
name = "talaria" name = "uniffi_talaria"
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
jni = { version = "0.10.2", default-features = false } jni = { version = "0.10.2", default-features = false }
uniffi = "0.21.0"
# v0.4 of grammers-* is currently unreleased; clone the project and use path dependencies # v0.4 of grammers-* is currently unreleased; clone the project and use path dependencies
grammers-client = { version = "0.4.1" } grammers-client = { version = "0.4.1" }
grammers-tl-types = { version = "0.4.0" } grammers-tl-types = { version = "0.4.0" }
@ -19,5 +20,8 @@ android_logger = "0.11.1"
once_cell = "1.15.0" once_cell = "1.15.0"
sqlite = "0.27.0" sqlite = "0.27.0"
[build-dependencies]
uniffi_build = "0.21.0"
[profile.release] [profile.release]
lto = true lto = true

3
native/build.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
uniffi_build::generate_scaffolding("src/talaria.udl").unwrap();
}

View File

@ -7,14 +7,10 @@ use grammers_client::types::{Dialog, LoginToken};
use grammers_client::{Client, Config, InitParams}; use grammers_client::{Client, Config, InitParams};
use grammers_session::{PackedChat, Session, UpdateState}; use grammers_session::{PackedChat, Session, UpdateState};
use grammers_tl_types as tl; use grammers_tl_types as tl;
use jni::objects::{JObject, JString};
use jni::sys::{jboolean, jint, jlong, jstring};
use jni::JNIEnv;
use log; use log;
use log::{error, info, Level}; use log::{error, info, Level};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::future::Future; use std::future::Future;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
@ -22,6 +18,8 @@ use std::sync::Mutex;
use tokio::runtime; use tokio::runtime;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
include!(concat!(env!("OUT_DIR"), "/talaria.uniffi.rs"));
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>; type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
const LOG_MIN_LEVEL: Level = Level::Trace; const LOG_MIN_LEVEL: Level = Level::Trace;
@ -211,37 +209,26 @@ async fn send_message(chat: PackedChat, text: &str) -> Result<()> {
Ok(()) Ok(())
} }
#[no_mangle] pub fn initDatabase(path: String) {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_initDatabase(
env: JNIEnv,
_: JObject,
path: JString,
) {
let mut guard = DATABASE.lock().unwrap(); let mut guard = DATABASE.lock().unwrap();
if guard.is_some() { if guard.is_some() {
info!("Database is already initialized"); info!("Database is already initialized");
} }
let path = CString::from(CStr::from_ptr(env.get_string(path).unwrap().as_ptr())); match db::init_connection(&path) {
match db::init_connection(path.to_str().unwrap()) {
Ok(conn) => *guard = Some(conn), Ok(conn) => *guard = Some(conn),
Err(e) => error!("Failed to initialize database: {}", e), Err(e) => error!("Failed to initialize database: {}", e),
} }
} }
#[no_mangle] pub fn initClient() {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_initClient(_: JNIEnv, _: JObject) {
match block_on(init_client()) { match block_on(init_client()) {
Ok(_) => info!("Client initialized"), Ok(_) => info!("Client initialized"),
Err(e) => error!("Failed to initialize client: {}", e), Err(e) => error!("Failed to initialize client: {}", e),
} }
} }
#[no_mangle] pub fn needLogin() -> bool {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_needLogin(
_: JNIEnv,
_: JObject,
) -> jboolean {
match block_on(need_login()) { match block_on(need_login()) {
Ok(login) => login.into(), Ok(login) => login.into(),
Err(e) => { Err(e) => {
@ -251,101 +238,56 @@ pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_needLogin(
} }
} }
#[no_mangle] pub fn requestLoginCode(phone: String) -> u64 {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_requestLoginCode( match block_on(request_login_code(&phone)) {
env: JNIEnv, Ok(token) => Box::into_raw(Box::new(token)) as u64,
_: JObject,
phone: JString,
) -> jlong {
let phone = CString::from(CStr::from_ptr(env.get_string(phone).unwrap().as_ptr()));
match block_on(request_login_code(phone.to_str().unwrap())) {
Ok(token) => Box::into_raw(Box::new(token)) as jlong,
Err(e) => { Err(e) => {
error!("Failed to request login code: {}", e); error!("Failed to request login code: {}", e);
0 as jlong 0 as u64
} }
} }
} }
#[no_mangle] pub fn signIn(token_ptr: u64, code: String) {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_signIn( let token = unsafe { *Box::from_raw(token_ptr as *mut LoginToken) };
env: JNIEnv,
_: JObject,
token_ptr: jlong,
code: JString,
) {
let token = *Box::from_raw(token_ptr as *mut LoginToken);
let code = CString::from(CStr::from_ptr(env.get_string(code).unwrap().as_ptr()));
match block_on(sign_in(token, code.to_str().unwrap())) { match block_on(sign_in(token, &code)) {
Ok(_) => info!("Sign in success"), Ok(_) => info!("Sign in success"),
Err(e) => error!("Failed to sign in: {}", e), Err(e) => error!("Failed to sign in: {}", e),
} }
} }
#[no_mangle] pub fn getDialogs() -> u64 {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_getDialogs(
_: JNIEnv,
_: JObject,
) -> jlong {
match block_on(get_dialogs()) { match block_on(get_dialogs()) {
Ok(dialogs) => Box::into_raw(Box::new(dialogs)) as jlong, Ok(dialogs) => Box::into_raw(Box::new(dialogs)) as u64,
Err(e) => { Err(e) => {
error!("Failed to get dialogs: {}", e); error!("Failed to get dialogs: {}", e);
0 as jlong 0 as u64
} }
} }
} }
#[no_mangle] pub fn dialogCount(dialogsPtr: u64) -> u32 {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogCount( let dialogs = unsafe { &mut *(dialogsPtr as *mut Vec<Dialog>) };
_: JNIEnv, dialogs.len() as u32
_: JObject,
dialogs_ptr: jlong,
) -> jint {
let dialogs = &mut *(dialogs_ptr as *mut Vec<Dialog>);
dialogs.len() as jint
} }
#[no_mangle] pub fn dialogPacked(dialogsPtr: u64, index: u32) -> String {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogPacked( let dialogs = unsafe { &mut *(dialogsPtr as *mut Vec<Dialog>) };
env: JNIEnv,
_: JObject,
dialogs_ptr: jlong,
index: jint,
) -> jstring {
let dialogs = &mut *(dialogs_ptr as *mut Vec<Dialog>);
let packed = dialogs[index as usize].chat().pack().to_hex(); dialogs[index as usize].chat().pack().to_hex()
let output = env.new_string(packed).unwrap();
output.into_inner()
} }
#[no_mangle] pub fn dialogTitle(dialogsPtr: u64, index: u32) -> String {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogTitle( let dialogs = unsafe { &mut *(dialogsPtr as *mut Vec<Dialog>) };
env: JNIEnv,
_: JObject,
dialogs_ptr: jlong,
index: jint,
) -> jstring {
let dialogs = &mut *(dialogs_ptr as *mut Vec<Dialog>);
let title = dialogs[index as usize].chat().name(); dialogs[index as usize].chat().name().to_string()
let output = env.new_string(title).unwrap();
output.into_inner()
} }
#[no_mangle] pub fn dialogSender(dialogsPtr: u64, index: u32) -> String {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogSender( let dialogs = unsafe { &mut *(dialogsPtr as *mut Vec<Dialog>) };
env: JNIEnv,
_: JObject,
dialogs_ptr: jlong,
index: jint,
) -> jstring {
let dialogs = &mut *(dialogs_ptr as *mut Vec<Dialog>);
let sender = if let Some(msg) = dialogs[index as usize].last_message.as_ref() { if let Some(msg) = dialogs[index as usize].last_message.as_ref() {
if let Some(sender) = msg.sender() { if let Some(sender) = msg.sender() {
sender.name().to_string() sender.name().to_string()
} else { } else {
@ -353,58 +295,34 @@ pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogSender(
} }
} else { } else {
String::new() String::new()
}; }
let output = env.new_string(sender).unwrap();
output.into_inner()
} }
#[no_mangle] pub fn dialogText(dialogsPtr: u64, index: u32) -> String {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogText( let dialogs = unsafe { &mut *(dialogsPtr as *mut Vec<Dialog>) };
env: JNIEnv,
_: JObject,
dialogs_ptr: jlong,
index: jint,
) -> jstring {
let dialogs = &mut *(dialogs_ptr as *mut Vec<Dialog>);
let text = if let Some(msg) = dialogs[index as usize].last_message.as_ref() { if let Some(msg) = dialogs[index as usize].last_message.as_ref() {
msg.text() msg.text().to_string()
} else { } else {
"" "".to_string()
}; }
let output = env.new_string(text).unwrap();
output.into_inner()
} }
#[no_mangle] pub fn dialogTime(dialogsPtr: u64, index: u32) -> String {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogTime( let dialogs = unsafe { &mut *(dialogsPtr as *mut Vec<Dialog>) };
env: JNIEnv,
_: JObject,
dialogs_ptr: jlong,
index: jint,
) -> jstring {
let dialogs = &mut *(dialogs_ptr as *mut Vec<Dialog>);
let time = if let Some(msg) = dialogs[index as usize].last_message.as_ref() { if let Some(msg) = dialogs[index as usize].last_message.as_ref() {
msg.date().to_rfc3339().to_string() msg.date().to_rfc3339().to_string()
} else { } else {
String::new() String::new()
}; }
let output = env.new_string(time).unwrap();
output.into_inner()
} }
#[no_mangle] pub fn dialogAck(dialogsPtr: u64, index: u32) -> u32 {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogAck( let dialogs = unsafe { &mut *(dialogsPtr as *mut Vec<Dialog>) };
_: JNIEnv,
_: JObject,
dialogs_ptr: jlong,
index: jint,
) -> jint {
let dialogs = &mut *(dialogs_ptr as *mut Vec<Dialog>);
let dialog = &dialogs[index as usize]; let dialog = &dialogs[index as usize];
let ack = if let Some(msg) = dialog.last_message.as_ref() { if let Some(msg) = dialog.last_message.as_ref() {
if msg.outgoing() { if msg.outgoing() {
match &dialog.dialog { match &dialog.dialog {
tl::enums::Dialog::Dialog(d) => { tl::enums::Dialog::Dialog(d) => {
@ -421,47 +339,26 @@ pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogAck(
} }
} else { } else {
0 0
}; }
ack
} }
#[no_mangle] pub fn dialogPin(dialogsPtr: u64, index: u32) -> u32 {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogPin( let dialogs = unsafe { &mut *(dialogsPtr as *mut Vec<Dialog>) };
_: JNIEnv,
_: JObject,
dialogs_ptr: jlong,
index: jint,
) -> jint {
let dialogs = &mut *(dialogs_ptr as *mut Vec<Dialog>);
let pinned = match &dialogs[index as usize].dialog { let pinned = match &dialogs[index as usize].dialog {
tl::enums::Dialog::Dialog(d) => d.pinned, tl::enums::Dialog::Dialog(d) => d.pinned,
tl::enums::Dialog::Folder(f) => f.pinned, tl::enums::Dialog::Folder(f) => f.pinned,
}; };
pinned as jint pinned as u32
} }
#[no_mangle] pub fn freeDialogs(dialogsPtr: u64) {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_freeDialogs( let _ = unsafe { Box::from_raw(dialogsPtr as *mut Vec<Dialog>) };
_: JNIEnv,
_: JObject,
dialogs_ptr: jlong,
) {
let _ = Box::from_raw(dialogs_ptr as *mut Vec<Dialog>);
} }
#[no_mangle] pub fn sendMessage(packed: String, text: String) {
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_sendMessage( let packed = PackedChat::from_hex(&packed).unwrap();
env: JNIEnv, match block_on(send_message(packed, &text)) {
_: JObject,
packed: JString,
text: JString,
) {
let packed = CString::from(CStr::from_ptr(env.get_string(packed).unwrap().as_ptr()));
let text = CString::from(CStr::from_ptr(env.get_string(text).unwrap().as_ptr()));
let packed = PackedChat::from_hex(packed.to_str().unwrap()).unwrap();
match block_on(send_message(packed, text.to_str().unwrap())) {
Ok(_) => info!("Message sent"), Ok(_) => info!("Message sent"),
Err(e) => error!("Failed to send message: {}", e), Err(e) => error!("Failed to send message: {}", e),
} }

18
native/src/talaria.udl Normal file
View File

@ -0,0 +1,18 @@
namespace talaria {
void initDatabase(string path);
void initClient();
boolean needLogin();
u64 requestLoginCode(string phone);
void signIn(u64 tokenPtr, string code);
u64 getDialogs();
u32 dialogCount(u64 dialogsPtr);
string dialogPacked(u64 dialogsPtr, u32 index);
string dialogTitle(u64 dialogsPtr, u32 index);
string dialogSender(u64 dialogsPtr, u32 index);
string dialogText(u64 dialogsPtr, u32 index);
string dialogTime(u64 dialogsPtr, u32 index);
u32 dialogAck(u64 dialogsPtr, u32 index);
u32 dialogPin(u64 dialogsPtr, u32 index);
void freeDialogs(u64 dialogsPtr);
void sendMessage(string packed, string text);
};