Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SS-54] Add dialog to allow local deletion if network deletion fails #1526

Merged
merged 10 commits into from
Jul 2, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,8 @@ private void loadEmojiSearchIndexIfNeeded() {
});
}

public void clearAllData(boolean isMigratingToV2KeyPair) {
// Method to clear the local data - returns true on success otherwise false
AL-Session marked this conversation as resolved.
Show resolved Hide resolved
public boolean clearAllData(boolean isMigratingToV2KeyPair) {
if (firebaseInstanceIdJob != null && firebaseInstanceIdJob.isActive()) {
firebaseInstanceIdJob.cancel(null);
}
Expand All @@ -515,9 +516,11 @@ public void clearAllData(boolean isMigratingToV2KeyPair) {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package org.thoughtcrime.securesms.preferences

import android.app.Dialog
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.widget.Toast
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
Expand All @@ -24,17 +27,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 {
AL-Session marked this conversation as resolved.
Show resolved Hide resolved
INFO_PROMPT,
NETWORK_PROMPT,
DELETING
DELETING,
RETRY_LOCAL_DELETE_ONLY_PROMPT
}

// Rather than passing a bool around we'll use an enum to clarify our intent
private enum class DeletionScope {
DeleteLocalDataOnly,
DeleteBothLocalAndNetworkData
}

var clearJob: Job? = null
private var clearJob: Job? = null

var step = Steps.INFO_PROMPT
private var step = Steps.INFO_PROMPT
set(value) {
field = value
updateUI()
Expand All @@ -46,8 +58,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 {
Expand All @@ -61,14 +73,15 @@ class ClearAllDataDialog : DialogFragment() {
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
Expand All @@ -86,8 +99,12 @@ 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.isGone = step == Steps.NETWORK_PROMPT || step == Steps.RETRY_LOCAL_DELETE_ONLY_PROMPT
AL-Session marked this conversation as resolved.
Show resolved Hide resolved
binding.cancelButton.isVisible = !isLoading
binding.clearAllDataButton.isVisible = !isLoading
binding.progressBar.isVisible = isLoading
Expand All @@ -97,46 +114,50 @@ class ClearAllDataDialog : DialogFragment() {
}
}

private fun clearAllData(deleteNetworkMessages: Boolean) {
private fun clearAllData(deletionScope: DeletionScope) {

clearJob = lifecycleScope.launch(Dispatchers.IO) {
val previousStep = step
withContext(Dispatchers.Main) {
step = Steps.DELETING
}
withContext(Dispatchers.Main) { step = Steps.DELETING }

if (deletionScope == DeletionScope.DeleteLocalDataOnly) {
AL-Session marked this conversation as resolved.
Show resolved Hide resolved

if (!deleteNetworkMessages) {
try {
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(requireContext()).get()
} catch (e: Exception) {
Log.e("Loki", "Failed to force sync", e)
Log.e(TAG, "Failed to force sync when deleting data", e)
Handler(Looper.getMainLooper()).post {
AL-Session marked this conversation as resolved.
Show resolved Hide resolved
Toast.makeText(ApplicationContext.getInstance(requireContext()), R.string.errorUnknown, Toast.LENGTH_LONG).show()
}
return@launch
AL-Session marked this conversation as resolved.
Show resolved Hide resolved
}
ApplicationContext.getInstance(context).clearAllData(false)
withContext(Dispatchers.Main) {
dismiss()
val success = ApplicationContext.getInstance(context).clearAllData(false)
withContext(Dispatchers.Main) { dismiss() }
if (!success) {
Handler(Looper.getMainLooper()).post {
AL-Session marked this conversation as resolved.
Show resolved Hide resolved
Toast.makeText(ApplicationContext.getInstance(requireContext()), R.string.errorUnknown, Toast.LENGTH_LONG).show()
}
}
} else {
// finish
val result = try {
}
else if (deletionScope == DeletionScope.DeleteBothLocalAndNetworkData) {
AL-Session marked this conversation as resolved.
Show resolved Hide resolved
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
}

if (result == null || result.values.any { !it } || result.isEmpty()) {
// didn't succeed (at least one)
withContext(Dispatchers.Main) {
step = previousStep
}
} else if (result.values.all { it }) {
// don't force sync because all the messages are deleted?
// 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(Dispatchers.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(Dispatchers.Main) {
dismiss()
}
withContext(Dispatchers.Main) { dismiss() }
}
}
}
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values-fa-rIR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values-fr-rFR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 0 additions & 2 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 1 addition & 1 deletion libsession-util/libsession-util
Submodule libsession-util updated 57 files
+36 −65 .drone.jsonnet
+0 −1 .gitignore
+0 −3 .gitmodules
+20 −17 cmake/StaticBuild.cmake
+0 −13 external/CMakeLists.txt
+1 −1 external/nlohmann-json
+0 −1 external/protobuf
+0 −116 include/session/blinding.h
+3 −91 include/session/blinding.hpp
+0 −5 include/session/config/groups/keys.h
+4 −5 include/session/config/groups/keys.hpp
+0 −61 include/session/curve25519.h
+0 −35 include/session/curve25519.hpp
+0 −100 include/session/ed25519.h
+0 −55 include/session/ed25519.hpp
+0 −36 include/session/hash.h
+0 −22 include/session/hash.hpp
+0 −176 include/session/onionreq/builder.h
+0 −82 include/session/onionreq/builder.hpp
+25 −9 include/session/onionreq/channel_encryption.hpp
+2 −2 include/session/onionreq/parser.hpp
+0 −61 include/session/onionreq/response_parser.h
+0 −31 include/session/onionreq/response_parser.hpp
+0 −24 include/session/random.h
+0 −18 include/session/random.hpp
+0 −219 include/session/session_encrypt.h
+5 −137 include/session/session_encrypt.hpp
+0 −10 include/session/util.hpp
+5 −9 include/session/xed25519.h
+32 −2 proto/CMakeLists.txt
+1 −7 src/CMakeLists.txt
+26 −381 src/blinding.cpp
+1 −13 src/config/base.cpp
+1 −3 src/config/contacts.cpp
+2 −2 src/config/groups/info.cpp
+5 −5 src/config/groups/keys.cpp
+0 −3 src/config/user_profile.cpp
+0 −87 src/curve25519.cpp
+0 −148 src/ed25519.cpp
+0 −65 src/hash.cpp
+0 −275 src/onionreq/builder.cpp
+25 −24 src/onionreq/channel_encryption.cpp
+2 −3 src/onionreq/parser.cpp
+0 −113 src/onionreq/response_parser.cpp
+0 −29 src/random.cpp
+15 −588 src/session_encrypt.cpp
+10 −11 src/xed25519.cpp
+0 −4 tests/CMakeLists.txt
+0 −150 tests/test_blinding.cpp
+0 −51 tests/test_curve25519.cpp
+0 −88 tests/test_ed25519.cpp
+10 −17 tests/test_group_keys.cpp
+0 −50 tests/test_hash.cpp
+3 −3 tests/test_onionreq.cpp
+0 −16 tests/test_random.cpp
+1 −308 tests/test_session_encrypt.cpp
+12 −6 tests/test_xed25519.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions libsession/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down