mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-25 09:17:44 +00:00
[SS-54] Add dialog to allow local deletion if network deletion fails (#1526)
* WIP * Push before attempting some HTTPRequestFailedException rate limiting * Functionality now works * Merging dev resulted in some subproject commit change so pushing that * Fixes #1525 * Addressed Andy PR feedback * Addressed further PR feedback from Andy --------- Co-authored-by: alansley <aclansley@gmail.com>
This commit is contained in:
parent
0da949c8e6
commit
a30f00104e
@ -501,7 +501,14 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
||||
});
|
||||
}
|
||||
|
||||
public void clearAllData(boolean isMigratingToV2KeyPair) {
|
||||
// Method to clear the local data - returns true on success otherwise false
|
||||
|
||||
/**
|
||||
* Clear all local profile data and message history then restart the app after a brief delay.
|
||||
* @param isMigratingToV2KeyPair whether we're upgrading to a more recent V2 key pair or not.
|
||||
* @return true on success, false otherwise.
|
||||
*/
|
||||
public boolean clearAllData(boolean isMigratingToV2KeyPair) {
|
||||
if (firebaseInstanceIdJob != null && firebaseInstanceIdJob.isActive()) {
|
||||
firebaseInstanceIdJob.cancel(null);
|
||||
}
|
||||
@ -515,9 +522,11 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
||||
getSharedPreferences(PREFERENCES_NAME, 0).edit().clear().commit();
|
||||
if (!deleteDatabase(SQLCipherOpenHelper.DATABASE_NAME)) {
|
||||
Log.d("Loki", "Failed to delete database.");
|
||||
return false;
|
||||
}
|
||||
configFactory.keyPairChanged();
|
||||
Util.runOnMain(() -> new Handler().postDelayed(ApplicationContext.this::restartApplication, 200));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void restartApplication() {
|
||||
|
@ -4,12 +4,13 @@ import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.view.isGone
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@ -24,17 +25,26 @@ import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||
|
||||
class ClearAllDataDialog : DialogFragment() {
|
||||
private val TAG = "ClearAllDataDialog"
|
||||
|
||||
private lateinit var binding: DialogClearAllDataBinding
|
||||
|
||||
enum class Steps {
|
||||
private enum class Steps {
|
||||
INFO_PROMPT,
|
||||
NETWORK_PROMPT,
|
||||
DELETING
|
||||
DELETING,
|
||||
RETRY_LOCAL_DELETE_ONLY_PROMPT
|
||||
}
|
||||
|
||||
var clearJob: Job? = null
|
||||
// Rather than passing a bool around we'll use an enum to clarify our intent
|
||||
private enum class DeletionScope {
|
||||
DeleteLocalDataOnly,
|
||||
DeleteBothLocalAndNetworkData
|
||||
}
|
||||
|
||||
var step = Steps.INFO_PROMPT
|
||||
private var clearJob: Job? = null
|
||||
|
||||
private var step = Steps.INFO_PROMPT
|
||||
set(value) {
|
||||
field = value
|
||||
updateUI()
|
||||
@ -46,8 +56,8 @@ class ClearAllDataDialog : DialogFragment() {
|
||||
|
||||
private fun createView(): View {
|
||||
binding = DialogClearAllDataBinding.inflate(LayoutInflater.from(requireContext()))
|
||||
val device = radioOption("deviceOnly", R.string.dialog_clear_all_data_clear_device_only)
|
||||
val network = radioOption("deviceAndNetwork", R.string.dialog_clear_all_data_clear_device_and_network)
|
||||
val device = radioOption("deviceOnly", R.string.clearDeviceOnly)
|
||||
val network = radioOption("deviceAndNetwork", R.string.clearDeviceAndNetwork)
|
||||
var selectedOption: RadioOption<String> = device
|
||||
val optionAdapter = RadioOptionAdapter { selectedOption = it }
|
||||
binding.recyclerView.apply {
|
||||
@ -57,18 +67,21 @@ class ClearAllDataDialog : DialogFragment() {
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
optionAdapter.submitList(listOf(device, network))
|
||||
|
||||
binding.cancelButton.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
binding.clearAllDataButton.setOnClickListener {
|
||||
when(step) {
|
||||
when (step) {
|
||||
Steps.INFO_PROMPT -> if (selectedOption == network) {
|
||||
step = Steps.NETWORK_PROMPT
|
||||
} else {
|
||||
clearAllData(false)
|
||||
clearAllData(DeletionScope.DeleteLocalDataOnly)
|
||||
}
|
||||
Steps.NETWORK_PROMPT -> clearAllData(true)
|
||||
Steps.NETWORK_PROMPT -> clearAllData(DeletionScope.DeleteBothLocalAndNetworkData)
|
||||
Steps.DELETING -> { /* do nothing intentionally */ }
|
||||
Steps.RETRY_LOCAL_DELETE_ONLY_PROMPT -> clearAllData(DeletionScope.DeleteLocalDataOnly)
|
||||
}
|
||||
}
|
||||
return binding.root
|
||||
@ -86,8 +99,13 @@ class ClearAllDataDialog : DialogFragment() {
|
||||
binding.dialogDescriptionText.setText(R.string.dialog_clear_all_data_clear_device_and_network_confirmation)
|
||||
}
|
||||
Steps.DELETING -> { /* do nothing intentionally */ }
|
||||
Steps.RETRY_LOCAL_DELETE_ONLY_PROMPT -> {
|
||||
binding.dialogDescriptionText.setText(R.string.clearDataErrorDescriptionGeneric)
|
||||
binding.clearAllDataButton.text = getString(R.string.clearDevice)
|
||||
}
|
||||
}
|
||||
binding.recyclerView.isGone = step == Steps.NETWORK_PROMPT
|
||||
|
||||
binding.recyclerView.isVisible = step == Steps.INFO_PROMPT
|
||||
binding.cancelButton.isVisible = !isLoading
|
||||
binding.clearAllDataButton.isVisible = !isLoading
|
||||
binding.progressBar.isVisible = isLoading
|
||||
@ -97,45 +115,55 @@ class ClearAllDataDialog : DialogFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearAllData(deleteNetworkMessages: Boolean) {
|
||||
clearJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||
val previousStep = step
|
||||
withContext(Dispatchers.Main) {
|
||||
step = Steps.DELETING
|
||||
private suspend fun performDeleteLocalDataOnlyStep() {
|
||||
try {
|
||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(requireContext()).get()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to force sync when deleting data", e)
|
||||
withContext(Main) {
|
||||
Toast.makeText(ApplicationContext.getInstance(requireContext()), R.string.errorUnknown, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
if (!deleteNetworkMessages) {
|
||||
try {
|
||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(requireContext()).get()
|
||||
} catch (e: Exception) {
|
||||
Log.e("Loki", "Failed to force sync", e)
|
||||
}
|
||||
ApplicationContext.getInstance(context).clearAllData(false)
|
||||
withContext(Dispatchers.Main) {
|
||||
return
|
||||
}
|
||||
ApplicationContext.getInstance(context).clearAllData(false).let { success ->
|
||||
withContext(Main) {
|
||||
if (success) {
|
||||
dismiss()
|
||||
} else {
|
||||
Toast.makeText(ApplicationContext.getInstance(requireContext()), R.string.errorUnknown, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
} else {
|
||||
// finish
|
||||
val result = try {
|
||||
val openGroups = DatabaseComponent.get(requireContext()).lokiThreadDatabase().getAllOpenGroups()
|
||||
openGroups.map { it.value.server }.toSet().forEach { server ->
|
||||
OpenGroupApi.deleteAllInboxMessages(server).get()
|
||||
}
|
||||
SnodeAPI.deleteAllMessages().get()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null || result.values.any { !it } || result.isEmpty()) {
|
||||
// didn't succeed (at least one)
|
||||
withContext(Dispatchers.Main) {
|
||||
step = previousStep
|
||||
private fun clearAllData(deletionScope: DeletionScope) {
|
||||
step = Steps.DELETING
|
||||
|
||||
clearJob = lifecycleScope.launch(Dispatchers.IO) {
|
||||
when (deletionScope) {
|
||||
DeletionScope.DeleteLocalDataOnly -> {
|
||||
performDeleteLocalDataOnlyStep()
|
||||
}
|
||||
DeletionScope.DeleteBothLocalAndNetworkData -> {
|
||||
val deletionResultMap: Map<String, Boolean>? = try {
|
||||
val openGroups = DatabaseComponent.get(requireContext()).lokiThreadDatabase().getAllOpenGroups()
|
||||
openGroups.map { it.value.server }.toSet().forEach { server ->
|
||||
OpenGroupApi.deleteAllInboxMessages(server).get()
|
||||
}
|
||||
SnodeAPI.deleteAllMessages().get()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to delete network messages - offering user option to delete local data only.", e)
|
||||
null
|
||||
}
|
||||
} else if (result.values.all { it }) {
|
||||
// don't force sync because all the messages are deleted?
|
||||
ApplicationContext.getInstance(context).clearAllData(false)
|
||||
withContext(Dispatchers.Main) {
|
||||
dismiss()
|
||||
|
||||
// If one or more deletions failed then inform the user and allow them to clear the device only if they wish..
|
||||
if (deletionResultMap == null || deletionResultMap.values.any { !it } || deletionResultMap.isEmpty()) {
|
||||
withContext(Main) { step = Steps.RETRY_LOCAL_DELETE_ONLY_PROMPT }
|
||||
}
|
||||
else if (deletionResultMap.values.all { it }) {
|
||||
// ..otherwise if the network data deletion was successful proceed to delete the local data as well.
|
||||
ApplicationContext.getInstance(context).clearAllData(false)
|
||||
withContext(Main) { dismiss() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -657,8 +657,6 @@
|
||||
<string name="dialog_clear_all_data_explanation">این گزینه به طور دائم پیامها، جلسات و مخاطبین شما را حذف میکند.</string>
|
||||
<string name="dialog_clear_all_data_network_explanation">آیا فقط میخواهید این دستگاه را پاک کنید یا میخواهید کل اکانت را پاک کنید؟</string>
|
||||
<string name="dialog_clear_all_data_message">این کار پیامها و مخاطبین شما را برای همیشه حذف میکند. آیا میخواهید فقط این دستگاه را پاک کنید یا داتا خود را از شبکه نیز حذف کنید?</string>
|
||||
<string name="dialog_clear_all_data_clear_device_only">فقط پاک کردن دستگاه</string>
|
||||
<string name="dialog_clear_all_data_clear_device_and_network">پاک کردن دستگاه و شبکه</string>
|
||||
<string name="dialog_clear_all_data_clear_device_and_network_confirmation">آیا مطمئن هستید که می خواهید داتا های خود را از شبکه حذف کنید؟ اگر ادامه دهید، نمیتوانید پیامها یا مخاطبین خود را بازیابی کنید.</string>
|
||||
<string name="dialog_clear_all_data_clear">پاک</string>
|
||||
<string name="dialog_clear_all_data_local_only">فقط حذف شود</string>
|
||||
|
@ -660,8 +660,6 @@
|
||||
<string name="dialog_clear_all_data_explanation">Cela supprimera définitivement vos messages, vos sessions et vos contacts.</string>
|
||||
<string name="dialog_clear_all_data_network_explanation">Souhaitez-vous effacer seulement cet appareil ou supprimer l\'ensemble de votre compte ?</string>
|
||||
<string name="dialog_clear_all_data_message">Cela supprimera définitivement vos messages, sessions et contacts. Voulez-vous uniquement effacer cet appareil ou supprimer l\'intégralité de votre compte ?</string>
|
||||
<string name="dialog_clear_all_data_clear_device_only">Effacer l\'appareil uniquement</string>
|
||||
<string name="dialog_clear_all_data_clear_device_and_network">Effacer l\'appareil et le réseau</string>
|
||||
<string name="dialog_clear_all_data_clear_device_and_network_confirmation">Êtes-vous sûr de vouloir supprimer vos données du réseau ? Si vous continuez, vous ne pourrez pas restaurer vos messages ou vos contacts.</string>
|
||||
<string name="dialog_clear_all_data_clear">Effacer</string>
|
||||
<string name="dialog_clear_all_data_local_only">Effacer seulement</string>
|
||||
|
@ -660,8 +660,6 @@
|
||||
<string name="dialog_clear_all_data_explanation">Cela supprimera définitivement vos messages, vos sessions et vos contacts.</string>
|
||||
<string name="dialog_clear_all_data_network_explanation">Souhaitez-vous effacer seulement cet appareil ou supprimer l\'ensemble de votre compte ?</string>
|
||||
<string name="dialog_clear_all_data_message">Cela supprimera définitivement vos messages, sessions et contacts. Voulez-vous uniquement effacer cet appareil ou supprimer l\'intégralité de votre compte ?</string>
|
||||
<string name="dialog_clear_all_data_clear_device_only">Effacer l\'appareil uniquement</string>
|
||||
<string name="dialog_clear_all_data_clear_device_and_network">Effacer l\'appareil et le réseau</string>
|
||||
<string name="dialog_clear_all_data_clear_device_and_network_confirmation">Êtes-vous sûr de vouloir supprimer vos données du réseau ? Si vous continuez, vous ne pourrez pas restaurer vos messages ou vos contacts.</string>
|
||||
<string name="dialog_clear_all_data_clear">Effacer</string>
|
||||
<string name="dialog_clear_all_data_local_only">Effacer seulement</string>
|
||||
|
@ -842,8 +842,6 @@
|
||||
<string name="dialog_clear_all_data_explanation">This will permanently delete your messages, sessions, and contacts.</string>
|
||||
<string name="dialog_clear_all_data_network_explanation">Would you like to clear only this device, or delete your entire account?</string>
|
||||
<string name="dialog_clear_all_data_message">This will permanently delete your messages, sessions, and contacts. Would you like to clear only this device, or delete your entire account?</string>
|
||||
<string name="dialog_clear_all_data_clear_device_only">Clear Device Only</string>
|
||||
<string name="dialog_clear_all_data_clear_device_and_network">Clear Device and Network</string>
|
||||
<string name="dialog_clear_all_data_clear_device_and_network_confirmation">Are you sure you want to delete your data from the network? If you continue you will not be able to restore your messages or contacts.</string>
|
||||
<string name="dialog_clear_all_data_clear">Clear</string>
|
||||
<string name="dialog_clear_all_data_local_only">Delete Only</string>
|
||||
|
@ -25,7 +25,6 @@ import org.session.libsignal.utilities.Snode
|
||||
import org.session.libsignal.utilities.ThreadUtils
|
||||
import org.session.libsignal.utilities.recover
|
||||
import org.session.libsignal.utilities.toHexString
|
||||
import java.util.Date
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.collections.set
|
||||
|
||||
|
@ -73,4 +73,10 @@
|
||||
<string name="ConversationItem_group_action_left">%1$s has left the group.</string>
|
||||
<!-- RecipientProvider -->
|
||||
<string name="RecipientProvider_unnamed_group">Unnamed group</string>
|
||||
|
||||
<string name="clearDataErrorDescriptionGeneric">An unknown error occurred and your data was not deleted. Do you want to delete your data from just this device instead?</string>
|
||||
<string name="errorUnknown">An unknown error occurred.</string>
|
||||
<string name="clearDevice">Clear Device</string>
|
||||
<string name="clearDeviceOnly">Clear device only</string>
|
||||
<string name="clearDeviceAndNetwork">Clear device and network</string>
|
||||
</resources>
|
||||
|
@ -4,7 +4,6 @@ import android.os.Process
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.SynchronousQueue
|
||||
import java.util.concurrent.ThreadPoolExecutor
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
Loading…
x
Reference in New Issue
Block a user