Extract 'manage folders' screen to ManageFoldersFragment

This commit is contained in:
cketti 2020-03-04 17:19:26 +01:00
parent 0e1804ff3e
commit 1bd52ae417
5 changed files with 228 additions and 145 deletions

View file

@ -1,171 +1,59 @@
package com.fsck.k9.ui.managefolders
import android.content.Context
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.ActionBar
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.SearchView.OnQueryTextListener
import androidx.recyclerview.widget.RecyclerView
import androidx.core.os.bundleOf
import androidx.navigation.NavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import com.fsck.k9.Account
import com.fsck.k9.Account.FolderMode
import com.fsck.k9.Preferences
import com.fsck.k9.activity.K9Activity
import com.fsck.k9.activity.setup.FolderSettings
import com.fsck.k9.controller.MessagingController
import com.fsck.k9.mailstore.DisplayFolder
import com.fsck.k9.ui.R
import com.fsck.k9.ui.folders.FolderIconProvider
import com.fsck.k9.ui.folders.FolderNameFormatter
import com.fsck.k9.ui.observeNotNull
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter
import java.util.Locale
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import com.fsck.k9.ui.findNavController
class ManageFoldersActivity : K9Activity() {
private val viewModel: ManageFoldersViewModel by viewModel()
private val folderNameFormatter: FolderNameFormatter by inject { parametersOf(this) }
private val messagingController: MessagingController by inject()
private val preferences: Preferences by inject()
private val folderIconProvider by lazy { FolderIconProvider(theme) }
private lateinit var account: Account
private lateinit var actionBar: ActionBar
private lateinit var itemAdapter: ItemAdapter<FolderListItem>
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setLayout(R.layout.folder_list)
setLayout(R.layout.activity_manage_folders)
if (!decodeArguments()) {
finish()
return
}
val accountUuid = intent.getStringExtra(EXTRA_ACCOUNT) ?: error("Missing Intent extra '$EXTRA_ACCOUNT'")
initializeActionBar()
initializeFolderList()
viewModel.getFolders(account).observeNotNull(this) { folders ->
updateFolderList(folders)
}
initializeNavController(accountUuid)
}
private fun decodeArguments(): Boolean {
val accountUuid = intent.getStringExtra(EXTRA_ACCOUNT) ?: return false
account = preferences.getAccount(accountUuid) ?: return false
private fun initializeNavController(accountUuid: String) {
navController = findNavController(R.id.nav_host_fragment)
val fragmentArguments = bundleOf(
ManageFoldersFragment.EXTRA_ACCOUNT to accountUuid
)
navController.setGraph(R.navigation.navigation_manage_folders, fragmentArguments)
val appBarConfiguration = AppBarConfiguration(topLevelDestinationIds = emptySet())
setupActionBarWithNavController(navController, appBarConfiguration)
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp() || navigateUpBySimulatedBackButtonPress()
}
private fun navigateUpBySimulatedBackButtonPress(): Boolean {
onBackPressed()
return true
}
private fun initializeActionBar() {
actionBar = supportActionBar!!
actionBar.setDisplayHomeAsUpEnabled(true)
}
private fun initializeFolderList() {
itemAdapter = ItemAdapter()
itemAdapter.itemFilter.filterPredicate = ::folderListFilter
val folderListAdapter = FastAdapter.with(itemAdapter).apply {
setHasStableIds(true)
onClickListener = { _, _, item: FolderListItem, _ ->
openFolderSettings(item.serverId)
true
}
}
val recyclerView = findViewById<RecyclerView>(R.id.folderList)
recyclerView.adapter = folderListAdapter
}
private fun updateFolderList(displayFolders: List<DisplayFolder>) {
val folderListItems = displayFolders.map { displayFolder ->
val databaseId = displayFolder.folder.id
val folderIconResource = folderIconProvider.getFolderIcon(displayFolder.folder.type)
val displayName = folderNameFormatter.displayName(displayFolder.folder)
val serverId = displayFolder.folder.serverId
FolderListItem(databaseId, folderIconResource, displayName, serverId)
}
itemAdapter.set(folderListItems)
}
private fun openFolderSettings(folderServerId: String) {
FolderSettings.actionSettings(this, account, folderServerId)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.folder_list_option, menu)
configureFolderSearchView(menu)
return true
}
private fun configureFolderSearchView(menu: Menu) {
val folderMenuItem = menu.findItem(R.id.filter_folders)
val folderSearchView = folderMenuItem.actionView as SearchView
folderSearchView.queryHint = getString(R.string.folder_list_filter_hint)
folderSearchView.setOnQueryTextListener(object : OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
itemAdapter.filter(query)
return true
}
override fun onQueryTextChange(newText: String): Boolean {
itemAdapter.filter(newText)
return true
}
})
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> onBackPressed()
R.id.list_folders -> refreshFolderList()
R.id.display_1st_class -> setDisplayMode(FolderMode.FIRST_CLASS)
R.id.display_1st_and_2nd_class -> setDisplayMode(FolderMode.FIRST_AND_SECOND_CLASS)
R.id.display_not_second_class -> setDisplayMode(FolderMode.NOT_SECOND_CLASS)
R.id.display_all -> setDisplayMode(FolderMode.ALL)
else -> return super.onOptionsItemSelected(item)
}
return true
}
private fun refreshFolderList() {
messagingController.refreshFolderList(account)
}
private fun setDisplayMode(newMode: FolderMode) {
account.folderDisplayMode = newMode
preferences.saveAccount(account)
itemAdapter.filter(null)
}
private fun folderListFilter(item: FolderListItem, constraint: CharSequence?): Boolean {
if (constraint.isNullOrEmpty()) return true
val locale = Locale.getDefault()
val displayName = item.displayName.toLowerCase(locale)
return constraint.splitToSequence(" ")
.map { it.toLowerCase(locale) }
.any { it in displayName }
}
companion object {
private const val EXTRA_ACCOUNT = "account"
@JvmStatic
fun launch(context: Context, account: Account) {
val intent = Intent(context, ManageFoldersActivity::class.java)
intent.putExtra(EXTRA_ACCOUNT, account.uuid)
context.startActivity(intent)
fun launch(activity: Activity, account: Account) {
val intent = Intent(activity, ManageFoldersActivity::class.java).apply {
putExtra(EXTRA_ACCOUNT, account.uuid)
}
activity.startActivity(intent)
}
}
}

View file

@ -0,0 +1,152 @@
package com.fsck.k9.ui.managefolders
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import com.fsck.k9.Account
import com.fsck.k9.Preferences
import com.fsck.k9.activity.setup.FolderSettings
import com.fsck.k9.controller.MessagingController
import com.fsck.k9.mailstore.DisplayFolder
import com.fsck.k9.ui.R
import com.fsck.k9.ui.folders.FolderIconProvider
import com.fsck.k9.ui.folders.FolderNameFormatter
import com.fsck.k9.ui.observeNotNull
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import java.util.Locale
class ManageFoldersFragment : Fragment() {
private val viewModel: ManageFoldersViewModel by viewModel()
private val folderNameFormatter: FolderNameFormatter by inject { parametersOf(requireActivity()) }
private val messagingController: MessagingController by inject()
private val preferences: Preferences by inject()
private val folderIconProvider by lazy { FolderIconProvider(requireActivity().theme) }
private lateinit var account: Account
private lateinit var itemAdapter: ItemAdapter<FolderListItem>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
val arguments = arguments ?: error("Missing arguments")
val accountUuid = arguments.getString(EXTRA_ACCOUNT) ?: error("Missing argument '$EXTRA_ACCOUNT'")
account = preferences.getAccount(accountUuid) ?: error("Missing account: $accountUuid")
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_manage_folders, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
initializeFolderList()
viewModel.getFolders(account).observeNotNull(this) { folders ->
updateFolderList(folders)
}
}
private fun initializeFolderList() {
itemAdapter = ItemAdapter()
itemAdapter.itemFilter.filterPredicate = ::folderListFilter
val folderListAdapter = FastAdapter.with(itemAdapter).apply {
setHasStableIds(true)
onClickListener = { _, _, item: FolderListItem, _ ->
openFolderSettings(item.serverId)
true
}
}
val recyclerView = requireView().findViewById<RecyclerView>(R.id.folderList)
recyclerView.adapter = folderListAdapter
}
private fun updateFolderList(displayFolders: List<DisplayFolder>) {
val folderListItems = displayFolders.map { displayFolder ->
val databaseId = displayFolder.folder.id
val folderIconResource = folderIconProvider.getFolderIcon(displayFolder.folder.type)
val displayName = folderNameFormatter.displayName(displayFolder.folder)
val serverId = displayFolder.folder.serverId
FolderListItem(databaseId, folderIconResource, displayName, serverId)
}
itemAdapter.set(folderListItems)
}
private fun openFolderSettings(folderServerId: String) {
FolderSettings.actionSettings(requireActivity(), account, folderServerId)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.folder_list_option, menu)
configureFolderSearchView(menu)
}
private fun configureFolderSearchView(menu: Menu) {
val folderMenuItem = menu.findItem(R.id.filter_folders)
val folderSearchView = folderMenuItem.actionView as SearchView
folderSearchView.queryHint = getString(R.string.folder_list_filter_hint)
folderSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
itemAdapter.filter(query)
return true
}
override fun onQueryTextChange(newText: String): Boolean {
itemAdapter.filter(newText)
return true
}
})
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.list_folders -> refreshFolderList()
R.id.display_1st_class -> setDisplayMode(Account.FolderMode.FIRST_CLASS)
R.id.display_1st_and_2nd_class -> setDisplayMode(Account.FolderMode.FIRST_AND_SECOND_CLASS)
R.id.display_not_second_class -> setDisplayMode(Account.FolderMode.NOT_SECOND_CLASS)
R.id.display_all -> setDisplayMode(Account.FolderMode.ALL)
else -> return super.onOptionsItemSelected(item)
}
return true
}
private fun refreshFolderList() {
messagingController.refreshFolderList(account)
}
private fun setDisplayMode(newMode: Account.FolderMode) {
account.folderDisplayMode = newMode
preferences.saveAccount(account)
itemAdapter.filter(null)
}
private fun folderListFilter(item: FolderListItem, constraint: CharSequence?): Boolean {
if (constraint.isNullOrEmpty()) return true
val locale = Locale.getDefault()
val displayName = item.displayName.toLowerCase(locale)
return constraint.splitToSequence(" ")
.map { it.toLowerCase(locale) }
.any { it in displayName }
}
companion object {
const val EXTRA_ACCOUNT = "account"
}
}

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true" />
</LinearLayout>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/folderList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/folder_list_item" />

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/navigation_onboarding"
app:startDestination="@id/manageFoldersScreen">
<fragment
android:id="@+id/manageFoldersScreen"
android:name="com.fsck.k9.ui.managefolders.ManageFoldersFragment"
android:label="@string/folders_action"
tools:layout="@layout/fragment_manage_folders">
</fragment>
</navigation>