Merge pull request #4709 from basilgello/restore-clear-local-folder

Restore per-folder clear local messages
This commit is contained in:
cketti 2020-05-04 14:39:44 +02:00 committed by GitHub
commit f62818b5ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 134 additions and 17 deletions

View file

@ -2235,20 +2235,17 @@ public class MessagingController {
});
}
public void clearFolder(final Account account, final String folderServerId, final MessagingListener listener) {
putBackground("clearFolder", listener, new Runnable() {
@Override
public void run() {
clearFolderSynchronous(account, folderServerId, listener);
}
});
public void clearFolder(Account account, long folderId) {
putBackground("clearFolder", null, () ->
clearFolderSynchronous(account, folderId)
);
}
@VisibleForTesting
protected void clearFolderSynchronous(Account account, String folderServerId, MessagingListener listener) {
protected void clearFolderSynchronous(Account account, long folderId) {
LocalFolder localFolder = null;
try {
localFolder = localStoreProvider.getInstance(account).getFolder(folderServerId);
localFolder = localStoreProvider.getInstance(account).getFolder(folderId);
localFolder.open();
localFolder.clearAllMessages();
} catch (UnavailableStorageException e) {

View file

@ -73,6 +73,7 @@ import static org.mockito.Mockito.when;
@SuppressWarnings("unchecked")
public class MessagingControllerTest extends K9RobolectricTest {
private static final long FOLDER_ID = 23;
private static final String FOLDER_NAME = "Folder";
private static final String SENT_FOLDER_NAME = "Sent";
private static final int MAXIMUM_SMALL_MESSAGE_SIZE = 1000;
@ -158,21 +159,21 @@ public class MessagingControllerTest extends K9RobolectricTest {
@Test
public void clearFolderSynchronous_shouldOpenFolderForWriting() throws MessagingException {
controller.clearFolderSynchronous(account, FOLDER_NAME, listener);
controller.clearFolderSynchronous(account, FOLDER_ID);
verify(localFolder).open();
}
@Test
public void clearFolderSynchronous_shouldClearAllMessagesInTheFolder() throws MessagingException {
controller.clearFolderSynchronous(account, FOLDER_NAME, listener);
controller.clearFolderSynchronous(account, FOLDER_ID);
verify(localFolder).clearAllMessages();
}
@Test
public void clearFolderSynchronous_shouldCloseTheFolder() throws MessagingException {
controller.clearFolderSynchronous(account, FOLDER_NAME, listener);
controller.clearFolderSynchronous(account, FOLDER_ID);
verify(localFolder, atLeastOnce()).close();
}
@ -181,7 +182,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
public void clearFolderSynchronous_whenStorageUnavailable_shouldThrowUnavailableAccountException() throws MessagingException {
doThrow(new UnavailableStorageException("Test")).when(localFolder).open();
controller.clearFolderSynchronous(account, FOLDER_NAME, listener);
controller.clearFolderSynchronous(account, FOLDER_ID);
}
@Test()
@ -189,7 +190,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
doThrow(new RuntimeException("Test")).when(localFolder).open();
try {
controller.clearFolderSynchronous(account, FOLDER_NAME, listener);
controller.clearFolderSynchronous(account, FOLDER_ID);
} catch (Exception ignored){
}
@ -486,6 +487,8 @@ public class MessagingControllerTest extends K9RobolectricTest {
private void configureLocalStore() throws MessagingException {
when(localStore.getFolder(FOLDER_NAME)).thenReturn(localFolder);
when(localStore.getFolder(FOLDER_ID)).thenReturn(localFolder);
when(localFolder.getDatabaseId()).thenReturn(FOLDER_ID);
when(localFolder.getServerId()).thenReturn(FOLDER_NAME);
when(localStore.getPersonalNamespaces(false)).thenReturn(Collections.singletonList(localFolder));
when(localStoreProvider.getInstance(account)).thenReturn(localStore);

View file

@ -1,9 +1,14 @@
package com.fsck.k9.ui.managefolders
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
import com.fsck.k9.fragment.ConfirmationDialogFragment
import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener
import com.fsck.k9.ui.R
import com.fsck.k9.ui.folders.FolderNameFormatter
import com.fsck.k9.ui.observeNotNull
@ -12,10 +17,15 @@ import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
class FolderSettingsFragment : PreferenceFragmentCompat() {
class FolderSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFragmentListener {
private val viewModel: FolderSettingsViewModel by viewModel()
private val folderNameFormatter: FolderNameFormatter by inject { parametersOf(requireActivity()) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {
// Set empty preferences resource while data is being loaded
setPreferencesFromResource(R.xml.empty_preferences, null)
@ -35,6 +45,26 @@ class FolderSettingsFragment : PreferenceFragmentCompat() {
is FolderSettingsData -> initPreferences(folderSettingsResult)
}
}
viewModel.getActionEvents().observeNotNull(this) { handleActionEvents(it) }
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.folder_settings_option, menu)
val clearFolderItem = menu.findItem(R.id.clear_local_folder)
clearFolderItem.isVisible = viewModel.showClearFolderInMenu
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.clear_local_folder -> {
viewModel.showClearFolderConfirmationDialog()
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun navigateBack() {
@ -46,6 +76,11 @@ class FolderSettingsFragment : PreferenceFragmentCompat() {
setPreferencesFromResource(R.xml.folder_settings_preferences, null)
setCategoryTitle(folderSettings)
updateMenu()
}
private fun updateMenu() {
requireActivity().invalidateOptionsMenu()
}
private fun setCategoryTitle(folderSettings: FolderSettingsData) {
@ -53,10 +88,44 @@ class FolderSettingsFragment : PreferenceFragmentCompat() {
findPreference<Preference>(PREFERENCE_TOP_CATEGORY)!!.title = folderDisplayName
}
private fun handleActionEvents(action: Action) {
when (action) {
is Action.ShowClearFolderConfirmationDialog -> showClearFolderConfirmationDialog()
}
}
private fun showClearFolderConfirmationDialog() {
val dialogFragment = ConfirmationDialogFragment.newInstance(
DIALOG_CLEAR_FOLDER,
getString(R.string.dialog_confirm_clear_local_folder_title),
getString(R.string.dialog_confirm_clear_local_folder_message),
getString(R.string.dialog_confirm_clear_local_folder_action),
getString(R.string.cancel_action)
)
dialogFragment.setTargetFragment(this, REQUEST_CLEAR_FOLDER)
dialogFragment.show(requireFragmentManager(), TAG_CLEAR_FOLDER_CONFIRMATION)
}
override fun doPositiveClick(dialogId: Int) {
when (dialogId) {
DIALOG_CLEAR_FOLDER -> {
viewModel.onClearFolderConfirmation()
}
}
}
override fun doNegativeClick(dialogId: Int) = Unit
override fun dialogCancelled(dialogId: Int) = Unit
companion object {
const val EXTRA_ACCOUNT = "account"
const val EXTRA_FOLDER_ID = "folderId"
private const val DIALOG_CLEAR_FOLDER = 1
private const val REQUEST_CLEAR_FOLDER = 1
private const val TAG_CLEAR_FOLDER_CONFIRMATION = "clear_folder_confirmation"
private const val PREFERENCE_TOP_CATEGORY = "folder_settings"
}
}

View file

@ -7,6 +7,8 @@ import androidx.lifecycle.viewModelScope
import com.fsck.k9.Account
import com.fsck.k9.Preferences
import com.fsck.k9.activity.FolderInfoHolder
import com.fsck.k9.controller.MessagingController
import com.fsck.k9.helper.SingleLiveEvent
import com.fsck.k9.mailstore.Folder
import com.fsck.k9.mailstore.FolderDetails
import com.fsck.k9.mailstore.FolderRepository
@ -15,12 +17,22 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import timber.log.Timber
private const val NO_FOLDER_ID = 0L
class FolderSettingsViewModel(
private val preferences: Preferences,
private val folderRepositoryManager: FolderRepositoryManager
private val folderRepositoryManager: FolderRepositoryManager,
private val messagingController: MessagingController
) : ViewModel() {
private val actionLiveData = SingleLiveEvent<Action>()
private var folderSettingsLiveData: LiveData<FolderSettingsResult>? = null
private lateinit var account: Account
private var folderId: Long = NO_FOLDER_ID
val showClearFolderInMenu: Boolean
get() = this::account.isInitialized && folderId != NO_FOLDER_ID
fun getFolderSettingsLiveData(accountUuid: String, folderId: Long): LiveData<FolderSettingsResult> {
return folderSettingsLiveData ?: createFolderSettingsLiveData(accountUuid, folderId).also {
folderSettingsLiveData = it
@ -41,6 +53,9 @@ class FolderSettingsViewModel(
return@liveData
}
this@FolderSettingsViewModel.account = account
this@FolderSettingsViewModel.folderId = folderId
val folderSettingsData = FolderSettingsData(
folder = createFolderObject(account, folderDetails.folder),
dataStore = FolderSettingsDataStore(folderRepository, folderDetails)
@ -70,8 +85,26 @@ class FolderSettingsViewModel(
type = folderType
)
}
fun showClearFolderConfirmationDialog() {
sendActionEvent(Action.ShowClearFolderConfirmationDialog)
}
fun onClearFolderConfirmation() {
messagingController.clearFolder(account, folderId)
}
fun getActionEvents(): LiveData<Action> = actionLiveData
private fun sendActionEvent(action: Action) {
actionLiveData.value = action
}
}
sealed class FolderSettingsResult
object FolderNotFound : FolderSettingsResult()
data class FolderSettingsData(val folder: Folder, val dataStore: FolderSettingsDataStore) : FolderSettingsResult()
sealed class Action {
object ShowClearFolderConfirmationDialog : Action()
}

View file

@ -5,5 +5,5 @@ import org.koin.dsl.module
val manageFoldersUiModule = module {
viewModel { ManageFoldersViewModel(foldersLiveDataFactory = get()) }
viewModel { FolderSettingsViewModel(preferences = get(), folderRepositoryManager = get()) }
viewModel { FolderSettingsViewModel(preferences = get(), folderRepositoryManager = get(), messagingController = get()) }
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/clear_local_folder"
android:title="@string/folder_settings_clear_local_folder_action"
app:showAsAction="never" />
</menu>

View file

@ -662,6 +662,7 @@ Please submit bug reports, contribute new features and ask questions at
<string name="folder_settings_folder_notify_mode_first_class">1st Class</string>
<string name="folder_settings_folder_notify_mode_second_class">2nd Class</string>
<string name="folder_settings_folder_notify_mode_inherited">Same as push class</string>
<string name="folder_settings_clear_local_folder_action">Clear local messages</string>
<string name="account_settings_incoming_label">Incoming server</string>
<string name="account_settings_incoming_summary">Configure the incoming mail server</string>
@ -893,6 +894,10 @@ Please submit bug reports, contribute new features and ask questions at
<string name="select_text_now">Select text to copy.</string>
<string name="dialog_confirm_clear_local_folder_title">Clear local messages?</string>
<string name="dialog_confirm_clear_local_folder_message">This will remove all local messages from the folder. No messages will be deleted from the server.</string>
<string name="dialog_confirm_clear_local_folder_action">Clear messages</string>
<string name="dialog_confirm_delete_title">Confirm deletion</string>
<string name="dialog_confirm_delete_message">Do you want to delete this message?</string>