mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
feat: add snode method delete_all with data class for params, refactoring ClearAllDataDialog.kt to handle async requests better and prevent ANR
This commit is contained in:
parent
ac4b576abe
commit
11f64a1d1a
@ -4,38 +4,77 @@ import android.app.Dialog
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import kotlinx.android.synthetic.main.dialog_clear_all_data.*
|
||||||
import kotlinx.android.synthetic.main.dialog_clear_all_data.view.*
|
import kotlinx.android.synthetic.main.dialog_clear_all_data.view.*
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
|
import org.session.libsession.snode.SnodeAPI
|
||||||
|
import org.session.libsession.snode.SnodeDeleteMessage
|
||||||
import org.session.libsession.utilities.KeyPairUtilities
|
import org.session.libsession.utilities.KeyPairUtilities
|
||||||
|
|
||||||
class ClearAllDataDialog : DialogFragment() {
|
class ClearAllDataDialog : DialogFragment() {
|
||||||
|
|
||||||
|
var clearJob: Job? = null
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val builder = AlertDialog.Builder(requireContext())
|
val builder = AlertDialog.Builder(requireContext())
|
||||||
val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_clear_all_data, null)
|
val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_clear_all_data, null)
|
||||||
contentView.cancelButton.setOnClickListener { dismiss() }
|
contentView.cancelButton.setOnClickListener { dismiss() }
|
||||||
contentView.clearAllDataButton.setOnClickListener { clearAllData() }
|
contentView.clearAllDataButton.setOnClickListener { clearAllData() }
|
||||||
builder.setView(contentView)
|
builder.setView(contentView)
|
||||||
|
builder.setCancelable(false)
|
||||||
val result = builder.create()
|
val result = builder.create()
|
||||||
result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateUI() {
|
||||||
|
if (clearJob?.isActive == true) {
|
||||||
|
// clear background job is running, prevent interaction
|
||||||
|
dialog?.let { view ->
|
||||||
|
view.cancelButton.isVisible = false
|
||||||
|
view.clearAllDataButton.isVisible = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dialog?.let { view ->
|
||||||
|
view.cancelButton.isVisible = false
|
||||||
|
view.clearAllDataButton.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun clearAllData() {
|
private fun clearAllData() {
|
||||||
if (KeyPairUtilities.hasV2KeyPair(requireContext())) {
|
if (KeyPairUtilities.hasV2KeyPair(requireContext())) {
|
||||||
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(requireContext())
|
clearJob = lifecycleScope.launch {
|
||||||
ApplicationContext.getInstance(context).clearAllData(false)
|
delay(5_000)
|
||||||
|
// finish
|
||||||
|
val userPublicKey = MessagingModuleConfiguration.shared.storage.getUserPublicKey()
|
||||||
|
|
||||||
|
|
||||||
|
val deleteMessage = SnodeDeleteMessage(userKey, System.currentTimeMillis(), )
|
||||||
|
SnodeAPI.deleteAllMessages()
|
||||||
|
// TODO: re-add the clear data here
|
||||||
|
//ApplicationContext.getInstance(context).clearAllData(false)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val dialog = AlertDialog.Builder(requireContext())
|
val dialog = AlertDialog.Builder(requireContext())
|
||||||
val message = "We’ve upgraded the way Session IDs are generated, so you will be unable to restore your current Session ID."
|
val message = "We’ve upgraded the way Session IDs are generated, so you will be unable to restore your current Session ID."
|
||||||
dialog.setMessage(message)
|
dialog.setMessage(message)
|
||||||
dialog.setPositiveButton("Yes") { _, _ ->
|
dialog.setPositiveButton("Yes") { _, _ ->
|
||||||
ApplicationContext.getInstance(context).clearAllData(false)
|
// TODO: re-add the clear data here
|
||||||
|
// ApplicationContext.getInstance(context).clearAllData(false)
|
||||||
}
|
}
|
||||||
dialog.setNegativeButton("Cancel") { _, _ ->
|
dialog.setNegativeButton("Cancel") { _, _ ->
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
@ -6,7 +6,6 @@ import android.os.Build
|
|||||||
import com.goterl.lazysodium.LazySodiumAndroid
|
import com.goterl.lazysodium.LazySodiumAndroid
|
||||||
import com.goterl.lazysodium.SodiumAndroid
|
import com.goterl.lazysodium.SodiumAndroid
|
||||||
import com.goterl.lazysodium.exceptions.SodiumException
|
import com.goterl.lazysodium.exceptions.SodiumException
|
||||||
import com.goterl.lazysodium.interfaces.AEAD
|
|
||||||
import com.goterl.lazysodium.interfaces.GenericHash
|
import com.goterl.lazysodium.interfaces.GenericHash
|
||||||
import com.goterl.lazysodium.interfaces.PwHash
|
import com.goterl.lazysodium.interfaces.PwHash
|
||||||
import com.goterl.lazysodium.interfaces.SecretBox
|
import com.goterl.lazysodium.interfaces.SecretBox
|
||||||
@ -298,6 +297,30 @@ object SnodeAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Deletes all messages owned by the given pubkey on this SN and broadcasts the delete request to
|
||||||
|
* all other swarm members.
|
||||||
|
* Returns dict of:
|
||||||
|
* - "swarms" dict mapping ed25519 pubkeys (in hex) of swarm members to dict values of:
|
||||||
|
* - "failed" and other failure keys -- see `recursive`.
|
||||||
|
* - "deleted": hashes of deleted messages.
|
||||||
|
* - "signature": signature of ( PUBKEY_HEX || TIMESTAMP || DELETEDHASH[0] || ... || DELETEDHASH[N] ), signed
|
||||||
|
* by the node's ed25519 pubkey.
|
||||||
|
*/
|
||||||
|
fun deleteAllMessages(deleteMessage: SnodeDeleteMessage): Promise<Set<RawResponsePromise>, Exception> {
|
||||||
|
// considerations: timestamp off in retrying logic, not being able to re-sign with latest timestamp? do we just not retry this as it will be synchronous
|
||||||
|
val destination = if (useTestnet) deleteMessage.pubKey.removing05PrefixIfNeeded() else deleteMessage.pubKey
|
||||||
|
return retryIfNeeded(maxRetryCount) {
|
||||||
|
getTargetSnodes(destination).map { swarm ->
|
||||||
|
swarm.map { snode ->
|
||||||
|
val parameters = deleteMessage.toJSON()
|
||||||
|
retryIfNeeded(maxRetryCount) {
|
||||||
|
invoke(Snode.Method.DeleteAll, snode, destination, parameters)
|
||||||
|
}
|
||||||
|
}.toSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parsing
|
// Parsing
|
||||||
private fun parseSnodes(rawResponse: Any): List<Snode> {
|
private fun parseSnodes(rawResponse: Any): List<Snode> {
|
||||||
val json = rawResponse as? Map<*, *>
|
val json = rawResponse as? Map<*, *>
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package org.session.libsession.snode
|
||||||
|
|
||||||
|
import org.session.libsignal.utilities.removing05PrefixIfNeeded
|
||||||
|
|
||||||
|
data class SnodeDeleteMessage(
|
||||||
|
/**
|
||||||
|
* The hex encoded public key of the user.
|
||||||
|
*/
|
||||||
|
val pubKey: String,
|
||||||
|
/**
|
||||||
|
* The timestamp at which this request was initiated, in milliseconds since unix epoch.
|
||||||
|
* Must be within Must be within ±60s of the current time.
|
||||||
|
* (For clients it is recommended to retrieve a timestamp via `info` first, to avoid client time sync issues).
|
||||||
|
*/
|
||||||
|
val timestamp: Long,
|
||||||
|
/**
|
||||||
|
* an Ed25519 signature of ( "delete_all" || timestamp ), signed by the ed25519
|
||||||
|
*/
|
||||||
|
val signature: String,
|
||||||
|
) {
|
||||||
|
|
||||||
|
internal fun toJSON(): Map<String, String> {
|
||||||
|
return mapOf(
|
||||||
|
"pubkey" to if (SnodeAPI.useTestnet) pubKey.removing05PrefixIfNeeded() else pubKey,
|
||||||
|
"timestamp" to timestamp.toString(),
|
||||||
|
"signature" to signature
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,7 +7,8 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?) {
|
|||||||
GetSwarm("get_snodes_for_pubkey"),
|
GetSwarm("get_snodes_for_pubkey"),
|
||||||
GetMessages("retrieve"),
|
GetMessages("retrieve"),
|
||||||
SendMessage("store"),
|
SendMessage("store"),
|
||||||
OxenDaemonRPCCall("oxend_request")
|
OxenDaemonRPCCall("oxend_request"),
|
||||||
|
DeleteAll("delete_all")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class KeySet(val ed25519Key: String, val x25519Key: String)
|
data class KeySet(val ed25519Key: String, val x25519Key: String)
|
||||||
|
Loading…
Reference in New Issue
Block a user