diff --git a/build.gradle b/build.gradle index 24768ee2..4933627f 100644 --- a/build.gradle +++ b/build.gradle @@ -47,6 +47,8 @@ ext { androidUtilsVersion = '1.1.1-SNAPSHOT' + fastAdapterVersion = "5.2.3" + materialDrawerVersion = "8.0.1" clansFloatingActionButtonVersion = '1.6.4' diff --git a/ui/BankingAndroidApp/build.gradle b/ui/BankingAndroidApp/build.gradle index 280f05ce..9561bcb9 100644 --- a/ui/BankingAndroidApp/build.gradle +++ b/ui/BankingAndroidApp/build.gradle @@ -129,6 +129,12 @@ dependencies { implementation "com.google.android.material:material:$materialComponentsVersion" implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion" + implementation "com.mikepenz:fastadapter:$fastAdapterVersion" + implementation "com.mikepenz:fastadapter-extensions-binding:$fastAdapterVersion" + implementation "com.mikepenz:fastadapter-extensions-drag:$fastAdapterVersion" + implementation "com.mikepenz:fastadapter-extensions-swipe:$fastAdapterVersion" + implementation "com.mikepenz:fastadapter-extensions-utils:$fastAdapterVersion" + // for MaterialDrawer implementation "com.mikepenz:materialdrawer:$materialDrawerVersion" implementation "com.mikepenz:materialdrawer-nav:$materialDrawerVersion" diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankAccountsAdapter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankAccountsAdapter.kt index 0d37a533..33eef006 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankAccountsAdapter.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankAccountsAdapter.kt @@ -1,13 +1,12 @@ package net.dankito.banking.ui.android.adapter import android.content.Context -import android.net.Uri import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.ImageView import kotlinx.android.synthetic.main.list_item_bank_account.view.* import net.dankito.banking.ui.android.R +import net.dankito.banking.ui.android.extensions.setIcon import net.dankito.banking.ui.model.TypedBankAccount import net.dankito.utils.android.ui.adapter.ListAdapter @@ -24,20 +23,10 @@ open class BankAccountsAdapter(accounts: List) : ListAdapter(), IDraggable { + + override var isDraggable = true + + override val type: Int + get() = R.id.bank_data_item_id + + override val layoutRes: Int + get() = R.layout.list_item_bank_data + + override fun getViewHolder(v: View): ViewHolder { + return ViewHolder(v) + } + + + class ViewHolder(view: View) : FastAdapter.ViewHolder(view) { + + protected var bankIcon: ImageView = view.findViewById(R.id.imgBankIcon) + protected var bankDisplayName: TextView = view.findViewById(R.id.txtBankDisplayName) + + + override fun bindView(item: BankDataAdapterItem, payloads: List) { + bankIcon.setIcon(item.bank) + bankDisplayName.text = item.bank.displayName + } + + override fun unbindView(item: BankDataAdapterItem) { + bankDisplayName.text = null + bankIcon.setImageURI(null) + } + } + +} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/FastAdapterRecyclerView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/FastAdapterRecyclerView.kt new file mode 100644 index 00000000..1ce5fa60 --- /dev/null +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/FastAdapterRecyclerView.kt @@ -0,0 +1,81 @@ +package net.dankito.banking.ui.android.adapter + +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.mikepenz.fastadapter.FastAdapter +import com.mikepenz.fastadapter.GenericItem +import com.mikepenz.fastadapter.adapters.ItemAdapter +import com.mikepenz.fastadapter.adapters.ItemAdapter.Companion.items +import com.mikepenz.fastadapter.drag.ItemTouchCallback +import com.mikepenz.fastadapter.drag.SimpleDragCallback +import com.mikepenz.fastadapter.select.getSelectExtension +import com.mikepenz.fastadapter.utils.DragDropUtil + + +open class FastAdapterRecyclerView( + recyclerView: RecyclerView, + items: List = listOf(), + enableDragAndDrop: Boolean = false, + open var itemDropped: ((oldPosition: Int, oldItem: Item, newPosition: Int, newItem: Item) -> Unit)? = null, + open var onClickListener: ((Item) -> Unit)? = null +) : ItemTouchCallback { + + + protected val fastAdapter: FastAdapter + protected val itemAdapter: ItemAdapter + + private lateinit var touchCallback: SimpleDragCallback + private lateinit var touchHelper: ItemTouchHelper + + + init { + itemAdapter = items() + + fastAdapter = FastAdapter.with(itemAdapter) + + init(recyclerView, items, enableDragAndDrop) + } + + + protected open fun init(recyclerView: RecyclerView, items: List, enableDragAndDrop: Boolean = true) { + val selectExtension = fastAdapter.getSelectExtension() + selectExtension.isSelectable = true + + fastAdapter.onClickListener = { _, _, item, _ -> + onClickListener?.invoke(item) + false + } + + recyclerView.layoutManager = LinearLayoutManager(recyclerView.context) + recyclerView.itemAnimator = DefaultItemAnimator() + recyclerView.adapter = fastAdapter + + itemAdapter.set(items) + + + if (enableDragAndDrop) { + touchCallback = SimpleDragCallback(this) + touchHelper = ItemTouchHelper(touchCallback) + touchHelper.attachToRecyclerView(recyclerView) + } + } + + + override fun itemTouchStartDrag(viewHolder: RecyclerView.ViewHolder) { + // add visual highlight to dragged item + } + + override fun itemTouchOnMove(oldPosition: Int, newPosition: Int): Boolean { + DragDropUtil.onMove(itemAdapter, oldPosition, newPosition) // change position + return true + } + + override fun itemTouchDropped(oldPosition: Int, newPosition: Int) { + // remove visual highlight to dropped item + + itemDropped?.invoke(oldPosition, itemAdapter.getAdapterItem(oldPosition), newPosition, itemAdapter.getAdapterItem(newPosition)) + } + +} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingComponent.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingComponent.kt index a5e97066..7fa2a8ae 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingComponent.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/di/BankingComponent.kt @@ -7,7 +7,7 @@ import net.dankito.banking.ui.android.dialogs.AddAccountDialog import net.dankito.banking.ui.android.dialogs.EnterTanDialog import net.dankito.banking.ui.android.dialogs.SendMessageLogDialog import net.dankito.banking.ui.android.dialogs.TransferMoneyDialog -import net.dankito.banking.ui.android.dialogs.settings.BankSettingsDialog +import net.dankito.banking.ui.android.dialogs.settings.SettingsDialogBase import net.dankito.banking.ui.android.home.HomeFragment import javax.inject.Singleton @@ -33,7 +33,7 @@ interface BankingComponent { fun inject(transferMoneyDialog: TransferMoneyDialog) - fun inject(bankSettingsDialog: BankSettingsDialog) + fun inject(settingsDialogBase: SettingsDialogBase) fun inject(sendMessageLogDialog: SendMessageLogDialog) diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/SendMessageLogDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/SendMessageLogDialog.kt index fa3253fe..a747e19c 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/SendMessageLogDialog.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/SendMessageLogDialog.kt @@ -51,7 +51,7 @@ open class SendMessageLogDialog : DialogFragment() { } protected open fun setupUI(rootView: View) { - val messageLog = presenter.getMessageLogForAccounts(presenter.allBanks).joinToString("\r\n\r\n") + val messageLog = presenter.getMessageLogForAccounts(presenter.allBanksSortedByDisplayIndex).joinToString("\r\n\r\n") if (messageLog.isBlank()) { rootView.txtvwInfoNoMessageLogEntriesYet.visibility = View.VISIBLE diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/BankSettingsDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/BankSettingsDialog.kt index 1c232bb9..02b67c91 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/BankSettingsDialog.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/BankSettingsDialog.kt @@ -18,7 +18,7 @@ import net.dankito.banking.ui.presenter.BankingPresenter import javax.inject.Inject -open class BankSettingsDialog : DialogFragment() { +open class BankSettingsDialog : SettingsDialogBase() { companion object { const val DialogTag = "BankSettingsDialog" @@ -28,23 +28,11 @@ open class BankSettingsDialog : DialogFragment() { protected lateinit var bank: TypedBankData - @Inject - protected lateinit var presenter: BankingPresenter - - init { - BankingComponent.component.inject(this) - } - - - - fun show(bank: TypedBankData, activity: AppCompatActivity, fullscreen: Boolean = false) { + fun show(bank: TypedBankData, activity: AppCompatActivity) { this.bank = bank - val style = if (fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.FloatingDialog - setStyle(STYLE_NORMAL, style) - - show(activity.supportFragmentManager, DialogTag) + show(activity, DialogTag) } @@ -59,12 +47,7 @@ open class BankSettingsDialog : DialogFragment() { protected open fun setupUI(rootView: View) { rootView.apply { toolbar.apply { - title = bank.bankName - - inflateMenu(R.menu.menu_bank_settings_dialog) - setOnMenuItemClickListener { item -> onOptionsItemSelected(item) } - - setNavigationOnClickListener { askToDismissChanges() } + setupToolbar(this, bank.bankName) } edtxtBankName.text = bank.displayName @@ -76,34 +59,12 @@ open class BankSettingsDialog : DialogFragment() { } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.mnitmSaveChanges -> saveChangesAndCloseDialog() - else -> super.onOptionsItemSelected(item) - } - } - - - protected val hasUnsavedChanges: Boolean + override val hasUnsavedChanges: Boolean get() = didChange(edtxtBankName, bank.displayName) || didChange(edtxtUserName, bank.userName) || didChange(edtxtPassword, bank.password) - protected open fun didChange(editedValue: FormEditText, originalValue: String): Boolean { - return editedValue.text != originalValue - } - - protected open fun saveChangesAndCloseDialog(): Boolean { - if (hasUnsavedChanges) { - saveChanges() - } - - closeDialog() - - return true - } - - protected open fun saveChanges() { + override fun saveChanges() { bank.userSetDisplayName = edtxtBankName.text bank.userName = edtxtUserName.text bank.password = edtxtPassword.text @@ -111,22 +72,10 @@ open class BankSettingsDialog : DialogFragment() { presenter.bankUpdated(bank) } - protected open fun askToDismissChanges() { - if (hasUnsavedChanges) { - AskDismissChangesAlert().show(this) - } - else { - closeDialog() - } - } - protected open fun askUserToDeleteAccount() { AskDeleteAccountAlert().show(bank, presenter, requireContext()) { closeDialog() } } - protected open fun closeDialog() { - dismiss() - } } \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialog.kt new file mode 100644 index 00000000..878fc70b --- /dev/null +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialog.kt @@ -0,0 +1,70 @@ +package net.dankito.banking.ui.android.dialogs.settings + +import android.os.Bundle +import android.view.* +import androidx.appcompat.app.AppCompatActivity +import kotlinx.android.synthetic.main.dialog_settings.view.* +import net.dankito.banking.ui.android.R +import net.dankito.banking.ui.android.adapter.BankDataAdapterItem +import net.dankito.banking.ui.android.adapter.FastAdapterRecyclerView +import net.dankito.banking.ui.model.TypedBankData + + +open class SettingsDialog : SettingsDialogBase() { + + companion object { + const val DialogTag = "SettingsDialog" + } + + + + fun show(activity: AppCompatActivity) { + show(activity, DialogTag) + } + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val rootView = inflater.inflate(R.layout.dialog_settings, container, false) + + setupUI(rootView) + + return rootView + } + + protected open fun setupUI(rootView: View) { + rootView.apply { + toolbar.apply { + setupToolbar(this, rootView.context.getString(R.string.dialog_settings_title), false) + } + + val items = presenter.allBanksSortedByDisplayIndex.map { BankDataAdapterItem(it) } + val adapter = FastAdapterRecyclerView(rootView.rcyBankCredentials, items, true) + adapter.onClickListener = { navigationToBankSettingsDialog(it.bank) } + adapter.itemDropped = { oldPosition, oldItem, newPosition, newItem -> reorderedBanks(oldPosition, oldItem.bank, newPosition, newItem.bank) } + + rootView.btnShowSendMessageLogDialog.setOnClickListener { presenter.showSendMessageLogDialog() } + } + } + + + protected open fun navigationToBankSettingsDialog(bank: TypedBankData) { + BankSettingsDialog().show(bank, requireActivity() as AppCompatActivity) + } + + protected open fun reorderedBanks(oldPosition: Int, oldItem: TypedBankData, newPosition: Int, newItem: TypedBankData) { + oldItem.displayIndex = oldPosition + newItem.displayIndex = newPosition + + presenter.bankDisplayIndexUpdated(oldItem) + presenter.bankDisplayIndexUpdated(newItem) + } + + + override val hasUnsavedChanges: Boolean + get() = false + + override fun saveChanges() { + + } + +} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialogBase.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialogBase.kt new file mode 100644 index 00000000..af7fb57c --- /dev/null +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/settings/SettingsDialogBase.kt @@ -0,0 +1,89 @@ +package net.dankito.banking.ui.android.dialogs.settings + +import android.view.* +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.DialogFragment +import net.dankito.banking.ui.android.R +import net.dankito.banking.ui.android.alerts.AskDismissChangesAlert +import net.dankito.banking.ui.android.di.BankingComponent +import net.dankito.banking.ui.android.views.FormEditText +import net.dankito.banking.ui.presenter.BankingPresenter +import javax.inject.Inject + + +abstract class SettingsDialogBase : DialogFragment() { + + protected abstract val hasUnsavedChanges: Boolean + + protected abstract fun saveChanges() + + + @Inject + protected lateinit var presenter: BankingPresenter + + + init { + BankingComponent.component.inject(this) + } + + + + fun show(activity: AppCompatActivity, dialogTag: String, fullscreen: Boolean = true) { + val style = if (fullscreen) R.style.FullscreenDialogWithStatusBar else R.style.FloatingDialog + setStyle(STYLE_NORMAL, style) + + show(activity.supportFragmentManager, dialogTag) + } + + + protected open fun setupToolbar(toolbar: Toolbar, dialogTitle: String, showSaveButton: Boolean = true) { + toolbar.apply { + title = dialogTitle + + inflateMenu(R.menu.menu_settings_dialog) + menu.findItem(R.id.mnitmSaveChanges).isVisible = showSaveButton + + setOnMenuItemClickListener { item -> onOptionsItemSelected(item) } + + setNavigationOnClickListener { askToDismissChanges() } + } + } + + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.mnitmSaveChanges -> saveChangesAndCloseDialog() + else -> super.onOptionsItemSelected(item) + } + } + + + protected open fun didChange(editedValue: FormEditText, originalValue: String): Boolean { + return editedValue.text != originalValue + } + + + protected open fun saveChangesAndCloseDialog(): Boolean { + if (hasUnsavedChanges) { + saveChanges() + } + + closeDialog() + + return true + } + + protected open fun askToDismissChanges() { + if (hasUnsavedChanges) { + AskDismissChangesAlert().show(this) + } + else { + closeDialog() + } + } + + protected open fun closeDialog() { + dismiss() + } +} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ImageViewExtensions.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ImageViewExtensions.kt new file mode 100644 index 00000000..07ce4090 --- /dev/null +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/extensions/ImageViewExtensions.kt @@ -0,0 +1,17 @@ +package net.dankito.banking.ui.android.extensions + +import android.net.Uri +import android.view.View +import android.widget.ImageView +import net.dankito.banking.ui.model.IBankData + + +fun ImageView.setIcon(bank: IBankData<*, *>) { + try { + val iconUrl = bank.iconUrl + this.visibility = if (iconUrl == null) View.GONE else View.VISIBLE + this.setImageURI(Uri.parse(iconUrl)) + } catch (e: Exception) { + this.visibility = View.GONE + } +} \ No newline at end of file diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt index 4679ae40..0a86f2c4 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt @@ -3,6 +3,7 @@ package net.dankito.banking.ui.android.views import android.view.View import android.widget.TextView import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome @@ -19,6 +20,7 @@ import com.mikepenz.materialdrawer.util.removeItemByPosition import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView import net.dankito.banking.ui.android.R import net.dankito.banking.ui.android.dialogs.settings.BankSettingsDialog +import net.dankito.banking.ui.android.dialogs.settings.SettingsDialog import net.dankito.banking.ui.android.extensions.withIcon import net.dankito.banking.ui.model.TypedBankData import net.dankito.banking.ui.presenter.BankingPresenter @@ -103,10 +105,11 @@ open class DrawerView( , PrimaryDrawerItem() - .withName(R.string.drawer_menu_send_message_log_title) - .withIcon(activity, GoogleMaterial.Icon.gmd_mail, R.color.primaryTextColor_Dark) + .withName(R.string.drawer_menu_show_settings_dialog_title) + .withIcon(R.drawable.ic_baseline_settings_24) + .withIconColor(ContextCompat.getColorStateList(activity, R.color.primaryTextColor_Dark)!!) .withSelectable(false) - .withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.showSendMessageLogDialog() } } + .withOnDrawerItemClickListener { _, _, _ -> itemClicked { SettingsDialog().show(activity) } } ) } @@ -129,7 +132,7 @@ open class DrawerView( } private fun createAccountsDrawerItems(): List> { - return presenter.allBanks.map { account -> + return presenter.allBanksSortedByDisplayIndex.map { account -> val accountItem = createAccountDrawerItem(account) val accountsItems = createBankAccountsDrawerItems(account).toMutableList() @@ -182,7 +185,7 @@ open class DrawerView( } private fun editAccount(bank: TypedBankData) { - BankSettingsDialog().show(bank, activity, true) + BankSettingsDialog().show(bank, activity) } private fun showAppVersion(navigationHeaderView: View?) { diff --git a/ui/BankingAndroidApp/src/main/res/drawable/ic_baseline_drag_handle_24.xml b/ui/BankingAndroidApp/src/main/res/drawable/ic_baseline_drag_handle_24.xml new file mode 100644 index 00000000..88fe5b40 --- /dev/null +++ b/ui/BankingAndroidApp/src/main/res/drawable/ic_baseline_drag_handle_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/ui/BankingAndroidApp/src/main/res/drawable/ic_baseline_send_24.xml b/ui/BankingAndroidApp/src/main/res/drawable/ic_baseline_send_24.xml new file mode 100644 index 00000000..fe37f93f --- /dev/null +++ b/ui/BankingAndroidApp/src/main/res/drawable/ic_baseline_send_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/ui/BankingAndroidApp/src/main/res/layout/dialog_settings.xml b/ui/BankingAndroidApp/src/main/res/layout/dialog_settings.xml new file mode 100644 index 00000000..2f875662 --- /dev/null +++ b/ui/BankingAndroidApp/src/main/res/layout/dialog_settings.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + +