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

Fix for Uncaught Exception and Crash, Lost Messages in Unsynchronized Local-Only Folders #8655

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions issueTestCases.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Task 1:
Working cases: starring/unstarring messages moved anywhere offline so long as it happens in the LIST VIEW
Failing cases: starring/unstarring messages moved anywhere offline, but the action is performed in MESSAGE VIEW
- Move to spam, then star/unstar
- Move to drafts, then star/unstar
- Move to outbox, then star/unstar
- Move to archive, then star/unstar
- Move to arbitrary folder (e.g. "Important"), then star/unstar


Task 2:
No working cases
Failing cases: any "ghost folders" existing in the app but NOT on the IMAP server will allow a user to move a message into the folder but not out of the folder (both online and offline)
Test cases (for move, copy, archive, spam, move to drafts, move to outbox, move to arbitrary folder):

-- ONLINE --
- Move/copy message into a ghost folder from List View
- Move/copy message into a ghost folder from Message View
- In both cases, message list should get refreshed before prompting user for target folder, and the app will not allow the user to complete the operation (folder no longer exists)

-- OFFLINE --
- Move/copy message into ghost folder from List View
- Move/copy message into ghost folder from Message View
- In each case, pop-up should appear prompting user to cancel the operation, proceed anyways, or to not show the pop-up again and to proceed
- If "don't show again" is selected, the pop-up should not appear if a user closes, reopens the app and repeats behavior (i.e. memory between sessions)
Original file line number Diff line number Diff line change
Expand Up @@ -974,8 +974,18 @@ void destroyPlaceholderMessages(LocalFolder localFolder, List<String> uids) thro
}

private void queueSetFlag(Account account, long folderId, boolean newState, Flag flag, List<String> uids) {
PendingCommand command = PendingSetFlag.create(folderId, newState, flag, uids);
queuePendingCommand(account, command);
//PendingCommand command = PendingSetFlag.create(folderId, newState, flag, uids);
String commandDescription = null;
try{
PendingCommand command = PendingSetFlag.create(folderId, newState, flag, uids);
if (command != null) {
commandDescription = command.getCommandName();
}
queuePendingCommand(account, command);
} catch (Exception e){

Timber.e(e, "Error running command '%s'", commandDescription);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ActionMode
import androidx.core.os.BundleCompat
import androidx.core.os.bundleOf
Expand Down Expand Up @@ -46,6 +47,7 @@ import com.fsck.k9.helper.Utility
import com.fsck.k9.helper.mapToSet
import com.fsck.k9.mail.Flag
import com.fsck.k9.mail.MessagingException
import com.fsck.k9.network.ConnectivityManager
import com.fsck.k9.search.getAccounts
import com.fsck.k9.ui.R
import com.fsck.k9.ui.changelog.RecentChangesActivity
Expand Down Expand Up @@ -82,6 +84,7 @@ class MessageListFragment :
private val messagingController: MessagingController by inject()
private val accountManager: AccountManager by inject()
private val clock: Clock by inject()
private val connectivityManager: ConnectivityManager by inject()

private val handler = MessageListHandler(this)
private val activityListener = MessageListActivityListener()
Expand Down Expand Up @@ -1035,11 +1038,47 @@ class MessageListFragment :
computeBatchDirection()
}

private fun onMove(message: MessageReference) {
onMove(listOf(message))
private interface ConnectivityDialogCallback {
fun onDialogResult(result: Int)
}

private fun onMove(messages: List<MessageReference>) {
private fun showConnectivityDialog(callback: ConnectivityDialogCallback) { // added
val sharedPreferences = context?.getSharedPreferences("app_preferences", Context.MODE_PRIVATE);
val dontShowAgain = sharedPreferences?.getBoolean("dont_show_connectivity_warning", false) ?: false;
//sharedPreferences?.edit()?.putBoolean("dont_show_connectivity_warning", false)?.apply()

//callback.onDialogResult(0);
if (dontShowAgain) {
// Proceed directly if "Don't show again" is checked
callback.onDialogResult(1);
return;
}
//callback.onDialogResult(0);
val builder = AlertDialog.Builder(requireContext())
builder.setTitle("No Internet Connection")
.setMessage("ThunderBird is not connected to the Internet. Any changes you make now may not be synced properly.")
.setPositiveButton("Proceed") { _, _ ->
callback.onDialogResult(1);

//Log.d("onMove", "User chose to proceed")
}
.setNegativeButton("Cancel") { _, _ ->
callback.onDialogResult(-1);

//Log.d("onMove", "User chose to cancel")
}
.setNeutralButton("Don't show again") { _, _ ->
// Save the preference to not show the dialog again
callback.onDialogResult(1);
sharedPreferences?.edit()?.putBoolean("dont_show_connectivity_warning", true)?.apply()
//Log.d("onMove", "User chose 'Don't show again'")
}
.show()

//return;
}

private fun proceedWithMove(messages: List<MessageReference>) {
if (!checkCopyOrMovePossible(messages, FolderOperation.MOVE)) return

val folderId = when {
Expand All @@ -1058,11 +1097,32 @@ class MessageListFragment :
)
}

private fun onCopy(message: MessageReference) {
onCopy(listOf(message))
private fun onMove(message: MessageReference) {
onMove(listOf(message))
}

private fun onCopy(messages: List<MessageReference>) {
private fun onMove(messages: List<MessageReference>) {

if (!preMove()) {
showConnectivityDialog(
object : ConnectivityDialogCallback {
override fun onDialogResult(result: Int) {
if (result == -1) {
return
} else if (result == 1) {
proceedWithMove(messages);
return;
}
}
},
)
} else {
proceedWithMove(messages);
return;
}
}

private fun proceedWithCopy(messages: List<MessageReference>) {
if (!checkCopyOrMovePossible(messages, FolderOperation.COPY)) return

val folderId = when {
Expand All @@ -1081,6 +1141,30 @@ class MessageListFragment :
)
}

private fun onCopy(message: MessageReference) {
onCopy(listOf(message))
}

private fun onCopy(messages: List<MessageReference>) {
if (!preMove()) {
showConnectivityDialog(
object : ConnectivityDialogCallback {
override fun onDialogResult(result: Int) {
if (result == -1) {
return
} else if (result == 1) {
proceedWithCopy(messages);
return;
}
}
},
)
} else {
proceedWithCopy(messages);
return;
}
}

private fun displayFolderChoice(
operation: FolderOperation,
requestCode: Int,
Expand Down Expand Up @@ -1172,11 +1256,43 @@ class MessageListFragment :
}

private fun copy(messages: List<MessageReference>, folderId: Long) {
copyOrMove(messages, folderId, FolderOperation.COPY)
if (!preMove()) {
showConnectivityDialog(
object : ConnectivityDialogCallback {
override fun onDialogResult(result: Int) {
if (result == -1) {
return
} else if (result == 1) {
copyOrMove(messages, folderId, FolderOperation.COPY)
return;
}
}
},
)
} else {
copyOrMove(messages, folderId, FolderOperation.COPY);
return;
}
}

private fun move(messages: List<MessageReference>, folderId: Long) {
copyOrMove(messages, folderId, FolderOperation.MOVE)
if (!preMove()) {
showConnectivityDialog(
object : ConnectivityDialogCallback {
override fun onDialogResult(result: Int) {
if (result == -1) {
return
} else if (result == 1) {
copyOrMove(messages, folderId, FolderOperation.MOVE)
return;
}
}
},
)
} else {
copyOrMove(messages, folderId, FolderOperation.MOVE);
return;
}
}

private fun copyOrMove(messages: List<MessageReference>, destinationFolderId: Long, operation: FolderOperation) {
Expand Down Expand Up @@ -1206,8 +1322,25 @@ class MessageListFragment :
}

private fun onMoveToDraftsFolder(messages: List<MessageReference>) {
messagingController.moveToDraftsFolder(account, currentFolder!!.databaseId, messages)
activeMessages = null
if (!preMove()) {
showConnectivityDialog(
object : ConnectivityDialogCallback {
override fun onDialogResult(result: Int) {
if (result == -1) {
return
} else if (result == 1) {
messagingController.moveToDraftsFolder(account, currentFolder!!.databaseId, messages)
activeMessages = null
return;
}
}
},
)
} else {
messagingController.moveToDraftsFolder(account, currentFolder!!.databaseId, messages)
activeMessages = null
return;
}
}

override fun doPositiveClick(dialogId: Int) {
Expand Down Expand Up @@ -1354,8 +1487,26 @@ class MessageListFragment :
}

fun onMove() {
selectedMessage?.let { message ->
onMove(message)
if (!preMove()) {
showConnectivityDialog(
object : ConnectivityDialogCallback {
override fun onDialogResult(result: Int) {
if (result == -1) {
return
} else if (result == 1) {
selectedMessage?.let { message ->
onMove(message)
}
return;
}
}
},
)
} else {
selectedMessage?.let { message ->
onMove(message)
}
return;
}
}

Expand All @@ -1366,8 +1517,26 @@ class MessageListFragment :
}

fun onCopy() {
selectedMessage?.let { message ->
onCopy(message)
if (!preMove()) {
showConnectivityDialog(
object : ConnectivityDialogCallback {
override fun onDialogResult(result: Int) {
if (result == -1) {
return
} else if (result == 1) {
selectedMessage?.let { message ->
onCopy(message)
}
return;
}
}
},
)
} else {
selectedMessage?.let { message ->
onCopy(message)
}
return;
}
}

Expand Down
Loading