From 013612c06bae576e7484a05485e39fed12897a44 Mon Sep 17 00:00:00 2001 From: zoff99 Date: Fri, 13 Oct 2023 08:14:29 +0200 Subject: [PATCH 01/25] remove unused import --- .../org/briarproject/briar/desktop/contact/ContactItem.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt index 9dfa8948..6ce9cf47 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactItem.kt @@ -18,8 +18,6 @@ package org.briarproject.briar.desktop.contact -import kotlin.random.Random - data class ContactItem( val name: String, val isConnected: Int, From f31bef3b30d901ae0baa07920dcf08e9b8859774 Mon Sep 17 00:00:00 2001 From: zoff99 Date: Fri, 13 Oct 2023 08:14:51 +0200 Subject: [PATCH 02/25] online status with color circle --- src/main/kotlin/Main.kt | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 7607d326..2703b172 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -1,5 +1,8 @@ import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add @@ -31,6 +34,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.runBlocking import org.briarproject.briar.desktop.contact.ContactItem import org.briarproject.briar.desktop.contact.ContactList +import org.briarproject.briar.desktop.contact.getConnectionColor import org.briarproject.briar.desktop.navigation.BriarSidebar import org.briarproject.briar.desktop.ui.VerticalDivider import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @@ -81,7 +85,7 @@ fun App() { VerticalDivider() Column(Modifier.fillMaxSize()) { Row(Modifier.wrapContentHeight(), Arrangement.spacedBy(5.dp)) { - Button(onClick = { + Button(modifier = Modifier.width(140.dp), onClick = { // start/stop tox button if (tox_running_state == "running") { tox_running_state = "stopping ..." start_button_text = tox_running_state @@ -122,17 +126,19 @@ fun App() { }) { Text(start_button_text) } - Button( + Button( // self connection state button onClick = {}, colors = ButtonDefaults.buttonColors(), - enabled = true + enabled = false ) { - Icon( - Icons.Filled.Add, contentDescription = online_button_text, - modifier = Modifier.size(ButtonDefaults.IconSize) + Box( + modifier = Modifier.size(16.dp). + border(1.dp, Color.Black, CircleShape) + .background(Color(online_button_color_wrapper), + CircleShape) ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) - Text(online_button_text) + Text(getOnlineButtonText(online_button_text)) Thread { while (true) { @@ -205,6 +211,14 @@ fun App() { } +fun getOnlineButtonText(text_in: String): String { + return when(text_in) { + "udp" -> "UDP" + "tcp" -> "TCP" + else -> "offline" + } +} + fun set_tox_running_state(new_state: String) { tox_running_state_wrapper = new_state start_button_text_wrapper = tox_running_state_wrapper @@ -228,16 +242,6 @@ fun set_tox_online_state(new_state: String) { } fun main() = application(exitProcessOnExit = true) { - /* - if ((OperatingSystem.getCurrent() == OperatingSystem.WINDOWS) - || (OperatingSystem.getCurrent() == OperatingSystem.MACOS) - ) { - val appIcon = painterResource("icon-linux.png") - Tray( - icon = appIcon, - ) - } - */ MainAppStart() } From 86fa26e15e4f5067c8fac2d10dfe4d11888fb8c9 Mon Sep 17 00:00:00 2001 From: zoff99 Date: Fri, 13 Oct 2023 20:01:32 +0200 Subject: [PATCH 03/25] add more features --- .gitignore | 1 + build.gradle.kts | 4 +- resources/common/main.db.txt | 390 ++++++++++++++++++ .../applications/trifa/TRIFAGlobals.java | 207 ++++++++++ src/main/kotlin/Main.kt | 164 +++++--- .../zoffcc/applications/trifa/MainActivity.kt | 188 +++++++-- .../zoffcc/applications/trifa/TRIFAGlobals.kt | 10 - .../applications/trifa/TrifaToxService.kt | 14 +- .../trifa/savepathenabled_state.kt | 65 +++ .../applications/trifa/toxdata_state.kt | 44 ++ .../com/zoffcc/applications/trifa2/ChatApp.kt | 4 + 11 files changed, 988 insertions(+), 103 deletions(-) create mode 100644 resources/common/main.db.txt create mode 100644 src/main/java/com/zoffcc/applications/trifa/TRIFAGlobals.java delete mode 100644 src/main/kotlin/com/zoffcc/applications/trifa/TRIFAGlobals.kt create mode 100644 src/main/kotlin/com/zoffcc/applications/trifa/savepathenabled_state.kt create mode 100644 src/main/kotlin/com/zoffcc/applications/trifa/toxdata_state.kt diff --git a/.gitignore b/.gitignore index deb35ea1..3d7b0331 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ savedata.tox bin/ deps/ toxid.txt +main.db diff --git a/build.gradle.kts b/build.gradle.kts index a8cc53d6..97183001 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,8 +34,10 @@ dependencies { @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class) implementation(compose.components.resources) // - implementation("com.russhwolf:multiplatform-settings:1.0.0") implementation(compose.materialIconsExtended) + // + // + implementation("org.xerial:sqlite-jdbc:3.42.0.0") } compose.desktop { diff --git a/resources/common/main.db.txt b/resources/common/main.db.txt new file mode 100644 index 00000000..1b1d808a --- /dev/null +++ b/resources/common/main.db.txt @@ -0,0 +1,390 @@ + +CREATE TABLE IF NOT EXISTS "BootstrapNodeEntryDB" ( + "num" INTEGER NOT NULL, + "udp_node" BOOLEAN NOT NULL, + "ip" TEXT NOT NULL, + "port" INTEGER NOT NULL, + "key_hex" TEXT NOT NULL, + "id" INTEGER, + PRIMARY KEY("id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "ConferenceDB" ( + "who_invited__tox_public_key_string" TEXT NOT NULL, + "name" TEXT, + "peer_count" INTEGER NOT NULL DEFAULT -1, + "own_peer_number" INTEGER NOT NULL DEFAULT -1, + "kind" INTEGER NOT NULL DEFAULT 0, + "tox_conference_number" INTEGER NOT NULL DEFAULT -1, + "conference_active" BOOLEAN NOT NULL DEFAULT false, + "notification_silent" BOOLEAN DEFAULT false, + "conference_identifier" TEXT, + PRIMARY KEY("conference_identifier") +); +CREATE TABLE IF NOT EXISTS "ConferenceMessage" ( + "message_id_tox" TEXT, + "conference_identifier" TEXT NOT NULL DEFAULT -1, + "tox_peerpubkey" TEXT NOT NULL, + "tox_peername" TEXT, + "direction" INTEGER NOT NULL, + "TOX_MESSAGE_TYPE" INTEGER NOT NULL, + "TRIFA_MESSAGE_TYPE" INTEGER NOT NULL DEFAULT 0, + "sent_timestamp" INTEGER, + "rcvd_timestamp" INTEGER, + "read" BOOLEAN NOT NULL DEFAULT 0, + "is_new" BOOLEAN NOT NULL DEFAULT 1, + "text" TEXT, + "was_synced" BOOLEAN NOT NULL DEFAULT 0, + "id" INTEGER, + PRIMARY KEY("id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "ConferencePeerCacheDB" ( + "conference_identifier" TEXT NOT NULL, + "peer_pubkey" TEXT NOT NULL, + "peer_name" TEXT NOT NULL, + "last_update_timestamp" INTEGER NOT NULL DEFAULT -1, + "id" INTEGER, + PRIMARY KEY("id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "FileDB" ( + "kind" INTEGER NOT NULL, + "direction" INTEGER NOT NULL, + "tox_public_key_string" TEXT NOT NULL, + "path_name" TEXT NOT NULL, + "file_name" TEXT NOT NULL, + "filesize" INTEGER NOT NULL DEFAULT -1, + "is_in_VFS" BOOLEAN NOT NULL DEFAULT true, + "id" INTEGER, + PRIMARY KEY("id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "Filetransfer" ( + "tox_public_key_string" TEXT NOT NULL, + "direction" INTEGER NOT NULL, + "file_number" INTEGER NOT NULL, + "kind" INTEGER NOT NULL, + "state" INTEGER NOT NULL, + "ft_accepted" BOOLEAN NOT NULL DEFAULT false, + "ft_outgoing_started" BOOLEAN NOT NULL DEFAULT false, + "path_name" TEXT NOT NULL, + "file_name" TEXT NOT NULL, + "fos_open" BOOLEAN NOT NULL DEFAULT false, + "filesize" INTEGER NOT NULL DEFAULT -1, + "current_position" INTEGER NOT NULL DEFAULT 0, + "message_id" INTEGER NOT NULL DEFAULT -1, + "id" INTEGER, + PRIMARY KEY("id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "TRIFADatabaseGlobals" ( + "key" TEXT NOT NULL, + "value" TEXT NOT NULL +); +CREATE TABLE IF NOT EXISTS "RelayListDB" ( + "TOX_CONNECTION" INTEGER NOT NULL DEFAULT 0, + "TOX_CONNECTION_on_off" INTEGER NOT NULL DEFAULT 0, + "own_relay" BOOLEAN NOT NULL DEFAULT false, + "last_online_timestamp" INTEGER NOT NULL DEFAULT -1, + "tox_public_key_string_of_owner" TEXT, + "tox_public_key_string" TEXT, + PRIMARY KEY("tox_public_key_string") +); +CREATE TABLE IF NOT EXISTS "Message" ( + "message_id" INTEGER NOT NULL, + "tox_friendpubkey" TEXT NOT NULL, + "direction" INTEGER NOT NULL, + "TOX_MESSAGE_TYPE" INTEGER NOT NULL, + "TRIFA_MESSAGE_TYPE" INTEGER NOT NULL DEFAULT 0, + "state" INTEGER NOT NULL DEFAULT 1, + "ft_accepted" BOOLEAN NOT NULL DEFAULT false, + "ft_outgoing_started" BOOLEAN NOT NULL DEFAULT false, + "filedb_id" INTEGER NOT NULL DEFAULT -1, + "filetransfer_id" INTEGER NOT NULL DEFAULT -1, + "sent_timestamp" INTEGER DEFAULT 0, + "sent_timestamp_ms" INTEGER DEFAULT 0, + "rcvd_timestamp" INTEGER DEFAULT 0, + "rcvd_timestamp_ms" INTEGER DEFAULT 0, + "read" BOOLEAN NOT NULL, + "send_retries" INTEGER NOT NULL DEFAULT 0, + "is_new" BOOLEAN NOT NULL, + "text" TEXT, + "filename_fullpath" TEXT, + "msg_id_hash" TEXT, + "raw_msgv2_bytes" TEXT, + "msg_version" INTEGER NOT NULL DEFAULT 0, + "resend_count" INTEGER NOT NULL DEFAULT 2, + "id" INTEGER, + PRIMARY KEY("id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "TRIFADatabaseGlobalsNew" ( + "value" TEXT NOT NULL, + "key" TEXT, + PRIMARY KEY("key") +); +CREATE TABLE IF NOT EXISTS "FriendList" ( + "name" TEXT, + "alias_name" TEXT, + "status_message" TEXT, + "TOX_CONNECTION" INTEGER NOT NULL DEFAULT 0, + "TOX_CONNECTION_real" INTEGER NOT NULL DEFAULT 0, + "TOX_CONNECTION_on_off" INTEGER NOT NULL DEFAULT 0, + "TOX_CONNECTION_on_off_real" INTEGER NOT NULL DEFAULT 0, + "TOX_USER_STATUS" INTEGER NOT NULL DEFAULT 0, + "avatar_pathname" TEXT, + "avatar_filename" TEXT, + "avatar_update" BOOLEAN DEFAULT false, + "avatar_update_timestamp" INTEGER NOT NULL DEFAULT -1, + "notification_silent" BOOLEAN DEFAULT false, + "sort" INTEGER NOT NULL DEFAULT 0, + "last_online_timestamp" INTEGER NOT NULL DEFAULT -1, + "last_online_timestamp_real" INTEGER NOT NULL DEFAULT -1, + "added_timestamp" INTEGER NOT NULL DEFAULT -1, + "is_relay" BOOLEAN DEFAULT false, + "tox_public_key_string" TEXT, + PRIMARY KEY("tox_public_key_string") +); +CREATE INDEX IF NOT EXISTS "index_num_on_BootstrapNodeEntryDB" ON "BootstrapNodeEntryDB" ( + "num" +); +CREATE INDEX IF NOT EXISTS "index_udp_node_on_BootstrapNodeEntryDB" ON "BootstrapNodeEntryDB" ( + "udp_node" +); +CREATE INDEX IF NOT EXISTS "index_ip_on_BootstrapNodeEntryDB" ON "BootstrapNodeEntryDB" ( + "ip" +); +CREATE INDEX IF NOT EXISTS "index_port_on_BootstrapNodeEntryDB" ON "BootstrapNodeEntryDB" ( + "port" +); +CREATE INDEX IF NOT EXISTS "index_key_hex_on_BootstrapNodeEntryDB" ON "BootstrapNodeEntryDB" ( + "key_hex" +); +CREATE INDEX IF NOT EXISTS "index_who_invited__tox_public_key_string_on_ConferenceDB" ON "ConferenceDB" ( + "who_invited__tox_public_key_string" +); +CREATE INDEX IF NOT EXISTS "index_name_on_ConferenceDB" ON "ConferenceDB" ( + "name" +); +CREATE INDEX IF NOT EXISTS "index_peer_count_on_ConferenceDB" ON "ConferenceDB" ( + "peer_count" +); +CREATE INDEX IF NOT EXISTS "index_own_peer_number_on_ConferenceDB" ON "ConferenceDB" ( + "own_peer_number" +); +CREATE INDEX IF NOT EXISTS "index_kind_on_ConferenceDB" ON "ConferenceDB" ( + "kind" +); +CREATE INDEX IF NOT EXISTS "index_tox_conference_number_on_ConferenceDB" ON "ConferenceDB" ( + "tox_conference_number" +); +CREATE INDEX IF NOT EXISTS "index_conference_active_on_ConferenceDB" ON "ConferenceDB" ( + "conference_active" +); +CREATE INDEX IF NOT EXISTS "index_notification_silent_on_ConferenceDB" ON "ConferenceDB" ( + "notification_silent" +); +CREATE INDEX IF NOT EXISTS "index_conference_identifier_on_ConferenceMessage" ON "ConferenceMessage" ( + "conference_identifier" +); +CREATE INDEX IF NOT EXISTS "index_tox_peerpubkey_on_ConferenceMessage" ON "ConferenceMessage" ( + "tox_peerpubkey" +); +CREATE INDEX IF NOT EXISTS "index_tox_peername_on_ConferenceMessage" ON "ConferenceMessage" ( + "tox_peername" +); +CREATE INDEX IF NOT EXISTS "index_direction_on_ConferenceMessage" ON "ConferenceMessage" ( + "direction" +); +CREATE INDEX IF NOT EXISTS "index_TOX_MESSAGE_TYPE_on_ConferenceMessage" ON "ConferenceMessage" ( + "TOX_MESSAGE_TYPE" +); +CREATE INDEX IF NOT EXISTS "index_TRIFA_MESSAGE_TYPE_on_ConferenceMessage" ON "ConferenceMessage" ( + "TRIFA_MESSAGE_TYPE" +); +CREATE INDEX IF NOT EXISTS "index_rcvd_timestamp_on_ConferenceMessage" ON "ConferenceMessage" ( + "rcvd_timestamp" +); +CREATE INDEX IF NOT EXISTS "index_is_new_on_ConferenceMessage" ON "ConferenceMessage" ( + "is_new" +); +CREATE UNIQUE INDEX IF NOT EXISTS "index_conference_identifier_peer_pubkey_on_ConferencePeerCacheDB" ON "ConferencePeerCacheDB" ( + "conference_identifier", + "peer_pubkey" +); +CREATE INDEX IF NOT EXISTS "index_conference_identifier_on_ConferencePeerCacheDB" ON "ConferencePeerCacheDB" ( + "conference_identifier" +); +CREATE INDEX IF NOT EXISTS "index_peer_pubkey_on_ConferencePeerCacheDB" ON "ConferencePeerCacheDB" ( + "peer_pubkey" +); +CREATE INDEX IF NOT EXISTS "index_peer_name_on_ConferencePeerCacheDB" ON "ConferencePeerCacheDB" ( + "peer_name" +); +CREATE INDEX IF NOT EXISTS "index_last_update_timestamp_on_ConferencePeerCacheDB" ON "ConferencePeerCacheDB" ( + "last_update_timestamp" +); +CREATE INDEX IF NOT EXISTS "index_kind_on_FileDB" ON "FileDB" ( + "kind" +); +CREATE INDEX IF NOT EXISTS "index_direction_on_FileDB" ON "FileDB" ( + "direction" +); +CREATE INDEX IF NOT EXISTS "index_tox_public_key_string_on_FileDB" ON "FileDB" ( + "tox_public_key_string" +); +CREATE INDEX IF NOT EXISTS "index_path_name_on_FileDB" ON "FileDB" ( + "path_name" +); +CREATE INDEX IF NOT EXISTS "index_file_name_on_FileDB" ON "FileDB" ( + "file_name" +); +CREATE INDEX IF NOT EXISTS "index_filesize_on_FileDB" ON "FileDB" ( + "filesize" +); +CREATE INDEX IF NOT EXISTS "index_is_in_VFS_on_FileDB" ON "FileDB" ( + "is_in_VFS" +); +CREATE INDEX IF NOT EXISTS "index_tox_public_key_string_on_Filetransfer" ON "Filetransfer" ( + "tox_public_key_string" +); +CREATE INDEX IF NOT EXISTS "index_direction_on_Filetransfer" ON "Filetransfer" ( + "direction" +); +CREATE INDEX IF NOT EXISTS "index_file_number_on_Filetransfer" ON "Filetransfer" ( + "file_number" +); +CREATE INDEX IF NOT EXISTS "index_kind_on_Filetransfer" ON "Filetransfer" ( + "kind" +); +CREATE INDEX IF NOT EXISTS "index_state_on_Filetransfer" ON "Filetransfer" ( + "state" +); +CREATE INDEX IF NOT EXISTS "index_ft_accepted_on_Filetransfer" ON "Filetransfer" ( + "ft_accepted" +); +CREATE INDEX IF NOT EXISTS "index_ft_outgoing_started_on_Filetransfer" ON "Filetransfer" ( + "ft_outgoing_started" +); +CREATE INDEX IF NOT EXISTS "index_path_name_on_Filetransfer" ON "Filetransfer" ( + "path_name" +); +CREATE INDEX IF NOT EXISTS "index_file_name_on_Filetransfer" ON "Filetransfer" ( + "file_name" +); +CREATE INDEX IF NOT EXISTS "index_message_id_on_Filetransfer" ON "Filetransfer" ( + "message_id" +); +CREATE INDEX IF NOT EXISTS "index_key_on_TRIFADatabaseGlobals" ON "TRIFADatabaseGlobals" ( + "key" +); +CREATE INDEX IF NOT EXISTS "index_value_on_TRIFADatabaseGlobals" ON "TRIFADatabaseGlobals" ( + "value" +); +CREATE INDEX IF NOT EXISTS "index_TOX_CONNECTION_on_RelayListDB" ON "RelayListDB" ( + "TOX_CONNECTION" +); +CREATE INDEX IF NOT EXISTS "index_TOX_CONNECTION_on_off_on_RelayListDB" ON "RelayListDB" ( + "TOX_CONNECTION_on_off" +); +CREATE INDEX IF NOT EXISTS "index_own_relay_on_RelayListDB" ON "RelayListDB" ( + "own_relay" +); +CREATE INDEX IF NOT EXISTS "index_last_online_timestamp_on_RelayListDB" ON "RelayListDB" ( + "last_online_timestamp" +); +CREATE INDEX IF NOT EXISTS "index_tox_public_key_string_of_owner_on_RelayListDB" ON "RelayListDB" ( + "tox_public_key_string_of_owner" +); +CREATE INDEX IF NOT EXISTS "index_message_id_on_Message" ON "Message" ( + "message_id" +); +CREATE INDEX IF NOT EXISTS "index_tox_friendpubkey_on_Message" ON "Message" ( + "tox_friendpubkey" +); +CREATE INDEX IF NOT EXISTS "index_direction_on_Message" ON "Message" ( + "direction" +); +CREATE INDEX IF NOT EXISTS "index_TOX_MESSAGE_TYPE_on_Message" ON "Message" ( + "TOX_MESSAGE_TYPE" +); +CREATE INDEX IF NOT EXISTS "index_TRIFA_MESSAGE_TYPE_on_Message" ON "Message" ( + "TRIFA_MESSAGE_TYPE" +); +CREATE INDEX IF NOT EXISTS "index_state_on_Message" ON "Message" ( + "state" +); +CREATE INDEX IF NOT EXISTS "index_ft_accepted_on_Message" ON "Message" ( + "ft_accepted" +); +CREATE INDEX IF NOT EXISTS "index_ft_outgoing_started_on_Message" ON "Message" ( + "ft_outgoing_started" +); +CREATE INDEX IF NOT EXISTS "index_filedb_id_on_Message" ON "Message" ( + "filedb_id" +); +CREATE INDEX IF NOT EXISTS "index_filetransfer_id_on_Message" ON "Message" ( + "filetransfer_id" +); +CREATE INDEX IF NOT EXISTS "index_rcvd_timestamp_on_Message" ON "Message" ( + "rcvd_timestamp" +); +CREATE INDEX IF NOT EXISTS "index_rcvd_timestamp_ms_on_Message" ON "Message" ( + "rcvd_timestamp_ms" +); +CREATE INDEX IF NOT EXISTS "index_send_retries_on_Message" ON "Message" ( + "send_retries" +); +CREATE INDEX IF NOT EXISTS "index_is_new_on_Message" ON "Message" ( + "is_new" +); +CREATE INDEX IF NOT EXISTS "index_msg_id_hash_on_Message" ON "Message" ( + "msg_id_hash" +); +CREATE INDEX IF NOT EXISTS "index_raw_msgv2_bytes_on_Message" ON "Message" ( + "raw_msgv2_bytes" +); +CREATE INDEX IF NOT EXISTS "index_msg_version_on_Message" ON "Message" ( + "msg_version" +); +CREATE INDEX IF NOT EXISTS "index_resend_count_on_Message" ON "Message" ( + "resend_count" +); +CREATE INDEX IF NOT EXISTS "index_value_on_TRIFADatabaseGlobalsNew" ON "TRIFADatabaseGlobalsNew" ( + "value" +); +CREATE INDEX IF NOT EXISTS "index_alias_name_on_FriendList" ON "FriendList" ( + "alias_name" +); +CREATE INDEX IF NOT EXISTS "index_TOX_CONNECTION_on_FriendList" ON "FriendList" ( + "TOX_CONNECTION" +); +CREATE INDEX IF NOT EXISTS "index_TOX_CONNECTION_real_on_FriendList" ON "FriendList" ( + "TOX_CONNECTION_real" +); +CREATE INDEX IF NOT EXISTS "index_TOX_CONNECTION_on_off_on_FriendList" ON "FriendList" ( + "TOX_CONNECTION_on_off" +); +CREATE INDEX IF NOT EXISTS "index_TOX_CONNECTION_on_off_real_on_FriendList" ON "FriendList" ( + "TOX_CONNECTION_on_off_real" +); +CREATE INDEX IF NOT EXISTS "index_TOX_USER_STATUS_on_FriendList" ON "FriendList" ( + "TOX_USER_STATUS" +); +CREATE INDEX IF NOT EXISTS "index_avatar_update_on_FriendList" ON "FriendList" ( + "avatar_update" +); +CREATE INDEX IF NOT EXISTS "index_avatar_update_timestamp_on_FriendList" ON "FriendList" ( + "avatar_update_timestamp" +); +CREATE INDEX IF NOT EXISTS "index_notification_silent_on_FriendList" ON "FriendList" ( + "notification_silent" +); +CREATE INDEX IF NOT EXISTS "index_sort_on_FriendList" ON "FriendList" ( + "sort" +); +CREATE INDEX IF NOT EXISTS "index_last_online_timestamp_on_FriendList" ON "FriendList" ( + "last_online_timestamp" +); +CREATE INDEX IF NOT EXISTS "index_added_timestamp_on_FriendList" ON "FriendList" ( + "added_timestamp" +); +CREATE INDEX IF NOT EXISTS "index_is_relay_on_FriendList" ON "FriendList" ( + "is_relay" +); + diff --git a/src/main/java/com/zoffcc/applications/trifa/TRIFAGlobals.java b/src/main/java/com/zoffcc/applications/trifa/TRIFAGlobals.java new file mode 100644 index 00000000..468ab07b --- /dev/null +++ b/src/main/java/com/zoffcc/applications/trifa/TRIFAGlobals.java @@ -0,0 +1,207 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.trifa; + +import java.awt.Color; + +import static com.zoffcc.applications.trifa.ToxVars.MAX_FILE_DATA_SIZE; + + +public class TRIFAGlobals +{ + static String global_my_toxid = ""; + static String global_my_name = ""; + static String global_my_status_message = ""; + static boolean bootstrapping = false; + static long global_self_last_went_online_timestamp = -1; + static long global_self_last_went_offline_timestamp = -1; + final static int TOX_BOOTSTRAP_AGAIN_AFTER_OFFLINE_MILLIS = + 1000 * 60 * 2; // bootstrap again after 2 minutes offline + + public static final String MY_PACKAGE_NAME = "com.zoffcc.applications.trifa"; + + // ---------- + // https://toxme.io/u/echobot + // echobot@toxme.io + final static String ECHOBOT_TOXID = "76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6"; + final static String ECHOBOT_INIT_NAME = "Echobot"; + final static String ECHOBOT_INIT_STATUSMSG = "A tiny bot to test Tox audio and video."; + final static String TOXIRC_TOKTOK_CONFID = "836eaf5f6af15a9608feb231e48112f074b7625c054446163a4d8311a5abbb19"; + final static String TOXIRC_PUBKEY = "A922A51E1C91205B9F7992E2273107D47C72E8AE909C61C28A77A4A2A115431B"; + // ---------- + // https://toxme.io/u/groupbot + // groupbot@toxme.io + final static String GROUPBOT_TOXID = "56A1ADE4B65B86BCD51CC73E2CD4E542179F47959FE3E0E21B4B0ACDADE51855D34D34D37CB5"; + + final static boolean ADD_BOTS_ON_STARTUP = true; + + final static int HIGHER_GLOBAL_VIDEO_BITRATE = 3500; + final static int NORMAL_GLOBAL_VIDEO_BITRATE = 2100; + final static int LOWER_GLOBAL_VIDEO_BITRATE = 250; + + final static int HIGHER_GLOBAL_AUDIO_BITRATE = 128; + final static int NORMAL_GLOBAL_AUDIO_BITRATE = 16; // 64; + final static int LOWER_GLOBAL_AUDIO_BITRATE = 8; + + final static int HIGHER_NGC_VIDEO_BITRATE = 400; + final static int LOWER_NGC_VIDEO_BITRATE = 90; + + final static int NGC_AUDIO_BITRATE = 8000; + + final static int HIGHER_NGC_VIDEO_QUANTIZER = 38; // higher here means "higer video quality" which is a lower q value! + final static int LOWER_NGC_VIDEO_QUANTIZER = 51; + + static int GLOBAL_VIDEO_BITRATE = NORMAL_GLOBAL_VIDEO_BITRATE; // this works nice: 2500; + static int GLOBAL_AUDIO_BITRATE = NORMAL_GLOBAL_AUDIO_BITRATE; // allowed values: (xx>=6) && (xx<=510) + + static final String GENERIC_TOR_USERAGENT = "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0"; + static final int PUSH_URL_TRIGGER_AGAIN_MAX_COUNT = 8; + static final int PUSH_URL_TRIGGER_AGAIN_SECONDS = 21; + static final int PUSH_URL_TRIGGER_GET_MESSAGE_FOR_delta_ms_prev = 100; + static final int PUSH_URL_TRIGGER_GET_MESSAGE_FOR_delta_ms_after = 1000; + + static final int VIDEO_CODEC_VP8 = 0; + static final int VIDEO_CODEC_H264 = 1; + + static final int MESSAGE_PAGING_NUM_MSGS_PER_PAGE = 200; + static final int MESSAGE_PAGING_LAST_PAGE_MARGIN = 40; + static final String MESSAGE_PAGING_SHOW_OLDER_HASH = "00000000000000001"; + static final String MESSAGE_PAGING_SHOW_NEWER_HASH = "00000000000000002"; + + static int VIDEO_FRAME_RATE_OUTGOING = 0; + static long last_video_frame_sent = -1; + static int count_video_frame_sent = 0; + static int VIDEO_FRAME_RATE_INCOMING = 0; + static long last_video_frame_received = -1; + static int count_video_frame_received = 0; + + static final int TOX_ITERATE_MS_MIN_NORMAL = 12; + static final int TOX_ITERATE_MS_MIN_FILETRANSFER = 8; + + static long ONE_HOUR_IN_MS = 3600 * 1000; + + final static int GLOBAL_MIN_VIDEO_BITRATE = 64; + final static int GLOBAL_MIN_AUDIO_BITRATE = 8; // allowed values: (xx>=6) && (xx<=510) + + static final int CAMPREVIEW_NUM_BUFFERS = 3; + + static final String TRIFA_SYSTEM_MESSAGE_PEER_PUBKEY = "-1"; + static final Color TRIFA_SYSTEM_MESSAGE_PEER_CHATCOLOR = new Color(0xffc35838, true); // red-ish + public static final int CONFERENCE_ID_LENGTH = 32; + public static final int GROUP_ID_LENGTH = 32; + + public static final int MAX_TEXTMSG_RESEND_COUNT_OLDMSG_VERSION = 4; + + public static final long UINT32_MAX_JAVA = 4294967295L; // 0xffffffff == UINT32_MAX + + static long global_last_activity_for_battery_savings_ts = -1; + static long LAST_ONLINE_TIMSTAMP_ONLINE_NOW = Long.MAX_VALUE - 1; + static long LAST_ONLINE_TIMSTAMP_ONLINE_OFFLINE = -1; + + static long global_last_activity_outgoung_ft_ts = -1; + + final static String NOTIFICATION_FCM_PUSH_URL_PREFIX = "https://tox.zoff.xyz/toxfcm/fcm.php?id="; + final static String NOTIFICATION_FCM_PUSH_URL_PREFIX_OLD = "https://toxcon2020.zoff.cc/toxfcm/fcm.php?id="; + final static String NOTIFICATION_UP_PUSH_URL_PREFIX = "https://gotify1.unifiedpush.org/UP?token="; + final static String NOTIFICATION_NTFY_PUSH_URL_PREFIX = "https://ntfy.sh/"; + + public static final Color CHAT_MSG_BG_SELF_COLOR = new Color(0xff33b5e5, true); + public static final Color CHAT_MSG_BG_OTHER_COLOR = new Color(0xffffbb33, true); + public static final Color SEE_THRU = new Color(0x00111111, true); + + static int MESSAGE_SYNC_DOUBLE_INTERVAL_SECS = 20; + static long MESSAGE_V2_MSG_SENT_OK = (Long.MAX_VALUE - 1); + static int global_self_connection_status = ToxVars.TOX_CONNECTION.TOX_CONNECTION_NONE.value; + + final static String VFS_TMP_FILE_DIR = "./tempdir/files/"; + // final static String VFS_TMP_AVATAR_DIR = "/avatar_tempdir/files/"; // TODO: avatar should get their own directory! + final static String VFS_FILE_DIR = "./datadir/files/"; + final static String VFS_OWN_AVATAR_DIR = "./datadir/myavatar/"; + static String VFS_PREFIX = ""; // only set for normal (unencrypted) storage + + final static String IMAGE_THUMBNAIL_PLACEHOLDER = "image_thumb.png"; + final static long IMAGE_FILESIZE_MAX_BYTES_FOR_THUMB = 5000000; + final static long IMAGE_FILE_MAX_RENDER_MS = 60; + + final static long AVATAR_INCOMING_MAX_BYTE_SIZE = 1 * 1024 * 1024; // limit incoming avatars at 1MByte size + final static long AVATAR_SELF_MAX_BYTE_SIZE = 1 * 1024 * 1024; // limit incoming avatars at 1MByte size + final static String FRIEND_AVATAR_FILENAME = "_____xyz____avatar.png"; + + // ---- lookup cache ---- + // static Map cache_ft_fis = new HashMap(); + // static Map cache_ft_fos = new HashMap(); + // static Map cache_ft_fos_normal = new HashMap(); + // ---- lookup cache ---- + + static final long UPDATE_MESSAGE_PROGRESS_AFTER_BYTES = + 120L * MAX_FILE_DATA_SIZE; // x kBytes // update FT and progress bars every XX bytes + static final long UPDATE_MESSAGE_PROGRESS_AFTER_BYTES_SMALL_FILES = 8L * MAX_FILE_DATA_SIZE; // x kBytes + static final long UPDATE_MESSAGE_PROGRESS_SMALL_FILE_IS_LESS_THAN_BYTES = 250000L; // x kBytes, less than this in bytes is a small file + + // static boolean global_incoming_ft_active = false; + + static final int FRIEND_NAME_DISPLAY_MENU_MAXLEN = 50; + static final int CONFERENCE_NAME_DISPLAY_MENU_MAXLEN = 50; + static final int FT_IMAGE_THUMBNAIL_WIDTH = 200; + static final int FT_IMAGE_THUMBNAIL_HEIGHT = 90; + + public static enum TRIFA_FT_DIRECTION + { + TRIFA_FT_DIRECTION_INCOMING(0), TRIFA_FT_DIRECTION_OUTGOING(1); + + public int value; + + private TRIFA_FT_DIRECTION(int value) + { + this.value = value; + } + + + } + + public static enum TRIFA_MSG_TYPE + { + TRIFA_MSG_TYPE_TEXT(0), TRIFA_MSG_FILE(1); + + public int value; + + private TRIFA_MSG_TYPE(int value) + { + this.value = value; + } + + } + + + public static enum CONTROL_PROXY_MESSAGE_TYPE + { + CONTROL_PROXY_MESSAGE_TYPE_FRIEND_PUBKEY_FOR_PROXY(175), CONTROL_PROXY_MESSAGE_TYPE_PROXY_PUBKEY_FOR_FRIEND( + 176), CONTROL_PROXY_MESSAGE_TYPE_ALL_MESSAGES_SENT(177), CONTROL_PROXY_MESSAGE_TYPE_PROXY_KILLSWITCH( + 178), CONTROL_PROXY_MESSAGE_TYPE_NOTIFICATION_TOKEN(179), CONTROL_PROXY_MESSAGE_TYPE_PUSH_URL_FOR_FRIEND( + 181); + + public int value; + + private CONTROL_PROXY_MESSAGE_TYPE(int value) + { + this.value = value; + } + } +} diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 2703b172..6dac6f38 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -3,9 +3,9 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.* import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.FormatSize import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -17,6 +17,8 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp @@ -25,6 +27,7 @@ import androidx.compose.ui.window.* import com.zoffcc.applications.trifa.Log import com.zoffcc.applications.trifa.MainActivity.Companion.main_init import com.zoffcc.applications.trifa.PrefsSettings +import com.zoffcc.applications.trifa.StateContacts import com.zoffcc.applications.trifa.TrifaToxService import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -32,9 +35,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.runBlocking -import org.briarproject.briar.desktop.contact.ContactItem import org.briarproject.briar.desktop.contact.ContactList -import org.briarproject.briar.desktop.contact.getConnectionColor import org.briarproject.briar.desktop.navigation.BriarSidebar import org.briarproject.briar.desktop.ui.VerticalDivider import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @@ -61,9 +62,6 @@ fun App() { var start_button_text by remember { mutableStateOf("start") } var tox_running_state: String by remember { mutableStateOf("stopped") } - var online_button_text by remember { mutableStateOf("offline") } - var online_button_color by remember { mutableStateOf(Color.White.toArgb()) } - Log.i(TAG, "CCCC:" + PrefsSettings::class.java) var uiscale_default = LocalDensity.current.density @@ -76,8 +74,6 @@ fun App() { } catch (_: Exception) { } - var ui_scale by remember { mutableStateOf(uiscale_default) } - MaterialTheme { Scaffold() { Row { @@ -126,74 +122,34 @@ fun App() { }) { Text(start_button_text) } + var online_button_text by remember { mutableStateOf("offline") } Button( // self connection state button onClick = {}, colors = ButtonDefaults.buttonColors(), enabled = false ) { Box( - modifier = Modifier.size(16.dp). - border(1.dp, Color.Black, CircleShape) - .background(Color(online_button_color_wrapper), - CircleShape) + modifier = Modifier.size(16.dp).border(1.dp, Color.Black, CircleShape) + .background( + Color(online_button_color_wrapper), + CircleShape + ) ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text(getOnlineButtonText(online_button_text)) - Thread { while (true) { - Thread.sleep(100) + Thread.sleep(200) if (online_button_text != online_button_text_wrapper) { online_button_text = online_button_text_wrapper - // online_button_color = online_button_color_wrapper } } }.start() } } - - DetailItem( - label = i18n("UI Scale"), - description = "${i18n("current_value:")}: " + " " + - ui_scale + ", " + - i18n("drag Slider to change") - ) { - Row( - horizontalArrangement = Arrangement.spacedBy(2.dp), - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.width(200.dp) - ) { - Icon(Icons.Default.FormatSize, null, Modifier.scale(0.7f)) - Slider( - value = ui_scale ?: LocalDensity.current.density, - onValueChange = { - ui_scale = it - prefs.putFloat("main.ui_scale_factor", ui_scale) - Log.i(TAG, "density: $ui_scale") - }, - onValueChangeFinished = { }, - valueRange = 1f..3f, - steps = 3, - // todo: without setting the width explicitly, - // the slider takes up the whole remaining space - modifier = Modifier.width(150.dp) - ) - Icon(Icons.Default.FormatSize, null) - } - } - - /* - var contacts = ArrayList() - for (i in 1..1) { - if (i.mod(2) == 0) { - val f1 = ContactItem(true) - contacts.add(f1) - } else { - val f1 = ContactItem(false) - contacts.add(f1) - } - } - */ + SaveDataPath() + ToxIDTextField() + // UIScaleSlider(uiscale_default) val contacts by contactstore.stateFlow.collectAsState() Row(modifier = Modifier.fillMaxWidth()) { ContactList( @@ -211,8 +167,88 @@ fun App() { } +@Composable +private fun UIScaleSlider(uiscale_default: Float) { + var ui_scale by remember { mutableStateOf(uiscale_default) } + DetailItem( + label = i18n("UI Scale"), + description = "${i18n("current_value:")}: " + " " + + ui_scale + ", " + + i18n("drag Slider to change") + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(2.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.width(200.dp) + ) { + Icon(Icons.Default.FormatSize, null, Modifier.scale(0.7f)) + Slider( + value = ui_scale ?: LocalDensity.current.density, + onValueChange = { + ui_scale = it + prefs.putFloat("main.ui_scale_factor", ui_scale) + Log.i(TAG, "density: $ui_scale") + }, + onValueChangeFinished = { }, + valueRange = 1f..3f, + steps = 3, + // todo: without setting the width explicitly, + // the slider takes up the whole remaining space + modifier = Modifier.width(150.dp) + ) + Icon(Icons.Default.FormatSize, null) + } + } +} + +@Composable +private fun ToxIDTextField() { + val toxdata by toxdatastore.stateFlow.collectAsState() + TextField( + enabled = true, + readOnly = true, + singleLine = true, + textStyle = TextStyle(fontSize = 18.sp), + modifier = Modifier.width(500.dp), + colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White), + keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.None, + autoCorrect = false, + ), + value = toxdata.mytoxid, + placeholder = { + Text("my ToxID ...") + }, + onValueChange = { + } + ) +} + +@Composable +private fun SaveDataPath() { + val savepathdata by savepathstore.stateFlow.collectAsState() + TextField( + enabled = savepathdata.savePathEnabled, + singleLine = true, + textStyle = TextStyle(fontSize = 18.sp), + modifier = Modifier.width(500.dp), + colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White), + keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.None, + autoCorrect = false, + ), + value = savepathdata.savePath, + placeholder = { + Text("save file path ...") + }, + onValueChange = { + savepathstore.updatePath(it) + } + ) +} + fun getOnlineButtonText(text_in: String): String { - return when(text_in) { + return when (text_in) { "udp" -> "UDP" "tcp" -> "TCP" else -> "offline" @@ -415,4 +451,12 @@ fun DetailItem( ) { Text(label) setting() -} \ No newline at end of file +} + +fun unlock_data_dir_input() { + savepathstore.updateEnabled(true) +} + +fun lock_data_dir_input() { + savepathstore.updateEnabled(false) +} diff --git a/src/main/kotlin/com/zoffcc/applications/trifa/MainActivity.kt b/src/main/kotlin/com/zoffcc/applications/trifa/MainActivity.kt index 329d0b72..7b3ceba6 100644 --- a/src/main/kotlin/com/zoffcc/applications/trifa/MainActivity.kt +++ b/src/main/kotlin/com/zoffcc/applications/trifa/MainActivity.kt @@ -6,15 +6,15 @@ import User import com.zoffcc.applications.trifa.HelperFriend.send_friend_msg_receipt_v2_wrapper import com.zoffcc.applications.trifa.HelperGeneric.bytesToHex import com.zoffcc.applications.trifa.HelperGeneric.update_savedata_file_wrapper -import com.zoffcc.applications.trifa.TRIFAGlobals.LOWER_NGC_VIDEO_BITRATE -import com.zoffcc.applications.trifa.TRIFAGlobals.LOWER_NGC_VIDEO_QUANTIZER -import com.zoffcc.applications.trifa.TRIFAGlobals.NGC_AUDIO_BITRATE +import com.zoffcc.applications.trifa.TRIFAGlobals.* import com.zoffcc.applications.trifa.ToxVars.TOX_HASH_LENGTH import contactstore +import lock_data_dir_input import org.briarproject.briar.desktop.contact.ContactItem import set_tox_online_state import store import timestampMs +import toxdatastore import java.io.File import java.io.PrintWriter import java.nio.ByteBuffer @@ -41,14 +41,15 @@ class MainActivity { // --------- global config --------- var native_lib_loaded = false var tox_service_fg: TrifaToxService? = null - var app_files_directory = "." - public var PREF__udp_enabled = 1 + var tox_savefile_directory = "." + var PREF__udp_enabled = 1 var PREF__orbot_enabled_to_int = 0 var PREF__local_discovery_enabled = 1 var PREF__ipv6_enabled = 1 var PREF__force_udp_only = 0 var incoming_messages_queue: BlockingQueue = LinkedBlockingQueue() + // var PREF__ngc_video_bitrate: Int = LOWER_NGC_VIDEO_BITRATE // ~600 kbits/s -> ~60 kbytes/s var PREF__ngc_video_max_quantizer: Int = @@ -57,6 +58,10 @@ class MainActivity { var PREF__ngc_audio_samplerate = 48000 var PREF__ngc_audio_channels = 1 + var PREF__tox_savefile_dir = "." + @JvmField + var PREF__database_files_dir = "." + // // !!!!!! DEBUG !!!!!! change to real password later !!!!!! // !!!!!! DEBUG !!!!!! change to real password later !!!!!! @@ -105,17 +110,27 @@ class MainActivity { tox_service_fg = TrifaToxService() + lock_data_dir_input() + if (!TrifaToxService.TOX_SERVICE_STARTED) { var ORBOT_PROXY_HOST = "" var ORBOT_PROXY_PORT: Long = 0 - app_files_directory = "." + File.separator + tox_savefile_directory = PREF__tox_savefile_dir + File.separator Log.i(TAG, "init:PREF__udp_enabled=$PREF__udp_enabled") init( - app_files_directory, PREF__udp_enabled, PREF__local_discovery_enabled, PREF__orbot_enabled_to_int, - ORBOT_PROXY_HOST, ORBOT_PROXY_PORT, password_hash, PREF__ipv6_enabled, PREF__force_udp_only, + tox_savefile_directory, + PREF__udp_enabled, + PREF__local_discovery_enabled, + PREF__orbot_enabled_to_int, + ORBOT_PROXY_HOST, + ORBOT_PROXY_PORT, + password_hash, + PREF__ipv6_enabled, + PREF__force_udp_only, PREF__ngc_video_bitrate, PREF__ngc_video_max_quantizer, - PREF__ngc_audio_bitrate, PREF__ngc_audio_samplerate, + PREF__ngc_audio_bitrate, + PREF__ngc_audio_samplerate, PREF__ngc_audio_channels ) tox_service_fg!!.tox_thread_start_fg() @@ -129,16 +144,24 @@ class MainActivity { } try { - PrintWriter("toxid.txt", "UTF-8").use { out -> out.write(my_tox_id_temp) } - Log.i(TAG, "writing toxid to: " - + File(".").canonicalPath + - File.separator + "toxid.txt") + toxdatastore.updateToxID(my_tox_id_temp) + } catch (_: Exception) { + } + + try { + PrintWriter(PREF__tox_savefile_dir + File.separator + "toxid.txt", "UTF-8") + .use { out -> out.write(my_tox_id_temp) } + Log.i( + TAG, "writing toxid to: " + + File(PREF__tox_savefile_dir).canonicalPath + + File.separator + "toxid.txt" + ) } catch (_: Exception) { } } init { - if (!MainActivity.native_lib_loaded) { + if (!native_lib_loaded) { val resourcesDir = File(System.getProperty("compose.application.resources.dir")) System.out.println("XXXXX1:" + resourcesDir) System.out.println("XXXXX1.1:OS:" + OperatingSystem.getCurrent()) @@ -155,15 +178,14 @@ class MainActivity { System.out.println("XXXXX1.1:OS:Unknown operating system:EXIT") System.exit(3) } - // var libFile = File("./jni_/libjni-c-toxcore.so"); System.out.println("XXXXX2:" + libFile + " " + libFile.canonicalPath) try { System.load(libFile.canonicalPath) - MainActivity.native_lib_loaded = true + native_lib_loaded = true Log.i(TAG, "successfully loaded native library") } catch (e: UnsatisfiedLinkError) { - MainActivity.native_lib_loaded = false + native_lib_loaded = false Log.i(TAG, "loadLibrary jni-c-toxcore failed!") e.printStackTrace() System.exit(4) @@ -199,24 +221,32 @@ class MainActivity { @JvmStatic external fun update_savedata_file(tox_encrypt_passphrase_hash: String?) + @JvmStatic external fun get_my_toxid(): String + @JvmStatic external fun add_tcp_relay_single(ip: String?, key_hex: String?, port: Long): Int + @JvmStatic external fun bootstrap_single(ip: String?, key_hex: String?, port: Long): Int + @JvmStatic external fun tox_self_get_connection_status(): Int + @JvmStatic external fun init_tox_callbacks() + @JvmStatic external fun tox_iteration_interval(): Long + @JvmStatic external fun tox_iterate(): Long // ----------- TRIfA internal ----------- @JvmStatic external fun jni_iterate_group_audio(delta_new: Int, want_ms_output: Int): Int + @JvmStatic external fun jni_iterate_videocall_audio( delta_new: Int, @@ -240,86 +270,125 @@ class MainActivity { @JvmStatic external fun tox_set_do_not_sync_av(do_not_sync_av: Int) + @JvmStatic external fun tox_set_onion_active(active: Int) // ----------- TRIfA internal ----------- @JvmStatic external fun tox_kill(): Long + @JvmStatic external fun exit() + @JvmStatic external fun tox_friend_send_message(friendnum: Long, a_TOX_MESSAGE_TYPE: Int, message: String?): Long + @JvmStatic external fun tox_version_major(): Long + @JvmStatic external fun tox_version_minor(): Long + @JvmStatic external fun tox_version_patch(): Long + @JvmStatic external fun jnictoxcore_version(): String + @JvmStatic external fun libavutil_version(): String? + @JvmStatic external fun libopus_version(): String? + @JvmStatic external fun libsodium_version(): String? + @JvmStatic external fun tox_max_filename_length(): Long + @JvmStatic external fun tox_file_id_length(): Long + @JvmStatic external fun tox_max_message_length(): Long + @JvmStatic external fun tox_friend_add(toxid_str: String?, message: String?): Long + @JvmStatic external fun tox_friend_add_norequest(public_key_str: String?): Long + @JvmStatic external fun tox_self_get_friend_list_size(): Long + @JvmStatic external fun tox_self_set_nospam(nospam: Long) // this actually needs an "uint32_t" which is an unsigned 32bit integer value + @JvmStatic external fun tox_self_get_nospam(): Long // this actually returns an "uint32_t" which is an unsigned 32bit integer value + @JvmStatic external fun tox_friend_by_public_key(friend_public_key_string: String?): Long + @JvmStatic external fun tox_friend_get_public_key(friend_number: Long): String? + @JvmStatic - external fun tox_friend_get_name(friend_number: Long): String? + external fun tox_friend_get_name(friend_number: Long): String? + @JvmStatic external fun tox_friend_get_capabilities(friend_number: Long): Long + @JvmStatic external fun tox_self_get_friend_list(): LongArray? + @JvmStatic external fun tox_self_set_name(name: String?): Int + @JvmStatic external fun tox_self_set_status_message(status_message: String?): Int + @JvmStatic external fun tox_self_set_status(a_TOX_USER_STATUS: Int) + @JvmStatic external fun tox_self_set_typing(friend_number: Long, typing: Int): Int + @JvmStatic external fun tox_friend_get_connection_status(friend_number: Long): Int + @JvmStatic external fun tox_friend_delete(friend_number: Long): Int + @JvmStatic external fun tox_self_get_name(): String? + @JvmStatic external fun tox_self_get_name_size(): Long + @JvmStatic external fun tox_self_get_status_message_size(): Long + @JvmStatic external fun tox_self_get_status_message(): String? + @JvmStatic external fun tox_friend_send_lossless_packet(friend_number: Long, data: ByteArray?, data_length: Int): Int + @JvmStatic external fun tox_file_control(friend_number: Long, file_number: Long, a_TOX_FILE_CONTROL: Int): Int + @JvmStatic external fun tox_hash(hash_buffer: ByteBuffer?, data_buffer: ByteBuffer?, data_length: Long): Int + @JvmStatic external fun tox_file_seek(friend_number: Long, file_number: Long, position: Long): Int + @JvmStatic external fun tox_file_get_file_id(friend_number: Long, file_number: Long, file_id_buffer: ByteBuffer?): Int + @JvmStatic external fun tox_file_send( friend_number: Long, @@ -344,6 +413,7 @@ class MainActivity { // --------------- Message V2 ------------- @JvmStatic external fun tox_messagev2_size(text_length: Long, type: Long, alter_type: Long): Long + @JvmStatic external fun tox_messagev2_wrap( text_length: Long, @@ -358,10 +428,13 @@ class MainActivity { @JvmStatic external fun tox_messagev2_get_message_id(raw_message_buffer: ByteBuffer?, msgid_buffer: ByteBuffer?): Int + @JvmStatic external fun tox_messagev2_get_ts_sec(raw_message_buffer: ByteBuffer?): Long + @JvmStatic external fun tox_messagev2_get_ts_ms(raw_message_buffer: ByteBuffer?): Long + @JvmStatic external fun tox_messagev2_get_message_text( raw_message_buffer: ByteBuffer?, @@ -373,8 +446,10 @@ class MainActivity { @JvmStatic external fun tox_messagev2_get_sync_message_pubkey(raw_message_buffer: ByteBuffer?): String? + @JvmStatic external fun tox_messagev2_get_sync_message_type(raw_message_buffer: ByteBuffer?): Long + @JvmStatic external fun tox_util_friend_send_msg_receipt_v2( friend_number: Long, @@ -409,6 +484,7 @@ class MainActivity { // --------------- Message V3 ------------- @JvmStatic external fun tox_messagev3_get_new_message_id(hash_buffer: ByteBuffer?): Int + @JvmStatic external fun tox_messagev3_friend_send_message( friendnum: Long, @@ -426,20 +502,28 @@ class MainActivity { // --------------- Conference ------------- @JvmStatic external fun tox_conference_join(friend_number: Long, cookie_buffer: ByteBuffer?, cookie_length: Long): Long + @JvmStatic external fun tox_conference_peer_count(conference_number: Long): Long + @JvmStatic external fun tox_conference_peer_get_name_size(conference_number: Long, peer_number: Long): Long + @JvmStatic external fun tox_conference_peer_get_name(conference_number: Long, peer_number: Long): String? + @JvmStatic external fun tox_conference_peer_get_public_key(conference_number: Long, peer_number: Long): String? + @JvmStatic external fun tox_conference_offline_peer_count(conference_number: Long): Long + @JvmStatic external fun tox_conference_offline_peer_get_name_size(conference_number: Long, offline_peer_number: Long): Long + @JvmStatic external fun tox_conference_offline_peer_get_name(conference_number: Long, offline_peer_number: Long): String? + @JvmStatic external fun tox_conference_offline_peer_get_public_key( conference_number: Long, @@ -454,12 +538,16 @@ class MainActivity { @JvmStatic external fun tox_conference_peer_number_is_ours(conference_number: Long, peer_number: Long): Int + @JvmStatic external fun tox_conference_get_title_size(conference_number: Long): Long + @JvmStatic external fun tox_conference_get_title(conference_number: Long): String? + @JvmStatic external fun tox_conference_get_type(conference_number: Long): Int + @JvmStatic external fun tox_conference_send_message( conference_number: Long, @@ -469,16 +557,22 @@ class MainActivity { @JvmStatic external fun tox_conference_delete(conference_number: Long): Int + @JvmStatic external fun tox_conference_get_chatlist_size(): Long + @JvmStatic external fun tox_conference_get_chatlist(): LongArray? + @JvmStatic external fun tox_conference_get_id(conference_number: Long, cookie_buffer: ByteBuffer?): Int + @JvmStatic external fun tox_conference_new(): Int + @JvmStatic external fun tox_conference_invite(friend_number: Long, conference_number: Long): Int + @JvmStatic external fun tox_conference_set_title(conference_number: Long, title: String?): Int // --------------- Conference ------------- @@ -533,58 +627,85 @@ class MainActivity { @JvmStatic external fun tox_group_leave(group_number: Long, part_message: String?): Int + @JvmStatic external fun tox_group_self_get_peer_id(group_number: Long): Long + @JvmStatic external fun tox_group_self_set_name(group_number: Long, my_peer_name: String?): Int + @JvmStatic external fun tox_group_self_get_public_key(group_number: Long): String? + @JvmStatic external fun tox_group_self_get_role(group_number: Long): Int + @JvmStatic external fun tox_group_peer_get_role(group_number: Long, peer_id: Long): Int + @JvmStatic external fun tox_group_get_chat_id(group_number: Long, chat_id_buffer: ByteBuffer?): Int + @JvmStatic external fun tox_group_get_number_groups(): Long + @JvmStatic external fun tox_group_get_grouplist(): LongArray? + @JvmStatic external fun tox_group_peer_count(group_number: Long): Long + @JvmStatic external fun tox_group_get_peer_limit(group_number: Long): Int + @JvmStatic external fun tox_group_founder_set_peer_limit(group_number: Long, max_peers: Int): Int + @JvmStatic external fun tox_group_offline_peer_count(group_number: Long): Long + @JvmStatic external fun tox_group_get_peerlist(group_number: Long): LongArray? + @JvmStatic external fun tox_group_by_chat_id(chat_id_buffer: ByteBuffer?): Long + @JvmStatic external fun tox_group_get_privacy_state(group_number: Long): Int + @JvmStatic external fun tox_group_mod_kick_peer(group_number: Long, peer_id: Long): Int + @JvmStatic external fun tox_group_mod_set_role(group_number: Long, peer_id: Long, a_Tox_Group_Role: Int): Int + @JvmStatic external fun tox_group_peer_get_public_key(group_number: Long, peer_id: Long): String? + @JvmStatic external fun tox_group_peer_by_public_key(group_number: Long, peer_public_key_string: String?): Long + @JvmStatic external fun tox_group_peer_get_name(group_number: Long, peer_id: Long): String? + @JvmStatic external fun tox_group_get_name(group_number: Long): String? + @JvmStatic external fun tox_group_get_topic(group_number: Long): String? + @JvmStatic external fun tox_group_peer_get_connection_status(group_number: Long, peer_id: Long): Int + @JvmStatic external fun tox_group_invite_friend(group_number: Long, friend_number: Long): Int + @JvmStatic external fun tox_group_is_connected(group_number: Long): Int + @JvmStatic external fun tox_group_reconnect(group_number: Long): Int + @JvmStatic external fun tox_group_send_custom_packet( group_number: Long, @@ -702,18 +823,25 @@ class MainActivity { // --------------- AV ------------- @JvmStatic external fun toxav_answer(friendnum: Long, audio_bit_rate: Long, video_bit_rate: Long): Int + @JvmStatic external fun toxav_iteration_interval(): Long + @JvmStatic external fun toxav_call(friendnum: Long, audio_bit_rate: Long, video_bit_rate: Long): Int + @JvmStatic external fun toxav_bit_rate_set(friendnum: Long, audio_bit_rate: Long, video_bit_rate: Long): Int + @JvmStatic external fun toxav_call_control(friendnum: Long, a_TOXAV_CALL_CONTROL: Int): Int + @JvmStatic external fun toxav_video_send_frame_uv_reversed(friendnum: Long, frame_width_px: Int, frame_height_px: Int): Int + @JvmStatic external fun toxav_video_send_frame(friendnum: Long, frame_width_px: Int, frame_height_px: Int): Int + @JvmStatic external fun toxav_video_send_frame_age( friendnum: Long, @@ -741,8 +869,10 @@ class MainActivity { @JvmStatic external fun toxav_option_set(friendnum: Long, a_TOXAV_OPTIONS_OPTION: Long, value: Long): Int + @JvmStatic external fun set_av_call_status(status: Int) + @JvmStatic external fun set_audio_play_volume_percent(volume_percent: Int) @@ -919,8 +1049,7 @@ class MainActivity { ) { try { var fname = tox_friend_get_name(friend_number) - if (fname == null) - { + if (fname == null) { fname = "Friend" } contactstore.update( @@ -955,8 +1084,7 @@ class MainActivity { update_savedata_file_wrapper() try { var fname = tox_friend_get_name(friend_number) - if (fname == null) - { + if (fname == null) { fname = "Friend" } contactstore.update( @@ -1012,8 +1140,7 @@ class MainActivity { return } - if (msgV3hash_hex_string != null) - { + if (msgV3hash_hex_string != null) { HelperMessage.send_msgv3_high_level_ack(friend_number, msgV3hash_hex_string); try { // ("msgv3:"+friend_message) @@ -1029,10 +1156,9 @@ class MainActivity { ) ) ) - } catch (_ : Exception) {} - } - else - { + } catch (_: Exception) { + } + } else { try { // ("msgv1:"+friend_message) val toxpk = tox_friend_get_public_key(friend_number) @@ -1047,7 +1173,8 @@ class MainActivity { ) ) ) - } catch (_ : Exception) {} + } catch (_: Exception) { + } } } @@ -1097,7 +1224,8 @@ class MainActivity { ) ) // incoming_messages_queue.offer(friend_message) // ("msgv2:"+friend_message) - } catch (_ : Exception) {} + } catch (_: Exception) { + } val pin_timestamp = System.currentTimeMillis() send_friend_msg_receipt_v2_wrapper(friend_number, msg_type, msg_id_buffer, (pin_timestamp / 1000)); diff --git a/src/main/kotlin/com/zoffcc/applications/trifa/TRIFAGlobals.kt b/src/main/kotlin/com/zoffcc/applications/trifa/TRIFAGlobals.kt deleted file mode 100644 index 5e3bcf13..00000000 --- a/src/main/kotlin/com/zoffcc/applications/trifa/TRIFAGlobals.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.zoffcc.applications.trifa - -object TRIFAGlobals { - public var bootstrapping = false - const val HIGHER_NGC_VIDEO_BITRATE = 400 - const val LOWER_NGC_VIDEO_BITRATE = 90 - const val NGC_AUDIO_BITRATE = 8000 - const val HIGHER_NGC_VIDEO_QUANTIZER = 38 // higher here means "higer video quality" which is a lower q value! - const val LOWER_NGC_VIDEO_QUANTIZER = 51 -} diff --git a/src/main/kotlin/com/zoffcc/applications/trifa/TrifaToxService.kt b/src/main/kotlin/com/zoffcc/applications/trifa/TrifaToxService.kt index 246a2a7d..f57b1f04 100644 --- a/src/main/kotlin/com/zoffcc/applications/trifa/TrifaToxService.kt +++ b/src/main/kotlin/com/zoffcc/applications/trifa/TrifaToxService.kt @@ -1,5 +1,6 @@ package com.zoffcc.applications.trifa +import com.zoffcc.applications.sorm.OrmaDatabase.init import com.zoffcc.applications.trifa.HelperGeneric.update_savedata_file_wrapper import com.zoffcc.applications.trifa.MainActivity.Companion.PREF__udp_enabled import com.zoffcc.applications.trifa.MainActivity.Companion.add_tcp_relay_single_wrapper @@ -14,6 +15,8 @@ import com.zoffcc.applications.trifa.MainActivity.Companion.tox_self_get_friend_ import contactstore import org.briarproject.briar.desktop.contact.ContactItem import set_tox_running_state +import toxdatastore +import unlock_data_dir_input class TrifaToxService { fun tox_thread_start_fg() { @@ -21,6 +24,8 @@ class TrifaToxService { ToxServiceThread = object : Thread() { override fun run() { + com.zoffcc.applications.sorm.OrmaDatabase.init() + // ------ correct startup order ------ val old_is_tox_started = is_tox_started Log.i(TAG, "is_tox_started:==============================") @@ -93,6 +98,12 @@ class TrifaToxService { is_tox_started = false set_tox_running_state("stopped") clear_friend() + com.zoffcc.applications.sorm.OrmaDatabase.shutdown() + unlock_data_dir_input() + try { + toxdatastore.updateToxID("") + } catch (_: Exception) { + } } } (ToxServiceThread as Thread).start() @@ -385,8 +396,7 @@ class TrifaToxService { tox_self_get_friend_list()?.forEach { Log.i(TAG, "friend:" + it) var fname = tox_friend_get_name(it) - if (fname == null) - { + if (fname == null) { fname = "Friend" } try { diff --git a/src/main/kotlin/com/zoffcc/applications/trifa/savepathenabled_state.kt b/src/main/kotlin/com/zoffcc/applications/trifa/savepathenabled_state.kt new file mode 100644 index 00000000..cfc623d0 --- /dev/null +++ b/src/main/kotlin/com/zoffcc/applications/trifa/savepathenabled_state.kt @@ -0,0 +1,65 @@ +package com.zoffcc.applications.trifa + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.consumeAsFlow +import kotlinx.coroutines.launch +import java.io.File + +data class savepathenabled_state( + val savePathEnabled: Boolean = true, + val savePath: String = File(MainActivity.PREF__tox_savefile_dir).canonicalPath + File.separator +) + +interface SavepathStore { + fun updatePath(p: String) + fun updateEnabled(e: Boolean) + val stateFlow: StateFlow + val state get() = stateFlow.value +} + +fun CoroutineScope.createSavepathStore(): SavepathStore { + val mutableStateFlow = MutableStateFlow(savepathenabled_state()) + val channelPath: Channel = Channel(Channel.UNLIMITED) + val channelEnabled: Channel = Channel(Channel.UNLIMITED) + + return object : SavepathStore { + override val stateFlow: StateFlow = mutableStateFlow + + override fun updatePath(p: String) { + launch { + channelPath.send(p) + } + } + + override fun updateEnabled(e: Boolean) { + launch { + channelEnabled.send(e) + } + } + + init { + launch { + channelEnabled.consumeAsFlow().collect { item -> + mutableStateFlow.value = + state.copy( + savePathEnabled = item, + savePath = state.savePath + ) + } + } + launch { + channelPath.consumeAsFlow().collect { item -> + mutableStateFlow.value = + state.copy( + savePathEnabled = state.savePathEnabled, + savePath = item + ) + } + } + + } + } +} diff --git a/src/main/kotlin/com/zoffcc/applications/trifa/toxdata_state.kt b/src/main/kotlin/com/zoffcc/applications/trifa/toxdata_state.kt new file mode 100644 index 00000000..ccc05d49 --- /dev/null +++ b/src/main/kotlin/com/zoffcc/applications/trifa/toxdata_state.kt @@ -0,0 +1,44 @@ +package com.zoffcc.applications.trifa + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.consumeAsFlow +import kotlinx.coroutines.launch + +data class toxdata_state( + val mytoxid: String = "" // own Tox ID as hex string uppercase +) + +interface ToxDataStore { + fun updateToxID(t: String) + val stateFlow: StateFlow + val state get() = stateFlow.value +} + +fun CoroutineScope.createToxDataStore(): ToxDataStore { + val mutableStateFlow = MutableStateFlow(toxdata_state()) + val channeltoxid: Channel = Channel(Channel.UNLIMITED) + + return object : ToxDataStore { + override val stateFlow: StateFlow = mutableStateFlow + + override fun updateToxID(p: String) { + launch { + channeltoxid.send(p) + } + } + + init { + launch { + channeltoxid.consumeAsFlow().collect { item -> + mutableStateFlow.value = + state.copy( + mytoxid = item + ) + } + } + } + } +} diff --git a/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt b/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt index 0f772e32..cb6b1f80 100644 --- a/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt +++ b/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt @@ -14,6 +14,8 @@ import com.zoffcc.applications.trifa.ContactStore import com.zoffcc.applications.trifa.MainActivity.Companion.tox_friend_send_message import com.zoffcc.applications.trifa.ToxVars.TOX_MESSAGE_TYPE import com.zoffcc.applications.trifa.createContactStore +import com.zoffcc.applications.trifa.createSavepathStore +import com.zoffcc.applications.trifa.createToxDataStore import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import org.jetbrains.compose.resources.ExperimentalResourceApi @@ -24,6 +26,8 @@ private const val TAG = "trifa.Chatapp" val myUser = User("Me", picture = null, toxpk = null) val store = CoroutineScope(SupervisorJob()).createStore() val contactstore = CoroutineScope(SupervisorJob()).createContactStore() +val savepathstore = CoroutineScope(SupervisorJob()).createSavepathStore() +val toxdatastore = CoroutineScope(SupervisorJob()).createToxDataStore() @Composable fun ChatAppWithScaffold(displayTextField: Boolean = true) { From 91814b11662978aa2de1140b46ab2568b99ac4b2 Mon Sep 17 00:00:00 2001 From: zoff99 Date: Fri, 13 Oct 2023 20:01:58 +0200 Subject: [PATCH 04/25] add StupidORM --- .../com/zoffcc/applications/sorm/Column.java | 84 ++ .../applications/sorm/ConferenceDB.java | 381 +++++++ .../applications/sorm/ConferenceMessage.java | 416 ++++++++ .../com/zoffcc/applications/sorm/FileDB.java | 319 ++++++ .../applications/sorm/Filetransfer.java | 567 +++++++++++ .../zoffcc/applications/sorm/FriendList.java | 722 +++++++++++++ .../com/zoffcc/applications/sorm/GroupDB.java | 381 +++++++ .../applications/sorm/GroupMessage.java | 433 ++++++++ .../com/zoffcc/applications/sorm/Index.java | 20 + .../com/zoffcc/applications/sorm/Message.java | 948 ++++++++++++++++++ .../zoffcc/applications/sorm/Nullable.java | 32 + .../zoffcc/applications/sorm/OnConflict.java | 18 + .../applications/sorm/OrmaDatabase.java | 939 +++++++++++++++++ .../zoffcc/applications/sorm/PrimaryKey.java | 17 + .../zoffcc/applications/sorm/RelayListDB.java | 278 +++++ .../sorm/TRIFADatabaseGlobalsNew.java | 249 +++++ .../com/zoffcc/applications/sorm/Table.java | 29 + 17 files changed, 5833 insertions(+) create mode 100644 src/main/java/com/zoffcc/applications/sorm/Column.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/ConferenceDB.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/ConferenceMessage.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/FileDB.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/Filetransfer.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/FriendList.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/GroupDB.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/GroupMessage.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/Index.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/Message.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/Nullable.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/OnConflict.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/OrmaDatabase.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/PrimaryKey.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/RelayListDB.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/TRIFADatabaseGlobalsNew.java create mode 100644 src/main/java/com/zoffcc/applications/sorm/Table.java diff --git a/src/main/java/com/zoffcc/applications/sorm/Column.java b/src/main/java/com/zoffcc/applications/sorm/Column.java new file mode 100644 index 00000000..46651bbc --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/Column.java @@ -0,0 +1,84 @@ +package com.zoffcc.applications.sorm; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.CLASS) +public @interface Column +{ + String value() default ""; + + boolean indexed() default false; + + boolean unique() default false; + + @OnConflict int uniqueOnConflict() default 0; + + Column.ForeignKeyAction onDelete() default Column.ForeignKeyAction.CASCADE; + + Column.ForeignKeyAction onUpdate() default Column.ForeignKeyAction.CASCADE; + + String defaultExpr() default ""; + + Column.Collate collate() default Column.Collate.BINARY; + + String storageType() default ""; + + @Column.Helpers long helpers() default 1L; + + @Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER, ElementType.METHOD}) + @Retention(RetentionPolicy.CLASS) + public @interface Helpers + { + long NONE = 0L; + long AUTO = 1L; + long CONDITION_EQ = 2L; + long CONDITION_NOT_EQ = 4L; + long CONDITION_IS_NULL = 8L; + long CONDITION_IS_NOT_NULL = 16L; + long CONDITION_IN = 32L; + long CONDITION_NOT_IN = 64L; + long CONDITION_GLOB = 128L; + long CONDITION_NOT_GLOB = 256L; + long CONDITION_LIKE = 512L; + long CONDITION_NOT_LIKE = 1024L; + long CONDITION_LT = 2048L; + long CONDITION_LE = 4096L; + long CONDITION_GT = 8192L; + long CONDITION_GE = 16384L; + long CONDITION_BETWEEN = 32768L; + long CONDITIONS = 65534L; + long ORDER_IN_ASC = 65536L; + long ORDER_IN_DESC = 131072L; + long ORDERS = 196608L; + long PLUCK = 262144L; + long MIN = 524288L; + long MAX = 1048576L; + long SUM = 2097152L; + long AVG = 4194304L; + long AGGREGATORS = 7864320L; + long ALL = 8388606L; + } + + public static enum ForeignKeyAction + { + NO_ACTION, RESTRICT, SET_NULL, SET_DEFAULT, CASCADE; + + private ForeignKeyAction() + { + } + } + + public static enum Collate + { + BINARY, NOCASE, RTRIM; + + private Collate() + { + } + } +} diff --git a/src/main/java/com/zoffcc/applications/sorm/ConferenceDB.java b/src/main/java/com/zoffcc/applications/sorm/ConferenceDB.java new file mode 100644 index 00000000..a716472f --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/ConferenceDB.java @@ -0,0 +1,381 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.sorm; + +import com.zoffcc.applications.trifa.Log; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import static com.zoffcc.applications.sorm.OrmaDatabase.*; +import static com.zoffcc.applications.trifa.ToxVars.TOX_CONFERENCE_TYPE.TOX_CONFERENCE_TYPE_TEXT; + +@Table +public class ConferenceDB +{ + private static final String TAG = "DB.ConferenceDB"; + + // conference id is always saved as lower case hex string!! ----------------- + @PrimaryKey + String conference_identifier = ""; + // conference id is always saved as lower case hex string!! ----------------- + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String who_invited__tox_public_key_string = ""; + + @Column(indexed = true, defaultExpr = "", helpers = Column.Helpers.ALL) + @Nullable + String name = ""; // saved for backup, when conference is offline! + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long peer_count = -1; + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long own_peer_number = -1; + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int kind = TOX_CONFERENCE_TYPE_TEXT.value; + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long tox_conference_number = -1; // this changes often!! + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + boolean conference_active = false; // is this conference active now? are we invited? + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + @Nullable + boolean notification_silent = false; // show notifications for this conference? + + static ConferenceDB deep_copy(ConferenceDB in) + { + ConferenceDB out = new ConferenceDB(); + out.conference_identifier = in.conference_identifier; + out.name = in.name; + out.peer_count = in.peer_count; + out.own_peer_number = in.own_peer_number; + out.kind = in.kind; + out.who_invited__tox_public_key_string = in.who_invited__tox_public_key_string; + out.tox_conference_number = in.tox_conference_number; + out.conference_active = in.conference_active; + out.notification_silent = in.notification_silent; + + return out; + } + + @Override + public String toString() + { + return "tox_conference_number=" + tox_conference_number + ", conference_active=" + conference_active + + ", conference_identifier=" + conference_identifier + ", who_invited__tox_public_key_string=" + + who_invited__tox_public_key_string + ", name=" + name + ", kind=" + kind + ", peer_count=" + peer_count + + ", own_peer_number=" + own_peer_number + ", notification_silent=" + notification_silent; + } + + String sql_start = ""; + String sql_set = ""; + String sql_where = "where 1=1 "; // where + String sql_orderby = ""; // order by + String sql_limit = ""; // limit + + public List toList() + { + List list = new ArrayList<>(); + + try + { + Statement statement = sqldb.createStatement(); + ResultSet rs = statement.executeQuery( + this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit); + while (rs.next()) + { + ConferenceDB out = new ConferenceDB(); + + out.conference_identifier = rs.getString("conference_identifier"); + out.name = rs.getString("name"); + out.peer_count = rs.getLong("peer_count"); + out.own_peer_number = rs.getLong("own_peer_number"); + out.kind = rs.getInt("kind"); + out.who_invited__tox_public_key_string = rs.getString("who_invited__tox_public_key_string"); + out.tox_conference_number = rs.getLong("tox_conference_number"); + out.conference_active = rs.getBoolean("conference_active"); + out.notification_silent = rs.getBoolean("notification_silent"); + + list.add(out); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return list; + } + + public long insert() + { + long ret = -1; + + try + { + // @formatter:off + Statement statement = sqldb.createStatement(); + final String sql_str="insert into " + this.getClass().getSimpleName() + + "(" + + "conference_identifier," + + "name,"+ + "peer_count,"+ + "own_peer_number," + + "kind," + + "who_invited__tox_public_key_string,"+ + "tox_conference_number,"+ + "conference_active,"+ + "notification_silent" + + ")" + + "values" + + "(" + + "'"+s(this.conference_identifier)+"'," + + "'"+s(this.name)+"'," + + "'"+s(this.peer_count)+"'," + + "'"+s(this.own_peer_number)+"'," + + "'"+s(this.kind)+"'," + + "'"+s(this.who_invited__tox_public_key_string)+"'," + + "'"+s(this.tox_conference_number)+"'," + + "'"+b(this.conference_active)+"'," + + "'"+b(this.notification_silent)+"'" + + ")"; + + statement.execute(sql_str); + ret = get_last_rowid(statement); + // @formatter:on + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return ret; + } + + public ConferenceDB get(int i) + { + this.sql_limit = " limit " + i + ",1 "; + return this.toList().get(0); + } + + public void execute() + { + try + { + Statement statement = sqldb.createStatement(); + final String sql = this.sql_start + " " + this.sql_set + " " + this.sql_where; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + statement.executeUpdate(sql); + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "EE1:" + e2.getMessage()); + } + } + + public int count() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + this.sql_start = "SELECT count(*) as count FROM " + this.getClass().getSimpleName(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + + if (rs.next()) + { + ret = rs.getInt("count"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public ConferenceDB limit(int i) + { + this.sql_limit = " limit " + i + " "; + return this; + } + + // ----------------------------------- // + // ----------------------------------- // + // ----------------------------------- // + + public ConferenceDB conference_identifierEq(String conference_identifier) + { + this.sql_where = this.sql_where + " and conference_identifier='" + s(conference_identifier) + "' "; + return this; + } + + public ConferenceDB conference_active(boolean b) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " conference_active='" + b(b) + "' "; + return this; + } + + public ConferenceDB kind(int kind) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " kind='" + s(kind) + "' "; + return this; + } + + public ConferenceDB tox_conference_number(long conference_number) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " tox_conference_number='" + s(conference_number) + "' "; + return this; + } + + public ConferenceDB orderByConference_activeDesc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " Conference_active DESC "; + return this; + } + + public ConferenceDB orderByNotification_silentAsc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " Notification_silent ASC "; + return this; + } + + public ConferenceDB tox_conference_numberEq(long conference_number) + { + this.sql_where = this.sql_where + " and tox_conference_number='" + s(conference_number) + "' "; + return this; + } + + public ConferenceDB conference_activeEq(boolean b) + { + this.sql_where = this.sql_where + " and conference_active='" + b(b) + "' "; + return this; + } + + public ConferenceDB name(String name) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " name='" + s(name) + "' "; + return this; + } + + public ConferenceDB tox_conference_numberNotEq(int tox_conference_number) + { + this.sql_where = this.sql_where + " and tox_conference_number<>'" + s(tox_conference_number) + "' "; + return this; + } +} diff --git a/src/main/java/com/zoffcc/applications/sorm/ConferenceMessage.java b/src/main/java/com/zoffcc/applications/sorm/ConferenceMessage.java new file mode 100644 index 00000000..7b711d3d --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/ConferenceMessage.java @@ -0,0 +1,416 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.sorm; + +import com.zoffcc.applications.trifa.Log; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import static com.zoffcc.applications.sorm.OrmaDatabase.*; +import static com.zoffcc.applications.trifa.TRIFAGlobals.TRIFA_MSG_TYPE.TRIFA_MSG_TYPE_TEXT; + +@Table +public class ConferenceMessage +{ + private static final String TAG = "DB.ConferenceMessage"; + + @PrimaryKey(autoincrement = true, auto = true) + long id; // uniqe message id!! + + @Column(indexed = true, helpers = Column.Helpers.ALL, defaultExpr = "") + @Nullable + String message_id_tox = ""; // Tox Group Message_ID + // this rolls over at UINT32_MAX + // its unique for "tox_peerpubkey + message_id_tox" + // it only increases (until it rolls over) but may increase by more than 1 + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + String conference_identifier = "-1"; // f_key -> ConferenceDB.conference_identifier + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String tox_peerpubkey; + + @Column(indexed = true, defaultExpr = "", helpers = Column.Helpers.ALL) + @Nullable + String tox_peername = ""; // saved for backup, when conference is offline! + + @Column(indexed = true, helpers = Column.Helpers.ALL) + int direction = 0; // 0 -> msg received, 1 -> msg sent + + @Column(indexed = true) + int TOX_MESSAGE_TYPE = 0; // 0 -> normal, 1 -> action + + @Column(indexed = true, defaultExpr = "0") + int TRIFA_MESSAGE_TYPE = TRIFA_MSG_TYPE_TEXT.value; + + @Column(helpers = Column.Helpers.ALL) + @Nullable + long sent_timestamp = 0L; + + @Column(indexed = true) + @Nullable + long rcvd_timestamp = 0L; + + @Column(helpers = Column.Helpers.ALL) + boolean read = false; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + boolean is_new = true; + + @Column(helpers = Column.Helpers.ALL) + @Nullable + String text = null; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + @Nullable + boolean was_synced = false; + + static ConferenceMessage deep_copy(ConferenceMessage in) + { + ConferenceMessage out = new ConferenceMessage(); + out.id = in.id; // TODO: is this a good idea??? + out.message_id_tox = in.message_id_tox; + out.conference_identifier = in.conference_identifier; + out.tox_peerpubkey = in.tox_peerpubkey; + out.direction = in.direction; + out.TOX_MESSAGE_TYPE = in.TOX_MESSAGE_TYPE; + out.TRIFA_MESSAGE_TYPE = in.TRIFA_MESSAGE_TYPE; + out.sent_timestamp = in.sent_timestamp; + out.rcvd_timestamp = in.rcvd_timestamp; + out.read = in.read; + out.is_new = in.is_new; + out.text = in.text; + out.tox_peername = in.tox_peername; + out.was_synced = in.was_synced; + + return out; + } + + @Override + public String toString() + { + return "id=" + id + ", message_id_tox=" + message_id_tox + ", tox_peername=" + tox_peername + + ", tox_peerpubkey=" + "*tox_peerpubkey*" + ", direction=" + direction + ", TRIFA_MESSAGE_TYPE=" + + TRIFA_MESSAGE_TYPE + ", TOX_MESSAGE_TYPE=" + TOX_MESSAGE_TYPE + ", sent_timestamp=" + sent_timestamp + + ", rcvd_timestamp=" + rcvd_timestamp + ", read=" + read + ", text=" + "xxxxxx" + ", is_new=" + is_new + + ", was_synced=" + was_synced; + } + + String sql_start = ""; + String sql_set = ""; + String sql_where = "where 1=1 "; // where + String sql_orderby = ""; // order by + String sql_limit = ""; // limit + + public List toList() + { + List list = new ArrayList<>(); + + try + { + Statement statement = sqldb.createStatement(); + ResultSet rs = statement.executeQuery( + this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit); + while (rs.next()) + { + ConferenceMessage out = new ConferenceMessage(); + + out.id = rs.getLong("id"); + out.conference_identifier = rs.getString("conference_identifier"); + out.message_id_tox = rs.getString("message_id_tox"); + out.tox_peerpubkey = rs.getString("tox_peerpubkey"); + out.direction = rs.getInt("direction"); + out.TOX_MESSAGE_TYPE = rs.getInt("TOX_MESSAGE_TYPE"); + out.TRIFA_MESSAGE_TYPE = rs.getInt("TRIFA_MESSAGE_TYPE"); + out.sent_timestamp = rs.getLong("sent_timestamp"); + out.rcvd_timestamp = rs.getLong("rcvd_timestamp"); + out.read = rs.getBoolean("read"); + out.is_new = rs.getBoolean("is_new"); + out.text = rs.getString("text"); + out.tox_peername = rs.getString("tox_peername"); + out.was_synced = rs.getBoolean("was_synced"); + + list.add(out); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return list; + } + + public long insert() + { + long ret = -1; + + try + { + // @formatter:off + Statement statement = sqldb.createStatement(); + final String sql_str="insert into " + this.getClass().getSimpleName() + + "(" + + "conference_identifier,"+ + "message_id_tox,"+ + "tox_peerpubkey,"+ + "direction,"+ + "TOX_MESSAGE_TYPE," + + "TRIFA_MESSAGE_TYPE," + + "sent_timestamp,"+ + "rcvd_timestamp,"+ + "read,"+ + "is_new," + + "text," + + "tox_peername,"+ + "was_synced"+ + ")" + + "values" + + "(" + + "'"+s(this.conference_identifier)+"'," + + "'"+s(this.message_id_tox)+"'," + + "'"+s(this.tox_peerpubkey)+"'," + + "'"+s(this.direction)+"'," + + "'"+s(this.TOX_MESSAGE_TYPE)+"'," + + "'"+s(this.TRIFA_MESSAGE_TYPE)+"'," + + "'"+s(this.sent_timestamp)+"'," + + "'"+s(this.rcvd_timestamp)+"'," + + "'"+b(this.read)+"'," + + "'"+b(this.is_new)+"'," + + "'"+s(this.text)+"'," + + "'"+s(this.tox_peername)+"'," + + "'"+b(this.was_synced)+"'" + + ")"; + + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql_str); + } + + statement.execute(sql_str); + ret = get_last_rowid(statement); + // @formatter:on + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return ret; + } + + public ConferenceMessage get(int i) + { + this.sql_limit = " limit " + i + ",1 "; + return this.toList().get(0); + } + + public void execute() + { + try + { + Statement statement = sqldb.createStatement(); + final String sql = this.sql_start + " " + this.sql_set + " " + this.sql_where; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + statement.executeUpdate(sql); + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "EE1:" + e2.getMessage()); + } + } + + public int count() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + this.sql_start = "SELECT count(*) as count FROM " + this.getClass().getSimpleName(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + + if (rs.next()) + { + ret = rs.getInt("count"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public ConferenceMessage limit(int i) + { + this.sql_limit = " limit " + i + " "; + return this; + } + + public ConferenceMessage limit(int rowcount, int offset) + { + this.sql_limit = " limit " + offset + " , " + rowcount; + return this; + } + + // ----------------------------------- // + // ----------------------------------- // + // ----------------------------------- // + + + public ConferenceMessage conference_identifierEq(String conference_identifier) + { + this.sql_where = this.sql_where + " and conference_identifier='" + s(conference_identifier) + "' "; + return this; + } + + public ConferenceMessage is_new(boolean is_new) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " is_new='" + b(is_new) + "' "; + return this; + } + + public ConferenceMessage tox_peerpubkeyNotEq(String tox_peerpubkey) + { + this.sql_where = this.sql_where + " and tox_peerpubkey<>'" + s(tox_peerpubkey) + "' "; + return this; + } + + public ConferenceMessage orderBySent_timestampAsc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " Sent_timestamp ASC "; + return this; + } + + public ConferenceMessage tox_peerpubkeyEq(String tox_peerpubkey) + { + this.sql_where = this.sql_where + " and tox_peerpubkey='" + s(tox_peerpubkey) + "' "; + return this; + } + + public ConferenceMessage message_id_toxEq(String message_id_tox) + { + this.sql_where = this.sql_where + " and message_id_tox='" + s(message_id_tox) + "' "; + return this; + } + + public ConferenceMessage sent_timestampGt(long sent_timestamp) + { + this.sql_where = this.sql_where + " and sent_timestamp>'" + s(sent_timestamp) + "' "; + return this; + } + + public ConferenceMessage sent_timestampLt(long sent_timestamp) + { + this.sql_where = this.sql_where + " and sent_timestamp<'" + s(sent_timestamp) + "' "; + return this; + } + + public ConferenceMessage idEq(long id) + { + this.sql_where = this.sql_where + " and id='" + s(id) + "' "; + return this; + } + + public ConferenceMessage orderByIdDesc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " id DESC "; + return this; + } + + public ConferenceMessage is_newEq(boolean is_new) + { + this.sql_where = this.sql_where + " and is_new='" + b(is_new) + "' "; + return this; + } + +} diff --git a/src/main/java/com/zoffcc/applications/sorm/FileDB.java b/src/main/java/com/zoffcc/applications/sorm/FileDB.java new file mode 100644 index 00000000..8ff8fb63 --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/FileDB.java @@ -0,0 +1,319 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.sorm; + +import com.zoffcc.applications.trifa.Log; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import static com.zoffcc.applications.sorm.OrmaDatabase.*; +import static com.zoffcc.applications.trifa.TRIFAGlobals.TRIFA_FT_DIRECTION.TRIFA_FT_DIRECTION_INCOMING; +import static com.zoffcc.applications.trifa.ToxVars.TOX_FILE_KIND.TOX_FILE_KIND_DATA; + +@Table +public class FileDB +{ + private static final String TAG = "DB.FileDB"; + + @PrimaryKey(autoincrement = true, auto = true) + long id; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + int kind = TOX_FILE_KIND_DATA.value; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + int direction = TRIFA_FT_DIRECTION_INCOMING.value; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String tox_public_key_string = ""; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String path_name = ""; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String file_name = ""; + + @Column(defaultExpr = "-1", indexed = true, helpers = Column.Helpers.ALL) + long filesize = -1; + + @Column(indexed = true, defaultExpr = "true", helpers = Column.Helpers.ALL) + boolean is_in_VFS = true; + + static FileDB deep_copy(FileDB in) + { + FileDB out = new FileDB(); + out.kind = in.kind; + out.direction = in.direction; + out.tox_public_key_string = in.tox_public_key_string; + out.path_name = in.path_name; + out.file_name = in.file_name; + out.filesize = in.filesize; + out.is_in_VFS = in.is_in_VFS; + return out; + } + + @Override + public String toString() + { + return "id=" + id + ", kind=" + kind + ", is_in_VFS=" + is_in_VFS + ", path_name=" + path_name + ", file_name" + + file_name + ", filesize=" + filesize + ", direction=" + direction; + } + + String sql_start = ""; + String sql_set = ""; + String sql_where = "where 1=1 "; // where + String sql_orderby = ""; // order by + String sql_limit = ""; // limit + + public List toList() + { + List list = new ArrayList<>(); + + try + { + Statement statement = sqldb.createStatement(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + ResultSet rs = statement.executeQuery(sql); + while (rs.next()) + { + FileDB out = new FileDB(); + + out.id = rs.getLong("id"); + out.kind = rs.getInt("kind"); + out.direction = rs.getInt("direction"); + out.tox_public_key_string = rs.getString("tox_public_key_string"); + out.path_name = rs.getString("path_name"); + out.file_name = rs.getString("file_name"); + out.filesize = rs.getLong("filesize"); + out.is_in_VFS = rs.getBoolean("is_in_VFS"); + + list.add(out); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return list; + } + + public long insert() + { + long ret = -1; + + try + { + // @formatter:off + Statement statement = sqldb.createStatement(); + final String sql_str="insert into " + this.getClass().getSimpleName() + + "(" + + "kind," + + "direction,"+ + "tox_public_key_string,"+ + "path_name," + + "file_name," + + "filesize,"+ + "is_in_VFS"+ + ")" + + "values" + + "(" + + "'"+s(this.kind)+"'," + + "'"+s(this.direction)+"'," + + "'"+s(this.tox_public_key_string)+"'," + + "'"+s(this.path_name)+"'," + + "'"+s(this.file_name)+"'," + + "'"+s(this.filesize)+"'," + + "'"+b(this.is_in_VFS)+"'" + + ")"; + + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql_str); + } + + statement.execute(sql_str); + ret = get_last_rowid(statement); + // @formatter:on + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return ret; + } + + public FileDB get(int i) + { + this.sql_limit = " limit " + i + ",1 "; + return this.toList().get(0); + } + + public void execute() + { + try + { + Statement statement = sqldb.createStatement(); + final String sql = this.sql_start + " " + this.sql_set + " " + this.sql_where; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + statement.executeUpdate(sql); + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "EE1:" + e2.getMessage()); + } + } + + public int count() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + this.sql_start = "SELECT count(*) as count FROM " + this.getClass().getSimpleName(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + + if (rs.next()) + { + ret = rs.getInt("count"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public FileDB limit(int i) + { + this.sql_limit = " limit " + i + " "; + return this; + } + + // ----------------------------------- // + // ----------------------------------- // + // ----------------------------------- // + + public FileDB tox_public_key_stringEq(String tox_public_key_string) + { + this.sql_where = this.sql_where + " and tox_public_key_string='" + s(tox_public_key_string) + "' "; + return this; + } + + public FileDB file_nameEq(String file_name) + { + this.sql_where = this.sql_where + " and file_name='" + s(file_name) + "' "; + return this; + } + + public FileDB orderByIdDesc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " id DESC "; + return this; + + } + + public FileDB path_nameEq(String path_name) + { + this.sql_where = this.sql_where + " and path_name='" + s(path_name) + "' "; + return this; + } + + public FileDB directionEq(int direction) + { + this.sql_where = this.sql_where + " and direction='" + s(direction) + "' "; + return this; + } + + public FileDB filesizeEq(long filesize) + { + this.sql_where = this.sql_where + " and filesize='" + s(filesize) + "' "; + return this; + } + + public FileDB idEq(long id) + { + this.sql_where = this.sql_where + " and id='" + s(id) + "' "; + return this; + } +} diff --git a/src/main/java/com/zoffcc/applications/sorm/Filetransfer.java b/src/main/java/com/zoffcc/applications/sorm/Filetransfer.java new file mode 100644 index 00000000..990b1316 --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/Filetransfer.java @@ -0,0 +1,567 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.sorm; + +import com.zoffcc.applications.trifa.Log; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import static com.zoffcc.applications.sorm.OrmaDatabase.*; +import static com.zoffcc.applications.trifa.TRIFAGlobals.TRIFA_FT_DIRECTION.TRIFA_FT_DIRECTION_INCOMING; +import static com.zoffcc.applications.trifa.ToxVars.TOX_FILE_CONTROL.TOX_FILE_CONTROL_PAUSE; +import static com.zoffcc.applications.trifa.ToxVars.TOX_FILE_KIND.TOX_FILE_KIND_DATA; + +@Table +public class Filetransfer +{ + private static final String TAG = "DB.Filetransfer"; + + @PrimaryKey(autoincrement = true, auto = true) + long id; // unique ID!! + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String tox_public_key_string = ""; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + int direction = TRIFA_FT_DIRECTION_INCOMING.value; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + long file_number = -1; // given from toxcore!! + + @Column(indexed = true, helpers = Column.Helpers.ALL) + int kind = TOX_FILE_KIND_DATA.value; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + int state = TOX_FILE_CONTROL_PAUSE.value; + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + boolean ft_accepted = false; + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + boolean ft_outgoing_started = false; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String path_name = ""; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String file_name = ""; + + @Column(defaultExpr = "false") + boolean fos_open = false; + + @Column(defaultExpr = "-1") + long filesize = -1; + + @Column(defaultExpr = "0") + long current_position = 0; + + @Column(indexed = true, defaultExpr = "-1") + long message_id; // f_key -> Message.id + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String tox_file_id_hex = ""; + + static Filetransfer deep_copy(Filetransfer in) + { + Filetransfer out = new Filetransfer(); + out.tox_public_key_string = in.tox_public_key_string; + out.direction = in.direction; + out.file_number = in.file_number; + out.kind = in.kind; + out.state = in.state; + out.ft_accepted = in.ft_accepted; + out.ft_outgoing_started = in.ft_outgoing_started; + out.path_name = in.path_name; + out.file_name = in.file_name; + out.fos_open = in.fos_open; + out.filesize = in.filesize; + out.current_position = in.current_position; + out.message_id = in.message_id; + out.tox_file_id_hex = in.tox_file_id_hex; + return out; + } + + @Override + public String toString() + { + return "id=" + id + ", kind=" + kind + ", state=" + state + ", direction=" + direction + ", path_name=" + + path_name + ", file_name=" + file_name + ", filesize=" + filesize + ", current_position=" + + current_position + ", message_id=" + message_id + ", tox_public_key_string=" + tox_public_key_string + + ", tox_file_id_hex=" + tox_file_id_hex; + } + + String sql_start = ""; + String sql_set = ""; + String sql_where = "where 1=1 "; // where + String sql_orderby = ""; // order by + String sql_limit = ""; // limit + + public List toList() + { + List fl = new ArrayList<>(); + + try + { + Statement statement = sqldb.createStatement(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + while (rs.next()) + { + Filetransfer out = new Filetransfer(); + out.id = rs.getLong("id"); + out.tox_public_key_string = rs.getString("tox_public_key_string"); + out.direction = rs.getInt("direction"); + out.file_number = rs.getLong("file_number"); + out.kind = rs.getInt("kind"); + out.state = rs.getInt("state"); + out.ft_accepted = rs.getBoolean("ft_accepted"); + out.ft_outgoing_started = rs.getBoolean("ft_outgoing_started"); + out.path_name = rs.getString("path_name"); + out.file_name = rs.getString("file_name"); + out.fos_open = rs.getBoolean("fos_open"); + out.filesize = rs.getLong("filesize"); + out.current_position = rs.getLong("current_position"); + out.message_id = rs.getLong("message_id"); + out.tox_file_id_hex = rs.getString("tox_file_id_hex"); + + fl.add(out); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return fl; + } + + public long insert() + { + long ret = -1; + + try + { + // @formatter:off + Statement statement = sqldb.createStatement(); + + final String sql_str="insert into " + this.getClass().getSimpleName() + + "(" + + "tox_public_key_string," + + "direction,"+ + "file_number,"+ + "kind," + + "state," + + "ft_accepted,"+ + "ft_outgoing_started,"+ + "path_name,"+ + "file_name," + + "fos_open,"+ + "filesize,"+ + "current_position,"+ + "message_id,"+ + "tox_file_id_hex"+ + ")" + + "values" + + "(" + + "'"+s(this.tox_public_key_string)+"'," + + "'"+s(this.direction)+"'," + + "'"+s(this.file_number)+"'," + + "'"+s(this.kind)+"'," + + "'"+s(this.state)+"'," + + "'"+b(this.ft_accepted)+"'," + + "'"+b(this.ft_outgoing_started)+"'," + + "'"+s(this.path_name)+"'," + + "'"+s(this.file_name)+"'," + + "'"+b(this.fos_open)+"'," + + "'"+s(this.filesize)+"'," + + "'"+s(this.current_position)+"'," + + "'"+s(this.message_id)+"'," + + "'"+s(this.tox_file_id_hex)+"'" + + ")"; + + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql_str); + } + + statement.execute(sql_str); + ret = get_last_rowid(statement); + // @formatter:on + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return ret; + } + + public Filetransfer get(int i) + { + this.sql_limit = " limit " + i + ",1 "; + return this.toList().get(0); + } + + public void execute() + { + try + { + Statement statement = sqldb.createStatement(); + final String sql = this.sql_start + " " + this.sql_set + " " + this.sql_where; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + statement.executeUpdate(sql); + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "EE1:" + e2.getMessage()); + } + } + + public int count() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + this.sql_start = "SELECT count(*) as count FROM " + this.getClass().getSimpleName(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + + if (rs.next()) + { + ret = rs.getInt("count"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public Filetransfer limit(int i) + { + this.sql_limit = " limit " + i + " "; + return this; + } + + // ----------------------------------- // + // ----------------------------------- // + // ----------------------------------- // + + public Filetransfer directionEq(int direction) + { + this.sql_where = this.sql_where + " and direction='" + s(direction) + "' "; + return this; + } + + public Filetransfer tox_public_key_stringEq(String tox_public_key_string) + { + this.sql_where = this.sql_where + " and tox_public_key_string='" + s(tox_public_key_string) + "' "; + return this; + } + + public Filetransfer file_numberEq(long file_number) + { + this.sql_where = this.sql_where + " and file_number='" + s(file_number) + "' "; + return this; + } + + public Filetransfer orderByIdDesc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " id DESC "; + return this; + } + + public Filetransfer idEq(long id) + { + this.sql_where = this.sql_where + " and id='" + s(id) + "' "; + return this; + } + + public Filetransfer tox_public_key_string(String tox_public_key_string) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " tox_public_key_string='" + s(tox_public_key_string) + "' "; + return this; + } + + public Filetransfer direction(int direction) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " direction='" + s(direction) + "' "; + return this; + } + + public Filetransfer file_number(long file_number) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " file_number='" + s(file_number) + "' "; + return this; + } + + public Filetransfer kind(int kind) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " kind='" + s(kind) + "' "; + return this; + } + + public Filetransfer state(int state) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " state='" + s(state) + "' "; + return this; + } + + public Filetransfer path_name(String path_name) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " path_name='" + s(path_name) + "' "; + return this; + } + + public Filetransfer message_id(long message_id) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " message_id='" + s(message_id) + "' "; + return this; + } + + public Filetransfer file_name(String file_name) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " file_name='" + s(file_name) + "' "; + return this; + } + + public Filetransfer fos_open(boolean fos_open) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " fos_open='" + b(fos_open) + "' "; + return this; + } + + public Filetransfer filesize(long filesize) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " filesize='" + s(filesize) + "' "; + return this; + } + + public Filetransfer current_position(long current_position) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " current_position='" + s(current_position) + "' "; + return this; + } + + public Filetransfer ft_accepted(boolean ft_accepted) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " ft_accepted='" + b(ft_accepted) + "' "; + return this; + } + + public Filetransfer ft_outgoing_started(boolean ft_outgoing_started) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " ft_outgoing_started='" + b(ft_outgoing_started) + "' "; + return this; + } + + public Filetransfer stateNotEq(int state) + { + this.sql_where = this.sql_where + " and state<>'" + s(state) + "' "; + return this; + } + + public Filetransfer tox_file_id_hex(String tox_file_id_hex) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " tox_file_id_hex='" + s(tox_file_id_hex) + "' "; + return this; + } + + public Filetransfer file_numberNotEq(long file_number) + { + this.sql_where = this.sql_where + " and file_number<>'" + s(file_number) + "' "; + return this; + } +} diff --git a/src/main/java/com/zoffcc/applications/sorm/FriendList.java b/src/main/java/com/zoffcc/applications/sorm/FriendList.java new file mode 100644 index 00000000..ae233e9e --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/FriendList.java @@ -0,0 +1,722 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.sorm; + +import com.zoffcc.applications.trifa.Log; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import static com.zoffcc.applications.sorm.OrmaDatabase.*; + +@Table +public class FriendList +{ + private static final String TAG = "DB.FriendList"; + + // pubkey is always saved as UPPER CASE hex string!! ----------------- + @PrimaryKey + String tox_public_key_string = ""; + // pubkey is always saved as UPPER CASE hex string!! ----------------- + + @Column + @Nullable + String name; + + @Column(indexed = true, defaultExpr = "", helpers = Column.Helpers.ALL) + @Nullable + String alias_name; + + @Column + @Nullable + String status_message; + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int TOX_CONNECTION; // 0 --> NONE (offline), 1 --> TCP (online), 2 --> UDP (online) + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int TOX_CONNECTION_real; // 0 --> NONE (offline), 1 --> TCP (online), 2 --> UDP (online) + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int TOX_CONNECTION_on_off; // 0 --> offline, 1 --> online + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int TOX_CONNECTION_on_off_real; // 0 --> offline, 1 --> online + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int TOX_USER_STATUS; // 0 --> NONE, 1 --> online AWAY, 2 --> online BUSY + + @Column + @Nullable + String avatar_pathname = null; + + @Column + @Nullable + String avatar_filename = null; + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + @Nullable + boolean avatar_update = false; // has avatar changed for this friend? + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long avatar_update_timestamp = -1L; + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + @Nullable + boolean notification_silent = false; // show notifications for this friend? + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int sort = 0; + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long last_online_timestamp = -1L; + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long last_online_timestamp_real = -1L; + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long added_timestamp = -1L; + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + @Nullable + boolean is_relay = false; + + @Column(indexed = true, defaultExpr = "", helpers = Column.Helpers.ALL) + @Nullable + String push_url; + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + long capabilities = 0; + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + long msgv3_capability = 0; + + static FriendList deep_copy(FriendList in) + { + FriendList out = new FriendList(); + out.tox_public_key_string = in.tox_public_key_string; + out.name = in.name; + out.status_message = in.status_message; + out.TOX_CONNECTION = in.TOX_CONNECTION; + out.TOX_CONNECTION_real = in.TOX_CONNECTION_real; + out.TOX_CONNECTION_on_off = in.TOX_CONNECTION_on_off; + out.TOX_CONNECTION_on_off_real = in.TOX_CONNECTION_on_off_real; + out.TOX_USER_STATUS = in.TOX_USER_STATUS; + out.avatar_filename = in.avatar_filename; + out.avatar_pathname = in.avatar_pathname; + out.avatar_update = in.avatar_update; + out.notification_silent = in.notification_silent; + out.sort = in.sort; + out.last_online_timestamp = in.last_online_timestamp; + out.last_online_timestamp_real = in.last_online_timestamp_real; + out.alias_name = in.alias_name; + out.is_relay = in.is_relay; + out.avatar_update_timestamp = in.avatar_update_timestamp; + out.added_timestamp = in.added_timestamp; + out.push_url = in.push_url; + out.capabilities = in.capabilities; + out.msgv3_capability = in.msgv3_capability; + + return out; + } + + @Override + public String toString() + { + try + { + return "tox_public_key_string=" + tox_public_key_string.substring(0, 4) + ", is_relay=" + is_relay + + ", name=" + name + ", status_message=" + status_message + ", TOX_CONNECTION=" + TOX_CONNECTION + + ", TOX_CONNECTION_on_off=" + TOX_CONNECTION_on_off + ", TOX_CONNECTION_real=" + TOX_CONNECTION_real + + ", TOX_USER_STATUS=" + TOX_USER_STATUS + ", avatar_pathname=" + avatar_pathname + + ", avatar_filename=" + avatar_filename + ", notification_silent=" + notification_silent + ", sort=" + + sort + ", last_online_timestamp=" + last_online_timestamp + ", alias_name=" + alias_name + + ", avatar_update=" + avatar_update + ", added_timestamp=" + added_timestamp + ", push_url=" + + "*****"; + } + catch (Exception e) + { + return "*Exception*"; + } + } + + String sql_start = ""; + String sql_set = ""; + String sql_where = "where 1=1 "; // where + String sql_orderby = ""; // order by + String sql_limit = ""; // limit + + public List toList() + { + List fl = new ArrayList<>(); + + try + { + Statement statement = sqldb.createStatement(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + while (rs.next()) + { + FriendList f = new FriendList(); + f.tox_public_key_string = rs.getString("tox_public_key_string"); + f.name = rs.getString("name"); + f.status_message = rs.getString("status_message"); + f.TOX_CONNECTION = rs.getInt("TOX_CONNECTION"); + f.TOX_CONNECTION_real = rs.getInt("TOX_CONNECTION_real"); + f.TOX_CONNECTION_on_off = rs.getInt("TOX_CONNECTION_on_off"); + f.TOX_CONNECTION_on_off_real = rs.getInt("TOX_CONNECTION_on_off_real"); + f.TOX_USER_STATUS = rs.getInt("TOX_USER_STATUS"); + f.avatar_filename = rs.getString("avatar_filename"); + f.avatar_pathname = rs.getString("avatar_pathname"); + f.avatar_update = rs.getBoolean("avatar_update"); + f.notification_silent = rs.getBoolean("notification_silent"); + f.sort = rs.getInt("sort"); + f.last_online_timestamp = rs.getLong("last_online_timestamp"); + f.last_online_timestamp_real = rs.getLong("last_online_timestamp_real"); + f.alias_name = rs.getString("alias_name"); + f.is_relay = rs.getBoolean("is_relay"); + f.avatar_update_timestamp = rs.getLong("avatar_update_timestamp"); + f.added_timestamp = rs.getLong("added_timestamp"); + f.push_url = rs.getString("push_url"); + f.capabilities = rs.getLong("capabilities"); + f.msgv3_capability = rs.getLong("msgv3_capability"); + + fl.add(f); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return fl; + } + + public long insert() + { + long ret = -1; + + try + { + // @formatter:off + Statement statement = sqldb.createStatement(); + final String sql_str="insert into " + this.getClass().getSimpleName() + + "(" + + "tox_public_key_string," + + "name,"+ + "alias_name,"+ + "status_message," + + "TOX_CONNECTION," + + "TOX_CONNECTION_real,"+ + "TOX_CONNECTION_on_off,"+ + "TOX_CONNECTION_on_off_real,"+ + "TOX_USER_STATUS," + + "avatar_pathname,"+ + "avatar_filename,"+ + "avatar_update,"+ + "avatar_update_timestamp,"+ + "notification_silent," + + "sort,"+ + "last_online_timestamp,"+ + "last_online_timestamp_real,"+ + "added_timestamp,"+ + "is_relay," + + "push_url," + + "capabilities," + + "msgv3_capability" + + ")" + + "values" + + "(" + + "'"+s(this.tox_public_key_string)+"'," + + "'"+s(this.name)+"'," + + "'"+s(this.alias_name)+"'," + + "'"+s(this.status_message)+"'," + + "'"+s(this.TOX_CONNECTION)+"'," + + "'"+s(this.TOX_CONNECTION_real)+"'," + + "'"+s(this.TOX_CONNECTION_on_off)+"'," + + "'"+s(this.TOX_CONNECTION_on_off_real)+"'," + + "'"+s(this.TOX_USER_STATUS)+"'," + + "'"+s(this.avatar_pathname)+"'," + + "'"+s(this.avatar_filename)+"'," + + "'"+b(this.avatar_update)+"'," + + "'"+s(this.avatar_update_timestamp)+"'," + + "'"+b(this.notification_silent)+"'," + + "'"+s(this.sort)+"'," + + "'"+s(this.last_online_timestamp)+"'," + + "'"+s(this.last_online_timestamp_real)+"'," + + "'"+s(this.added_timestamp)+"'," + + "'"+b(this.is_relay)+"'," + + "'"+s(this.push_url)+"'," + + "'"+s(this.capabilities)+"'," + + "'"+s(this.msgv3_capability)+"'" + + ")"; + + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql_str); + } + + statement.execute(sql_str); + ret = get_last_rowid(statement); + // @formatter:on + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return ret; + } + + public FriendList get(int i) + { + this.sql_limit = " limit " + i + ",1 "; + return this.toList().get(0); + } + + public void execute() + { + try + { + Statement statement = sqldb.createStatement(); + final String sql = this.sql_start + " " + this.sql_set + " " + this.sql_where; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + statement.executeUpdate(sql); + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "EE1:" + e2.getMessage()); + } + } + + public int count() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + this.sql_start = "SELECT count(*) as count FROM " + this.getClass().getSimpleName(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + + if (rs.next()) + { + ret = rs.getInt("count"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public FriendList limit(int i) + { + this.sql_limit = " limit " + i + " "; + return this; + } + + // ----------------------------------- // + // ----------------------------------- // + // ----------------------------------- // + + public FriendList tox_public_key_stringEq(String tox_public_key_string) + { + this.sql_where = this.sql_where + " and tox_public_key_string='" + s(tox_public_key_string) + "' "; + return this; + } + + public FriendList is_relayNotEq(boolean b) + { + this.sql_where = this.sql_where + " and is_relay<>'" + b(b) + "' "; + return this; + } + + public FriendList added_timestampGt(long l) + { + this.sql_where = this.sql_where + " and added_timestamp>'" + s(l) + "' "; + return this; + } + + public FriendList orderByTOX_CONNECTION_on_offDesc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " TOX_CONNECTION_on_off DESC "; + return this; + } + + public FriendList orderByNotification_silentAsc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " Notification_silent ASC "; + return this; + } + + public FriendList orderByLast_online_timestampDesc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " Last_online_timestamp DESC "; + return this; + } + + public FriendList added_timestampLe(long l) + { + this.sql_where = this.sql_where + " and added_timestamp <= '" + s(l) + "' "; + return this; + } + + public FriendList name(String name) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " name='" + s(name) + "' "; + return this; + } + + public FriendList push_url(String push_url) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " push_url='" + s(push_url) + "' "; + return this; + } + + public FriendList status_message(String status_message) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " status_message='" + s(status_message) + "' "; + return this; + } + + public FriendList TOX_CONNECTION(int TOX_CONNECTION) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " TOX_CONNECTION='" + s(TOX_CONNECTION) + "' "; + return this; + } + + public FriendList TOX_CONNECTION_on_off(int TOX_CONNECTION_on_off) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " TOX_CONNECTION_on_off='" + s(TOX_CONNECTION_on_off) + "' "; + return this; + } + + public FriendList TOX_USER_STATUS(int TOX_USER_STATUS) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " TOX_USER_STATUS='" + s(TOX_USER_STATUS) + "' "; + return this; + } + + public FriendList TOX_CONNECTION_real(int TOX_CONNECTION_real) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " TOX_CONNECTION_real='" + s(TOX_CONNECTION_real) + "' "; + return this; + } + + public FriendList TOX_CONNECTION_on_off_real(int TOX_CONNECTION_on_off_real) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " TOX_CONNECTION_on_off_real='" + s(TOX_CONNECTION_on_off_real) + "' "; + return this; + } + + public FriendList last_online_timestamp_real(long last_online_timestamp_real) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " last_online_timestamp_real='" + s(last_online_timestamp_real) + "' "; + return this; + } + + public FriendList last_online_timestamp(long last_online_timestamp) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " last_online_timestamp='" + s(last_online_timestamp) + "' "; + return this; + } + + public FriendList last_online_timestampEq(long last_online_timestamp) + { + this.sql_where = this.sql_where + " and last_online_timestamp='" + s(last_online_timestamp) + "' "; + return this; + } + + public FriendList last_online_timestamp_realEq(long last_online_timestamp_real) + { + this.sql_where = this.sql_where + " and last_online_timestamp_real='" + s(last_online_timestamp_real) + "' "; + return this; + } + + public FriendList is_relay(boolean is_relay) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " is_relay='" + b(is_relay) + "' "; + return this; + } + + public FriendList alias_name(String alias_name) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " alias_name='" + s(alias_name) + "' "; + return this; + } + + public FriendList avatar_pathname(String avatar_pathname) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " avatar_pathname='" + s(avatar_pathname) + "' "; + return this; + } + + public FriendList avatar_filename(String avatar_filename) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " avatar_filename='" + s(avatar_filename) + "' "; + return this; + } + + public FriendList avatar_update(boolean avatar_update) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " avatar_update='" + b(avatar_update) + "' "; + return this; + } + + public FriendList avatar_update_timestamp(long avatar_update_timestamp) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " avatar_update_timestamp='" + s(avatar_update_timestamp) + "' "; + return this; + } + + public FriendList msgv3_capability(long msgv3_capability) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " msgv3_capability='" + s(msgv3_capability) + "' "; + return this; + } + + public FriendList capabilities(long capabilities) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " capabilities='" + s(capabilities) + "' "; + return this; + } +} diff --git a/src/main/java/com/zoffcc/applications/sorm/GroupDB.java b/src/main/java/com/zoffcc/applications/sorm/GroupDB.java new file mode 100644 index 00000000..994bc35f --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/GroupDB.java @@ -0,0 +1,381 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 - 2022 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.sorm; + +import com.zoffcc.applications.trifa.Log; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import static com.zoffcc.applications.sorm.OrmaDatabase.*; +import static com.zoffcc.applications.trifa.ToxVars.TOX_GROUP_PRIVACY_STATE; + +@Table +public class GroupDB +{ + private static final String TAG = "DB.GroupDB"; + + // group id is always saved as lower case hex string!! ----------------- + @PrimaryKey + String group_identifier = ""; + // group id is always saved as lower case hex string!! ----------------- + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String who_invited__tox_public_key_string = ""; + + @Column(indexed = true, defaultExpr = "", helpers = Column.Helpers.ALL) + @Nullable + String name = ""; + + @Column(indexed = true, defaultExpr = "", helpers = Column.Helpers.ALL) + @Nullable + String topic = ""; + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long peer_count = -1; + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long own_peer_number = -1; + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int privacy_state = TOX_GROUP_PRIVACY_STATE.TOX_GROUP_PRIVACY_STATE_PUBLIC.value; + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long tox_group_number = -1; // this changes often!! + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + boolean group_active = false; // is this conference active now? are we invited? + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + @Nullable + boolean notification_silent = false; // show notifications for this conference? + + static GroupDB deep_copy(GroupDB in) + { + GroupDB out = new GroupDB(); + out.group_identifier = in.group_identifier; + out.name = in.name; + out.topic = in.topic; + out.peer_count = in.peer_count; + out.own_peer_number = in.own_peer_number; + out.privacy_state = in.privacy_state; + out.who_invited__tox_public_key_string = in.who_invited__tox_public_key_string; + out.tox_group_number = in.tox_group_number; + out.group_active = in.group_active; + out.notification_silent = in.notification_silent; + + return out; + } + + @Override + public String toString() + { + return "tox_group_number=" + tox_group_number + ", group_identifier=" + group_identifier + + ", who_invited__tox_public_key_string=" + who_invited__tox_public_key_string + ", name=" + name + + ", topic=" + topic + ", privacy_state=" + privacy_state + ", peer_count=" + peer_count + + ", own_peer_number=" + own_peer_number + ", notification_silent=" + notification_silent + + ", group_active=" + group_active; + } + + + + String sql_start = ""; + String sql_set = ""; + String sql_where = "where 1=1 "; // where + String sql_orderby = ""; // order by + String sql_limit = ""; // limit + + public List toList() + { + List list = new ArrayList<>(); + + try + { + Statement statement = sqldb.createStatement(); + ResultSet rs = statement.executeQuery( + this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit); + while (rs.next()) + { + GroupDB out = new GroupDB(); + + out.group_identifier = rs.getString("group_identifier"); + out.who_invited__tox_public_key_string = rs.getString("who_invited__tox_public_key_string"); + out.name = rs.getString("name"); + out.topic = rs.getString("topic"); + out.peer_count = rs.getLong("peer_count"); + out.own_peer_number = rs.getLong("own_peer_number"); + out.privacy_state = rs.getInt("privacy_state"); + out.tox_group_number = rs.getLong("tox_group_number"); + out.group_active = rs.getBoolean("group_active"); + out.notification_silent = rs.getBoolean("notification_silent"); + + list.add(out); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return list; + } + + public long insert() + { + long ret = -1; + + try + { + // @formatter:off + Statement statement = sqldb.createStatement(); + final String sql_str="insert into " + this.getClass().getSimpleName() + + "(" + + "group_identifier," + + "who_invited__tox_public_key_string,"+ + "name,"+ + "topic," + + "peer_count," + + "own_peer_number,"+ + "privacy_state,"+ + "tox_group_number,"+ + "group_active,"+ + "notification_silent" + + ")" + + "values" + + "(" + + "'"+s(this.group_identifier)+"'," + + "'"+s(this.who_invited__tox_public_key_string)+"'," + + "'"+s(this.name)+"'," + + "'"+s(this.topic)+"'," + + "'"+s(this.peer_count)+"'," + + "'"+s(this.own_peer_number)+"'," + + "'"+s(this.privacy_state)+"'," + + "'"+s(this.tox_group_number)+"'," + + "'"+b(this.group_active)+"'," + + "'"+b(this.notification_silent)+"'" + + ")"; + + statement.execute(sql_str); + ret = get_last_rowid(statement); + // @formatter:on + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return ret; + } + + public GroupDB get(int i) + { + this.sql_limit = " limit " + i + ",1 "; + return this.toList().get(0); + } + + public void execute() + { + try + { + Statement statement = sqldb.createStatement(); + final String sql = this.sql_start + " " + this.sql_set + " " + this.sql_where; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + statement.executeUpdate(sql); + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "EE1:" + e2.getMessage()); + } + } + + public int count() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + this.sql_start = "SELECT count(*) as count FROM " + this.getClass().getSimpleName(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + + if (rs.next()) + { + ret = rs.getInt("count"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public GroupDB limit(int i) + { + this.sql_limit = " limit " + i + " "; + return this; + } + + // ----------------------------------- // + // ----------------------------------- // + // ----------------------------------- // + + public GroupDB group_identifierEq(String group_identifier) + { + this.sql_where = this.sql_where + " and group_identifier='" + s(group_identifier) + "' "; + return this; + } + + public GroupDB privacy_state(int privacy_state) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " privacy_state='" + s(privacy_state) + "' "; + return this; + } + + public GroupDB tox_group_number(long tox_group_number) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " tox_group_number='" + s(tox_group_number) + "' "; + return this; + } + + public GroupDB group_active(boolean group_active) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " group_active='" + b(group_active) + "' "; + return this; + } + + public GroupDB name(String name) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " name='" + s(name) + "' "; + return this; + } + + public GroupDB topic(String topic) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " topic='" + s(topic) + "' "; + return this; + } + + public GroupDB orderByNotification_silentAsc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " Notification_silent ASC "; + return this; + } + + public GroupDB tox_group_numberNotEq(int tox_group_number) + { + this.sql_where = this.sql_where + " and tox_group_number<>'" + s(tox_group_number) + "' "; + return this; + } +} diff --git a/src/main/java/com/zoffcc/applications/sorm/GroupMessage.java b/src/main/java/com/zoffcc/applications/sorm/GroupMessage.java new file mode 100644 index 00000000..d8a374f5 --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/GroupMessage.java @@ -0,0 +1,433 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 - 2022 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.sorm; + +import com.zoffcc.applications.trifa.Log; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import static com.zoffcc.applications.sorm.OrmaDatabase.*; +import static com.zoffcc.applications.trifa.TRIFAGlobals.TRIFA_MSG_TYPE.TRIFA_MSG_TYPE_TEXT; + +@Table +public class GroupMessage +{ + private static final String TAG = "DB.GroupMessage"; + + @PrimaryKey(autoincrement = true, auto = true) + long id; // uniqe message id!! + + @Column(indexed = true, helpers = Column.Helpers.ALL, defaultExpr = "") + @Nullable + String message_id_tox = ""; // Tox Group Message_ID (4 bytes as hex string lowercase) + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + String group_identifier = "-1"; // f_key -> GroupDB.group_identifier + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String tox_group_peer_pubkey; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + @Nullable + int private_message = 0; // 0 -> message to group, 1 -> msg privately to/from peer + + @Column(indexed = true, defaultExpr = "", helpers = Column.Helpers.ALL) + @Nullable + String tox_group_peername = ""; // saved for backup, when conference is offline! + + @Column(indexed = true, helpers = Column.Helpers.ALL) + int direction = 0; // 0 -> msg received, 1 -> msg sent + + @Column(indexed = true) + int TOX_MESSAGE_TYPE = 0; // 0 -> normal, 1 -> action + + @Column(indexed = true, defaultExpr = "0") + int TRIFA_MESSAGE_TYPE = TRIFA_MSG_TYPE_TEXT.value; + + @Column(helpers = Column.Helpers.ALL) + @Nullable + long sent_timestamp = 0L; + + @Column(indexed = true) + @Nullable + long rcvd_timestamp = 0L; + + @Column(helpers = Column.Helpers.ALL) + boolean read = false; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + boolean is_new = true; + + @Column(helpers = Column.Helpers.ALL) + @Nullable + String text = null; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + @Nullable + boolean was_synced = false; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + @Nullable + String msg_id_hash = null; // 32byte hash + + static GroupMessage deep_copy(GroupMessage in) + { + GroupMessage out = new GroupMessage(); + out.id = in.id; // TODO: is this a good idea??? + out.message_id_tox = in.message_id_tox; + out.group_identifier = in.group_identifier; + out.tox_group_peer_pubkey = in.tox_group_peer_pubkey; + out.private_message = in.private_message; + out.direction = in.direction; + out.TOX_MESSAGE_TYPE = in.TOX_MESSAGE_TYPE; + out.TRIFA_MESSAGE_TYPE = in.TRIFA_MESSAGE_TYPE; + out.sent_timestamp = in.sent_timestamp; + out.rcvd_timestamp = in.rcvd_timestamp; + out.read = in.read; + out.is_new = in.is_new; + out.text = in.text; + out.tox_group_peername = in.tox_group_peername; + out.was_synced = in.was_synced; + out.msg_id_hash = in.msg_id_hash; + + return out; + } + + @Override + public String toString() + { + return "id=" + id + ", message_id_tox=" + message_id_tox + ", tox_group_peername=" + tox_group_peername + + ", tox_peerpubkey=" + "*tox_peerpubkey*" + ", private_message=" + private_message + ", direction=" + + direction + ", TRIFA_MESSAGE_TYPE=" + TRIFA_MESSAGE_TYPE + ", TOX_MESSAGE_TYPE=" + TOX_MESSAGE_TYPE + + ", sent_timestamp=" + sent_timestamp + ", rcvd_timestamp=" + rcvd_timestamp + ", read=" + read + + ", text=" + "xxxxxx" + ", is_new=" + is_new + ", was_synced=" + was_synced; + } + + String sql_start = ""; + String sql_set = ""; + String sql_where = "where 1=1 "; // where + String sql_orderby = ""; // order by + String sql_limit = ""; // limit + + public List toList() + { + List list = new ArrayList<>(); + + try + { + Statement statement = sqldb.createStatement(); + ResultSet rs = statement.executeQuery( + this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit); + while (rs.next()) + { + GroupMessage out = new GroupMessage(); + + out.id = rs.getLong("id"); + out.message_id_tox = rs.getString("message_id_tox"); + out.group_identifier = rs.getString("group_identifier"); + out.tox_group_peer_pubkey = rs.getString("tox_group_peer_pubkey"); + out.private_message = rs.getInt("private_message"); + out.tox_group_peername = rs.getString("tox_group_peername"); + out.direction = rs.getInt("direction"); + out.TOX_MESSAGE_TYPE = rs.getInt("TOX_MESSAGE_TYPE"); + out.TRIFA_MESSAGE_TYPE = rs.getInt("TRIFA_MESSAGE_TYPE"); + out.sent_timestamp = rs.getLong("sent_timestamp"); + out.rcvd_timestamp = rs.getLong("rcvd_timestamp"); + out.read = rs.getBoolean("read"); + out.is_new = rs.getBoolean("is_new"); + out.text = rs.getString("text"); + out.was_synced = rs.getBoolean("was_synced"); + out.msg_id_hash = rs.getString("msg_id_hash"); + + list.add(out); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return list; + } + + public long insert() + { + long ret = -1; + + try + { + // @formatter:off + Statement statement = sqldb.createStatement(); + final String sql_str="insert into " + this.getClass().getSimpleName() + + "(" + + "message_id_tox,"+ + "group_identifier,"+ + "tox_group_peer_pubkey,"+ + "private_message,"+ + "tox_group_peername,"+ + "direction,"+ + "TOX_MESSAGE_TYPE," + + "TRIFA_MESSAGE_TYPE," + + "sent_timestamp,"+ + "rcvd_timestamp,"+ + "read,"+ + "is_new," + + "text," + + "was_synced,"+ + "msg_id_hash"+ + ")" + + "values" + + "(" + + "'"+s(this.message_id_tox)+"'," + + "'"+s(this.group_identifier)+"'," + + "'"+s(this.tox_group_peer_pubkey)+"'," + + "'"+s(this.private_message)+"'," + + "'"+s(this.tox_group_peername)+"'," + + "'"+s(this.direction)+"'," + + "'"+s(this.TOX_MESSAGE_TYPE)+"'," + + "'"+s(this.TRIFA_MESSAGE_TYPE)+"'," + + "'"+s(this.sent_timestamp)+"'," + + "'"+s(this.rcvd_timestamp)+"'," + + "'"+b(this.read)+"'," + + "'"+b(this.is_new)+"'," + + "'"+s(this.text)+"'," + + "'"+b(this.was_synced)+"'," + + "'"+s(this.msg_id_hash)+"'" + + ")"; + + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql_str); + } + + statement.execute(sql_str); + ret = get_last_rowid(statement); + // @formatter:on + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return ret; + } + + public GroupMessage get(int i) + { + this.sql_limit = " limit " + i + ",1 "; + return this.toList().get(0); + } + + public void execute() + { + try + { + Statement statement = sqldb.createStatement(); + final String sql = this.sql_start + " " + this.sql_set + " " + this.sql_where; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + statement.executeUpdate(sql); + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "EE1:" + e2.getMessage()); + } + } + + public int count() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + this.sql_start = "SELECT count(*) as count FROM " + this.getClass().getSimpleName(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + + if (rs.next()) + { + ret = rs.getInt("count"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public GroupMessage limit(int i) + { + this.sql_limit = " limit " + i + " "; + return this; + } + + public GroupMessage limit(int rowcount, int offset) + { + this.sql_limit = " limit " + offset + " , " + rowcount; + return this; + } + + // ----------------------------------- // + // ----------------------------------- // + // ----------------------------------- // + + public GroupMessage group_identifierEq(String group_identifier) + { + this.sql_where = this.sql_where + " and group_identifier='" + s(group_identifier) + "' "; + return this; + } + + public GroupMessage is_new(boolean is_new) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " is_new='" + b(is_new) + "' "; + return this; + } + + public GroupMessage tox_group_peer_pubkeyNotEq(String tox_group_peer_pubkey) + { + this.sql_where = this.sql_where + " and tox_group_peer_pubkey<>'" + s(tox_group_peer_pubkey) + "' "; + return this; + } + + public GroupMessage is_newEq(boolean is_new) + { + this.sql_where = this.sql_where + " and is_new='" + b(is_new) + "' "; + return this; + } + + public GroupMessage idEq(long id) + { + this.sql_where = this.sql_where + " and id='" + s(id) + "' "; + return this; + } + + public GroupMessage orderByIdDesc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " id DESC "; + return this; + } + + public GroupMessage tox_group_peer_pubkeyEq(String tox_group_peer_pubkey) + { + this.sql_where = this.sql_where + " and tox_group_peer_pubkey='" + s(tox_group_peer_pubkey) + "' "; + return this; + } + + public GroupMessage message_id_toxEq(String message_id_tox) + { + this.sql_where = this.sql_where + " and message_id_tox='" + s(message_id_tox) + "' "; + return this; + } + + public GroupMessage sent_timestampGt(long sent_timestamp) + { + this.sql_where = this.sql_where + " and sent_timestamp>'" + s(sent_timestamp) + "' "; + return this; + } + + public GroupMessage sent_timestampLt(long sent_timestamp) + { + this.sql_where = this.sql_where + " and sent_timestamp<'" + s(sent_timestamp) + "' "; + return this; + } + + public GroupMessage textEq(String text) + { + this.sql_where = this.sql_where + " and text='" + s(text) + "' "; + return this; + } + + public GroupMessage orderBySent_timestampAsc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " sent_timestamp ASC "; + return this; + } +} diff --git a/src/main/java/com/zoffcc/applications/sorm/Index.java b/src/main/java/com/zoffcc/applications/sorm/Index.java new file mode 100644 index 00000000..1ae90b1b --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/Index.java @@ -0,0 +1,20 @@ +package com.zoffcc.applications.sorm; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface Index +{ + String[] value(); + + boolean unique() default false; + + String name() default ""; + + @Column.Helpers long helpers() default 1L; +} + diff --git a/src/main/java/com/zoffcc/applications/sorm/Message.java b/src/main/java/com/zoffcc/applications/sorm/Message.java new file mode 100644 index 00000000..171596ea --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/Message.java @@ -0,0 +1,948 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.sorm; + +import com.zoffcc.applications.trifa.Log; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JButton; + +import static com.zoffcc.applications.sorm.OrmaDatabase.*; +import static com.zoffcc.applications.trifa.TRIFAGlobals.TRIFA_MSG_TYPE.TRIFA_MSG_TYPE_TEXT; +import static com.zoffcc.applications.trifa.ToxVars.TOX_FILE_CONTROL.TOX_FILE_CONTROL_PAUSE; +import static com.zoffcc.applications.trifa.ToxVars.TOX_FILE_KIND.TOX_FILE_KIND_DATA; + +@Table +public class Message +{ + private static final String TAG = "DB.Message"; + + @PrimaryKey(autoincrement = true, auto = true) + long id; // uniqe message id!! + + @Column(indexed = true, helpers = Column.Helpers.ALL) + long message_id = -1; // ID given from toxcore!! + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String tox_friendpubkey; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + int direction = 0; // 0 -> msg received, 1 -> msg sent + + @Column(indexed = true) + int TOX_MESSAGE_TYPE = 0; // 0 -> normal, 1 -> action + + @Column(indexed = true, defaultExpr = "0") + int TRIFA_MESSAGE_TYPE = TRIFA_MSG_TYPE_TEXT.value; + + @Column(indexed = true, defaultExpr = "1", helpers = Column.Helpers.ALL) + int state = TOX_FILE_CONTROL_PAUSE.value; + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + boolean ft_accepted = false; + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + boolean ft_outgoing_started = false; + + @Column(indexed = true, defaultExpr = "-1") + long filedb_id; // f_key -> FileDB.id + + @Column(indexed = true, defaultExpr = "-1") + long filetransfer_id; // f_key -> Filetransfer.id + + @Column(helpers = Column.Helpers.ALL, defaultExpr = "0") + @Nullable + long sent_timestamp = 0L; + + @Column(helpers = Column.Helpers.ALL, defaultExpr = "0") + @Nullable + long sent_timestamp_ms = 0L; + + @Column(indexed = true, defaultExpr = "0") + @Nullable + long rcvd_timestamp = 0L; + + @Column(indexed = true, defaultExpr = "0") + @Nullable + long rcvd_timestamp_ms = 0L; + + @Column(helpers = Column.Helpers.ALL) + boolean read = false; + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int send_retries = 0; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + boolean is_new = true; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + @Nullable + String text = null; + + @Column(helpers = Column.Helpers.ALL) + @Nullable + String filename_fullpath = null; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + @Nullable + String msg_id_hash = null; // 32byte hash, used for MessageV2 Messages! and otherwise NULL + + @Column(indexed = true, helpers = Column.Helpers.ALL) + @Nullable + String raw_msgv2_bytes = null; // used for MessageV2 Messages! and otherwise NULL + + @Column(indexed = true, defaultExpr = "0") + int msg_version; // 0 -> old Message, 1 -> for MessageV2 Message + + @Column(indexed = true, defaultExpr = "2") + int resend_count; // 2 -> do not resend msg anymore, 0 or 1 -> resend count + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + boolean ft_outgoing_queued = false; + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + boolean msg_at_relay = false; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + @Nullable + String msg_idv3_hash = null; // 32byte hash, used for MessageV3 Messages! and otherwise NULL + + @Column(helpers = Column.Helpers.ALL) + @Nullable + int sent_push = 0; + + @Column(helpers = Column.Helpers.ALL, defaultExpr = "0") + @Nullable + int filetransfer_kind = TOX_FILE_KIND_DATA.value; + + // ------- SWING UI elements ------- // + JButton _swing_ok = null; + JButton _swing_cancel = null; + // ------- SWING UI elements ------- // + + static Message deep_copy(Message in) + { + Message out = new Message(); + out.id = in.id; // TODO: is this a good idea??? + out.message_id = in.message_id; + out.tox_friendpubkey = in.tox_friendpubkey; + out.direction = in.direction; + out.TOX_MESSAGE_TYPE = in.TOX_MESSAGE_TYPE; + out.TRIFA_MESSAGE_TYPE = in.TRIFA_MESSAGE_TYPE; + out.state = in.state; + out.ft_accepted = in.ft_accepted; + out.ft_outgoing_started = in.ft_outgoing_started; + out.filedb_id = in.filedb_id; + out.filetransfer_id = in.filetransfer_id; + out.sent_timestamp = in.sent_timestamp; + out.sent_timestamp_ms = in.sent_timestamp_ms; + out.rcvd_timestamp = in.rcvd_timestamp; + out.rcvd_timestamp_ms = in.rcvd_timestamp_ms; + out.read = in.read; + out.send_retries = in.send_retries; + out.is_new = in.is_new; + out.text = in.text; + out.filename_fullpath = in.filename_fullpath; + out.msg_id_hash = in.msg_id_hash; + out.msg_version = in.msg_version; + out.raw_msgv2_bytes = in.raw_msgv2_bytes; + out.resend_count = in.resend_count; + out.ft_outgoing_queued = in.ft_outgoing_queued; + out.msg_at_relay = in.msg_at_relay; + out.msg_idv3_hash = in.msg_idv3_hash; + out.sent_push = in.sent_push; + out.filetransfer_kind = in.filetransfer_kind; + + return out; + } + + @Override + public String toString() + { + return "id=" + id + ", message_id=" + message_id + ", filetransfer_id=" + filetransfer_id + ", filedb_id=" + + filedb_id + ", tox_friendpubkey=" + "*pubkey*" + ", direction=" + direction + ", state=" + state + + ", TRIFA_MESSAGE_TYPE=" + TRIFA_MESSAGE_TYPE + ", TOX_MESSAGE_TYPE=" + TOX_MESSAGE_TYPE + + ", sent_timestamp=" + sent_timestamp + ", rcvd_timestamp=" + rcvd_timestamp + ", read=" + read + + ", send_retries=" + send_retries + ", text=" + "xxxxxx" + ", filename_fullpath=" + filename_fullpath + + ", is_new=" + is_new + ", msg_id_hash=" + msg_id_hash + ", msg_version=" + msg_version + + ", resend_count=" + resend_count + ", raw_msgv2_bytes=" + "xxxxxx" + ", ft_outgoing_queued=" + + ft_outgoing_queued + ", msg_at_relay=" + msg_at_relay + ", sent_push=" + sent_push + + ", filetransfer_kind=" + filetransfer_kind; + } + + String sql_start = ""; + String sql_set = ""; + String sql_where = "where 1=1 "; // where + String sql_orderby = ""; // order by + String sql_limit = ""; // limit + Object[] sql_bind_vars = new Object[]{}; + + public List toList() + { + List list = new ArrayList<>(); + + try + { + Statement statement = sqldb.createStatement(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + ResultSet rs = statement.executeQuery(sql); + while (rs.next()) + { + Message out = new Message(); + + out.id = rs.getLong("id"); + out.message_id = rs.getLong("message_id"); + out.tox_friendpubkey = rs.getString("tox_friendpubkey"); + out.direction = rs.getInt("direction"); + out.TOX_MESSAGE_TYPE = rs.getInt("TOX_MESSAGE_TYPE"); + out.TRIFA_MESSAGE_TYPE = rs.getInt("TRIFA_MESSAGE_TYPE"); + out.state = rs.getInt("state"); + out.ft_accepted = rs.getBoolean("ft_accepted"); + out.ft_outgoing_started = rs.getBoolean("ft_outgoing_started"); + out.filedb_id = rs.getLong("filedb_id"); + out.filetransfer_id = rs.getLong("filetransfer_id"); + out.sent_timestamp = rs.getLong("sent_timestamp"); + out.sent_timestamp_ms = rs.getLong("sent_timestamp_ms"); + out.rcvd_timestamp = rs.getLong("rcvd_timestamp"); + out.rcvd_timestamp_ms = rs.getLong("rcvd_timestamp_ms"); + out.read = rs.getBoolean("read"); + out.send_retries = rs.getInt("send_retries"); + out.is_new = rs.getBoolean("is_new"); + out.text = rs.getString("text"); + out.filename_fullpath = rs.getString("filename_fullpath"); + out.msg_id_hash = rs.getString("msg_id_hash"); + out.msg_version = rs.getInt("msg_version"); + out.raw_msgv2_bytes = rs.getString("raw_msgv2_bytes"); + out.resend_count = rs.getInt("resend_count"); + out.ft_outgoing_queued = rs.getBoolean("ft_outgoing_queued"); + out.msg_at_relay = rs.getBoolean("msg_at_relay"); + out.msg_idv3_hash = rs.getString("msg_idv3_hash"); + out.sent_push = rs.getInt("sent_push"); + out.filetransfer_kind = rs.getInt("filetransfer_kind"); + + list.add(out); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return list; + } + + public long insert() + { + long ret = -1; + + try + { + String insert_pstmt_sql = null; + PreparedStatement insert_pstmt = null; + + // @formatter:off + insert_pstmt_sql ="insert into " + this.getClass().getSimpleName() + + "(" + + "message_id," + + "tox_friendpubkey," + + "direction," + + "TOX_MESSAGE_TYPE," + + "TRIFA_MESSAGE_TYPE," + + "state," + + "ft_accepted," + + "ft_outgoing_started," + + "filedb_id," + + "filetransfer_id," + + "sent_timestamp," + + "sent_timestamp_ms," + + "rcvd_timestamp," + + "rcvd_timestamp_ms," + + "read," + + "send_retries," + + "is_new," + + "text," + + "filename_fullpath," + + "msg_id_hash," + + "msg_version," + + "raw_msgv2_bytes," + + "resend_count," + + "ft_outgoing_queued," + + "msg_at_relay," + + "msg_idv3_hash," + + "filetransfer_kind," + + "sent_push" + + ")" + + "values" + + "(" + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?," + + "?" + + ")"; + insert_pstmt = sqldb.prepareStatement(insert_pstmt_sql, Statement.RETURN_GENERATED_KEYS); + + insert_pstmt.clearParameters(); + + insert_pstmt.setLong(1, this.message_id); + insert_pstmt.setString(2, this.tox_friendpubkey); + insert_pstmt.setInt(3, this.direction); + insert_pstmt.setInt(4, this.TOX_MESSAGE_TYPE); + insert_pstmt.setInt(5, this.TRIFA_MESSAGE_TYPE); + insert_pstmt.setInt(6, this.state); + insert_pstmt.setBoolean(7, this.ft_accepted); + insert_pstmt.setBoolean(8, this.ft_outgoing_started); + insert_pstmt.setLong(9, this.filedb_id); + insert_pstmt.setLong(10, this.filetransfer_id); + insert_pstmt.setLong(11, this.sent_timestamp); + insert_pstmt.setLong(12, this.sent_timestamp_ms); + insert_pstmt.setLong(13, this.rcvd_timestamp); + insert_pstmt.setLong(14, this.rcvd_timestamp_ms); + insert_pstmt.setBoolean(15, this.read); + insert_pstmt.setInt(16, this.send_retries); + insert_pstmt.setBoolean(17, this.is_new); + insert_pstmt.setString(18, this.text); + insert_pstmt.setString(19, this.filename_fullpath); + insert_pstmt.setString(20, this.msg_id_hash); + insert_pstmt.setInt(21, this.msg_version); + insert_pstmt.setString(22, this.raw_msgv2_bytes); + insert_pstmt.setInt(23, this.resend_count); + insert_pstmt.setBoolean(24, this.ft_outgoing_queued); + insert_pstmt.setBoolean(25, this.msg_at_relay); + insert_pstmt.setString(26, this.msg_idv3_hash); + insert_pstmt.setInt(27, this.filetransfer_kind); + insert_pstmt.setInt(28, this.sent_push); + // @formatter:on + + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + insert_pstmt); + } + + insert_pstmt.executeUpdate(); + ret = get_last_rowid_pstmt(insert_pstmt); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return ret; + } + + public Message get(int i) + { + this.sql_limit = " limit " + i + ",1 "; + return this.toList().get(0); + } + + public void execute() + { + try + { + Statement statement = sqldb.createStatement(); + final String sql = this.sql_start + " " + this.sql_set + " " + this.sql_where; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + statement.executeUpdate(sql); + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "EE1:" + e2.getMessage()); + } + } + + public int count() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + this.sql_start = "SELECT count(*) as count FROM " + this.getClass().getSimpleName(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + + if (rs.next()) + { + ret = rs.getInt("count"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public Message limit(int rowcount) + { + this.sql_limit = " limit " + rowcount + " "; + return this; + } + + public Message limit(int rowcount, int offset) + { + this.sql_limit = " limit " + offset + " , " + rowcount; + return this; + } + + // ----------------------------------- // + // ----------------------------------- // + // ----------------------------------- // + + public Message tox_friendpubkeyEq(String tox_friendpubkey) + { + this.sql_where = this.sql_where + " and tox_friendpubkey='" + s(tox_friendpubkey) + "' "; + return this; + } + + public Message orderBySent_timestampAsc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " sent_timestamp ASC "; + return this; + } + + public Message orderBySent_timestamp_msAsc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " sent_timestamp_ms ASC "; + return this; + } + + public Message directionEq(int i) + { + this.sql_where = this.sql_where + " and direction='" + s(i) + "' "; + return this; + } + + public Message TRIFA_MESSAGE_TYPEEq(int value) + { + this.sql_where = this.sql_where + " and TRIFA_MESSAGE_TYPE='" + s(value) + "' "; + return this; + } + + public Message resend_countEq(int i) + { + this.sql_where = this.sql_where + " and resend_count='" + s(i) + "' "; + return this; + } + + public Message readEq(boolean b) + { + this.sql_where = this.sql_where + " and read='" + b(b) + "' "; + return this; + } + + public Message idEq(long id) + { + this.sql_where = this.sql_where + " and id='" + s(id) + "' "; + return this; + } + + public Message message_id(long message_id) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " message_id='" + s(message_id) + "' "; + return this; + } + + public Message text(String text) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " text='" + s(text) + "' "; + return this; + } + + public Message sent_timestamp(long sent_timestamp) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " sent_timestamp='" + s(sent_timestamp) + "' "; + return this; + } + + public Message msg_version(int msg_version) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " msg_version='" + s(msg_version) + "' "; + return this; + } + + public Message filename_fullpath(String filename_fullpath) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " filename_fullpath='" + s(filename_fullpath) + "' "; + return this; + } + + public Message raw_msgv2_bytes(String raw_msgv2_bytes) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " raw_msgv2_bytes='" + s(raw_msgv2_bytes) + "' "; + return this; + } + + public Message msg_id_hash(String msg_id_hash) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " msg_id_hash='" + s(msg_id_hash) + "' "; + return this; + } + + public Message resend_count(int resend_count) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " resend_count='" + s(resend_count) + "' "; + return this; + } + + public Message msg_versionEq(int msg_version) + { + this.sql_where = this.sql_where + " and msg_version='" + s(msg_version) + "' "; + return this; + } + + public Message message_idEq(long message_id) + { + this.sql_where = this.sql_where + " and message_id='" + s(message_id) + "' "; + return this; + } + + public Message orderByIdDesc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " id DESC "; + return this; + } + + public Message read(boolean read) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " read='" + b(read) + "' "; + return this; + } + + public Message rcvd_timestamp(long rcvd_timestamp) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " rcvd_timestamp='" + s(rcvd_timestamp) + "' "; + return this; + } + + public Message msg_id_hashEq(String msg_id_hash) + { + this.sql_where = this.sql_where + " and msg_id_hash='" + s(msg_id_hash) + "' "; + return this; + } + + public Message ft_accepted(boolean ft_accepted) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " ft_accepted='" + b(ft_accepted) + "' "; + return this; + } + + public Message state(int state) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " state='" + s(state) + "' "; + return this; + } + + public Message filetransfer_idEq(long filetransfer_id) + { + this.sql_where = this.sql_where + " and filetransfer_id='" + s(filetransfer_id) + "' "; + return this; + } + + public Message filedb_id(long filedb_id) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " filedb_id='" + s(filedb_id) + "' "; + return this; + } + + public Message filetransfer_id(long filetransfer_id) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " filetransfer_id='" + s(filetransfer_id) + "' "; + return this; + } + + public Message ft_outgoing_started(boolean ft_outgoing_started) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " ft_outgoing_started='" + b(ft_outgoing_started) + "' "; + return this; + } + + public Message is_newEq(boolean is_new) + { + this.sql_where = this.sql_where + " and is_new='" + b(is_new) + "' "; + return this; + } + + public Message is_new(boolean is_new) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " is_new='" + b(is_new) + "' "; + return this; + } + + public Message ft_outgoing_queued(boolean ft_outgoing_queued) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " ft_outgoing_queued='" + b(ft_outgoing_queued) + "' "; + return this; + } + + public Message ft_outgoing_queuedEq(boolean ft_outgoing_queued) + { + this.sql_where = this.sql_where + " and ft_outgoing_queued='" + b(ft_outgoing_queued) + "' "; + return this; + } + + public Message stateNotEq(int state) + { + this.sql_where = this.sql_where + " and state != '" + s(state) + "' "; + return this; + } + + public Message msg_at_relay(boolean msg_at_relay) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " msg_at_relay='" + b(msg_at_relay) + "' "; + return this; + } + + public Message orderBySent_timestampDesc() + { + if (this.sql_orderby.equals("")) + { + this.sql_orderby = " order by "; + } + else + { + this.sql_orderby = this.sql_orderby + " , "; + } + this.sql_orderby = this.sql_orderby + " sent_timestamp DESC "; + return this; + } + + public Message msg_at_relayEq(boolean msg_at_relay) + { + this.sql_where = this.sql_where + " and msg_at_relay='" + b(msg_at_relay) + "' "; + return this; + } + + public Message filedb_idEq(long filedb_id) + { + this.sql_where = this.sql_where + " and filedb_id='" + s(filedb_id) + "' "; + return this; + } + + public Message msg_idv3_hashEq(String msg_idv3_hash) + { + this.sql_where = this.sql_where + " and msg_idv3_hash='" + s(msg_idv3_hash) + "' "; + return this; + } + + public Message msg_idv3_hash(String msg_idv3_hash) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " msg_idv3_hash='" + s(msg_idv3_hash) + "' "; + return this; + } + + public Message resend_countLt(int resend_count) + { + this.sql_where = this.sql_where + " and resend_count<'" + s(filetransfer_id) + "' "; + return this; + } + + public Message sent_pushEq(int sent_push) + { + this.sql_where = this.sql_where + " and sent_push='" + s(sent_push) + "' "; + return this; + } + + public Message sent_timestampLt(long sent_timestamp) + { + this.sql_where = this.sql_where + " and sent_timestamp<'" + s(sent_timestamp) + "' "; + return this; + } + + public Message sent_timestampBetween(long sent_timestamp1, long sent_timestamp2) + { + this.sql_where = this.sql_where + " and sent_timestamp>'" + s(sent_timestamp1) + "' and sent_timestamp<'" + + s(sent_timestamp2) + "' "; + return this; + } + + public Message sent_push(int sent_push) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " sent_push='" + s(sent_push) + "' "; + return this; + } + + public Message filetransfer_kind(int filetransfer_kind) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " filetransfer_kind='" + s(filetransfer_kind) + "' "; + return this; + } +} diff --git a/src/main/java/com/zoffcc/applications/sorm/Nullable.java b/src/main/java/com/zoffcc/applications/sorm/Nullable.java new file mode 100644 index 00000000..f3ab66b0 --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/Nullable.java @@ -0,0 +1,32 @@ +package com.zoffcc.applications.sorm; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PACKAGE; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.CLASS; + +/** + * Denotes that a parameter, field or method return value can be null. + *

+ * When decorating a method call parameter, this denotes that the parameter can + * legitimately be null and the method will gracefully deal with it. Typically + * used on optional parameters. + *

+ * When decorating a method, this denotes the method might legitimately return + * null. + *

+ * This is a marker annotation and it has no specific attributes. + */ +@Documented +@Retention(CLASS) +@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE}) +public @interface Nullable +{ +} \ No newline at end of file diff --git a/src/main/java/com/zoffcc/applications/sorm/OnConflict.java b/src/main/java/com/zoffcc/applications/sorm/OnConflict.java new file mode 100644 index 00000000..9ca8ddba --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/OnConflict.java @@ -0,0 +1,18 @@ +package com.zoffcc.applications.sorm; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.CLASS) +public @interface OnConflict +{ + int NONE = 0; + int ROLLBACK = 1; + int ABORT = 2; + int FAIL = 3; + int IGNORE = 4; + int REPLACE = 5; +} diff --git a/src/main/java/com/zoffcc/applications/sorm/OrmaDatabase.java b/src/main/java/com/zoffcc/applications/sorm/OrmaDatabase.java new file mode 100644 index 00000000..0886b11e --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/OrmaDatabase.java @@ -0,0 +1,939 @@ +package com.zoffcc.applications.sorm; + +import androidx.compose.ui.res.FileResourceLoader; +import com.zoffcc.applications.trifa.Log; +import com.zoffcc.applications.trifa.MainActivity; +import com.zoffcc.applications.trifa.OperatingSystem; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.sql.*; +import java.util.Base64; +import java.util.List; + +import static com.zoffcc.applications.trifa.MainActivity.PREF__database_files_dir; + +public class OrmaDatabase +{ + private static final String TAG = "trifa.OrmaDatabase"; + final static boolean ORMA_TRACE = false; // set "false" for release builds + + private static final String CREATE_DB_FILE_SHA256SUM = "GE/avgqgDL4L1v35QvL2DIXdFMVOVKm8Ic8hG7v1BeA="; + static Connection sqldb = null; + static int current_db_version = 0; + + public OrmaDatabase() + { + } + + public static String bytesToString(byte[] bytes) + { + return Base64.getEncoder().encodeToString(bytes); + } + + public static String sha256sum_of_file(String filename_with_path) + { + try + { + byte[] buffer = new byte[8192]; + int count; + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filename_with_path)); + while ((count = bis.read(buffer)) > 0) + { + digest.update(buffer, 0, count); + } + bis.close(); + + byte[] hash = digest.digest(); + return (bytesToString(hash)); + } + catch (Exception e) + { + e.printStackTrace(); + return null; + } + } + + /* + * repair or finally replace a string that is not correct UTF-8 + */ + static String safe_string_sql(String in) + { + if (in == null) + { + return null; + } + + if (in.equals("")) + { + return ""; + } + + try + { + byte[] bytes = in.getBytes(StandardCharsets.UTF_8); + for (int i = 0; i < bytes.length; i++) + { + if (bytes[i] == 0) + { + bytes[i] = '_'; + } + } + return (new String(bytes, StandardCharsets.UTF_8)); + } + catch (Exception e) + { + Log.i(TAG, "safe_string_sql:EE:" + e.getMessage()); + e.printStackTrace(); + } + return "__ERROR_IN_STRING__"; + } + + public static long get_last_rowid_pstmt(java.sql.PreparedStatement statement) + { + try + { + long ret = -1; + + ResultSet rs = statement.getGeneratedKeys(); + while (rs.next()) + { + ret = rs.getLong(1); + } + rs.close(); + // Log.i(TAG, "get_last_rowid:ret=" + ret); + return ret; + } + catch (Exception e) + { + e.printStackTrace(); + Log.i(TAG, "get_last_rowid_pstmt:EE1:" + e.getMessage()); + return -1; + } + } + + public static long get_last_rowid(Statement statement) + { + try + { + long ret = -1; + ResultSet rs = statement.executeQuery("select last_insert_rowid() as lastrowid"); + if (rs.next()) + { + ret = rs.getLong("lastrowid"); + } + // Log.i(TAG, "get_last_rowid:ret=" + ret); + return ret; + } + catch (Exception e) + { + e.printStackTrace(); + Log.i(TAG, "get_last_rowid:EE1:" + e.getMessage()); + return -1; + } + } + + /* + * escape to prevent SQL injection, very basic and bad! + * TODO: make me better (and later use prepared statements) + */ + public static String s(String str) + { + // TODO: bad!! use prepared statements + String data = ""; + + str = safe_string_sql(str); + + if (str == null || str.length() == 0) + { + return ""; + } + + if (str != null && str.length() > 0) + { + str = str. + // replace("\\", "\\\\"). // \ -> \\ + // replace("%", "\\%"). // % -> \% + // replace("_", "\\_"). // _ -> \_ + replace("'", "''"). // ' -> '' + replace("\\x1a", "\\Z"); // \\x1a --> EOF char + data = str; + } + + return data; + } + + public static String s(int i) + { + return "" + i; + } + + public static String s(long l) + { + return "" + l; + } + + public static int b(boolean in) + { + if (in == true) + { + return 1; + } + else + { + return 0; + } + } + + public static String readSQLFileAsString(String filePath) throws java.io.IOException + { + BufferedReader reader = new BufferedReader(new FileReader(filePath)); + String line, results = ""; + while ((line = reader.readLine()) != null) + { + results += line; + } + reader.close(); + return results; + } + + public static String get_current_sqlite_version() + { + String ret = "unknown"; + + try + { + final Statement statement = sqldb.createStatement(); + final ResultSet rs = statement.executeQuery("SELECT sqlite_version()"); + if (rs.next()) + { + ret = rs.getString(1); + } + + try + { + statement.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public static int get_current_db_version() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + ResultSet rs = statement.executeQuery( + "select db_version from orma_schema order by db_version desc limit 1"); + if (rs.next()) + { + ret = rs.getInt("db_version"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + + return ret; + } + catch (Exception e) + { + ret = 0; + + try + { + final String update_001 = "CREATE TABLE orma_schema (db_version INTEGER NOT NULL);"; + run_multi_sql(update_001); + final String update_002 = "insert into orma_schema values ('0');"; + run_multi_sql(update_002); + } + catch (Exception e2) + { + e2.printStackTrace(); + } + } + + return ret; + } + + public static void set_new_db_version(int new_version) + { + try + { + final String update_001 = "update orma_schema set db_version='" + new_version + "';"; + run_multi_sql(update_001); + } + catch (Exception e2) + { + e2.printStackTrace(); + } + } + + public static int update_db(int current_db_version) + { + if (current_db_version < 1) + { + try + { + final String update_001 = "CREATE UNIQUE INDEX ux_tox_public_key_string_of_owner ON RelayListDB(tox_public_key_string_of_owner);"; + run_multi_sql(update_001); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + if (current_db_version < 2) + { + try + { + final String update_001 = + "alter table Message add ft_outgoing_queued BOOLEAN NOT NULL DEFAULT false;" + "\n" + + "CREATE INDEX index_ft_outgoing_queued_on_Message ON Message (ft_outgoing_queued);"; + run_multi_sql(update_001); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + if (current_db_version < 3) + { + try + { + final String update_001 = + "alter table Message add msg_at_relay BOOLEAN NOT NULL DEFAULT false;" + "\n" + + "CREATE INDEX index_msg_at_relay_on_Message ON Message (msg_at_relay);"; + run_multi_sql(update_001); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + if (current_db_version < 4) + { + try + { + final String update_001 = "alter table FriendList add push_url TEXT DEFAULT NULL;" + "\n" + + "CREATE INDEX index_push_url_on_FriendList ON FriendList (push_url);"; + run_multi_sql(update_001); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + if (current_db_version < 5) + { + try + { + final String update_001 = "alter table Message add msg_idv3_hash TEXT DEFAULT NULL;" + "\n" + + "CREATE INDEX index_msg_idv3_hash_on_Message ON Message (msg_idv3_hash);"; + run_multi_sql(update_001); + final String update_002 = "alter table Message add sent_push INTEGER DEFAULT '0';" + "\n" + + "CREATE INDEX index_sent_push_on_Message ON Message (sent_push);"; + run_multi_sql(update_002); + + final String update_003 = "alter table FriendList add capabilities INTEGER DEFAULT '0';" + "\n" + + "CREATE INDEX index_capabilities_on_FriendList ON FriendList (capabilities);"; + run_multi_sql(update_003); + final String update_004 = "alter table FriendList add msgv3_capability INTEGER DEFAULT '0';" + "\n" + + "CREATE INDEX index_msgv3_capability_on_FriendList ON FriendList (msgv3_capability);"; + run_multi_sql(update_004); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + if (current_db_version < 6) + { + try + { + // @formatter:off + final String update_001 = "CREATE TABLE IF NOT EXISTS GroupDB ( " + + "who_invited__tox_public_key_string TEXT, "+ + "name TEXT, "+ + "topic TEXT, "+ + "peer_count INTEGER NOT NULL DEFAULT -1, "+ + "own_peer_number INTEGER NOT NULL DEFAULT -1, "+ + "privacy_state INTEGER NOT NULL DEFAULT 1, "+ + "tox_group_number INTEGER NOT NULL DEFAULT -1, "+ + "group_active BOOLEAN DEFAULT false, "+ + "notification_silent BOOLEAN DEFAULT false, "+ + "group_identifier TEXT, "+ + "PRIMARY KEY(\"group_identifier\") "+ + ");"; + // @formatter:on + run_multi_sql(update_001); + + // @formatter:off + final String update_002 = "CREATE TABLE IF NOT EXISTS GroupMessage ( " + + "message_id_tox TEXT , "+ + "group_identifier TEXT NOT NULL DEFAULT \"-1\", "+ + "tox_group_peer_pubkey TEXT NOT NULL, "+ + "private_message INTEGER NOT NULL DEFAULT 0, "+ + "tox_group_peername TEXT, "+ + "direction INTEGER NOT NULL , "+ + "TOX_MESSAGE_TYPE INTEGER NOT NULL , "+ + "TRIFA_MESSAGE_TYPE INTEGER NOT NULL DEFAULT 0 , "+ + "sent_timestamp INTEGER, "+ + "rcvd_timestamp INTEGER, "+ + "read BOOLEAN NOT NULL DEFAULT 0 , "+ + "is_new BOOLEAN NOT NULL DEFAULT 1 , "+ + "text TEXT, "+ + "was_synced BOOLEAN NOT NULL DEFAULT 0 , "+ + "msg_id_hash TEXT, "+ + "id INTEGER, "+ + "PRIMARY KEY(\"id\") "+ + ");"; + // @formatter:on + run_multi_sql(update_002); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + if (current_db_version < 7) + { + try + { + // @formatter:off + final String update_001 = "CREATE INDEX IF NOT EXISTS index_message_id_tox_on_GroupMessage ON GroupMessage (message_id_tox);\n" + + "CREATE INDEX IF NOT EXISTS index_group_identifier_tox_on_GroupMessage ON GroupMessage (group_identifier);\n" + + "CREATE INDEX IF NOT EXISTS index_tox_group_peer_pubkey_on_GroupMessage ON GroupMessage (tox_group_peer_pubkey);\n" + + "CREATE INDEX IF NOT EXISTS index_direction_on_GroupMessage ON GroupMessage (direction);\n" + + "CREATE INDEX IF NOT EXISTS index_TOX_MESSAGE_TYPE_on_GroupMessage ON GroupMessage (TOX_MESSAGE_TYPE);\n" + + "CREATE INDEX IF NOT EXISTS index_TRIFA_MESSAGE_TYPE_on_GroupMessage ON GroupMessage (TRIFA_MESSAGE_TYPE);\n" + + "CREATE INDEX IF NOT EXISTS index_rcvd_timestamp_on_GroupMessage ON GroupMessage (rcvd_timestamp);\n" + + "CREATE INDEX IF NOT EXISTS index_sent_timestamp_on_GroupMessage ON GroupMessage (sent_timestamp);\n" + + "CREATE INDEX IF NOT EXISTS index_private_message_on_GroupMessage ON GroupMessage (private_message);\n" + + "CREATE INDEX IF NOT EXISTS index_tox_group_peername_on_GroupMessage ON GroupMessage (tox_group_peername);\n" + + "CREATE INDEX IF NOT EXISTS index_was_synced_on_GroupMessage ON GroupMessage (was_synced);\n" + + "CREATE INDEX IF NOT EXISTS index_is_new_on_GroupMessage ON GroupMessage (is_new);\n" + + "CREATE INDEX IF NOT EXISTS index_msg_id_hash_on_GroupMessage ON GroupMessage (msg_id_hash);"; + run_multi_sql(update_001); + // @formatter:on + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + if (current_db_version < 8) + { + try + { + // @formatter:off + final String update_001 = "CREATE INDEX IF NOT EXISTS index_who_invited__tox_public_key_string_on_GroupDB ON GroupDB (who_invited__tox_public_key_string);\n" + + "CREATE INDEX IF NOT EXISTS index_name_on_GroupDB ON GroupDB (name);\n" + + "CREATE INDEX IF NOT EXISTS index_topic_on_GroupDB ON GroupDB (topic);\n" + + "CREATE INDEX IF NOT EXISTS index_peer_count_on_GroupDB ON GroupDB (peer_count);\n" + + "CREATE INDEX IF NOT EXISTS index_own_peer_number_on_GroupDB ON GroupDB (own_peer_number);\n" + + "CREATE INDEX IF NOT EXISTS index_privacy_state_on_GroupDB ON GroupDB (privacy_state);\n" + + "CREATE INDEX IF NOT EXISTS index_tox_group_number_on_GroupDB ON GroupDB (tox_group_number);\n" + + "CREATE INDEX IF NOT EXISTS index_group_active_on_GroupDB ON GroupDB (group_active);\n" + + "CREATE INDEX IF NOT EXISTS index_notification_silent_on_GroupDB ON GroupDB (notification_silent);\n" + + "CREATE INDEX IF NOT EXISTS index_group_identifier_on_GroupDB ON GroupDB (group_identifier);"; + + run_multi_sql(update_001); + // @formatter:on + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + if (current_db_version < 9) + { + try + { + final String update_001 = "alter table Filetransfer add tox_file_id_hex TEXT DEFAULT NULL;" + "\n" + + "CREATE INDEX index_tox_file_id_hex_on_Filetransfer ON Filetransfer (tox_file_id_hex);"; + run_multi_sql(update_001); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + if (current_db_version < 10) + { + try + { + final String update_001 = "alter table Message add filetransfer_kind INTEGER NOT NULL DEFAULT 0;" + "\n" + + "CREATE INDEX index_filetransfer_kind_on_Message ON Message (filetransfer_kind);"; + run_multi_sql(update_001); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + final int new_db_version = 10; + set_new_db_version(new_db_version); + // return the updated DB VERSION + return new_db_version; + } + + public static void shutdown() + { + Log.i(TAG, "SHUTDOWN:start"); + try + { + sqldb.close(); + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "SHUTDOWN:Error:" + e2.getMessage()); + } + Log.i(TAG, "SHUTDOWN:finished"); + } + + public static void init() + { + Log.i(TAG, "INIT:start"); + // create a database connection + try + { + // Class.forName("org.sqlite.JDBC"); + sqldb = DriverManager.getConnection("jdbc:sqlite:" + PREF__database_files_dir + "/main.db"); + } + catch (Exception e) + { + e.printStackTrace(); + Log.i(TAG, "INIT:Error:" + e.getMessage()); + } + + Log.i(TAG, "loaded:sqlite:" + get_current_sqlite_version()); + + // --------------- CREATE THE DATABASE --------------- + // --------------- CREATE THE DATABASE --------------- + // --------------- CREATE THE DATABASE --------------- + // --------------- CREATE THE DATABASE --------------- + current_db_version = get_current_db_version(); + Log.i(TAG, "trifa:current_db_version=" + current_db_version); + create_db(current_db_version); + current_db_version = update_db(current_db_version); + Log.i(TAG, "trifa:new_db_version=" + current_db_version); + // --------------- CREATE THE DATABASE --------------- + // --------------- CREATE THE DATABASE --------------- + // --------------- CREATE THE DATABASE --------------- + // --------------- CREATE THE DATABASE --------------- + Log.i(TAG, "INIT:finished"); + } + + public static void create_db(int current_db_version) + { + try + { + final File resources_dir = new File(System.getProperty("compose.application.resources.dir")); + Log.i(TAG, "resources dir: " + resources_dir); + String asset_filename = resources_dir.getCanonicalPath() + File.separator + "main.db.txt"; + Log.i(TAG, "loading asset file: " + asset_filename); + String sha256sum_of_create_db_file = sha256sum_of_file(asset_filename); + Log.i(TAG, "create_db:sha256sum_of_create_db_file=" + sha256sum_of_create_db_file); + // TODO: on some windows systems the checksum does not seem to match? + // maybe "\r\n" or the file is not read as UTF-8 ? + if ((sha256sum_of_create_db_file.equals(CREATE_DB_FILE_SHA256SUM)) || + (OperatingSystem.getCurrent() == OperatingSystem.WINDOWS)) + { + String create_db_sqls = readSQLFileAsString(asset_filename); + if (current_db_version == 0) + { + run_multi_sql(create_db_sqls); + } + } + else + { + Log.i(TAG, "create_db:input file sha256 hash does not match!"); + System.exit(5); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + /* + * Runs SQL statements that are seperated by ";" character + */ + public static void run_multi_sql(String sql_multi) + { + try + { + Statement statement = null; + + try + { + statement = sqldb.createStatement(); + statement.setQueryTimeout(10); // set timeout to x sec. + } + catch (SQLException e) + { + System.err.println(e.getMessage()); + } + + String[] queries = sql_multi.split(";"); + for (String query : queries) + { + try + { + // Log.i(TAG, "SQL:" + query); + statement.executeUpdate(query); + } + catch (SQLException e) + { + System.err.println(e.getMessage()); + } + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + } + } + + /** + * Starts building a query: {@code SELECT * FROM FriendList ...}. + */ + public FriendList selectFromFriendList() + { + FriendList ret = new FriendList(); + ret.sql_start = "SELECT * FROM FriendList"; + return ret; + } + + public long insertIntoFriendList(FriendList f) + { + return f.insert(); + } + + /** + * Starts building a query: {@code SELECT * FROM Message ...}. + */ + public Message selectFromMessage() + { + Message ret = new Message(); + ret.sql_start = "SELECT * FROM Message"; + return ret; + } + + public List selectFromMessageCustomSQL(String statement) + { + Message ret = new Message(); + ret.sql_where = ""; + ret.sql_start = "SELECT * FROM Message " + statement; + return ret.toList(); + } + + /** + * Starts building a query: {@code UPDATE Message ...}. + */ + public Message updateMessage() + { + Message ret = new Message(); + ret.sql_start = "UPDATE Message"; + return ret; + } + + /** + * Starts building a query: {@code SELECT * FROM ConferenceDB ...}. + */ + public ConferenceDB selectFromConferenceDB() + { + ConferenceDB ret = new ConferenceDB(); + ret.sql_start = "SELECT * FROM ConferenceDB"; + return ret; + } + + /** + * Starts building a query: {@code UPDATE ConferenceDB ...}. + */ + public ConferenceDB updateConferenceDB() + { + ConferenceDB ret = new ConferenceDB(); + ret.sql_start = "UPDATE ConferenceDB"; + return ret; + } + + public long insertIntoConferenceDB(ConferenceDB conf_new) + { + return conf_new.insert(); + } + + /** + * Starts building a query: {@code UPDATE ConferenceMessage ...}. + */ + public ConferenceMessage updateConferenceMessage() + { + ConferenceMessage ret = new ConferenceMessage(); + ret.sql_start = "UPDATE ConferenceMessage"; + return ret; + } + + /** + * Starts building a query: {@code SELECT * FROM ConferenceMessage ...}. + */ + public ConferenceMessage selectFromConferenceMessage() + { + ConferenceMessage ret = new ConferenceMessage(); + ret.sql_start = "SELECT * FROM ConferenceMessage"; + return ret; + } + + public long insertIntoConferenceMessage(ConferenceMessage m) + { + return m.insert(); + } + + /** + * Starts building a query: {@code UPDATE FriendList ...}. + */ + public FriendList updateFriendList() + { + FriendList ret = new FriendList(); + ret.sql_start = "UPDATE FriendList"; + return ret; + } + + /** + * Starts building a query: {@code SELECT * FROM Filetransfer ...}. + */ + public Filetransfer selectFromFiletransfer() + { + Filetransfer ret = new Filetransfer(); + ret.sql_start = "SELECT * FROM Filetransfer"; + return ret; + } + + public long insertIntoFiletransfer(Filetransfer f) + { + return f.insert(); + } + + /** + * Starts building a query: {@code UPDATE Filetransfer ...}. + */ + public Filetransfer updateFiletransfer() + { + Filetransfer ret = new Filetransfer(); + ret.sql_start = "UPDATE Filetransfer"; + return ret; + } + + public long insertIntoFileDB(FileDB f) + { + return f.insert(); + } + + /** + * Starts building a query: {@code SELECT * FROM FileDB ...}. + */ + public FileDB selectFromFileDB() + { + FileDB ret = new FileDB(); + ret.sql_start = "SELECT * FROM FileDB"; + return ret; + } + + public Filetransfer deleteFromFiletransfer() + { + Filetransfer ret = new Filetransfer(); + ret.sql_start = "DELETE FROM Filetransfer"; + return ret; + } + + public long insertIntoMessage(Message m) + { + return m.insert(); + } + + /** + * Starts building a query: {@code SELECT * FROM RelayListDB ...}. + */ + public RelayListDB selectFromRelayListDB() + { + RelayListDB ret = new RelayListDB(); + ret.sql_start = "SELECT * FROM RelayListDB"; + return ret; + } + + public long insertIntoRelayListDB(RelayListDB f) + { + return f.insert(); + } + + public long insertIntoTRIFADatabaseGlobalsNew(TRIFADatabaseGlobalsNew o) + { + return o.insert(); + } + + /** + * Starts building a query: {@code UPDATE TRIFADatabaseGlobalsNew ...}. + */ + public TRIFADatabaseGlobalsNew updateTRIFADatabaseGlobalsNew() + { + TRIFADatabaseGlobalsNew ret = new TRIFADatabaseGlobalsNew(); + ret.sql_start = "UPDATE TRIFADatabaseGlobalsNew"; + return ret; + } + + public TRIFADatabaseGlobalsNew deleteFromTRIFADatabaseGlobalsNew() + { + TRIFADatabaseGlobalsNew ret = new TRIFADatabaseGlobalsNew(); + ret.sql_start = "DELETE FROM TRIFADatabaseGlobalsNew"; + return ret; + } + + /** + * Starts building a query: {@code SELECT * FROM TRIFADatabaseGlobalsNew ...}. + */ + public TRIFADatabaseGlobalsNew selectFromTRIFADatabaseGlobalsNew() + { + TRIFADatabaseGlobalsNew ret = new TRIFADatabaseGlobalsNew(); + ret.sql_start = "SELECT * FROM TRIFADatabaseGlobalsNew"; + return ret; + } + + public FileDB deleteFromFileDB() + { + FileDB ret = new FileDB(); + ret.sql_start = "DELETE FROM FileDB"; + return ret; + } + + public Message deleteFromMessage() + { + Message ret = new Message(); + ret.sql_start = "DELETE FROM Message"; + return ret; + } + + public FriendList deleteFromFriendList() + { + FriendList ret = new FriendList(); + ret.sql_start = "DELETE FROM FriendList"; + return ret; + } + + public ConferenceMessage deleteFromConferenceMessage() + { + ConferenceMessage ret = new ConferenceMessage(); + ret.sql_start = "DELETE FROM ConferenceMessage"; + return ret; + } + + public ConferenceDB deleteFromConferenceDB() + { + ConferenceDB ret = new ConferenceDB(); + ret.sql_start = "DELETE FROM ConferenceDB"; + return ret; + } + + /** + * Starts building a query: {@code SELECT * FROM GroupDB ...}. + */ + public GroupDB selectFromGroupDB() + { + GroupDB ret = new GroupDB(); + ret.sql_start = "SELECT * FROM GroupDB"; + return ret; + } + + /** + * Starts building a query: {@code UPDATE GroupDB ...}. + */ + public GroupDB updateGroupDB() + { + GroupDB ret = new GroupDB(); + ret.sql_start = "UPDATE GroupDB"; + return ret; + } + + public long insertIntoGroupDB(GroupDB group_new) + { + return group_new.insert(); + } + + public GroupDB deleteFromGroupDB() + { + GroupDB ret = new GroupDB(); + ret.sql_start = "DELETE FROM GroupDB"; + return ret; + } + + /** + * Starts building a query: {@code SELECT * FROM GroupMessage ...}. + */ + public GroupMessage selectFromGroupMessage() + { + GroupMessage ret = new GroupMessage(); + ret.sql_start = "SELECT * FROM GroupMessage"; + return ret; + } + + /** + * Starts building a query: {@code UPDATE GroupMessage ...}. + */ + public GroupMessage updateGroupMessage() + { + GroupMessage ret = new GroupMessage(); + ret.sql_start = "UPDATE GroupMessage"; + return ret; + } + + public long insertIntoGroupMessage(GroupMessage groupmessage_new) + { + return groupmessage_new.insert(); + } + + public GroupMessage deleteFromGroupMessage() + { + GroupMessage ret = new GroupMessage(); + ret.sql_start = "DELETE FROM GroupMessage"; + return ret; + } +} + diff --git a/src/main/java/com/zoffcc/applications/sorm/PrimaryKey.java b/src/main/java/com/zoffcc/applications/sorm/PrimaryKey.java new file mode 100644 index 00000000..f72c7a1a --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/PrimaryKey.java @@ -0,0 +1,17 @@ +package com.zoffcc.applications.sorm; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.CLASS) +public @interface PrimaryKey +{ + boolean autoincrement() default false; + + boolean auto() default true; + + @OnConflict int onConflict() default 0; +} \ No newline at end of file diff --git a/src/main/java/com/zoffcc/applications/sorm/RelayListDB.java b/src/main/java/com/zoffcc/applications/sorm/RelayListDB.java new file mode 100644 index 00000000..6a7919dd --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/RelayListDB.java @@ -0,0 +1,278 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.sorm; + +import com.zoffcc.applications.trifa.Log; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import static com.zoffcc.applications.sorm.OrmaDatabase.*; + +@Table +public class RelayListDB +{ + private static final String TAG = "DB.RelayListDB"; + + @PrimaryKey + String tox_public_key_string = ""; + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int TOX_CONNECTION; // 0 --> NONE (offline), 1 --> TCP (online), 2 --> UDP (online) + + @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) + int TOX_CONNECTION_on_off; // 0 --> offline, 1 --> online + + @Column(indexed = true, defaultExpr = "false", helpers = Column.Helpers.ALL) + boolean own_relay = false; // false --> friends relay, true --> my relay + + @Column(indexed = true, defaultExpr = "-1", helpers = Column.Helpers.ALL) + long last_online_timestamp = -1L; + + @Column(indexed = true, defaultExpr = "", helpers = Column.Helpers.ALL) + @Nullable + String tox_public_key_string_of_owner = ""; + + static RelayListDB deep_copy(RelayListDB in) + { + RelayListDB out = new RelayListDB(); + out.tox_public_key_string = in.tox_public_key_string; + out.TOX_CONNECTION = in.TOX_CONNECTION; + out.TOX_CONNECTION_on_off = in.TOX_CONNECTION_on_off; + out.own_relay = in.own_relay; + out.last_online_timestamp = in.last_online_timestamp; + out.tox_public_key_string_of_owner = in.tox_public_key_string_of_owner; + + return out; + } + + @Override + public String toString() + { + try + { + return "tox_public_key_string=" + tox_public_key_string.substring(0, 4) + ", ownder_pubkey=" + + tox_public_key_string_of_owner.substring(0, 4) + ", own_relay=" + own_relay + ", TOX_CONNECTION=" + + TOX_CONNECTION + ", TOX_CONNECTION_on_off=" + TOX_CONNECTION_on_off + ", last_online_timestamp=" + + last_online_timestamp; + } + catch (Exception e) + { + return "*Exception*"; + } + } + + String sql_start = ""; + String sql_set = ""; + String sql_where = "where 1=1 "; // where + String sql_orderby = ""; // order by + String sql_limit = ""; // limit + + public List toList() + { + List list = new ArrayList<>(); + + try + { + Statement statement = sqldb.createStatement(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + ResultSet rs = statement.executeQuery(sql); + while (rs.next()) + { + RelayListDB out = new RelayListDB(); + + out.tox_public_key_string = rs.getString("tox_public_key_string"); + out.TOX_CONNECTION = rs.getInt("TOX_CONNECTION"); + out.TOX_CONNECTION_on_off = rs.getInt("TOX_CONNECTION_on_off"); + out.own_relay = rs.getBoolean("own_relay"); + out.last_online_timestamp = rs.getLong("last_online_timestamp"); + out.tox_public_key_string_of_owner = rs.getString("tox_public_key_string_of_owner"); + + list.add(out); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return list; + } + + public long insert() + { + long ret = -1; + + try + { + // @formatter:off + Statement statement = sqldb.createStatement(); + final String sql_str="insert into " + this.getClass().getSimpleName() + + "(" + + "tox_public_key_string," + + "TOX_CONNECTION,"+ + "TOX_CONNECTION_on_off,"+ + "own_relay," + + "last_online_timestamp," + + "tox_public_key_string_of_owner"+ + ")" + + "values" + + "(" + + "'"+s(this.tox_public_key_string)+"'," + + "'"+s(this.TOX_CONNECTION)+"'," + + "'"+s(this.TOX_CONNECTION_on_off)+"'," + + "'"+b(this.own_relay)+"'," + + "'"+s(this.TOX_CONNECTION)+"'," + + "'"+s(this.tox_public_key_string_of_owner)+"'" + + ")"; + + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql_str); + } + + statement.execute(sql_str); + ret = get_last_rowid(statement); + // @formatter:on + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return ret; + } + + public RelayListDB get(int i) + { + this.sql_limit = " limit " + i + ",1 "; + return this.toList().get(0); + } + + public void execute() + { + try + { + Statement statement = sqldb.createStatement(); + final String sql = this.sql_start + " " + this.sql_set + " " + this.sql_where; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + statement.executeUpdate(sql); + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "EE1:" + e2.getMessage()); + } + } + + public int count() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + this.sql_start = "SELECT count(*) as count FROM " + this.getClass().getSimpleName(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + + if (rs.next()) + { + ret = rs.getInt("count"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public RelayListDB limit(int i) + { + this.sql_limit = " limit " + i + " "; + return this; + } + + // ----------------------------------- // + // ----------------------------------- // + // ----------------------------------- // + + public RelayListDB own_relayEq(boolean own_relay) + { + this.sql_where = this.sql_where + " and own_relay='" + b(own_relay) + "' "; + return this; + } + + public RelayListDB tox_public_key_stringEq(String tox_public_key_string) + { + this.sql_where = this.sql_where + " and tox_public_key_string='" + s(tox_public_key_string) + "' "; + return this; + } +} diff --git a/src/main/java/com/zoffcc/applications/sorm/TRIFADatabaseGlobalsNew.java b/src/main/java/com/zoffcc/applications/sorm/TRIFADatabaseGlobalsNew.java new file mode 100644 index 00000000..db751c3d --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/TRIFADatabaseGlobalsNew.java @@ -0,0 +1,249 @@ +/** + * [TRIfA], Java part of Tox Reference Implementation for Android + * Copyright (C) 2017 Zoff + *

+ * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +package com.zoffcc.applications.sorm; + +import com.zoffcc.applications.trifa.Log; + +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import static com.zoffcc.applications.sorm.OrmaDatabase.*; + +@Table +public class TRIFADatabaseGlobalsNew +{ + private static final String TAG = "DB.TRIFADatabaseGlobalsNew"; + + @PrimaryKey + String key; + + @Column(indexed = true, helpers = Column.Helpers.ALL) + String value; + + static TRIFADatabaseGlobalsNew deep_copy(TRIFADatabaseGlobalsNew in) + { + TRIFADatabaseGlobalsNew out = new TRIFADatabaseGlobalsNew(); + out.key = in.key; + out.value = in.value; + + return out; + } + + @Override + public String toString() + { + return "key=" + key + ", value=" + value; + } + + String sql_start = ""; + String sql_set = ""; + String sql_where = "where 1=1 "; // where + String sql_orderby = ""; // order by + String sql_limit = ""; // limit + + public List toList() + { + List list = new ArrayList<>(); + + try + { + Statement statement = sqldb.createStatement(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + ResultSet rs = statement.executeQuery(sql); + while (rs.next()) + { + TRIFADatabaseGlobalsNew out = new TRIFADatabaseGlobalsNew(); + + out.key = rs.getString("key"); + out.value = rs.getString("value"); + + list.add(out); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return list; + } + + public long insert() + { + long ret = -1; + + try + { + // @formatter:off + Statement statement = sqldb.createStatement(); + + final String sql_str="insert into " + this.getClass().getSimpleName() + + "(" + + "key," + + "value" + + ")" + + "values" + + "(" + + "'"+s(this.key)+"'," + + "'"+s(this.value)+"'" + + ")"; + + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql_str); + } + + statement.execute(sql_str); + ret = get_last_rowid(statement); + // @formatter:on + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + return ret; + } + + public TRIFADatabaseGlobalsNew get(int i) + { + this.sql_limit = " limit " + i + ",1 "; + return this.toList().get(0); + } + + public void execute() + { + try + { + Statement statement = sqldb.createStatement(); + final String sql = this.sql_start + " " + this.sql_set + " " + this.sql_where; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + statement.executeUpdate(sql); + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e2) + { + e2.printStackTrace(); + Log.i(TAG, "EE1:" + e2.getMessage()); + } + } + + public int count() + { + int ret = 0; + + try + { + Statement statement = sqldb.createStatement(); + this.sql_start = "SELECT count(*) as count FROM " + this.getClass().getSimpleName(); + + final String sql = this.sql_start + " " + this.sql_where + " " + this.sql_orderby + " " + this.sql_limit; + if (ORMA_TRACE) + { + Log.i(TAG, "sql=" + sql); + } + + ResultSet rs = statement.executeQuery(sql); + + if (rs.next()) + { + ret = rs.getInt("count"); + } + + try + { + statement.close(); + } + catch (Exception ignored) + { + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + return ret; + } + + public TRIFADatabaseGlobalsNew limit(int i) + { + this.sql_limit = " limit " + i + " "; + return this; + } + + public TRIFADatabaseGlobalsNew keyEq(String key) + { + this.sql_where = this.sql_where + " and key='" + s(key) + "' "; + return this; + } + + public TRIFADatabaseGlobalsNew value(String value) + { + if (this.sql_set.equals("")) + { + this.sql_set = " set "; + } + else + { + this.sql_set = this.sql_set + " , "; + } + this.sql_set = this.sql_set + " value='" + s(value) + "' "; + return this; + } + + // ----------------------------------- // + // ----------------------------------- // + // ----------------------------------- // + +} diff --git a/src/main/java/com/zoffcc/applications/sorm/Table.java b/src/main/java/com/zoffcc/applications/sorm/Table.java new file mode 100644 index 00000000..490f527d --- /dev/null +++ b/src/main/java/com/zoffcc/applications/sorm/Table.java @@ -0,0 +1,29 @@ +package com.zoffcc.applications.sorm; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.CLASS) +public @interface Table +{ + String value() default ""; + + String[] constraints() default {}; + + Index[] indexes() default {}; + + String schemaClassName() default ""; + + String relationClassName() default ""; + + String updaterClassName() default ""; + + String deleterClassName() default ""; + + String selectorClassName() default ""; + + String associationConditionClassName() default ""; +} From 0254f398cfecabd42e516b7bc41d6fc98939e524 Mon Sep 17 00:00:00 2001 From: zoff99 Date: Fri, 13 Oct 2023 20:28:20 +0200 Subject: [PATCH 05/25] select friend --- .../applications/trifa/TRIFAGlobals.java | 2 ++ src/main/kotlin/Main.kt | 2 -- .../zoffcc/applications/trifa/ContactStore.kt | 23 +++++++++++++++---- .../briar/desktop/contact/ContactList.kt | 21 +++++++---------- .../briar/desktop/ui/ListItemView.kt | 10 ++++++-- 5 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/zoffcc/applications/trifa/TRIFAGlobals.java b/src/main/java/com/zoffcc/applications/trifa/TRIFAGlobals.java index 468ab07b..0af1b221 100644 --- a/src/main/java/com/zoffcc/applications/trifa/TRIFAGlobals.java +++ b/src/main/java/com/zoffcc/applications/trifa/TRIFAGlobals.java @@ -162,6 +162,8 @@ public class TRIFAGlobals static final int FT_IMAGE_THUMBNAIL_WIDTH = 200; static final int FT_IMAGE_THUMBNAIL_HEIGHT = 90; + public static final Color COLOR_SELECTED_TOX_FRIEND = new Color(0x33cc00ff, true); + public static enum TRIFA_FT_DIRECTION { TRIFA_FT_DIRECTION_INCOMING(0), TRIFA_FT_DIRECTION_OUTGOING(1); diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 6dac6f38..cd6ef505 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -154,8 +154,6 @@ fun App() { Row(modifier = Modifier.fillMaxWidth()) { ContactList( contactList = contacts, - isSelected = null, - selectContact = null ) VerticalDivider() ChatAppWithScaffold() diff --git a/src/main/kotlin/com/zoffcc/applications/trifa/ContactStore.kt b/src/main/kotlin/com/zoffcc/applications/trifa/ContactStore.kt index e85146bd..f80c9cc6 100644 --- a/src/main/kotlin/com/zoffcc/applications/trifa/ContactStore.kt +++ b/src/main/kotlin/com/zoffcc/applications/trifa/ContactStore.kt @@ -9,7 +9,8 @@ import kotlinx.coroutines.launch import org.briarproject.briar.desktop.contact.ContactItem data class StateContacts( - val contacts: List = emptyList() + val contacts: List = emptyList(), + val selectedContact: String? = null ) const val TAG = "trifa.ContactsStore" @@ -17,6 +18,7 @@ const val TAG = "trifa.ContactsStore" interface ContactStore { fun add(item: ContactItem) fun remove(item: ContactItem) + fun select(pubkey: String?) fun clear() fun update(item: ContactItem) val stateFlow: StateFlow @@ -70,6 +72,16 @@ fun CoroutineScope.createContactStore(): ContactStore { } } + override fun select(pubkey: String?) { + launch { + mutableStateFlow.value = + state.copy( + contacts = state.contacts, + selectedContact = pubkey + ) + } + } + override fun update(item: ContactItem) { launch { var update_item: ContactItem? = null @@ -81,12 +93,14 @@ fun CoroutineScope.createContactStore(): ContactStore { if (update_item != null) { mutableStateFlow.value = state.copy( - contacts = (state.contacts + item - update_item!!) + contacts = (state.contacts + item - update_item!!), + selectedContact = state.selectedContact ) } else { mutableStateFlow.value = state.copy( - contacts = (state.contacts + item) + contacts = (state.contacts + item), + selectedContact = state.selectedContact ) } } @@ -96,7 +110,8 @@ fun CoroutineScope.createContactStore(): ContactStore { launch { mutableStateFlow.value = state.copy( - contacts = emptyList() + contacts = emptyList(), + selectedContact = null ) } } diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt index aea649bc..90ed61f5 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import com.zoffcc.applications.trifa.StateContacts +import contactstore import org.briarproject.briar.desktop.ui.ListItemView import org.briarproject.briar.desktop.ui.VerticallyScrollableArea import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @@ -21,8 +22,6 @@ import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @Composable fun ContactList( contactList: StateContacts, - isSelected: String?, - selectContact: ContactItem?, ) = Column( modifier = Modifier.fillMaxHeight().width(CONTACT_COLUMN_WIDTH).background(Color.Transparent), ) { @@ -41,8 +40,8 @@ fun ContactList( contentType = { item -> item::class } ) { item -> ListItemView( - // onSelect = {}, - // selected = isSelected(item), + onSelect = { contactstore.select(item.pubkey) }, + selected = (contactList.selectedContact == item.pubkey), // let divider start at horizontal position of text dividerOffsetFromStart = (16 + 36 + 12).dp, ) { @@ -51,18 +50,14 @@ fun ContactList( .fillMaxWidth() .padding(vertical = 8.dp) .padding(start = 16.dp, end = 4.dp) - - when (item) { - is ContactItem -> { - ContactItemView( - contactItem = item, - modifier = modifier - ) - } - } + ContactItemView( + contactItem = item, + modifier = modifier + ) } } } } } + diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/ListItemView.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/ListItemView.kt index 90f89502..ec4920f9 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/ui/ListItemView.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/ListItemView.kt @@ -36,6 +36,8 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import com.zoffcc.applications.trifa.TRIFAGlobals +import com.zoffcc.applications.trifa.TRIFAGlobals.COLOR_SELECTED_TOX_FRIEND import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n @Composable @@ -43,11 +45,15 @@ fun ListItemView( selected: Boolean? = null, onSelect: () -> Unit = {}, dividerOffsetFromStart: Dp = 0.dp, - multiSelectWithCheckbox: Boolean = false, modifier: Modifier = Modifier, content: @Composable () -> Unit, ) = Column(modifier.fillMaxWidth()) { - val bgColor = if (selected != null && selected) Color.Blue else Color.Transparent + val bgColor = if (selected != null && selected) Color( + COLOR_SELECTED_TOX_FRIEND.red, + COLOR_SELECTED_TOX_FRIEND.green, + COLOR_SELECTED_TOX_FRIEND.blue, + COLOR_SELECTED_TOX_FRIEND.alpha + ) else Color.Transparent Row( verticalAlignment = Alignment.CenterVertically, modifier = modifier From cd18f5f8c70aef7d41fda4658ee330843fb4e896 Mon Sep 17 00:00:00 2001 From: zoff99 Date: Sat, 14 Oct 2023 07:51:35 +0200 Subject: [PATCH 06/25] fix exception --- src/main/kotlin/Main.kt | 15 ++++++++------- .../briarproject/briar/desktop/ui/ListItemView.kt | 3 --- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index cd6ef505..dcabf1aa 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -24,17 +24,15 @@ import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.* -import com.zoffcc.applications.trifa.Log +import com.zoffcc.applications.trifa.* import com.zoffcc.applications.trifa.MainActivity.Companion.main_init -import com.zoffcc.applications.trifa.PrefsSettings -import com.zoffcc.applications.trifa.StateContacts -import com.zoffcc.applications.trifa.TrifaToxService import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.runBlocking +import org.briarproject.briar.desktop.contact.ContactItem import org.briarproject.briar.desktop.contact.ContactList import org.briarproject.briar.desktop.navigation.BriarSidebar import org.briarproject.briar.desktop.ui.VerticalDivider @@ -139,9 +137,12 @@ fun App() { Text(getOnlineButtonText(online_button_text)) Thread { while (true) { - Thread.sleep(200) - if (online_button_text != online_button_text_wrapper) { - online_button_text = online_button_text_wrapper + try { + Thread.sleep(200) + if (online_button_text != online_button_text_wrapper) { + online_button_text = online_button_text_wrapper + } + } catch (_: Exception) { } } }.start() diff --git a/src/main/kotlin/org/briarproject/briar/desktop/ui/ListItemView.kt b/src/main/kotlin/org/briarproject/briar/desktop/ui/ListItemView.kt index ec4920f9..c2b0933f 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/ui/ListItemView.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/ui/ListItemView.kt @@ -24,8 +24,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.selection.selectable -import androidx.compose.material.Checkbox -import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -36,7 +34,6 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.zoffcc.applications.trifa.TRIFAGlobals import com.zoffcc.applications.trifa.TRIFAGlobals.COLOR_SELECTED_TOX_FRIEND import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n From 6c955e5268f76edc1dfaac7ee9e2ee57c991dac8 Mon Sep 17 00:00:00 2001 From: zoff99 Date: Sat, 14 Oct 2023 08:28:42 +0200 Subject: [PATCH 07/25] contacts features --- src/main/kotlin/Main.kt | 11 +++++----- .../zoffcc/applications/trifa/ContactStore.kt | 20 +++++++++++++++++-- .../com/zoffcc/applications/trifa2/ChatApp.kt | 20 ++++++++++--------- .../briar/desktop/contact/ContactList.kt | 4 +--- .../strings/trifa_material.properties | 3 ++- 5 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index dcabf1aa..7ebbbb6f 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -32,7 +32,6 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.runBlocking -import org.briarproject.briar.desktop.contact.ContactItem import org.briarproject.briar.desktop.contact.ContactList import org.briarproject.briar.desktop.navigation.BriarSidebar import org.briarproject.briar.desktop.ui.VerticalDivider @@ -153,11 +152,13 @@ fun App() { // UIScaleSlider(uiscale_default) val contacts by contactstore.stateFlow.collectAsState() Row(modifier = Modifier.fillMaxWidth()) { - ContactList( - contactList = contacts, - ) + ContactList(contactList = contacts) VerticalDivider() - ChatAppWithScaffold() + if (contacts.selectedContactPubkey == null) { + ExplainerChat() + } else { + ChatAppWithScaffold(contactList = contacts) + } } } } diff --git a/src/main/kotlin/com/zoffcc/applications/trifa/ContactStore.kt b/src/main/kotlin/com/zoffcc/applications/trifa/ContactStore.kt index f80c9cc6..9eee20c7 100644 --- a/src/main/kotlin/com/zoffcc/applications/trifa/ContactStore.kt +++ b/src/main/kotlin/com/zoffcc/applications/trifa/ContactStore.kt @@ -10,7 +10,8 @@ import org.briarproject.briar.desktop.contact.ContactItem data class StateContacts( val contacts: List = emptyList(), - val selectedContact: String? = null + val selectedContactPubkey: String? = null, + val selectedContact: ContactItem? = null ) const val TAG = "trifa.ContactsStore" @@ -74,10 +75,22 @@ fun CoroutineScope.createContactStore(): ContactStore { override fun select(pubkey: String?) { launch { + var wanted_contact_item: ContactItem? = null + state.contacts.forEach { + if (pubkey == it.pubkey) { + wanted_contact_item = it + } + } + var used_pubkey = pubkey + if (wanted_contact_item == null) + { + used_pubkey = null + } mutableStateFlow.value = state.copy( contacts = state.contacts, - selectedContact = pubkey + selectedContactPubkey = used_pubkey, + selectedContact = wanted_contact_item ) } } @@ -94,12 +107,14 @@ fun CoroutineScope.createContactStore(): ContactStore { mutableStateFlow.value = state.copy( contacts = (state.contacts + item - update_item!!), + selectedContactPubkey = state.selectedContactPubkey, selectedContact = state.selectedContact ) } else { mutableStateFlow.value = state.copy( contacts = (state.contacts + item), + selectedContactPubkey = state.selectedContactPubkey, selectedContact = state.selectedContact ) } @@ -111,6 +126,7 @@ fun CoroutineScope.createContactStore(): ContactStore { mutableStateFlow.value = state.copy( contacts = emptyList(), + selectedContactPubkey = null, selectedContact = null ) } diff --git a/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt b/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt index cb6b1f80..ad875553 100644 --- a/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt +++ b/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt @@ -10,14 +10,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.sp -import com.zoffcc.applications.trifa.ContactStore +import com.zoffcc.applications.trifa.* import com.zoffcc.applications.trifa.MainActivity.Companion.tox_friend_send_message import com.zoffcc.applications.trifa.ToxVars.TOX_MESSAGE_TYPE -import com.zoffcc.applications.trifa.createContactStore -import com.zoffcc.applications.trifa.createSavepathStore -import com.zoffcc.applications.trifa.createToxDataStore import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob +import org.briarproject.briar.desktop.contact.ContactItem import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource @@ -27,15 +25,17 @@ val myUser = User("Me", picture = null, toxpk = null) val store = CoroutineScope(SupervisorJob()).createStore() val contactstore = CoroutineScope(SupervisorJob()).createContactStore() val savepathstore = CoroutineScope(SupervisorJob()).createSavepathStore() -val toxdatastore = CoroutineScope(SupervisorJob()).createToxDataStore() +val toxdatastore = CoroutineScope(SupervisorJob()).createToxDataStore() @Composable -fun ChatAppWithScaffold(displayTextField: Boolean = true) { +fun ChatAppWithScaffold(displayTextField: Boolean = true, contactList: StateContacts) { Theme { Scaffold( topBar = { TopAppBar( - title = { Text("Tox Chat") }, + title = { + contactList.selectedContact?.let { Text(it.name) } + }, backgroundColor = MaterialTheme.colors.background, ) }) { @@ -51,10 +51,12 @@ fun ChatApp(displayTextField: Boolean = true) { Theme { Surface { Box(modifier = Modifier.fillMaxSize()) { - Image(painterResource("background.jpg"), + Image( + painterResource("background.jpg"), modifier = Modifier.fillMaxSize(), contentDescription = null, - contentScale = ContentScale.Crop) + contentScale = ContentScale.Crop + ) Column( modifier = Modifier.fillMaxSize() ) { diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt index 90ed61f5..70ac5f72 100644 --- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt +++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt @@ -41,9 +41,7 @@ fun ContactList( ) { item -> ListItemView( onSelect = { contactstore.select(item.pubkey) }, - selected = (contactList.selectedContact == item.pubkey), - // let divider start at horizontal position of text - dividerOffsetFromStart = (16 + 36 + 12).dp, + selected = (contactList.selectedContactPubkey == item.pubkey) ) { val modifier = Modifier .heightIn(min = TOP_HEADER_SIZE) diff --git a/src/main/resources/strings/trifa_material.properties b/src/main/resources/strings/trifa_material.properties index d4ea5972..6ef6fb4f 100644 --- a/src/main/resources/strings/trifa_material.properties +++ b/src/main/resources/strings/trifa_material.properties @@ -6,4 +6,5 @@ # Miscellaneous Yes=Yes Close\ TRIfA\ ?=Close TRIfA ? - +No\ Contacts\ selected=No Contacts selected +Select\ a\ Contact\ to\ start\ chatting=Select a Contact to start chatting From 3f06d94b6b54e7b477aba6ddbc7148879172a962 Mon Sep 17 00:00:00 2001 From: zoff99 Date: Sat, 14 Oct 2023 08:28:51 +0200 Subject: [PATCH 08/25] contacts features --- src/main/java/ExplainerChat.kt | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/main/java/ExplainerChat.kt diff --git a/src/main/java/ExplainerChat.kt b/src/main/java/ExplainerChat.kt new file mode 100644 index 00000000..87fe301f --- /dev/null +++ b/src/main/java/ExplainerChat.kt @@ -0,0 +1,42 @@ +import androidx.compose.foundation.layout.* +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n + +@Composable +fun ExplainerChat() { + Theme { + Explainer( + headline = i18n("No Contacts selected"), + text = i18n("Select a Contact to start chatting") + ) + } +} + +val PARAGRAPH_WIDTH = 540.dp + +@Composable +fun Explainer(headline: String, text: String, content: @Composable () -> Unit = {}) = + Column( + modifier = Modifier.padding(16.dp).fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + // BriarLogo(modifier = Modifier.size(200.dp)) + Text( + text = headline, + modifier = Modifier.padding(top = 16.dp, bottom = 4.dp), + style = MaterialTheme.typography.h3 + ) + Text( + text = text, + modifier = Modifier.padding(top = 4.dp, bottom = 16.dp).widthIn(max = PARAGRAPH_WIDTH), + style = MaterialTheme.typography.body2.copy(textAlign = TextAlign.Center) + ) + content() + } From d415db5ce56fae4b6c726a84ce4ad4263b13919f Mon Sep 17 00:00:00 2001 From: zoff99 Date: Sat, 14 Oct 2023 09:18:05 +0200 Subject: [PATCH 09/25] .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3d7b0331..a9d16601 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ bin/ deps/ toxid.txt main.db +!.idea/codeStyles/* From dd974e6d7401244726eda4c599baad5e4d0c68c9 Mon Sep 17 00:00:00 2001 From: zoff99 Date: Sat, 14 Oct 2023 09:19:39 +0200 Subject: [PATCH 10/25] .gitignore add codestyles --- .gitignore | 4 +- .idea/codeStyles/Project.xml | 29 ++ .idea/codeStyles/codeStyleConfig.xml | 5 + .../com/zoffcc/applications/sorm/Message.java | 28 +- src/main/kotlin/Main.kt | 1 + .../zoffcc/applications/trifa/MainActivity.kt | 325 ++++++++++++------ .../com/zoffcc/applications/trifa2/ChatApp.kt | 17 +- .../com/zoffcc/applications/trifa2/Reducer.kt | 24 +- .../com/zoffcc/applications/trifa2/Store.kt | 8 +- 9 files changed, 300 insertions(+), 141 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml diff --git a/.gitignore b/.gitignore index a9d16601..b889b672 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -.idea/ +.idea/* +!.idea/codeStyles build/ .gradle/ savedata.tox @@ -6,4 +7,3 @@ bin/ deps/ toxid.txt main.db -!.idea/codeStyles/* diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..22c8ab1e --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/src/main/java/com/zoffcc/applications/sorm/Message.java b/src/main/java/com/zoffcc/applications/sorm/Message.java index 171596ea..2f612e33 100644 --- a/src/main/java/com/zoffcc/applications/sorm/Message.java +++ b/src/main/java/com/zoffcc/applications/sorm/Message.java @@ -46,16 +46,16 @@ public class Message long message_id = -1; // ID given from toxcore!! @Column(indexed = true, helpers = Column.Helpers.ALL) - String tox_friendpubkey; + public String tox_friendpubkey; @Column(indexed = true, helpers = Column.Helpers.ALL) - int direction = 0; // 0 -> msg received, 1 -> msg sent + public int direction = 0; // 0 -> msg received, 1 -> msg sent @Column(indexed = true) - int TOX_MESSAGE_TYPE = 0; // 0 -> normal, 1 -> action + public int TOX_MESSAGE_TYPE = 0; // 0 -> normal, 1 -> action @Column(indexed = true, defaultExpr = "0") - int TRIFA_MESSAGE_TYPE = TRIFA_MSG_TYPE_TEXT.value; + public int TRIFA_MESSAGE_TYPE = TRIFA_MSG_TYPE_TEXT.value; @Column(indexed = true, defaultExpr = "1", helpers = Column.Helpers.ALL) int state = TOX_FILE_CONTROL_PAUSE.value; @@ -74,32 +74,32 @@ public class Message @Column(helpers = Column.Helpers.ALL, defaultExpr = "0") @Nullable - long sent_timestamp = 0L; + public long sent_timestamp = 0L; @Column(helpers = Column.Helpers.ALL, defaultExpr = "0") @Nullable - long sent_timestamp_ms = 0L; + public long sent_timestamp_ms = 0L; @Column(indexed = true, defaultExpr = "0") @Nullable - long rcvd_timestamp = 0L; + public long rcvd_timestamp = 0L; @Column(indexed = true, defaultExpr = "0") @Nullable - long rcvd_timestamp_ms = 0L; + public long rcvd_timestamp_ms = 0L; @Column(helpers = Column.Helpers.ALL) - boolean read = false; + public boolean read = false; @Column(indexed = true, defaultExpr = "0", helpers = Column.Helpers.ALL) int send_retries = 0; @Column(indexed = true, helpers = Column.Helpers.ALL) - boolean is_new = true; + public boolean is_new = true; @Column(indexed = true, helpers = Column.Helpers.ALL) @Nullable - String text = null; + public String text = null; @Column(helpers = Column.Helpers.ALL) @Nullable @@ -114,7 +114,7 @@ public class Message String raw_msgv2_bytes = null; // used for MessageV2 Messages! and otherwise NULL @Column(indexed = true, defaultExpr = "0") - int msg_version; // 0 -> old Message, 1 -> for MessageV2 Message + public int msg_version; // 0 -> old Message, 1 -> for MessageV2 Message @Column(indexed = true, defaultExpr = "2") int resend_count; // 2 -> do not resend msg anymore, 0 or 1 -> resend count @@ -127,11 +127,11 @@ public class Message @Column(indexed = true, helpers = Column.Helpers.ALL) @Nullable - String msg_idv3_hash = null; // 32byte hash, used for MessageV3 Messages! and otherwise NULL + public String msg_idv3_hash = null; // 32byte hash, used for MessageV3 Messages! and otherwise NULL @Column(helpers = Column.Helpers.ALL) @Nullable - int sent_push = 0; + public int sent_push = 0; @Column(helpers = Column.Helpers.ALL, defaultExpr = "0") @Nullable diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 7ebbbb6f..4a5e4ee1 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -157,6 +157,7 @@ fun App() { if (contacts.selectedContactPubkey == null) { ExplainerChat() } else { + store.send(Action.Clear(0)) ChatAppWithScaffold(contactList = contacts) } } diff --git a/src/main/kotlin/com/zoffcc/applications/trifa/MainActivity.kt b/src/main/kotlin/com/zoffcc/applications/trifa/MainActivity.kt index 7b3ceba6..d76fc501 100644 --- a/src/main/kotlin/com/zoffcc/applications/trifa/MainActivity.kt +++ b/src/main/kotlin/com/zoffcc/applications/trifa/MainActivity.kt @@ -23,13 +23,14 @@ import java.util.concurrent.BlockingQueue import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.Semaphore - @Suppress("UNUSED_PARAMETER") -class MainActivity { +class MainActivity +{ val nativeLibAPI: String? external get - companion object { + companion object + { private const val TAG = "trifa.MainActivity" const val Version = "0.99.0" @@ -47,7 +48,6 @@ class MainActivity { var PREF__local_discovery_enabled = 1 var PREF__ipv6_enabled = 1 var PREF__force_udp_only = 0 - var incoming_messages_queue: BlockingQueue = LinkedBlockingQueue() // @@ -57,8 +57,8 @@ class MainActivity { var PREF__ngc_audio_bitrate: Int = NGC_AUDIO_BITRATE var PREF__ngc_audio_samplerate = 48000 var PREF__ngc_audio_channels = 1 - var PREF__tox_savefile_dir = "." + @JvmField var PREF__database_files_dir = "." @@ -73,16 +73,18 @@ class MainActivity { // !!!!!! DEBUG !!!!!! change to real password later !!!!!! // var semaphore_tox_savedata: Semaphore? = Semaphore(1) - - fun main_init() { + fun main_init() + { println("Version:" + Version) - try { + try + { println("java.vm.name:" + System.getProperty("java.vm.name")) println("java.home:" + System.getProperty("java.home")) println("java.vendor:" + System.getProperty("java.vendor")) println("java.version:" + System.getProperty("java.version")) println("java.specification.vendor:" + System.getProperty("java.specification.vendor")) - } catch (e: Exception) { + } catch (e: Exception) + { e.printStackTrace() } val locale = Locale.getDefault() @@ -93,9 +95,11 @@ class MainActivity { Log.i(TAG, locale.isO3Language) Log.i(TAG, locale.language) Log.i(TAG, locale.country) - try { + try + { Thread.currentThread().name = "t_main" - } catch (e: Exception) { + } catch (e: Exception) + { } Log.i(TAG, "java.library.path:" + System.getProperty("java.library.path")) Log.i( @@ -112,7 +116,8 @@ class MainActivity { lock_data_dir_input() - if (!TrifaToxService.TOX_SERVICE_STARTED) { + if (!TrifaToxService.TOX_SERVICE_STARTED) + { var ORBOT_PROXY_HOST = "" var ORBOT_PROXY_PORT: Long = 0 tox_savefile_directory = PREF__tox_savefile_dir + File.separator @@ -135,20 +140,24 @@ class MainActivity { ) tox_service_fg!!.tox_thread_start_fg() } - val my_tox_id_temp = get_my_toxid() Log.i(TAG, "MyToxID:$my_tox_id_temp") - try { + try + { Thread.currentThread().name = "t_main" - } catch (_: Exception) { + } catch (_: Exception) + { } - try { - toxdatastore.updateToxID(my_tox_id_temp) - } catch (_: Exception) { + try + { + toxdatastore.updateToxID(my_tox_id_temp) + } catch (_: Exception) + { } - try { + try + { PrintWriter(PREF__tox_savefile_dir + File.separator + "toxid.txt", "UTF-8") .use { out -> out.write(my_tox_id_temp) } Log.i( @@ -156,35 +165,44 @@ class MainActivity { + File(PREF__tox_savefile_dir).canonicalPath + File.separator + "toxid.txt" ) - } catch (_: Exception) { + } catch (_: Exception) + { } } - init { - if (!native_lib_loaded) { + init + { + if (!native_lib_loaded) + { val resourcesDir = File(System.getProperty("compose.application.resources.dir")) System.out.println("XXXXX1:" + resourcesDir) System.out.println("XXXXX1.1:OS:" + OperatingSystem.getCurrent()) System.out.println("XXXXX1.2:OS:" + OperatingSystem.getName()) System.out.println("XXXXX1.3:OS:" + OperatingSystem.getArchitecture()) var libFile = File(resourcesDir, "libjni-c-toxcore.so") - if (OperatingSystem.getCurrent() == OperatingSystem.LINUX) { + if (OperatingSystem.getCurrent() == OperatingSystem.LINUX) + { libFile = File(resourcesDir, "libjni-c-toxcore.so") - } else if (OperatingSystem.getCurrent() == OperatingSystem.WINDOWS) { + } else if (OperatingSystem.getCurrent() == OperatingSystem.WINDOWS) + { libFile = File(resourcesDir, "jni-c-toxcore.dll") - } else if (OperatingSystem.getCurrent() == OperatingSystem.MACOS) { + } else if (OperatingSystem.getCurrent() == OperatingSystem.MACOS) + { libFile = File(resourcesDir, "libjni-c-toxcore.jnilib") - } else { + } else + { System.out.println("XXXXX1.1:OS:Unknown operating system:EXIT") System.exit(3) } System.out.println("XXXXX2:" + libFile + " " + libFile.canonicalPath) - try { + try + { System.load(libFile.canonicalPath) native_lib_loaded = true Log.i(TAG, "successfully loaded native library") - } catch (e: UnsatisfiedLinkError) { + } catch (e: UnsatisfiedLinkError) + { native_lib_loaded = false Log.i(TAG, "loadLibrary jni-c-toxcore failed!") e.printStackTrace() @@ -267,7 +285,6 @@ class MainActivity { ) // ------------------------------ - @JvmStatic external fun tox_set_do_not_sync_av(do_not_sync_av: Int) @@ -929,7 +946,8 @@ class MainActivity { // -------- called by AV native methods -------- // -------- called by AV native methods -------- @JvmStatic - fun android_toxav_callback_call_cb_method(friend_number: Long, audio_enabled: Int, video_enabled: Int) { + fun android_toxav_callback_call_cb_method(friend_number: Long, audio_enabled: Int, video_enabled: Int) + { } @JvmStatic @@ -940,11 +958,13 @@ class MainActivity { ystride: Long, ustride: Long, vstride: Long - ) { + ) + { } @JvmStatic - fun android_toxav_callback_call_state_cb_method(friend_number: Long, a_TOXAV_FRIEND_CALL_STATE: Int) { + fun android_toxav_callback_call_state_cb_method(friend_number: Long, a_TOXAV_FRIEND_CALL_STATE: Int) + { } @JvmStatic @@ -952,7 +972,8 @@ class MainActivity { friend_number: Long, audio_bit_rate: Long, video_bit_rate: Long - ) { + ) + { } @JvmStatic @@ -961,7 +982,8 @@ class MainActivity { sample_count: Long, channels: Int, sampling_rate: Long - ) { + ) + { } @JvmStatic @@ -971,7 +993,8 @@ class MainActivity { channels: Int, sampling_rate: Long, pts: Long - ) { + ) + { } @JvmStatic @@ -983,11 +1006,13 @@ class MainActivity { ustride: Long, vstride: Long, pts: Long - ) { + ) + { } @JvmStatic - fun android_toxav_callback_video_receive_frame_h264_cb_method(friend_number: Long, buf_size: Long) { + fun android_toxav_callback_video_receive_frame_h264_cb_method(friend_number: Long, buf_size: Long) + { } @JvmStatic @@ -997,7 +1022,8 @@ class MainActivity { sample_count: Long, channels: Int, sampling_rate: Long - ) { + ) + { } @JvmStatic @@ -1005,7 +1031,8 @@ class MainActivity { friend_number: Long, a_TOXAV_CALL_COMM_INFO: Long, comm_number: Long - ) { + ) + { } // -------- called by AV native methods -------- @@ -1015,21 +1042,27 @@ class MainActivity { // -------- called by native methods -------- // -------- called by native methods -------- @JvmStatic - fun android_tox_callback_self_connection_status_cb_method(a_TOX_CONNECTION: Int) { + fun android_tox_callback_self_connection_status_cb_method(a_TOX_CONNECTION: Int) + { Log.i(TAG, "android_tox_callback_self_connection_status_cb_method: " + a_TOX_CONNECTION) update_savedata_file_wrapper() - if (a_TOX_CONNECTION == ToxVars.TOX_CONNECTION.TOX_CONNECTION_TCP.value) { + if (a_TOX_CONNECTION == ToxVars.TOX_CONNECTION.TOX_CONNECTION_TCP.value) + { set_tox_online_state("tcp") - } else if (a_TOX_CONNECTION == ToxVars.TOX_CONNECTION.TOX_CONNECTION_UDP.value) { + } else if (a_TOX_CONNECTION == ToxVars.TOX_CONNECTION.TOX_CONNECTION_UDP.value) + { set_tox_online_state("udp") - } else { + } else + { set_tox_online_state("offline") } } @JvmStatic - fun android_tox_callback_friend_name_cb_method(friend_number: Long, friend_name: String?, length: Long) { - try { + fun android_tox_callback_friend_name_cb_method(friend_number: Long, friend_name: String?, length: Long) + { + try + { contactstore.update( item = ContactItem( name = friend_name!!, @@ -1037,7 +1070,8 @@ class MainActivity { pubkey = tox_friend_get_public_key(friend_number)!! ) ) - } catch (_: Exception) { + } catch (_: Exception) + { } } @@ -1046,10 +1080,13 @@ class MainActivity { friend_number: Long, status_message: String?, length: Long - ) { - try { + ) + { + try + { var fname = tox_friend_get_name(friend_number) - if (fname == null) { + if (fname == null) + { fname = "Friend" } contactstore.update( @@ -1059,7 +1096,8 @@ class MainActivity { pubkey = tox_friend_get_public_key(friend_number)!! ) ) - } catch (_: Exception) { + } catch (_: Exception) + { } } @@ -1068,23 +1106,28 @@ class MainActivity { friend_number: Long, data: ByteArray?, length: Long - ) { + ) + { } @JvmStatic - fun android_tox_callback_friend_status_cb_method(friend_number: Long, a_TOX_USER_STATUS: Int) { + fun android_tox_callback_friend_status_cb_method(friend_number: Long, a_TOX_USER_STATUS: Int) + { } @JvmStatic - fun android_tox_callback_friend_connection_status_cb_method(friend_number: Long, a_TOX_CONNECTION: Int) { + fun android_tox_callback_friend_connection_status_cb_method(friend_number: Long, a_TOX_CONNECTION: Int) + { Log.i( TAG, "android_tox_callback_friend_connection_status_cb_method: fn=" + friend_number + " " + a_TOX_CONNECTION ) update_savedata_file_wrapper() - try { + try + { var fname = tox_friend_get_name(friend_number) - if (fname == null) { + if (fname == null) + { fname = "Friend" } contactstore.update( @@ -1094,16 +1137,19 @@ class MainActivity { pubkey = tox_friend_get_public_key(friend_number)!! ) ) - } catch (_: Exception) { + } catch (_: Exception) + { } } @JvmStatic - fun android_tox_callback_friend_typing_cb_method(friend_number: Long, typing: Int) { + fun android_tox_callback_friend_typing_cb_method(friend_number: Long, typing: Int) + { } @JvmStatic - fun android_tox_callback_friend_read_receipt_cb_method(friend_number: Long, message_id: Long) { + fun android_tox_callback_friend_read_receipt_cb_method(friend_number: Long, message_id: Long) + { } @JvmStatic @@ -1111,7 +1157,8 @@ class MainActivity { friend_public_key: String?, friend_request_message: String?, length: Long - ) { + ) + { Log.i(TAG, "android_tox_callback_friend_request_cb_method: friend_public_key=" + friend_public_key) tox_friend_add_norequest(friend_public_key) update_savedata_file_wrapper() @@ -1125,29 +1172,33 @@ class MainActivity { length: Long, msgV3hash_bin: ByteArray?, message_timestamp: Long - ) { + ) + { Log.i( TAG, "android_tox_callback_friend_message_cb_method: fn=" + friend_number + " friend_message=" + friend_message ) - var msgV3hash_hex_string: String? = null - if (msgV3hash_bin != null) { + if (msgV3hash_bin != null) + { msgV3hash_hex_string = bytesToHex(msgV3hash_bin, 0, msgV3hash_bin.size) } - if (message_type == ToxVars.TOX_MESSAGE_TYPE.TOX_MESSAGE_TYPE_HIGH_LEVEL_ACK.value) { + if (message_type == ToxVars.TOX_MESSAGE_TYPE.TOX_MESSAGE_TYPE_HIGH_LEVEL_ACK.value) + { return } - if (msgV3hash_hex_string != null) { + if (msgV3hash_hex_string != null) + { HelperMessage.send_msgv3_high_level_ack(friend_number, msgV3hash_hex_string); - try { + try + { // ("msgv3:"+friend_message) val toxpk = tox_friend_get_public_key(friend_number) val friend_user = User("Friend " + friend_number, picture = "friend_avatar.png", toxpk = toxpk) store.send( - Action.SendMessage( + Action.ReceiveMessage( message = Message( user = friend_user, timeMs = timestampMs(), @@ -1156,15 +1207,18 @@ class MainActivity { ) ) ) - } catch (_: Exception) { + } catch (_: Exception) + { } - } else { - try { + } else + { + try + { // ("msgv1:"+friend_message) val toxpk = tox_friend_get_public_key(friend_number) val friend_user = User("Friend " + friend_number, picture = "friend_avatar.png", toxpk = toxpk) store.send( - Action.SendMessage( + Action.ReceiveMessage( message = Message( user = friend_user, timeMs = timestampMs(), @@ -1173,7 +1227,8 @@ class MainActivity { ) ) ) - } catch (_: Exception) { + } catch (_: Exception) + { } } } @@ -1187,12 +1242,12 @@ class MainActivity { ts_ms: Long, raw_message: ByteArray?, raw_message_length: Long - ) { + ) + { Log.i( TAG, "android_tox_callback_friend_message_v2_cb_method: fn=" + friend_number + " friend_message=" + friend_message ) - val msg_type = 1 val raw_message_buf = ByteBuffer.allocateDirect(raw_message_length.toInt()) raw_message_buf.put(raw_message, 0, raw_message_length.toInt()) @@ -1200,7 +1255,6 @@ class MainActivity { tox_messagev2_get_message_id(raw_message_buf, msg_id_buffer) val ts_sec = tox_messagev2_get_ts_sec(raw_message_buf) val ts_ms = tox_messagev2_get_ts_ms(raw_message_buf) - val msg_id_buffer_compat = ByteBufferCompat(msg_id_buffer) val msg_id_as_hex_string: String? = msg_id_buffer_compat.array()?.let { bytesToHex( @@ -1210,11 +1264,28 @@ class MainActivity { } Log.i(TAG, "TOX_FILE_KIND_MESSAGEV2_SEND:MSGv2HASH:2=" + msg_id_as_hex_string); - try { + try + { val toxpk = tox_friend_get_public_key(friend_number) + val message_timestamp = System.currentTimeMillis() + val m = com.zoffcc.applications.sorm.Message() + m.is_new = false + m.tox_friendpubkey = toxpk + m.direction = 0 // msg received + m.TOX_MESSAGE_TYPE = 0 + m.read = false + m.TRIFA_MESSAGE_TYPE = TRIFA_MSG_TYPE.TRIFA_MSG_TYPE_TEXT.value + m.rcvd_timestamp = System.currentTimeMillis() + m.rcvd_timestamp_ms = 0 + m.sent_timestamp = message_timestamp + m.sent_timestamp_ms = 0 + m.text = friend_message + m.msg_version = 0 + m.msg_idv3_hash = "" + m.sent_push = 0 val friend_user = User("Friend " + friend_number, picture = "friend_avatar.png", toxpk = toxpk) store.send( - Action.SendMessage( + Action.ReceiveMessage( message = Message( user = friend_user, timeMs = timestampMs(), @@ -1223,10 +1294,9 @@ class MainActivity { ) ) ) - // incoming_messages_queue.offer(friend_message) // ("msgv2:"+friend_message) - } catch (_: Exception) { + } catch (_: Exception) + { } - val pin_timestamp = System.currentTimeMillis() send_friend_msg_receipt_v2_wrapper(friend_number, msg_type, msg_id_buffer, (pin_timestamp / 1000)); } @@ -1240,7 +1310,8 @@ class MainActivity { raw_message_length: Long, raw_data: ByteArray?, raw_data_length: Long - ) { + ) + { } @JvmStatic @@ -1251,7 +1322,8 @@ class MainActivity { raw_message_buf_wrapped: ByteBuffer?, msg_id_buffer: ByteBuffer?, real_sender_as_hex_string: String? - ) { + ) + { } @JvmStatic @@ -1259,11 +1331,11 @@ class MainActivity { friend_number: Long, ts_sec: Long, msg_id: ByteArray? - ) { + ) + { val msg_id_buffer = ByteBuffer.allocateDirect(TOX_HASH_LENGTH) msg_id_buffer.put(msg_id, 0, TOX_HASH_LENGTH) val msg_id_buffer_compat = ByteBufferCompat(msg_id_buffer) - val message_id_hash_as_hex_string = bytesToHex( msg_id_buffer_compat.array()!!, msg_id_buffer_compat.arrayOffset(), @@ -1277,7 +1349,8 @@ class MainActivity { friend_number: Long, file_number: Long, a_TOX_FILE_CONTROL: Int - ) { + ) + { } @JvmStatic @@ -1286,7 +1359,8 @@ class MainActivity { file_number: Long, position: Long, length: Long - ) { + ) + { } @JvmStatic @@ -1297,7 +1371,8 @@ class MainActivity { file_size: Long, filename: String?, filename_length: Long - ) { + ) + { } @JvmStatic @@ -1307,7 +1382,8 @@ class MainActivity { position: Long, data: ByteArray?, length: Long - ) { + ) + { } @JvmStatic @@ -1317,8 +1393,10 @@ class MainActivity { line: Long, function: String?, message: String? - ) { - if (CTOXCORE_NATIVE_LOGGING) { + ) + { + if (CTOXCORE_NATIVE_LOGGING) + { Log.i( TAG, "C-TOXCORE:" + ToxVars.TOX_LOG_LEVEL.value_str(a_TOX_LOG_LEVEL) + ":file=" + file + ":linenum=" + @@ -1339,11 +1417,13 @@ class MainActivity { a_TOX_CONFERENCE_TYPE: Int, cookie_buffer: ByteArray?, cookie_length: Long - ) { + ) + { } @JvmStatic - fun android_tox_callback_conference_connected_cb_method(conference_number: Long) { + fun android_tox_callback_conference_connected_cb_method(conference_number: Long) + { } @JvmStatic @@ -1353,7 +1433,8 @@ class MainActivity { a_TOX_MESSAGE_TYPE: Int, message_orig: String?, length: Long - ) { + ) + { } @JvmStatic @@ -1362,7 +1443,8 @@ class MainActivity { peer_number: Long, title: String?, title_length: Long - ) { + ) + { } @JvmStatic @@ -1371,11 +1453,13 @@ class MainActivity { peer_number: Long, name: String?, name_length: Long - ) { + ) + { } @JvmStatic - fun android_tox_callback_conference_peer_list_changed_cb_method(conference_number: Long) { + fun android_tox_callback_conference_peer_list_changed_cb_method(conference_number: Long) + { } @JvmStatic @@ -1383,7 +1467,8 @@ class MainActivity { conference_number: Long, peer_number: Long, a_TOX_CONFERENCE_STATE_CHANGE: Int - ) { + ) + { } // -------- called by native Conference methods -------- @@ -1400,7 +1485,8 @@ class MainActivity { message_orig: String?, length: Long, message_id: Long - ) { + ) + { } @JvmStatic @@ -1410,11 +1496,13 @@ class MainActivity { a_TOX_MESSAGE_TYPE: Int, message_orig: String?, length: Long - ) { + ) + { } @JvmStatic - fun android_tox_callback_group_privacy_state_cb_method(group_number: Long, a_TOX_GROUP_PRIVACY_STATE: Int) { + fun android_tox_callback_group_privacy_state_cb_method(group_number: Long, a_TOX_GROUP_PRIVACY_STATE: Int) + { } @JvmStatic @@ -1423,11 +1511,13 @@ class MainActivity { invite_data: ByteArray?, invite_data_length: Long, group_name: String? - ) { + ) + { } @JvmStatic - fun android_tox_callback_group_peer_join_cb_method(group_number: Long, peer_id: Long) { + fun android_tox_callback_group_peer_join_cb_method(group_number: Long, peer_id: Long) + { } @JvmStatic @@ -1435,19 +1525,23 @@ class MainActivity { group_number: Long, peer_id: Long, a_Tox_Group_Exit_Type: Int - ) { + ) + { } @JvmStatic - fun android_tox_callback_group_peer_name_cb_method(group_number: Long, peer_id: Long) { + fun android_tox_callback_group_peer_name_cb_method(group_number: Long, peer_id: Long) + { } @JvmStatic - fun android_tox_callback_group_join_fail_cb_method(group_number: Long, a_Tox_Group_Join_Fail: Int) { + fun android_tox_callback_group_join_fail_cb_method(group_number: Long, a_Tox_Group_Join_Fail: Int) + { } @JvmStatic - fun android_tox_callback_group_self_join_cb_method(group_number: Long) { + fun android_tox_callback_group_self_join_cb_method(group_number: Long) + { } @JvmStatic @@ -1456,14 +1550,16 @@ class MainActivity { source_peer_id: Long, target_peer_id: Long, a_Tox_Group_Mod_Event: Int - ) { + ) + { } @JvmStatic fun android_tox_callback_group_connection_status_cb_method( group_number: Long, a_TOX_GROUP_CONNECTION_STATUS: Int - ) { + ) + { } @JvmStatic @@ -1472,7 +1568,8 @@ class MainActivity { peer_id: Long, topic: String?, topic_length: Long - ) { + ) + { } @JvmStatic @@ -1481,7 +1578,8 @@ class MainActivity { peer_id: Long, data: ByteArray?, length: Long - ) { + ) + { } @JvmStatic @@ -1490,16 +1588,19 @@ class MainActivity { peer_id: Long, data: ByteArray?, length: Long - ) { + ) + { } @JvmStatic - fun bootstrap_single_wrapper(ip: String, port: Int, key_hex: String): Int { + fun bootstrap_single_wrapper(ip: String, port: Int, key_hex: String): Int + { return bootstrap_single(ip, key_hex, port.toLong()) } @JvmStatic - fun add_tcp_relay_single_wrapper(ip: String, port: Int, key_hex: String): Int { + fun add_tcp_relay_single_wrapper(ip: String, port: Int, key_hex: String): Int + { return add_tcp_relay_single(ip, key_hex, port.toLong()) } } diff --git a/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt b/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt index ad875553..d5f06480 100644 --- a/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt +++ b/src/main/kotlin/com/zoffcc/applications/trifa2/ChatApp.kt @@ -11,11 +11,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.sp import com.zoffcc.applications.trifa.* +import com.zoffcc.applications.trifa.MainActivity.Companion.tox_friend_by_public_key import com.zoffcc.applications.trifa.MainActivity.Companion.tox_friend_send_message import com.zoffcc.applications.trifa.ToxVars.TOX_MESSAGE_TYPE import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob -import org.briarproject.briar.desktop.contact.ContactItem import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.painterResource @@ -39,14 +39,14 @@ fun ChatAppWithScaffold(displayTextField: Boolean = true, contactList: StateCont backgroundColor = MaterialTheme.colors.background, ) }) { - ChatApp(displayTextField = displayTextField) + ChatApp(displayTextField = displayTextField, contactList.selectedContactPubkey) } } } @OptIn(ExperimentalResourceApi::class) @Composable -fun ChatApp(displayTextField: Boolean = true) { +fun ChatApp(displayTextField: Boolean = true, selectedContactPubkey: String?) { val state by store.stateFlow.collectAsState() Theme { Surface { @@ -65,16 +65,11 @@ fun ChatApp(displayTextField: Boolean = true) { } if (displayTextField) { SendMessage { text -> - // !!!!! DEBUG DEBUG !!!!! - // !!!!! DEBUG DEBUG !!!!! - // !!!!! DEBUG DEBUG !!!!! - // send it to tox friend "0" (zero) + Log.i(TAG,"selectedContactPubkey=" + selectedContactPubkey) + val friend_num = tox_friend_by_public_key(selectedContactPubkey) tox_friend_send_message( - 0, TOX_MESSAGE_TYPE.TOX_MESSAGE_TYPE_NORMAL.value, text + friend_num, TOX_MESSAGE_TYPE.TOX_MESSAGE_TYPE_NORMAL.value, text ) - // !!!!! DEBUG DEBUG !!!!! - // !!!!! DEBUG DEBUG !!!!! - // !!!!! DEBUG DEBUG !!!!! store.send( Action.SendMessage( Message(myUser, timeMs = timestampMs(), text, toxpk = myUser.toxpk) diff --git a/src/main/kotlin/com/zoffcc/applications/trifa2/Reducer.kt b/src/main/kotlin/com/zoffcc/applications/trifa2/Reducer.kt index 401c5470..a537dbe4 100644 --- a/src/main/kotlin/com/zoffcc/applications/trifa2/Reducer.kt +++ b/src/main/kotlin/com/zoffcc/applications/trifa2/Reducer.kt @@ -1,16 +1,38 @@ sealed interface Action { data class SendMessage(val message: Message) : Action + data class ReceiveMessage(val message: Message) : Action + data class Clear(val dummy: Int) : Action } data class State( val messages: List = emptyList() ) +const val maxMessages = 5000 + fun chatReducer(state: State, action: Action): State = when (action) { is Action.SendMessage -> { state.copy( - messages = (state.messages + action.message).takeLast(100) + messages = (state.messages + action.message).takeLast(maxMessages) + ) + } + + is Action.ReceiveMessage -> { + state.copy( + messages = (state.messages + action.message).takeLast(maxMessages) + ) + } + + is Action.Clear -> { + state.copy( + messages = emptyList() + ) + } + + else -> { + state.copy( + messages = emptyList() ) } } diff --git a/src/main/kotlin/com/zoffcc/applications/trifa2/Store.kt b/src/main/kotlin/com/zoffcc/applications/trifa2/Store.kt index 4c6c7c12..46fe4855 100644 --- a/src/main/kotlin/com/zoffcc/applications/trifa2/Store.kt +++ b/src/main/kotlin/com/zoffcc/applications/trifa2/Store.kt @@ -26,7 +26,13 @@ fun CoroutineScope.createStore(): Store { override fun send(action: Action) { launch { - channel.send(action) + if (action is Action.ReceiveMessage) { + if (contactstore.state.selectedContactPubkey == action.message.toxpk) { + channel.send(action) + } + } else { + channel.send(action) + } } } From bc6642f397b294d77f5679b7fadf3e6abbc86b94 Mon Sep 17 00:00:00 2001 From: zoff99 Date: Sat, 14 Oct 2023 09:43:22 +0200 Subject: [PATCH 11/25] formatting --- .idea/codeStyles/Project.xml | 2 + .../applications/sorm/OrmaDatabase.java | 2 +- src/main/kotlin/Main.kt | 375 +++++----- .../zoffcc/applications/trifa/ContactStore.kt | 93 ++- .../zoffcc/applications/trifa/MainActivity.kt | 645 ++++-------------- .../applications/trifa/TrifaToxService.kt | 430 ++++-------- .../com/zoffcc/applications/trifa2/ChatApp.kt | 68 +- .../zoffcc/applications/trifa2/ChatMessage.kt | 2 +- .../com/zoffcc/applications/trifa2/Data.kt | 3 +- .../zoffcc/applications/trifa2/Messages.kt | 2 +- .../com/zoffcc/applications/trifa2/Reducer.kt | 55 +- .../com/zoffcc/applications/trifa2/Store.kt | 24 +- 12 files changed, 568 insertions(+), 1133 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 22c8ab1e..82168356 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -21,6 +21,8 @@