commit
e8e539c425
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.LabeledEditText
|
||||
android:id="@+id/urlEditText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
app:labeledEditText_background="@color/loki_darkest_gray"
|
||||
app:labeledEditText_label="@string/fragment_add_public_chat_url_edit_text_label"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/explanationTextView"
|
||||
style="@style/Signal.Text.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/fragment_add_public_chat_explanation" />
|
||||
|
||||
<com.dd.CircularProgressButton
|
||||
android:id="@+id/addButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:background="@color/signal_primary"
|
||||
android:textColor="@color/white"
|
||||
app:cpb_colorIndicator="@color/white"
|
||||
app:cpb_colorProgress="@color/textsecure_primary"
|
||||
app:cpb_cornerRadius="4dp"
|
||||
app:cpb_selectorIdle="@drawable/progress_button_state"
|
||||
app:cpb_textIdle="@string/fragment_add_public_chat_add_button_title_1" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
@ -0,0 +1,74 @@
|
||||
package org.thoughtcrime.securesms.loki
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Patterns
|
||||
import android.view.MenuItem
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import kotlinx.android.synthetic.main.activity_add_public_chat.*
|
||||
import network.loki.messenger.R
|
||||
import nl.komponents.kovenant.ui.failUi
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
|
||||
class AddPublicChatActivity : PassphraseRequiredActionBarActivity() {
|
||||
private val dynamicTheme = DynamicTheme()
|
||||
|
||||
override fun onPreCreate() {
|
||||
dynamicTheme.onCreate(this)
|
||||
}
|
||||
|
||||
override fun onCreate(bundle: Bundle?, isReady: Boolean) {
|
||||
supportActionBar!!.setTitle(R.string.fragment_add_public_chat_title)
|
||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
setContentView(R.layout.activity_add_public_chat)
|
||||
updateUI(false)
|
||||
addButton.setOnClickListener { addPublicChatIfPossible() }
|
||||
}
|
||||
|
||||
public override fun onResume() {
|
||||
super.onResume()
|
||||
dynamicTheme.onResume(this)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun addPublicChatIfPossible() {
|
||||
val inputMethodManager = getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
inputMethodManager.hideSoftInputFromWindow(urlEditText.windowToken, 0)
|
||||
val url = urlEditText.text.toString().toLowerCase().replace("http://", "https://")
|
||||
if (!Patterns.WEB_URL.matcher(url).matches() || !url.startsWith("https://")) {
|
||||
return Toast.makeText(this, R.string.fragment_add_public_chat_invalid_url_message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
updateUI(true)
|
||||
val application = ApplicationContext.getInstance(this)
|
||||
val channel: Long = 1
|
||||
val displayName = TextSecurePreferences.getProfileName(this)
|
||||
val lokiPublicChatAPI = application.lokiPublicChatAPI!!
|
||||
application.lokiPublicChatManager.addChat(url, channel).successUi {
|
||||
lokiPublicChatAPI.getMessages(channel, url)
|
||||
lokiPublicChatAPI.setDisplayName(displayName, url)
|
||||
finish()
|
||||
}.failUi {
|
||||
updateUI(false)
|
||||
Toast.makeText(this, R.string.fragment_add_public_chat_connection_failed_message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUI(isConnecting: Boolean) {
|
||||
addButton.isEnabled = !isConnecting
|
||||
val text = if (isConnecting) R.string.fragment_add_public_chat_add_button_title_2 else R.string.fragment_add_public_chat_add_button_title_1
|
||||
addButton.setText(text)
|
||||
urlEditText.isEnabled = !isConnecting
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package org.thoughtcrime.securesms.loki
|
||||
|
||||
import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.text.TextUtils
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.functional.bind
|
||||
import nl.komponents.kovenant.functional.map
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.database.DatabaseContentProviders
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.groups.GroupManager
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.loki.api.LokiPublicChat
|
||||
import java.util.*
|
||||
|
||||
class LokiPublicChatManager(private val context: Context) {
|
||||
private var chats = mutableMapOf<Long, LokiPublicChat>()
|
||||
private val pollers = mutableMapOf<Long, LokiPublicChatPoller>()
|
||||
private val observers = mutableMapOf<Long, ContentObserver>()
|
||||
private var isPolling = false
|
||||
|
||||
public fun startPollersIfNeeded() {
|
||||
refreshChatsAndPollers()
|
||||
|
||||
for ((threadId, chat) in chats) {
|
||||
val poller = pollers[threadId] ?: LokiPublicChatPoller(context, chat)
|
||||
poller.startIfNeeded()
|
||||
listenToThreadDeletion(threadId)
|
||||
if (!pollers.containsKey(threadId)) { pollers[threadId] = poller }
|
||||
}
|
||||
isPolling = true
|
||||
}
|
||||
|
||||
public fun stopPollers() {
|
||||
pollers.values.forEach { it.stop() }
|
||||
isPolling = false
|
||||
}
|
||||
|
||||
public fun addChat(server: String, channel: Long): Promise<LokiPublicChat, Exception> {
|
||||
val groupChatAPI = ApplicationContext.getInstance(context).lokiPublicChatAPI ?: return Promise.ofFail(IllegalStateException("LokiPublicChatAPI is not set!"))
|
||||
return groupChatAPI.getAuthToken(server).bind {
|
||||
groupChatAPI.getChannelInfo(channel, server)
|
||||
}.map {
|
||||
addChat(server, channel, it)
|
||||
}
|
||||
}
|
||||
|
||||
public fun addChat(server: String, channel: Long, name: String): LokiPublicChat {
|
||||
val chat = LokiPublicChat(channel, server, name, true)
|
||||
var threadID = GroupManager.getThreadId(chat.id, context)
|
||||
// Create the group if we don't have one
|
||||
if (threadID < 0) {
|
||||
val result = GroupManager.createGroup(chat.id, context, HashSet(), null, chat.displayName, false)
|
||||
threadID = result.threadId
|
||||
}
|
||||
DatabaseFactory.getLokiThreadDatabase(context).setPublicChat(chat, threadID)
|
||||
// Set our name on the server
|
||||
val displayName = TextSecurePreferences.getProfileName(context)
|
||||
if (!TextUtils.isEmpty(displayName)) {
|
||||
ApplicationContext.getInstance(context).lokiPublicChatAPI?.setDisplayName(displayName, server)
|
||||
}
|
||||
// Start polling
|
||||
Util.runOnMain{ startPollersIfNeeded() }
|
||||
|
||||
return chat
|
||||
}
|
||||
|
||||
private fun refreshChatsAndPollers() {
|
||||
val chatsInDB = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats()
|
||||
val removedChatThreadIds = chats.keys.filter { !chatsInDB.keys.contains(it) }
|
||||
removedChatThreadIds.forEach { pollers.remove(it)?.stop() }
|
||||
|
||||
// Only append to chats if we have a thread for the chat
|
||||
chats = chatsInDB.filter { GroupManager.getThreadId(it.value.id, context) > -1 }.toMutableMap()
|
||||
}
|
||||
|
||||
private fun listenToThreadDeletion(threadID: Long) {
|
||||
if (threadID < 0 || observers[threadID] != null) { return }
|
||||
val observer = createDeletionObserver(threadID, Runnable {
|
||||
val chat = chats[threadID]
|
||||
|
||||
// Reset last message cache
|
||||
if (chat != null) {
|
||||
val apiDatabase = DatabaseFactory.getLokiAPIDatabase(context)
|
||||
apiDatabase.removeLastDeletionServerID(chat.channel, chat.server)
|
||||
apiDatabase.removeLastMessageServerID(chat.channel, chat.server)
|
||||
}
|
||||
|
||||
DatabaseFactory.getLokiThreadDatabase(context).removePublicChat(threadID)
|
||||
pollers.remove(threadID)?.stop()
|
||||
observers.remove(threadID)
|
||||
startPollersIfNeeded()
|
||||
})
|
||||
observers[threadID] = observer
|
||||
|
||||
context.applicationContext.contentResolver.registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(threadID), true, observer)
|
||||
}
|
||||
|
||||
private fun createDeletionObserver(threadID: Long, onDelete: Runnable): ContentObserver {
|
||||
return object : ContentObserver(null) {
|
||||
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
super.onChange(selfChange)
|
||||
// Stop the poller if thread is deleted
|
||||
try {
|
||||
if (!DatabaseFactory.getThreadDatabase(context).hasThread(threadID)) {
|
||||
onDelete.run()
|
||||
context.applicationContext.contentResolver.unregisterContentObserver(this)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// TODO: Handle
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue