Compare commits
No commits in common. "912949a07943a1d43bf942e11ba64cf3dd256c1f" and "b5dddcef0203f185e3d95998a5dc3f594de9d7fc" have entirely different histories.
912949a079
...
b5dddcef02
|
@ -1,7 +1,6 @@
|
||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
<option name="ALLOW_TRAILING_COMMA" value="true" />
|
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
</JetCodeStyleSettings>
|
</JetCodeStyleSettings>
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
|
|
|
@ -57,7 +57,6 @@ 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'
|
||||||
|
@ -69,7 +68,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 = "uniffi_talaria"
|
libname = "talaria"
|
||||||
targets = ["arm64"]
|
targets = ["arm64"]
|
||||||
profile = 'release'
|
profile = 'release'
|
||||||
}
|
}
|
||||||
|
@ -79,14 +78,3 @@ 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")
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,9 +7,8 @@ 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?) {
|
||||||
|
@ -25,7 +24,7 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initDatabase(getDatabasePath("talaria.db").path)
|
Native.initDatabase(getDatabasePath("talaria.db").path)
|
||||||
initClient()
|
Native.initClient()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(!needLogin()) }
|
val loggedIn by remember { mutableStateOf(!Native.needLogin()) }
|
||||||
var selectedDialog by remember { mutableStateOf("") }
|
var selectedDialog by remember { mutableStateOf("") }
|
||||||
|
|
||||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
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)
|
||||||
|
}
|
|
@ -1,10 +1,11 @@
|
||||||
package dev.lonami.talaria.data
|
package dev.lonami.talaria.data
|
||||||
|
|
||||||
import uniffi.talaria.Dialog
|
import dev.lonami.talaria.bindings.Native
|
||||||
import uniffi.talaria.MessageAck
|
import dev.lonami.talaria.models.Dialog
|
||||||
import uniffi.talaria.MessagePreview
|
import dev.lonami.talaria.models.MessageAck
|
||||||
import uniffi.talaria.getDialogs
|
import dev.lonami.talaria.models.MessagePreview
|
||||||
import java.time.Instant
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
interface DialogRepository {
|
interface DialogRepository {
|
||||||
fun loadDialogs(): List<Dialog>
|
fun loadDialogs(): List<Dialog>
|
||||||
|
@ -12,7 +13,39 @@ interface DialogRepository {
|
||||||
|
|
||||||
class NativeDialogRepository : DialogRepository {
|
class NativeDialogRepository : DialogRepository {
|
||||||
override fun loadDialogs(): List<Dialog> {
|
override fun loadDialogs(): List<Dialog> {
|
||||||
return getDialogs()
|
val dialogs = mutableListOf<Dialog>()
|
||||||
|
|
||||||
|
val dialogPtr = Native.getDialogs()
|
||||||
|
try {
|
||||||
|
val dialogCount = Native.dialogCount(dialogPtr)
|
||||||
|
for (i in 0 until dialogCount) {
|
||||||
|
dialogs.add(
|
||||||
|
Dialog(
|
||||||
|
id = Native.dialogPacked(dialogPtr, i),
|
||||||
|
title = Native.dialogTitle(dialogPtr, i),
|
||||||
|
lastMessage = MessagePreview(
|
||||||
|
sender = Native.dialogSender(dialogPtr, i),
|
||||||
|
text = Native.dialogText(dialogPtr, i),
|
||||||
|
date = LocalDateTime.parse(
|
||||||
|
Native.dialogTime(dialogPtr, i),
|
||||||
|
DateTimeFormatter.ISO_OFFSET_DATE_TIME
|
||||||
|
),
|
||||||
|
ack = when (Native.dialogAck(dialogPtr, i)) {
|
||||||
|
0 -> MessageAck.RECEIVED
|
||||||
|
1 -> MessageAck.SENT
|
||||||
|
2 -> MessageAck.SEEN
|
||||||
|
else -> MessageAck.RECEIVED
|
||||||
|
}
|
||||||
|
),
|
||||||
|
pinned = Native.dialogPin(dialogPtr, i) != 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Native.freeDialogs(dialogPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialogs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +71,7 @@ class MockDialogRepository : DialogRepository {
|
||||||
} else {
|
} else {
|
||||||
"Sample Message $i"
|
"Sample Message $i"
|
||||||
},
|
},
|
||||||
date = Instant.now(),
|
date = LocalDateTime.now(),
|
||||||
ack = when (i % 3) {
|
ack = when (i % 3) {
|
||||||
0 -> MessageAck.RECEIVED
|
0 -> MessageAck.RECEIVED
|
||||||
1 -> MessageAck.SENT
|
1 -> MessageAck.SENT
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dev.lonami.talaria.models
|
||||||
|
|
||||||
|
data class Dialog(
|
||||||
|
val id: String,
|
||||||
|
val title: String,
|
||||||
|
val lastMessage: MessagePreview?,
|
||||||
|
val pinned: Boolean
|
||||||
|
)
|
|
@ -0,0 +1,7 @@
|
||||||
|
package dev.lonami.talaria.models
|
||||||
|
|
||||||
|
enum class MessageAck {
|
||||||
|
RECEIVED,
|
||||||
|
SENT,
|
||||||
|
SEEN,
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package dev.lonami.talaria.models
|
||||||
|
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
data class MessagePreview(
|
||||||
|
val sender: String,
|
||||||
|
val text: String,
|
||||||
|
val date: LocalDateTime,
|
||||||
|
val ack: MessageAck
|
||||||
|
)
|
|
@ -23,12 +23,10 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import dev.lonami.talaria.R
|
import dev.lonami.talaria.R
|
||||||
import dev.lonami.talaria.data.MockDialogRepository
|
import dev.lonami.talaria.data.MockDialogRepository
|
||||||
|
import dev.lonami.talaria.models.Dialog
|
||||||
|
import dev.lonami.talaria.models.MessageAck
|
||||||
import dev.lonami.talaria.ui.state.DialogViewModel
|
import dev.lonami.talaria.ui.state.DialogViewModel
|
||||||
import dev.lonami.talaria.ui.theme.TalariaTheme
|
import dev.lonami.talaria.ui.theme.TalariaTheme
|
||||||
import uniffi.talaria.Dialog
|
|
||||||
import uniffi.talaria.MessageAck
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.ZoneOffset
|
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.time.format.FormatStyle
|
import java.time.format.FormatStyle
|
||||||
|
|
||||||
|
@ -56,8 +54,8 @@ fun Dialog(dialog: Dialog, onDialogSelected: () -> Unit, modifier: Modifier = Mo
|
||||||
Text(
|
Text(
|
||||||
stringResource(
|
stringResource(
|
||||||
R.string.message_preview,
|
R.string.message_preview,
|
||||||
dialog.lastMessage!!.sender,
|
dialog.lastMessage.sender,
|
||||||
dialog.lastMessage!!.text
|
dialog.lastMessage.text
|
||||||
),
|
),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
@ -67,7 +65,7 @@ fun Dialog(dialog: Dialog, onDialogSelected: () -> Unit, modifier: Modifier = Mo
|
||||||
Column {
|
Column {
|
||||||
if (dialog.lastMessage != null) {
|
if (dialog.lastMessage != null) {
|
||||||
Row {
|
Row {
|
||||||
when (dialog.lastMessage!!.ack) {
|
when (dialog.lastMessage.ack) {
|
||||||
MessageAck.RECEIVED -> {}
|
MessageAck.RECEIVED -> {}
|
||||||
MessageAck.SENT -> Icon(
|
MessageAck.SENT -> Icon(
|
||||||
painterResource(R.drawable.sent),
|
painterResource(R.drawable.sent),
|
||||||
|
@ -80,7 +78,7 @@ fun Dialog(dialog: Dialog, onDialogSelected: () -> Unit, modifier: Modifier = Mo
|
||||||
}
|
}
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
Text(
|
Text(
|
||||||
LocalDateTime.ofInstant(dialog.lastMessage!!.date, ZoneOffset.UTC).format(
|
dialog.lastMessage.date.format(
|
||||||
DateTimeFormatter.ofLocalizedTime(
|
DateTimeFormatter.ofLocalizedTime(
|
||||||
FormatStyle.SHORT
|
FormatStyle.SHORT
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,9 +18,8 @@ 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,
|
||||||
|
@ -106,7 +105,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(0UL) }
|
var tokenPtr by remember { mutableStateOf(0L) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
@ -128,7 +127,7 @@ fun LoginScreen(onConfirmOtp: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
phone,
|
phone,
|
||||||
onPhoneChanged = { phone = it },
|
onPhoneChanged = { phone = it },
|
||||||
onSendCode = {
|
onSendCode = {
|
||||||
tokenPtr = requestLoginCode(phone)
|
tokenPtr = Native.requestLoginCode(phone)
|
||||||
stage = LoginStage.ASK_CODE
|
stage = LoginStage.ASK_CODE
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -136,7 +135,7 @@ fun LoginScreen(onConfirmOtp: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
otp,
|
otp,
|
||||||
onOtpChanged = { otp = it },
|
onOtpChanged = { otp = it },
|
||||||
onConfirmOtp = {
|
onConfirmOtp = {
|
||||||
signIn(tokenPtr, otp)
|
Native.signIn(tokenPtr, otp)
|
||||||
onConfirmOtp()
|
onConfirmOtp()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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
|
||||||
|
@ -17,7 +18,7 @@ class ChatViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendMessage(dialog: String, message: String) {
|
fun sendMessage(dialog: String, message: String) {
|
||||||
sendMessage(dialog, message)
|
Native.sendMessage(dialog, message)
|
||||||
_uiState.update { state ->
|
_uiState.update { state ->
|
||||||
state.messages.add(Message("You", message))
|
state.messages.add(Message("You", message))
|
||||||
state
|
state
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package dev.lonami.talaria.ui.state
|
package dev.lonami.talaria.ui.state
|
||||||
|
|
||||||
import uniffi.talaria.Dialog
|
import dev.lonami.talaria.models.Dialog
|
||||||
|
|
||||||
data class DialogUiState(val dialogs: List<Dialog> = listOf())
|
data class DialogUiState(val dialogs: List<Dialog> = listOf())
|
||||||
|
|
|
@ -4,10 +4,8 @@ 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 {
|
||||||
// uniffi-recommended Gradle script fails with Android plugin 7.3.1.
|
id 'com.android.application' version '7.3.0' apply false
|
||||||
// See https://github.com/mozilla/uniffi-rs/issues/1386 for details.
|
id 'com.android.library' version '7.3.0' apply false
|
||||||
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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,11 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "uniffi_talaria"
|
name = "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" }
|
||||||
|
@ -20,8 +19,5 @@ 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
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
uniffi_build::generate_scaffolding("src/talaria.udl").unwrap();
|
|
||||||
}
|
|
|
@ -3,24 +3,26 @@
|
||||||
|
|
||||||
mod db;
|
mod db;
|
||||||
|
|
||||||
use grammers_client::types::LoginToken;
|
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::fmt;
|
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};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::time::SystemTime;
|
|
||||||
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>>;
|
||||||
|
|
||||||
const LOG_MIN_LEVEL: Level = Level::Trace;
|
const LOG_MIN_LEVEL: Level = Level::Trace;
|
||||||
const LOG_TAG: &str = ".native.talari";
|
const LOG_TAG: &str = ".native.talari";
|
||||||
|
@ -45,50 +47,6 @@ static RUNTIME: OnceCell<Runtime> = OnceCell::new();
|
||||||
static CLIENT: OnceCell<Client> = OnceCell::new();
|
static CLIENT: OnceCell<Client> = OnceCell::new();
|
||||||
static DATABASE: Mutex<Option<sqlite::Connection>> = Mutex::new(None);
|
static DATABASE: Mutex<Option<sqlite::Connection>> = Mutex::new(None);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum NativeError {
|
|
||||||
Initialization,
|
|
||||||
Database,
|
|
||||||
Network,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for NativeError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Initialization => write!(f, "a resource could not be initialized correctly"),
|
|
||||||
Self::Database => write!(f, "a query to the local database failed"),
|
|
||||||
Self::Network => write!(f, "a network request could not complete successfully"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for NativeError {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum MessageAck {
|
|
||||||
Received,
|
|
||||||
Seen,
|
|
||||||
Sent,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct MessagePreview {
|
|
||||||
sender: String,
|
|
||||||
text: String,
|
|
||||||
date: SystemTime,
|
|
||||||
ack: MessageAck,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Dialog {
|
|
||||||
id: String,
|
|
||||||
title: String,
|
|
||||||
last_message: Option<MessagePreview>,
|
|
||||||
pinned: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, NativeError>;
|
|
||||||
|
|
||||||
fn block_on<F: Future>(future: F) -> F::Output {
|
fn block_on<F: Future>(future: F) -> F::Output {
|
||||||
if RUNTIME.get().is_none() {
|
if RUNTIME.get().is_none() {
|
||||||
RUNTIME
|
RUNTIME
|
||||||
|
@ -104,22 +62,7 @@ fn block_on<F: Future>(future: F) -> F::Output {
|
||||||
RUNTIME.get().unwrap().block_on(future)
|
RUNTIME.get().unwrap().block_on(future)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_database(path: String) -> Result<()> {
|
async fn init_client() -> Result<()> {
|
||||||
let mut guard = DATABASE.lock().unwrap();
|
|
||||||
if guard.is_some() {
|
|
||||||
info!("Database is already initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
match db::init_connection(&path) {
|
|
||||||
Ok(conn) => {
|
|
||||||
*guard = Some(conn);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(_) => Err(NativeError::Database),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_client() -> Result<()> {
|
|
||||||
android_logger::init_once(
|
android_logger::init_once(
|
||||||
android_logger::Config::default()
|
android_logger::Config::default()
|
||||||
.with_min_level(LOG_MIN_LEVEL)
|
.with_min_level(LOG_MIN_LEVEL)
|
||||||
|
@ -135,8 +78,7 @@ pub fn init_client() -> Result<()> {
|
||||||
let conn = match guard.as_ref() {
|
let conn = match guard.as_ref() {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
None => {
|
None => {
|
||||||
error!("Database was not initialized");
|
return Err("Database was not initialized".into());
|
||||||
return Err(NativeError::Initialization);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,7 +86,7 @@ pub fn init_client() -> Result<()> {
|
||||||
|
|
||||||
let session = Session::new();
|
let session = Session::new();
|
||||||
|
|
||||||
let sessions = db::get_sessions(conn).map_err(|_| NativeError::Database)?;
|
let sessions = db::get_sessions(conn)?;
|
||||||
if let Some(s) = sessions.get(0) {
|
if let Some(s) = sessions.get(0) {
|
||||||
match (s.user_id, s.dc_id, s.bot) {
|
match (s.user_id, s.dc_id, s.bot) {
|
||||||
(Some(id), Some(dc), Some(bot)) => session.set_user(id, dc, bot),
|
(Some(id), Some(dc), Some(bot)) => session.set_user(id, dc, bot),
|
||||||
|
@ -170,7 +112,7 @@ pub fn init_client() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = block_on(Client::connect(Config {
|
let client = Client::connect(Config {
|
||||||
session,
|
session,
|
||||||
api_id: API_ID,
|
api_id: API_ID,
|
||||||
api_hash: API_HASH.to_string(),
|
api_hash: API_HASH.to_string(),
|
||||||
|
@ -182,46 +124,42 @@ pub fn init_client() -> Result<()> {
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
}))
|
})
|
||||||
.map_err(|_| NativeError::Network)?;
|
.await?;
|
||||||
|
|
||||||
info!("Connected!");
|
info!("Connected!");
|
||||||
|
|
||||||
CLIENT
|
CLIENT
|
||||||
.set(client)
|
.set(client)
|
||||||
.map_err(|_| NativeError::Initialization)?;
|
.map_err(|_| "Client was already initialized")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn need_login() -> Result<bool> {
|
async fn need_login() -> Result<bool> {
|
||||||
let client = CLIENT.get().ok_or(NativeError::Initialization)?;
|
let client = CLIENT.get().ok_or("Client not initialized")?;
|
||||||
block_on(client.is_authorized()).map_err(|_| NativeError::Network)
|
Ok(client.is_authorized().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_login_code(phone: String) -> Result<u64> {
|
async fn request_login_code(phone: &str) -> Result<LoginToken> {
|
||||||
let client = CLIENT.get().ok_or(NativeError::Initialization)?;
|
let client = CLIENT.get().ok_or("Client not initialized")?;
|
||||||
block_on(client.request_login_code(&phone, API_ID, API_HASH))
|
let token = client.request_login_code(&phone, API_ID, API_HASH).await?;
|
||||||
.map(|token| Box::into_raw(Box::new(token)) as u64)
|
Ok(token)
|
||||||
.map_err(|_| NativeError::Network)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign_in(token_ptr: u64, code: String) -> Result<()> {
|
async fn sign_in(token: LoginToken, code: &str) -> Result<()> {
|
||||||
let token = unsafe { *Box::from_raw(token_ptr as *mut LoginToken) };
|
let client = CLIENT.get().ok_or("Client not initialized")?;
|
||||||
let client = CLIENT.get().ok_or(NativeError::Initialization)?;
|
client.sign_in(&token, &code).await?;
|
||||||
|
|
||||||
block_on(client.sign_in(&token, &code)).map_err(|_| NativeError::Network)?;
|
|
||||||
|
|
||||||
let guard = DATABASE.lock().unwrap();
|
let guard = DATABASE.lock().unwrap();
|
||||||
let conn = match guard.as_ref() {
|
let conn = match guard.as_ref() {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
None => {
|
None => {
|
||||||
error!("Database was not initialized");
|
return Err("Database was not initialized".into());
|
||||||
return Err(NativeError::Initialization);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut session = db::create_session(conn).map_err(|_| NativeError::Database)?;
|
let mut session = db::create_session(conn)?;
|
||||||
let s = client.session();
|
let s = client.session();
|
||||||
if let Some(user) = s.get_user() {
|
if let Some(user) = s.get_user() {
|
||||||
session.user_id = Some(user.id);
|
session.user_id = Some(user.id);
|
||||||
|
@ -251,63 +189,280 @@ pub fn sign_in(token_ptr: u64, code: String) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db::update_session(conn, &session).map_err(|_| NativeError::Database)?;
|
db::update_session(conn, &session)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dialogs() -> Result<Vec<Dialog>> {
|
async fn get_dialogs() -> Result<Vec<Dialog>> {
|
||||||
let client = CLIENT.get().ok_or(NativeError::Initialization)?;
|
let client = CLIENT.get().ok_or("Client not initialized")?;
|
||||||
|
|
||||||
block_on(async {
|
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
let mut dialogs = client.iter_dialogs();
|
let mut dialogs = client.iter_dialogs();
|
||||||
while let Some(dialog) = dialogs.next().await.map_err(|_| NativeError::Network)? {
|
while let Some(dialog) = dialogs.next().await? {
|
||||||
result.push(dialog);
|
result.push(dialog);
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
})
|
}
|
||||||
.map(|dialogs| {
|
|
||||||
dialogs
|
async fn send_message(chat: PackedChat, text: &str) -> Result<()> {
|
||||||
.into_iter()
|
let client = CLIENT.get().ok_or("Client not initialized")?;
|
||||||
.map(|d| Dialog {
|
client.send_message(chat, text).await?;
|
||||||
id: d.chat().pack().to_hex(),
|
Ok(())
|
||||||
title: d.chat().name().to_string(),
|
}
|
||||||
last_message: d.last_message.map(|m| MessagePreview {
|
|
||||||
sender: if let Some(sender) = m.sender() {
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_initDatabase(
|
||||||
|
env: JNIEnv,
|
||||||
|
_: JObject,
|
||||||
|
path: JString,
|
||||||
|
) {
|
||||||
|
let mut guard = DATABASE.lock().unwrap();
|
||||||
|
if guard.is_some() {
|
||||||
|
info!("Database is already initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = CString::from(CStr::from_ptr(env.get_string(path).unwrap().as_ptr()));
|
||||||
|
match db::init_connection(path.to_str().unwrap()) {
|
||||||
|
Ok(conn) => *guard = Some(conn),
|
||||||
|
Err(e) => error!("Failed to initialize database: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_initClient(_: JNIEnv, _: JObject) {
|
||||||
|
match block_on(init_client()) {
|
||||||
|
Ok(_) => info!("Client initialized"),
|
||||||
|
Err(e) => error!("Failed to initialize client: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_needLogin(
|
||||||
|
_: JNIEnv,
|
||||||
|
_: JObject,
|
||||||
|
) -> jboolean {
|
||||||
|
match block_on(need_login()) {
|
||||||
|
Ok(login) => login.into(),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to check if user is authorized: {}", e);
|
||||||
|
false.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_requestLoginCode(
|
||||||
|
env: JNIEnv,
|
||||||
|
_: 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) => {
|
||||||
|
error!("Failed to request login code: {}", e);
|
||||||
|
0 as jlong
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_signIn(
|
||||||
|
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())) {
|
||||||
|
Ok(_) => info!("Sign in success"),
|
||||||
|
Err(e) => error!("Failed to sign in: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_getDialogs(
|
||||||
|
_: JNIEnv,
|
||||||
|
_: JObject,
|
||||||
|
) -> jlong {
|
||||||
|
match block_on(get_dialogs()) {
|
||||||
|
Ok(dialogs) => Box::into_raw(Box::new(dialogs)) as jlong,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to get dialogs: {}", e);
|
||||||
|
0 as jlong
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogCount(
|
||||||
|
_: JNIEnv,
|
||||||
|
_: JObject,
|
||||||
|
dialogs_ptr: jlong,
|
||||||
|
) -> jint {
|
||||||
|
let dialogs = &mut *(dialogs_ptr as *mut Vec<Dialog>);
|
||||||
|
dialogs.len() as jint
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogPacked(
|
||||||
|
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();
|
||||||
|
let output = env.new_string(packed).unwrap();
|
||||||
|
output.into_inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogTitle(
|
||||||
|
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();
|
||||||
|
let output = env.new_string(title).unwrap();
|
||||||
|
output.into_inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogSender(
|
||||||
|
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(sender) = msg.sender() {
|
||||||
sender.name().to_string()
|
sender.name().to_string()
|
||||||
} else {
|
} else {
|
||||||
"unknown".to_string()
|
"unknown".to_string()
|
||||||
},
|
|
||||||
text: m.text().to_string(),
|
|
||||||
date: m.date().into(),
|
|
||||||
ack: if m.outgoing() {
|
|
||||||
match &d.dialog {
|
|
||||||
tl::enums::Dialog::Dialog(d) => {
|
|
||||||
if m.id() <= d.read_inbox_max_id {
|
|
||||||
MessageAck::Seen
|
|
||||||
} else {
|
|
||||||
MessageAck::Sent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tl::enums::Dialog::Folder(_) => MessageAck::Received,
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MessageAck::Received
|
String::new()
|
||||||
},
|
};
|
||||||
}),
|
let output = env.new_string(sender).unwrap();
|
||||||
pinned: match d.dialog {
|
output.into_inner()
|
||||||
tl::enums::Dialog::Dialog(d) => d.pinned,
|
|
||||||
tl::enums::Dialog::Folder(f) => f.pinned,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_message(packed: String, text: String) -> Result<()> {
|
#[no_mangle]
|
||||||
let chat = PackedChat::from_hex(&packed).unwrap();
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogText(
|
||||||
let client = CLIENT.get().ok_or(NativeError::Initialization)?;
|
env: JNIEnv,
|
||||||
block_on(client.send_message(chat, text)).map_err(|_| NativeError::Network)?;
|
_: JObject,
|
||||||
Ok(())
|
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() {
|
||||||
|
msg.text()
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
let output = env.new_string(text).unwrap();
|
||||||
|
output.into_inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogTime(
|
||||||
|
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() {
|
||||||
|
msg.date().to_rfc3339().to_string()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
let output = env.new_string(time).unwrap();
|
||||||
|
output.into_inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogAck(
|
||||||
|
_: JNIEnv,
|
||||||
|
_: JObject,
|
||||||
|
dialogs_ptr: jlong,
|
||||||
|
index: jint,
|
||||||
|
) -> jint {
|
||||||
|
let dialogs = &mut *(dialogs_ptr as *mut Vec<Dialog>);
|
||||||
|
|
||||||
|
let dialog = &dialogs[index as usize];
|
||||||
|
let ack = if let Some(msg) = dialog.last_message.as_ref() {
|
||||||
|
if msg.outgoing() {
|
||||||
|
match &dialog.dialog {
|
||||||
|
tl::enums::Dialog::Dialog(d) => {
|
||||||
|
if msg.id() <= d.read_inbox_max_id {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tl::enums::Dialog::Folder(_) => 0,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
ack
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogPin(
|
||||||
|
_: 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 {
|
||||||
|
tl::enums::Dialog::Dialog(d) => d.pinned,
|
||||||
|
tl::enums::Dialog::Folder(f) => f.pinned,
|
||||||
|
};
|
||||||
|
pinned as jint
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_freeDialogs(
|
||||||
|
_: JNIEnv,
|
||||||
|
_: JObject,
|
||||||
|
dialogs_ptr: jlong,
|
||||||
|
) {
|
||||||
|
let _ = Box::from_raw(dialogs_ptr as *mut Vec<Dialog>);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_sendMessage(
|
||||||
|
env: JNIEnv,
|
||||||
|
_: 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"),
|
||||||
|
Err(e) => error!("Failed to send message: {}", e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
[Error]
|
|
||||||
enum NativeError {
|
|
||||||
"Initialization",
|
|
||||||
"Database",
|
|
||||||
"Network",
|
|
||||||
};
|
|
||||||
|
|
||||||
enum MessageAck {
|
|
||||||
"Received",
|
|
||||||
"Seen",
|
|
||||||
"Sent",
|
|
||||||
};
|
|
||||||
|
|
||||||
dictionary MessagePreview {
|
|
||||||
string sender;
|
|
||||||
string text;
|
|
||||||
timestamp date;
|
|
||||||
MessageAck ack;
|
|
||||||
};
|
|
||||||
|
|
||||||
dictionary Dialog {
|
|
||||||
string id;
|
|
||||||
string title;
|
|
||||||
MessagePreview? last_message;
|
|
||||||
boolean pinned;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace talaria {
|
|
||||||
[Throws=NativeError]
|
|
||||||
void init_database(string path);
|
|
||||||
[Throws=NativeError]
|
|
||||||
void init_client();
|
|
||||||
[Throws=NativeError]
|
|
||||||
boolean need_login();
|
|
||||||
[Throws=NativeError]
|
|
||||||
u64 request_login_code(string phone);
|
|
||||||
[Throws=NativeError]
|
|
||||||
void sign_in(u64 tokenPtr, string code);
|
|
||||||
[Throws=NativeError]
|
|
||||||
sequence<Dialog> get_dialogs();
|
|
||||||
[Throws=NativeError]
|
|
||||||
void send_message(string packed, string text);
|
|
||||||
};
|
|
Loading…
Reference in New Issue