Thread deletion cleanup.

This commit is contained in:
Anton Chekulaev 2020-11-23 16:59:44 +11:00
parent 4307140e5c
commit 0ebb382edd
3 changed files with 50 additions and 60 deletions

View File

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.groups;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -23,7 +24,6 @@ import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;

View File

@ -9,7 +9,6 @@ import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.os.AsyncTask import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.text.Spannable import android.text.Spannable
import android.text.SpannableString import android.text.SpannableString
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
@ -18,11 +17,15 @@ import android.view.View
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.loader.app.LoaderManager import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader import androidx.loader.content.Loader
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_home.* import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
@ -71,24 +74,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady) super.onCreate(savedInstanceState, isReady)
// Process any outstanding deletes
val threadDatabase = DatabaseFactory.getThreadDatabase(this)
val archivedConversationCount = threadDatabase.archivedConversationListCount
if (archivedConversationCount > 0) {
val archivedConversations = threadDatabase.archivedConversationList
archivedConversations.moveToFirst()
fun deleteThreadAtCurrentPosition() {
val threadID = archivedConversations.getLong(archivedConversations.getColumnIndex(ThreadDatabase.ID))
AsyncTask.execute {
threadDatabase.deleteConversation(threadID)
(applicationContext as ApplicationContext).messageNotifier.updateNotification(this)
}
}
deleteThreadAtCurrentPosition()
while (archivedConversations.moveToNext()) {
deleteThreadAtCurrentPosition()
}
}
// Double check that the long poller is up // Double check that the long poller is up
(applicationContext as ApplicationContext).startPollingIfNeeded() (applicationContext as ApplicationContext).startPollingIfNeeded()
// Set content view // Set content view
@ -341,58 +326,56 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
val threadID = thread.threadId val threadID = thread.threadId
val recipient = thread.recipient val recipient = thread.recipient
val threadDB = DatabaseFactory.getThreadDatabase(this) val threadDB = DatabaseFactory.getThreadDatabase(this)
val deleteThread = Runnable {
//TODO Move open group related logic to OpenGroupUtilities / PublicChatManager / GroupManager
AsyncTask.execute {
val publicChat = DatabaseFactory.getLokiThreadDatabase(this@HomeActivity).getPublicChat(threadID)
if (publicChat != null) {
val apiDB = DatabaseFactory.getLokiAPIDatabase(this@HomeActivity)
apiDB.removeLastMessageServerID(publicChat.channel, publicChat.server)
apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server)
apiDB.clearOpenGroupProfilePictureURL(publicChat.channel, publicChat.server)
ApplicationContext.getInstance(this@HomeActivity).publicChatAPI!!.leave(publicChat.channel, publicChat.server)
//FIXME Group deletion should be synchronized with the related thread deletion.
val groupId = threadDB.getRecipientForThreadId(threadID)!!.address.serialize()
GroupManager.deleteGroup(groupId, this@HomeActivity)
}
threadDB.deleteConversation(threadID)
ApplicationContext.getInstance(this@HomeActivity).messageNotifier.updateNotification(this@HomeActivity)
}
}
val dialogMessage = if (recipient.isGroupRecipient) R.string.activity_home_leave_group_dialog_message else R.string.activity_home_delete_conversation_dialog_message val dialogMessage = if (recipient.isGroupRecipient) R.string.activity_home_leave_group_dialog_message else R.string.activity_home_delete_conversation_dialog_message
val dialog = AlertDialog.Builder(this) val dialog = AlertDialog.Builder(this)
dialog.setMessage(dialogMessage) dialog.setMessage(dialogMessage)
dialog.setPositiveButton(R.string.yes) { _, _ -> dialog.setPositiveButton(R.string.yes) { _, _ -> lifecycleScope.launch(Dispatchers.Main) {
val context = this@HomeActivity as Context
val isClosedGroup = recipient.address.isClosedGroup val isClosedGroup = recipient.address.isClosedGroup
// Send a leave group message if this is an active closed group // Send a leave group message if this is an active closed group
if (isClosedGroup && DatabaseFactory.getGroupDatabase(this).isActive(recipient.address.toGroupString())) { if (isClosedGroup && DatabaseFactory.getGroupDatabase(context).isActive(recipient.address.toGroupString())) {
var isSSKBasedClosedGroup: Boolean var isSSKBasedClosedGroup: Boolean
var groupPublicKey: String? var groupPublicKey: String?
try { try {
groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(recipient.address.toString()).toHexString() groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(recipient.address.toString()).toHexString()
isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey) isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)
} catch (e: IOException) { } catch (e: IOException) {
groupPublicKey = null groupPublicKey = null
isSSKBasedClosedGroup = false isSSKBasedClosedGroup = false
} }
if (isSSKBasedClosedGroup) { if (isSSKBasedClosedGroup) {
ClosedGroupsProtocol.leave(this, groupPublicKey!!) ClosedGroupsProtocol.leave(context, groupPublicKey!!)
} else if (!ClosedGroupsProtocol.leaveLegacyGroup(this, recipient)) { } else if (!ClosedGroupsProtocol.leaveLegacyGroup(context, recipient)) {
Toast.makeText(this, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show() Toast.makeText(context, R.string.activity_home_leaving_group_failed_message, Toast.LENGTH_LONG).show()
return@setPositiveButton return@launch
} }
} }
// Archive the conversation and then delete it after 10 seconds (the case where the
// app was closed before the conversation could be deleted is handled in onCreate) withContext(Dispatchers.IO) {
threadDB.archiveConversation(threadID) val publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
val delay = if (isClosedGroup) 10000L else 1000L //TODO Move open group related logic to OpenGroupUtilities / PublicChatManager / GroupManager
val handler = Handler() if (publicChat != null) {
handler.postDelayed(deleteThread, delay) val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
apiDB.removeLastMessageServerID(publicChat.channel, publicChat.server)
apiDB.removeLastDeletionServerID(publicChat.channel, publicChat.server)
apiDB.clearOpenGroupProfilePictureURL(publicChat.channel, publicChat.server)
ApplicationContext.getInstance(context).publicChatAPI!!
.leave(publicChat.channel, publicChat.server)
ApplicationContext.getInstance(context).publicChatManager
.removeChat(publicChat.server, publicChat.channel)
} else {
threadDB.deleteConversation(threadID)
}
ApplicationContext.getInstance(context).messageNotifier.updateNotification(context)
}
// Notify the user // Notify the user
val toastMessage = if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message val toastMessage = if (recipient.isGroupRecipient) R.string.MessageRecord_left_group else R.string.activity_home_conversation_deleted_message
Toast.makeText(this, toastMessage, Toast.LENGTH_LONG).show() Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show()
} }}
dialog.setNegativeButton(R.string.no) { _, _ -> dialog.setNegativeButton(R.string.no) { _, _ ->
// Do nothing // Do nothing
} }

View File

@ -5,9 +5,6 @@ import android.database.ContentObserver
import android.graphics.Bitmap import android.graphics.Bitmap
import android.text.TextUtils import android.text.TextUtils
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
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.ApplicationContext
import org.thoughtcrime.securesms.database.DatabaseContentProviders import org.thoughtcrime.securesms.database.DatabaseContentProviders
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
@ -101,6 +98,16 @@ class PublicChatManager(private val context: Context) {
return chat return chat
} }
public fun removeChat(server: String, channel: Long) {
val threadDB = DatabaseFactory.getThreadDatabase(context)
val groupId = PublicChat.getId(channel, server)
val threadId = GroupManager.getOpenGroupThreadID(groupId, context)
val groupAddress = threadDB.getRecipientForThreadId(threadId)!!.address.serialize()
GroupManager.deleteGroup(groupAddress, context)
Util.runOnMain { startPollersIfNeeded() }
}
private fun refreshChatsAndPollers() { private fun refreshChatsAndPollers() {
val chatsInDB = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats() val chatsInDB = DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats()
val removedChatThreadIds = chats.keys.filter { !chatsInDB.keys.contains(it) } val removedChatThreadIds = chats.keys.filter { !chatsInDB.keys.contains(it) }