diff --git a/apps/android/.gitignore b/apps/android/.gitignore
index b2a8a63042..aa724b7707 100644
--- a/apps/android/.gitignore
+++ b/apps/android/.gitignore
@@ -8,17 +8,8 @@
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
-build/
-release/
-debug/
+/build
/captures
.externalNativeBuild
.cxx
local.properties
-*.jar
-*.war
-*.nar
-*.ear
-*.zip
-*.tar.gz
-*.rar
diff --git a/apps/android/.idea/.gitignore b/apps/android/.idea/.gitignore
deleted file mode 100644
index 26d33521af..0000000000
--- a/apps/android/.idea/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
diff --git a/apps/android/.idea/codeStyles/Project.xml b/apps/android/.idea/codeStyles/Project.xml
deleted file mode 100644
index 4bec4ea8ae..0000000000
--- a/apps/android/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,117 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- xmlns:android
-
- ^$
-
-
-
-
-
-
-
-
- xmlns:.*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*:id
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- .*:name
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- name
-
- ^$
-
-
-
-
-
-
-
-
- style
-
- ^$
-
-
-
-
-
-
-
-
- .*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*
-
- http://schemas.android.com/apk/res/android
-
-
- ANDROID_ATTRIBUTE_ORDER
-
-
-
-
-
-
- .*
-
- .*
-
-
- BY_NAME
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/apps/android/.idea/codeStyles/codeStyleConfig.xml b/apps/android/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100644
index a55e7a179b..0000000000
--- a/apps/android/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/apps/android/.idea/deploymentTargetDropDown.xml b/apps/android/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 0000000000..b2a5949802
--- /dev/null
+++ b/apps/android/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/android/.idea/inspectionProfiles/Project_Default.xml b/apps/android/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000000..28422375b6
--- /dev/null
+++ b/apps/android/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/android/.idea/misc.xml b/apps/android/.idea/misc.xml
index 345106f12f..1c2eb53046 100644
--- a/apps/android/.idea/misc.xml
+++ b/apps/android/.idea/misc.xml
@@ -3,9 +3,10 @@
diff --git a/apps/android/app/.gitignore b/apps/android/app/.gitignore
new file mode 100644
index 0000000000..42afabfd2a
--- /dev/null
+++ b/apps/android/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/apps/android/app/build.gradle b/apps/android/app/build.gradle
index dc2b3ed857..0f2c5d0297 100644
--- a/apps/android/app/build.gradle
+++ b/apps/android/app/build.gradle
@@ -1,6 +1,7 @@
plugins {
id 'com.android.application'
- id 'kotlin-android'
+ id 'org.jetbrains.kotlin.android'
+ id 'org.jetbrains.kotlin.plugin.serialization'
}
android {
@@ -17,6 +18,9 @@ android {
ndk {
abiFilters 'arm64-v8a'
}
+ vectorDrawables {
+ useSupportLibrary true
+ }
externalNativeBuild {
cmake {
cppFlags ''
@@ -44,17 +48,29 @@ android {
}
}
buildFeatures {
- viewBinding true
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion compose_version
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
}
}
dependencies {
-
implementation 'androidx.core:core-ktx:1.7.0'
- implementation 'androidx.appcompat:appcompat:1.4.1'
- implementation 'com.google.android.material:material:1.5.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
- testImplementation 'junit:junit:4.+'
+ implementation "androidx.compose.ui:ui:$compose_version"
+ implementation "androidx.compose.material:material:$compose_version"
+ implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
+ implementation 'androidx.activity:activity-compose:1.3.1'
+ implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2'
+ testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
-}
\ No newline at end of file
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
+ debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
+}
diff --git a/apps/android/app/src/main/AndroidManifest.xml b/apps/android/app/src/main/AndroidManifest.xml
index 7113a1c6ac..b0fdea62da 100644
--- a/apps/android/app/src/main/AndroidManifest.xml
+++ b/apps/android/app/src/main/AndroidManifest.xml
@@ -7,6 +7,7 @@
+ android:exported="true"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.SimpleX">
diff --git a/apps/android/app/src/main/cpp/simplex-api.c b/apps/android/app/src/main/cpp/simplex-api.c
index 7aa1924386..f2dadcb131 100644
--- a/apps/android/app/src/main/cpp/simplex-api.c
+++ b/apps/android/app/src/main/cpp/simplex-api.c
@@ -8,7 +8,7 @@ void setLineBuffering(void);
int pipe_std_to_socket(const char * name);
JNIEXPORT jint JNICALL
-Java_chat_simplex_app_MainActivityKt_pipeStdOutToSocket(JNIEnv *env, __unused jclass clazz, jstring socket_name) {
+Java_chat_simplex_app_SimplexAppKt_pipeStdOutToSocket(JNIEnv *env, __unused jclass clazz, jstring socket_name) {
const char *name = (*env)->GetStringUTFChars(env, socket_name, JNI_FALSE);
int ret = pipe_std_to_socket(name);
(*env)->ReleaseStringUTFChars(env, socket_name, name);
@@ -16,7 +16,7 @@ Java_chat_simplex_app_MainActivityKt_pipeStdOutToSocket(JNIEnv *env, __unused jc
}
JNIEXPORT void JNICALL
-Java_chat_simplex_app_MainActivityKt_initHS(__unused JNIEnv *env, __unused jclass clazz) {
+Java_chat_simplex_app_SimplexAppKt_initHS(__unused JNIEnv *env, __unused jclass clazz) {
hs_init(NULL, NULL);
setLineBuffering();
}
@@ -33,7 +33,7 @@ extern char *chat_send_cmd(controller ctl, const char *cmd);
extern char *chat_recv_msg(controller ctl);
JNIEXPORT jlong JNICALL
-Java_chat_simplex_app_MainActivityKt_chatInit(JNIEnv *env, __unused jclass clazz, jstring datadir) {
+Java_chat_simplex_app_SimplexAppKt_chatInit(JNIEnv *env, __unused jclass clazz, jstring datadir) {
const char *_data = (*env)->GetStringUTFChars(env, datadir, JNI_FALSE);
jlong res = (jlong)chat_init_store(_data);
(*env)->ReleaseStringUTFChars(env, datadir, _data);
@@ -41,12 +41,12 @@ Java_chat_simplex_app_MainActivityKt_chatInit(JNIEnv *env, __unused jclass clazz
}
JNIEXPORT jstring JNICALL
-Java_chat_simplex_app_MainActivityKt_chatGetUser(JNIEnv *env, __unused jclass clazz, jlong controller) {
+Java_chat_simplex_app_SimplexAppKt_chatGetUser(JNIEnv *env, __unused jclass clazz, jlong controller) {
return (*env)->NewStringUTF(env, chat_get_user((void*)controller));
}
JNIEXPORT jstring JNICALL
-Java_chat_simplex_app_MainActivityKt_chatCreateUser(JNIEnv *env, __unused jclass clazz, jlong controller, jstring data) {
+Java_chat_simplex_app_SimplexAppKt_chatCreateUser(JNIEnv *env, __unused jclass clazz, jlong controller, jstring data) {
const char *_data = (*env)->GetStringUTFChars(env, data, JNI_FALSE);
jstring res = (*env)->NewStringUTF(env, chat_create_user((void*)controller, _data));
(*env)->ReleaseStringUTFChars(env, data, _data);
@@ -54,12 +54,12 @@ Java_chat_simplex_app_MainActivityKt_chatCreateUser(JNIEnv *env, __unused jclass
}
JNIEXPORT jlong JNICALL
-Java_chat_simplex_app_MainActivityKt_chatStart(JNIEnv *env, jclass clazz, jlong controller) {
+Java_chat_simplex_app_SimplexAppKt_chatStart(JNIEnv *env, jclass clazz, jlong controller) {
return (jlong)chat_start((void*)controller);
}
JNIEXPORT jstring JNICALL
-Java_chat_simplex_app_MainActivityKt_chatSendCmd(JNIEnv *env, __unused jclass clazz, jlong controller, jstring msg) {
+Java_chat_simplex_app_SimplexAppKt_chatSendCmd(JNIEnv *env, __unused jclass clazz, jlong controller, jstring msg) {
const char *_msg = (*env)->GetStringUTFChars(env, msg, JNI_FALSE);
jstring res = (*env)->NewStringUTF(env, chat_send_cmd((void*)controller, _msg));
(*env)->ReleaseStringUTFChars(env, msg, _msg);
@@ -67,6 +67,6 @@ Java_chat_simplex_app_MainActivityKt_chatSendCmd(JNIEnv *env, __unused jclass cl
}
JNIEXPORT jstring JNICALL
-Java_chat_simplex_app_MainActivityKt_chatRecvMsg(JNIEnv *env, __unused jclass clazz, jlong controller) {
+Java_chat_simplex_app_SimplexAppKt_chatRecvMsg(JNIEnv *env, __unused jclass clazz, jlong controller) {
return (*env)->NewStringUTF(env, chat_recv_msg((void*)controller));
}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt
index 20897023c3..2a62c745fb 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt
@@ -1,123 +1,37 @@
package chat.simplex.app
-import android.net.LocalServerSocket
+import android.app.Application
import android.os.Bundle
-import android.util.Log
-import android.view.inputmethod.EditorInfo
-import android.widget.ScrollView
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.AppCompatEditText
-import java.io.BufferedReader
-import java.io.InputStreamReader
-import java.lang.ref.WeakReference
-import java.util.*
-import java.util.concurrent.Semaphore
-import kotlin.concurrent.thread
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.viewModels
+import androidx.compose.runtime.Composable
+import chat.simplex.app.ui.theme.SimpleXTheme
+import androidx.lifecycle.AndroidViewModel
+import chat.simplex.app.model.*
+import chat.simplex.app.views.TerminalView
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
-// ghc's rts
-external fun initHS()
-// android-support
-external fun pipeStdOutToSocket(socketName: String) : Int
-// simplex-chat
-typealias Store = Long
-typealias Controller = Long
-external fun chatInit(filesDir: String): Store
-external fun chatGetUser(controller: Store) : String
-external fun chatCreateUser(controller: Store, data: String) : String
-external fun chatStart(controller: Store) : Controller
-external fun chatSendCmd(controller: Controller, msg: String) : String
-external fun chatRecvMsg(controller: Controller) : String
-
-class MainActivity : AppCompatActivity() {
+class MainActivity: ComponentActivity() {
+ private val viewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
- weakActivity = WeakReference(this)
-
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
-
- val store : Store = chatInit(this.applicationContext.filesDir.toString())
- // create user if needed
- if(chatGetUser(store) == "{}") {
- chatCreateUser(store, """
- {"displayName": "test", "fullName": "android test"}
- """.trimIndent())
- }
- Log.d("SIMPLEX (user)", chatGetUser(store))
-
- val controller = chatStart(store)
-
- val cmdinput = this.findViewById(R.id.cmdInput)
-
- cmdinput.setOnEditorActionListener { _, actionId, _ ->
- when (actionId) {
- EditorInfo.IME_ACTION_SEND -> {
- Log.d("SIMPLEX SEND", chatSendCmd(controller, cmdinput.text.toString()))
- cmdinput.text?.clear()
- true
- }
- else -> false
+ setContent {
+ SimpleXTheme {
+ MainPage(viewModel)
}
}
-
- thread(name="receiver") {
- val chatlog = FifoQueue(500)
- while(true) {
- val msg = chatRecvMsg(controller)
- Log.d("SIMPLEX RECV", msg)
- chatlog.add(msg)
- val currentText = chatlog.joinToString("\n")
- weakActivity.get()?.runOnUiThread {
- val log = weakActivity.get()?.findViewById(R.id.chatlog)
- val scroll = weakActivity.get()?.findViewById(R.id.scroller)
- log?.text = currentText
- scroll?.scrollTo(0, scroll.getChildAt(0).height)
- }
- }
- }
- }
-
- companion object {
- lateinit var weakActivity : WeakReference
- init {
- val socketName = "local.socket.address.listen.native.cmd2"
-
- val s = Semaphore(0)
- thread(name="stdout/stderr pipe") {
- Log.d("SIMPLEX", "starting server")
- val server = LocalServerSocket(socketName)
- Log.d("SIMPLEX", "started server")
- s.release()
- val receiver = server.accept()
- Log.d("SIMPLEX", "started receiver")
- val logbuffer = FifoQueue(500)
- if (receiver != null) {
- val inStream = receiver.inputStream
- val inStreamReader = InputStreamReader(inStream)
- val input = BufferedReader(inStreamReader)
-
- while(true) {
- val line = input.readLine() ?: break
- Log.d("SIMPLEX (stdout/stderr)", line)
- logbuffer.add(line)
- }
- }
- }
-
- System.loadLibrary("app-lib")
-
- s.acquire()
- pipeStdOutToSocket(socketName)
-
- initHS()
- }
}
}
-class FifoQueue(private var capacity: Int) : LinkedList() {
- override fun add(element: E): Boolean {
- if(size > capacity) removeFirst()
- return super.add(element)
- }
+class SimplexViewModel(application: Application) : AndroidViewModel(application) {
+ val chatModel = getApplication().chatModel
+}
+
+@Composable
+fun MainPage(vm: SimplexViewModel) {
+ TerminalView(vm.chatModel)
}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt b/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt
new file mode 100644
index 0000000000..b08096a37e
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt
@@ -0,0 +1,95 @@
+package chat.simplex.app
+
+import android.app.Application
+import android.net.LocalServerSocket
+import android.util.Log
+import chat.simplex.app.model.ChatController
+import chat.simplex.app.model.ChatModel
+import java.io.BufferedReader
+import java.io.InputStreamReader
+import java.lang.ref.WeakReference
+import java.util.*
+import java.util.concurrent.Semaphore
+import kotlin.concurrent.thread
+
+// ghc's rts
+external fun initHS()
+// android-support
+external fun pipeStdOutToSocket(socketName: String) : Int
+
+// SimpleX API
+typealias Controller = Long
+typealias Store = Long
+external fun chatInit(filesDir: String): Store
+external fun chatGetUser(controller: Store) : String
+external fun chatCreateUser(controller: Store, data: String) : String
+external fun chatStart(controller: Store) : Controller
+external fun chatSendCmd(controller: Controller, msg: String) : String
+external fun chatRecvMsg(controller: Controller) : String
+
+class SimplexApp: Application() {
+ private lateinit var controller: ChatController
+
+ override fun onCreate() {
+ super.onCreate()
+ val store: Store = chatInit(applicationContext.filesDir.toString())
+ // create user if needed
+ if (chatGetUser(store) == "{}") {
+ chatCreateUser(store, """
+ {"displayName": "test", "fullName": "android test"}
+ """.trimIndent())
+ }
+ Log.d("SIMPLEX (user)", chatGetUser(store))
+ controller = ChatController(chatStart(store))
+ }
+
+ val chatModel by lazy {
+ val m = ChatModel(controller)
+ controller.setModel(m)
+ controller.startReceiver()
+ m
+ }
+
+ companion object {
+ lateinit var weakActivity: WeakReference
+ init {
+ val socketName = "local.socket.address.listen.native.cmd2"
+
+ val s = Semaphore(0)
+ thread(name="stdout/stderr pipe") {
+ Log.d("SIMPLEX", "starting server")
+ val server = LocalServerSocket(socketName)
+ Log.d("SIMPLEX", "started server")
+ s.release()
+ val receiver = server.accept()
+ Log.d("SIMPLEX", "started receiver")
+ val logbuffer = FifoQueue(500)
+ if (receiver != null) {
+ val inStream = receiver.inputStream
+ val inStreamReader = InputStreamReader(inStream)
+ val input = BufferedReader(inStreamReader)
+
+ while(true) {
+ val line = input.readLine() ?: break
+ Log.d("SIMPLEX (stdout/stderr)", line)
+ logbuffer.add(line)
+ }
+ }
+ }
+
+ System.loadLibrary("app-lib")
+
+ s.acquire()
+ pipeStdOutToSocket(socketName)
+
+ initHS()
+ }
+ }
+}
+
+class FifoQueue(private var capacity: Int) : LinkedList() {
+ override fun add(element: E): Boolean {
+ if(size > capacity) removeFirst()
+ return super.add(element)
+ }
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt
new file mode 100644
index 0000000000..0ecab183ad
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt
@@ -0,0 +1,105 @@
+package chat.simplex.app.model
+
+import androidx.compose.runtime.mutableStateListOf
+import kotlinx.serialization.Serializable
+import java.util.*
+
+class ChatModel(val controller: ChatController) {
+ val currentUser: User? = null
+ var terminalItems = mutableStateListOf()
+
+ companion object {
+ val sampleData: ChatModel get() {
+ val m = ChatModel(ChatController.Mock())
+ m.terminalItems = mutableStateListOf(
+ TerminalItem.Cmd(CC.ShowActiveUser()),
+ TerminalItem.Resp(CR.ActiveUser(User.sampleData))
+ )
+ return m
+ }
+ }
+}
+
+enum class ChatType(val type: String) {
+ Direct("@"),
+ Group("#"),
+ ContactRequest("<@")
+}
+
+@Serializable
+class User (
+ val userId: Int,
+ val userContactId: Int,
+ val localDisplayName: String,
+ val profile: Profile,
+ val activeUser: Boolean
+) : NamedChat {
+ override val displayName: String get() = profile.displayName
+ override val fullName: String get() = profile.fullName
+
+ companion object {
+ val sampleData = User(
+ userId = 1,
+ userContactId = 1,
+ localDisplayName = "alice",
+ profile = Profile.sampleData,
+ activeUser = true
+ )
+ }
+}
+
+typealias ChatId = String
+
+@Serializable
+class Contact(
+ val contactId: Int,
+ val localDisplayName: String,
+ val profile: Profile,
+ val activeConn: Connection,
+ val viaGroup: Int? = null,
+// no serializer for type Date?
+// val createdAt: Date
+): NamedChat {
+ val id: ChatId get() = "@$contactId"
+ val apiId: Int get() = contactId
+ val ready: Boolean get() = activeConn.connStatus == "ready" || activeConn.connStatus == "snd-ready"
+ override val displayName: String get() = profile.displayName
+ override val fullName: String get() = profile.fullName
+
+ companion object {
+ val sampleData = Contact(
+ contactId = 1,
+ localDisplayName = "alice",
+ profile = Profile.sampleData,
+ activeConn = Connection.sampleData
+// createdAt = Date()
+ )
+ }
+}
+
+@Serializable
+class Connection(val connStatus: String) {
+ companion object {
+ val sampleData = Connection(connStatus = "ready")
+ }
+}
+
+@Serializable
+class Profile(
+ val displayName: String,
+ val fullName: String
+ ) {
+ companion object {
+ val sampleData = Profile(
+ displayName = "alice",
+ fullName = "Alice"
+ )
+ }
+}
+
+interface NamedChat {
+ abstract val displayName: String
+ abstract val fullName: String
+ val chatViewName: String
+ get() = displayName + (if (fullName == "" || fullName == displayName) "" else " / $fullName")
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SImpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SImpleXAPI.kt
new file mode 100644
index 0000000000..c6bc16871a
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/model/SImpleXAPI.kt
@@ -0,0 +1,161 @@
+package chat.simplex.app.model
+
+import android.util.Log
+import chat.simplex.app.chatRecvMsg
+import chat.simplex.app.chatSendCmd
+import kotlinx.serialization.*
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.jsonObject
+import kotlinx.serialization.json.jsonPrimitive
+import java.lang.Exception
+import java.util.*
+import kotlin.concurrent.thread
+
+typealias Controller = Long
+
+open class ChatController(val ctrl: Controller) {
+ private lateinit var chatModel: ChatModel
+
+ fun setModel(m: ChatModel) {
+ chatModel = m
+ }
+
+ fun startReceiver() {
+ thread(name="receiver") {
+// val chatlog = FifoQueue(500)
+ while(true) {
+ val json = chatRecvMsg(ctrl)
+ Log.d("SIMPLEX chatRecvMsg: ", json)
+ chatModel.terminalItems.add(TerminalItem.Resp(APIResponse.decodeStr(json)))
+ }
+ }
+ }
+
+ fun sendCmd(cmd: String) {
+ val json = chatSendCmd(ctrl, cmd)
+ Log.d("SIMPLEX chatSendCmd: ", cmd)
+ Log.d("SIMPLEX chatSendCmd response: ", json)
+ chatModel.terminalItems.add(TerminalItem.Resp(APIResponse.decodeStr(json)))
+ }
+
+ class Mock: ChatController(0) {}
+}
+
+// ChatCommand
+abstract class CC {
+ abstract val cmdString: String
+ abstract val cmdType: String
+
+ class Console(val cmd: String): CC() {
+ override val cmdString get() = cmd
+ override val cmdType get() = "console command"
+ }
+
+ class ShowActiveUser: CC() {
+ override val cmdString get() = "/u"
+ override val cmdType get() = "ShowActiveUser"
+ }
+
+ class CreateActiveUser(val profile: Profile): CC() {
+ override val cmdString get() = "/u ${profile.displayName} ${profile.fullName}"
+ override val cmdType get() = "CreateActiveUser"
+ }
+
+ class StartChat: CC() {
+ override val cmdString get() = "/_start"
+ override val cmdType get() = "StartChat"
+ }
+
+ class ApiGetChats: CC() {
+ override val cmdString get() = "/_get chats"
+ override val cmdType get() = "ApiGetChats"
+ }
+
+ companion object {
+ fun chatRef(type: ChatType, id: String) = "${type}${id}"
+ }
+}
+
+val json = Json {
+ prettyPrint = true
+ ignoreUnknownKeys = true
+}
+
+@Serializable
+class APIResponse(val resp: CR) {
+ companion object {
+ fun decodeStr(str: String): CR {
+ try {
+ return json.decodeFromString(str).resp
+ } catch(e: Exception) {
+ try {
+ val data = json.parseToJsonElement(str)
+ return CR.Response(data.jsonObject["resp"]!!.jsonObject["type"]?.toString() ?: "invalid", json.encodeToString(data))
+ } catch(e: Exception) {
+ return CR.Invalid(str)
+ }
+ }
+ }
+ }
+}
+
+// ChatResponse
+@Serializable
+sealed class CR {
+ abstract val responseType: String
+ abstract val details: String
+
+ @Serializable
+ @SerialName("activeUser")
+ class ActiveUser(val user: User): CR() {
+ override val responseType get() = "activeUser"
+ override val details get() = user.toString()
+ }
+
+ @Serializable
+ @SerialName("contactSubscribed")
+ class ContactSubscribed(val contact: Contact): CR() {
+ override val responseType get() = "contactSubscribed"
+ override val details get() = contact.toString()
+ }
+
+ @Serializable
+ class Response(val type: String, val json: String): CR() {
+ override val responseType get() = "* ${type}"
+ override val details get() = json
+ }
+
+ @Serializable
+ class Invalid(val str: String): CR() {
+ override val responseType get() = "* invalid json"
+ override val details get() = str
+ }
+
+ // {"resp": {"activeUser": {"user": {}}}}
+ // {"resp": {"anythingElse": }} -> Unknown(type = "anythingElse", json = "")
+
+ // {"type": "activeUser", "user": }
+}
+
+abstract class TerminalItem {
+ val date = Date()
+ abstract val label: String
+ abstract val details: String
+
+ class Cmd(val cmd: CC): TerminalItem() {
+ override val label get() = "> ${cmd.cmdString.substring(0, 30)}"
+ override val details get() = cmd.cmdString
+ }
+
+ class Resp(val resp: CR): TerminalItem() {
+ override val label get() = "< ${resp.responseType}"
+ override val details get() = resp.details
+ }
+
+ companion object {
+ val sampleData = listOf(
+ TerminalItem.Cmd(CC.ShowActiveUser()),
+ TerminalItem.Resp(CR.ActiveUser(User.sampleData))
+ )
+ }
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt
new file mode 100644
index 0000000000..40f91fc96e
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Color.kt
@@ -0,0 +1,8 @@
+package chat.simplex.app.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple200 = Color(0xFFBB86FC)
+val Purple500 = Color(0xFF6200EE)
+val Purple700 = Color(0xFF3700B3)
+val Teal200 = Color(0xFF03DAC5)
\ No newline at end of file
diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Shape.kt b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Shape.kt
new file mode 100644
index 0000000000..d0a00450f6
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Shape.kt
@@ -0,0 +1,11 @@
+package chat.simplex.app.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(4.dp),
+ large = RoundedCornerShape(0.dp)
+)
\ No newline at end of file
diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt
new file mode 100644
index 0000000000..3d9d30c142
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt
@@ -0,0 +1,44 @@
+package chat.simplex.app.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+ primary = Purple200,
+ primaryVariant = Purple700,
+ secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+ primary = Purple500,
+ primaryVariant = Purple700,
+ secondary = Teal200
+
+ /* Other default colors to override
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black,
+ */
+)
+
+@Composable
+fun SimpleXTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Type.kt b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Type.kt
new file mode 100644
index 0000000000..bb89f6e849
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Type.kt
@@ -0,0 +1,28 @@
+package chat.simplex.app.ui.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp
+ )
+ /* Other default text styles to override
+ button = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.W500,
+ fontSize = 14.sp
+ ),
+ caption = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt
new file mode 100644
index 0000000000..c85f9124e1
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt
@@ -0,0 +1,38 @@
+package chat.simplex.app.views
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import chat.simplex.app.model.ChatModel
+import chat.simplex.app.model.TerminalItem
+import chat.simplex.app.ui.theme.SimpleXTheme
+import chat.simplex.app.views.chat.SendMsgView
+
+
+@Composable
+fun TerminalView(chatModel: ChatModel) {
+ Column {
+ TerminalLog(chatModel.terminalItems)
+ SendMsgView(chatModel.controller::sendCmd)
+ }
+}
+
+@Composable
+fun TerminalLog(terminalItems: List) {
+ LazyColumn {
+ items(terminalItems) { item ->
+ Text(item.label)
+ }
+ }
+}
+
+@Preview
+@Composable
+fun PreviewSendMsgView() {
+ SimpleXTheme {
+ TerminalView(chatModel = ChatModel.sampleData)
+ }
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt
new file mode 100644
index 0000000000..0573b30e7d
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt
@@ -0,0 +1,40 @@
+package chat.simplex.app.views.chat
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.Button
+import androidx.compose.material.Text
+import androidx.compose.material.TextField
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import chat.simplex.app.ui.theme.SimpleXTheme
+
+@Composable
+fun SendMsgView (sendMessage: (String) -> Unit) {
+ var cmd by remember { mutableStateOf("") }
+ Row {
+ TextField(value = cmd, onValueChange = { cmd = it }, modifier = Modifier.height(60.dp))
+ Spacer(Modifier.height(10.dp))
+ Button(
+ onClick = {
+ sendMessage(cmd)
+ cmd = ""
+ },
+ modifier = Modifier.width(40.dp).height(60.dp),
+ enabled = cmd.isNotEmpty()
+ ) {
+ Text("Go")
+ }
+ }
+}
+
+@Preview
+@Composable
+fun PreviewSendMsgView() {
+ SimpleXTheme {
+ SendMsgView(
+ sendMessage = { msg -> println(msg) }
+ )
+ }
+}
diff --git a/apps/android/app/src/main/res/layout/activity_main.xml b/apps/android/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 87429b38a0..0000000000
--- a/apps/android/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/android/app/src/main/res/values-night/themes.xml b/apps/android/app/src/main/res/values-night/themes.xml
deleted file mode 100644
index 639e5393c7..0000000000
--- a/apps/android/app/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/apps/android/app/src/main/res/values/themes.xml b/apps/android/app/src/main/res/values/themes.xml
index 781d40bcf4..ced725b2b2 100644
--- a/apps/android/app/src/main/res/values/themes.xml
+++ b/apps/android/app/src/main/res/values/themes.xml
@@ -1,16 +1,7 @@
-
-
-
\ No newline at end of file
diff --git a/apps/android/build.gradle b/apps/android/build.gradle
index 9988a75fc6..6cb503be83 100644
--- a/apps/android/build.gradle
+++ b/apps/android/build.gradle
@@ -1,16 +1,25 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext {
+ compose_version = '1.1.0'
+ }
repositories {
google()
mavenCentral()
}
dependencies {
- classpath "com.android.tools.build:gradle:7.0.4"
+ classpath 'com.android.tools.build:gradle:7.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
+ classpath "org.jetbrains.kotlin:kotlin-serialization:1.3.2"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
+}// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '7.1.1' apply false
+ id 'com.android.library' version '7.1.1' apply false
+ id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
+ id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.10'
}
task clean(type: Delete) {
diff --git a/apps/android/gradle.properties b/apps/android/gradle.properties
index 98bed167dc..831abc7d9f 100644
--- a/apps/android/gradle.properties
+++ b/apps/android/gradle.properties
@@ -15,7 +15,11 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
-# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
-kotlin.code.style=official
\ No newline at end of file
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
\ No newline at end of file
diff --git a/apps/android/gradle/wrapper/gradle-wrapper.jar b/apps/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..e708b1c023
Binary files /dev/null and b/apps/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/apps/android/gradle/wrapper/gradle-wrapper.properties b/apps/android/gradle/wrapper/gradle-wrapper.properties
index 05bd558a5e..2da1e43ef5 100644
--- a/apps/android/gradle/wrapper/gradle-wrapper.properties
+++ b/apps/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri Jan 21 23:13:54 GMT 2022
+#Mon Feb 14 14:23:51 GMT 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/apps/android/settings.gradle b/apps/android/settings.gradle
index 71e4f1f472..64c6620acf 100644
--- a/apps/android/settings.gradle
+++ b/apps/android/settings.gradle
@@ -1,9 +1,15 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ mavenCentral()
+ }
+}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
- jcenter() // Warning: this repository is going to shut down soon
}
}
rootProject.name = "SimpleX"