Implemented showing accounts and bank accounts hierarchically in Drawer

This commit is contained in:
dankito 2020-04-22 20:33:14 +02:00
parent 563afba3bc
commit 0699eaf977
13 changed files with 211 additions and 190 deletions

View File

@ -14,7 +14,9 @@ ext {
/* Android */ /* Android */
androidUtilsVersion = '1.1.0' androidUtilsVersion = '1.1.1-SNAPSHOT'
materialDrawerVersion = "8.0.1"
clansFloatingActionButtonVersion = '1.6.4' clansFloatingActionButtonVersion = '1.6.4'

View File

@ -8,7 +8,7 @@ android {
defaultConfig { defaultConfig {
applicationId "net.dankito.banking.fints4java.android" applicationId "net.dankito.banking.fints4java.android"
minSdkVersion 15 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 28
versionCode appVersionCode versionCode appVersionCode
versionName appVersionName versionName appVersionName
@ -77,6 +77,14 @@ dependencies {
implementation "com.google.android.material:material:$materialComponentsVersion" implementation "com.google.android.material:material:$materialComponentsVersion"
implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion" implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
// for MaterialDrawer
implementation "com.mikepenz:materialdrawer:$materialDrawerVersion"
implementation "com.mikepenz:materialdrawer-nav:$materialDrawerVersion"
implementation "com.mikepenz:materialdrawer-iconics:$materialDrawerVersion"
implementation "com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar"
implementation "com.mikepenz:fontawesome-typeface:5.9.0.0-kotlin@aar"
implementation 'androidx.navigation:navigation-fragment:2.0.0-rc02' implementation 'androidx.navigation:navigation-fragment:2.0.0-rc02'
implementation 'androidx.navigation:navigation-ui:2.0.0-rc02' implementation 'androidx.navigation:navigation-ui:2.0.0-rc02'

View File

@ -2,23 +2,18 @@ package net.dankito.banking.fints4java.android
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem
import android.view.MotionEvent import android.view.MotionEvent
import android.view.SubMenu
import android.widget.TextView
import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import com.github.clans.fab.FloatingActionMenu import com.github.clans.fab.FloatingActionMenu
import com.google.android.material.navigation.NavigationView import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.action_view_account_menu_item.view.* import net.dankito.banking.fints4java.android.ui.views.DrawerView
import net.dankito.banking.fints4java.android.ui.views.MainActivityFloatingActionMenuButton import net.dankito.banking.fints4java.android.ui.views.MainActivityFloatingActionMenuButton
import net.dankito.banking.fints4java.android.util.Base64ServiceAndroid import net.dankito.banking.fints4java.android.util.Base64ServiceAndroid
import net.dankito.banking.fints4javaBankingClientCreator import net.dankito.banking.fints4javaBankingClientCreator
import net.dankito.banking.persistence.BankingPersistenceJson import net.dankito.banking.persistence.BankingPersistenceJson
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.utils.web.client.OkHttpWebClient import net.dankito.utils.web.client.OkHttpWebClient
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -36,6 +31,8 @@ class MainActivity : AppCompatActivity() {
private lateinit var drawerToggle: ActionBarDrawerToggle private lateinit var drawerToggle: ActionBarDrawerToggle
private lateinit var drawerView: DrawerView
private lateinit var floatingActionMenuButton: MainActivityFloatingActionMenuButton private lateinit var floatingActionMenuButton: MainActivityFloatingActionMenuButton
@ -60,7 +57,6 @@ class MainActivity : AppCompatActivity() {
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navigationView: NavigationView = findViewById(R.id.nav_view)
// val navController = findNavController(R.id.nav_host_fragment) // val navController = findNavController(R.id.nav_host_fragment)
// // Passing each menu ID as a set of Ids because each // // Passing each menu ID as a set of Ids because each
@ -80,7 +76,9 @@ class MainActivity : AppCompatActivity() {
drawerLayout.addDrawerListener(drawerToggle) drawerLayout.addDrawerListener(drawerToggle)
drawerToggle.syncState() drawerToggle.syncState()
setupNavigationView(navigationView, drawerLayout) slider?.let { slider ->
drawerView = DrawerView(this, slider, presenter)
}
val floatingActionMenu = findViewById<FloatingActionMenu>(R.id.floatingActionMenu) val floatingActionMenu = findViewById<FloatingActionMenu>(R.id.floatingActionMenu)
floatingActionMenuButton = MainActivityFloatingActionMenuButton(floatingActionMenu, presenter) floatingActionMenuButton = MainActivityFloatingActionMenuButton(floatingActionMenu, presenter)
@ -93,89 +91,6 @@ class MainActivity : AppCompatActivity() {
} }
private fun setupNavigationView(navigationView: NavigationView, drawerLayout: DrawerLayout) {
showAppVersion(navigationView)
val navigationMenu = navigationView.menu
val accountsMenuItem = navigationMenu.findItem(R.id.navBankAccountsSectionItem)
val accountsMenu = accountsMenuItem.subMenu
presenter.addAccountsChangedListener {
runOnUiThread { updateNavigationMenuItems(accountsMenu) }
}
updateNavigationMenuItems(accountsMenu)
navigationView.setNavigationItemSelectedListener { navigationItemSelected(it) }
}
private fun updateNavigationMenuItems(accountsMenu: SubMenu) {
accountsMenu.findItem(R.id.navAllBankAccounts).isVisible = presenter.accounts.isNotEmpty()
// removes previously shown accounts; index 0 = 'Add account', 1 = 'All accounts', don't remove these
for (index in (accountsMenu.size() - 1) downTo 2) {
accountsMenu.removeItem(accountsMenu.getItem(index).itemId)
}
presenter.accounts.forEach { account ->
val accountMenuItem = accountsMenu.add("")
accountMenuItem.setActionView(R.layout.action_view_account_menu_item)
accountMenuItem.actionView.txtvwAccountName.text = account.displayName
accountMenuItem.actionView.imgvwEditAccount.setImageResource(R.drawable.ic_build_white_48dp)
accountMenuItem.actionView.imgvwEditAccount.setOnClickListener { editAccount(account) }
accountMenuItem.setOnMenuItemClickListener { setSelectedAccount(account) }
}
}
private fun setSelectedAccount(account: Account): Boolean {
presenter.selectedAccount(account)
closeDrawer()
return true
}
private fun editAccount(account: Account) {
// TODO: implement
log.info("Edit account $account")
closeDrawer()
}
private fun showAppVersion(navigationView: NavigationView) {
try {
val packageInfo = this.packageManager.getPackageInfo(packageName, 0)
val version = packageInfo.versionName
(navigationView.getHeaderView(0).findViewById(R.id.txtAppVersion) as? TextView)?.text = version
} catch (e: Exception) {
log.error("Could not read application version")
}
}
private fun navigationItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.navAddBankAccount -> presenter.showAddAccountDialog()
R.id.navAllBankAccounts -> presenter.selectedAllBankAccounts()
}
closeDrawer()
return true
}
private fun closeDrawer() {
val drawer = findViewById(R.id.drawer_layout) as DrawerLayout
drawer.closeDrawer(GravityCompat.START)
}
// override fun onSupportNavigateUp(): Boolean {
// val navController = findNavController(R.id.nav_host_fragment)
// return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
// }
override fun dispatchTouchEvent(event: MotionEvent): Boolean { override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if(floatingActionMenuButton.handlesTouch(event)) { // close menu when menu is opened and touch is outside floatingActionMenuButton if(floatingActionMenuButton.handlesTouch(event)) { // close menu when menu is opened and touch is outside floatingActionMenuButton
return true return true

View File

@ -0,0 +1,19 @@
package net.dankito.banking.fints4java.android.ui.extensions
import android.content.Context
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.materialdrawer.iconics.withIcon
import com.mikepenz.materialdrawer.model.AbstractBadgeableDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.withIconColor
import net.dankito.utils.android.extensions.createColorStateList
fun <Item : AbstractBadgeableDrawerItem<Item>> AbstractBadgeableDrawerItem<Item>.withIcon(
context: Context, icon: IIcon, iconColorId: Int): AbstractBadgeableDrawerItem<Item> {
withIcon(icon)
withIconColor(context.createColorStateList(iconColorId))
return this
}

View File

@ -0,0 +1,148 @@
package net.dankito.banking.fints4java.android.ui.views
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.mikepenz.materialdrawer.model.SectionDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.util.addItems
import com.mikepenz.materialdrawer.util.addItemsAtPosition
import com.mikepenz.materialdrawer.util.removeItemByPosition
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
import net.dankito.banking.fints4java.android.R
import net.dankito.banking.fints4java.android.ui.extensions.withIcon
import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.presenter.BankingPresenter
import org.slf4j.LoggerFactory
open class DrawerView(
protected val activity: AppCompatActivity,
protected val slider: MaterialDrawerSliderView,
protected val presenter: BankingPresenter
) {
companion object {
private const val AccountLevel = 2
private const val BankAccountLevel = 7
private const val AccountsSectionHeaderId = 1000L
private const val AllAccountsId = 1001L
private const val AddAccountId = 1002L
private const val CountDefaultAccountItems = 3
private val log = LoggerFactory.getLogger(DrawerView::class.java)
}
init {
setupDrawerView()
}
protected open fun setupDrawerView() {
slider.headerView = activity.layoutInflater.inflate(R.layout.nav_header_main, null)
showAppVersion(slider.headerView)
setDefaultDrawerItems()
presenter.addAccountsChangedListener {
activity.runOnUiThread { updateDrawerItems() }
}
updateDrawerItems()
}
private fun setDefaultDrawerItems() {
slider.apply {
addItems(
SectionDrawerItem()
.withName(R.string.drawer_menu_bank_accounts_section_title)
.withIdentifier(AccountsSectionHeaderId)
.withDivider(false)
,
PrimaryDrawerItem()
.withName(R.string.drawer_menu_all_bank_accounts_title)
.withIdentifier(AllAccountsId)
.withLevel(AccountLevel)
.withSelected(true)
.withIcon(activity, GoogleMaterial.Icon.gmd_account_balance, R.color.primaryTextColor_Dark)
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAllBankAccounts() } }
,
PrimaryDrawerItem()
.withName(R.string.drawer_menu_add_bank_account_title)
.withIdentifier(AddAccountId)
.withLevel(AccountLevel)
.withIcon(activity, GoogleMaterial.Icon.gmd_add, R.color.primaryTextColor_Dark)
.withSelectable(false)
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.showAddAccountDialog() } }
)
}
}
private fun updateDrawerItems() {
// removes previously shown accounts; index 1 = 'Accounts header', 1 = 'All accounts', index 2 = 'Add account', don't remove these
while (slider.itemAdapter.adapterItems.size > CountDefaultAccountItems) {
slider.removeItemByPosition(CountDefaultAccountItems)
}
val accountItems = createAccountsDrawerItems()
slider.addItemsAtPosition(CountDefaultAccountItems, *accountItems.toTypedArray())
}
private fun createAccountsDrawerItems(): List<IDrawerItem<*>> {
return presenter.accounts.map { account ->
val accountItem = createAccountDrawerItem(account)
val bankAccountsItems = createBankAccountsDrawerItems(account).toMutableList()
bankAccountsItems.add(0, accountItem)
return@map bankAccountsItems
}.flatten()
}
private fun createAccountDrawerItem(account: Account): PrimaryDrawerItem {
return PrimaryDrawerItem()
.withName(account.displayName)
.withLevel(AccountLevel)
.withIcon(activity, FontAwesome.Icon.faw_piggy_bank, R.color.primaryTextColor_Dark)
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedAccount(account) } }
}
private fun createBankAccountsDrawerItems(account: Account): List<IDrawerItem<*>> {
return account.bankAccounts.map { bankAccount ->
SecondaryDrawerItem()
.withName(bankAccount.displayName)
.withLevel(BankAccountLevel)
.withOnDrawerItemClickListener { _, _, _ -> itemClicked { presenter.selectedBankAccount(bankAccount) } }
}
}
private fun itemClicked(action: () -> Unit): Boolean {
action()
return false
}
private fun showAppVersion(navigationHeaderView: View?) {
try {
val packageInfo = activity.packageManager.getPackageInfo(activity.packageName, 0)
val version = packageInfo.versionName
(navigationHeaderView?.findViewById(R.id.txtAppVersion) as? TextView)?.text = version
} catch (e: Exception) {
log.error("Could not read application version")
}
}
}

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/txtvwAccountName"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_toLeftOf="@+id/imgvwEditAccount"
android:layout_toStartOf="@+id/imgvwEditAccount"
android:gravity="center_vertical"
android:textColor="@color/primaryTextColor_Dark"
/>
<ImageView
android:id="@+id/imgvwEditAccount"
android:layout_width="@dimen/navigation_menu_icon_edit_account_width_and_height"
android:layout_height="@dimen/navigation_menu_icon_edit_account_width_and_height"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="@dimen/navigation_menu_icon_and_space_width"
android:layout_marginEnd="@dimen/navigation_menu_icon_and_space_width"
android:src="@drawable/ic_check_circle_white_48dp"
/>
</RelativeLayout>

View File

@ -15,16 +15,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<com.google.android.material.navigation.NavigationView <com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
android:id="@+id/nav_view" android:id="@+id/slider"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="start" android:layout_gravity="start"
android:fitsSystemWindows="true" android:fitsSystemWindows="false"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer"
android:theme="@style/AppTheme.NavigationView"
android:background="@color/backgroundColor_Dark"
/> />
</androidx.drawerlayout.widget.DrawerLayout> </androidx.drawerlayout.widget.DrawerLayout>

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view"
>
<item
android:id="@+id/navBankAccountsSectionItem"
android:title="@string/nav_menu_bank_accounts_section_title"
>
<menu
android:id="@+id/navBankAccountsSectionMenu"
>
<item
android:id="@+id/navAddBankAccount"
android:icon="@drawable/ic_add_white_48dp"
android:title="@string/nav_menu_add_bank_account_title"
/>
<item
android:id="@+id/navAllBankAccounts"
android:title="@string/nav_menu_all_bank_accounts_title"
android:visible="false"
/>
</menu>
</item>
</menu>

View File

@ -15,9 +15,9 @@
<string name="nav_header_title">@string/app_name</string> <string name="nav_header_title">@string/app_name</string>
<string name="nav_header_version_label">Version\u0020</string> <string name="nav_header_version_label">Version\u0020</string>
<string name="nav_header_desc">Navigationsbereich</string> <string name="nav_header_desc">Navigationsbereich</string>
<string name="nav_menu_bank_accounts_section_title">Konten</string> <string name="drawer_menu_bank_accounts_section_title">Konten</string>
<string name="nav_menu_add_bank_account_title">Konto hinzufügen</string> <string name="drawer_menu_add_bank_account_title">Konto hinzufügen</string>
<string name="nav_menu_all_bank_accounts_title">Alle Konten</string> <string name="drawer_menu_all_bank_accounts_title">Alle Konten</string>
<string name="floating_action_menu_add_account">Konto</string> <string name="floating_action_menu_add_account">Konto</string>
<string name="floating_action_menu_transfer_money">Überweisung</string> <string name="floating_action_menu_transfer_money">Überweisung</string>

View File

@ -4,9 +4,6 @@
<dimen name="activity_vertical_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="nav_header_vertical_spacing">8dp</dimen> <dimen name="nav_header_vertical_spacing">8dp</dimen>
<dimen name="nav_header_height">176dp</dimen> <dimen name="nav_header_height">176dp</dimen>
<dimen name="navigation_menu_icon_title_space">8dp</dimen>
<dimen name="navigation_menu_icon_and_space_width">56dp</dimen>
<dimen name="navigation_menu_icon_edit_account_width_and_height">24dp</dimen>
<dimen name="fab_margin">16dp</dimen> <dimen name="fab_margin">16dp</dimen>
<dimen name="fab_margin_bottom_without_toolbar">16dp</dimen> <dimen name="fab_margin_bottom_without_toolbar">16dp</dimen>
<dimen name="fab_margin_bottom_with_toolbar">42dp</dimen> <dimen name="fab_margin_bottom_with_toolbar">42dp</dimen>

View File

@ -1,3 +0,0 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
</resources>

View File

@ -15,9 +15,9 @@
<string name="nav_header_title">@string/app_name</string> <string name="nav_header_title">@string/app_name</string>
<string name="nav_header_version_label">Version\u0020</string> <string name="nav_header_version_label">Version\u0020</string>
<string name="nav_header_desc">Navigation header</string> <string name="nav_header_desc">Navigation header</string>
<string name="nav_menu_bank_accounts_section_title">Accounts</string> <string name="drawer_menu_bank_accounts_section_title">Accounts</string>
<string name="nav_menu_add_bank_account_title">Add account</string> <string name="drawer_menu_all_bank_accounts_title">All accounts</string>
<string name="nav_menu_all_bank_accounts_title">All accounts</string> <string name="drawer_menu_add_bank_account_title">Add account</string>
<string name="floating_action_menu_add_account">Account</string> <string name="floating_action_menu_add_account">Account</string>
<string name="floating_action_menu_transfer_money">Transfer money</string> <string name="floating_action_menu_transfer_money">Transfer money</string>

View File

@ -10,6 +10,18 @@
<item name="fabTextColor">@color/fabTextColor</item> <item name="fabTextColor">@color/fabTextColor</item>
<item name="fabLabelBackgroundColor">@color/fabLabelBackgroundColor</item> <item name="fabLabelBackgroundColor">@color/fabLabelBackgroundColor</item>
<item name="fabShadowColor">@color/fabShadowColor</item> <item name="fabShadowColor">@color/fabShadowColor</item>
<!-- for MaterialDrawer-->
<item name="materialDrawerStyle">@style/Widget.MaterialDrawerStyleCustom</item>
<item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
</style>
<style name="Widget.MaterialDrawerStyleCustom" parent="Widget.MaterialDrawerStyle">
<item name="materialDrawerBackground">@color/backgroundColor_Dark</item>
<item name="materialDrawerPrimaryText">@color/primaryTextColor_Dark</item>
<item name="materialDrawerSecondaryText">@color/primaryTextColor_Dark</item>
<item name="materialDrawerDividerColor">@color/primaryTextColor_Dark</item>
<item name="materialDrawerSelectedBackgroundColor">@color/colorAccent</item>
</style> </style>
<style name="AppTheme.NoActionBar"> <style name="AppTheme.NoActionBar">
@ -21,15 +33,6 @@
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<style name="AppTheme.NavigationView">
<!-- Sets group title's text color -->
<item name="android:textColorSecondary">@color/primaryTextColor_Dark</item>
<!-- Sets menu items' text color -->
<item name="itemTextColor">@color/primaryTextColor_Dark</item>
<item name="itemIconTint">@color/primaryTextColor_Dark</item>
<item name="itemIconPadding">@dimen/navigation_menu_icon_title_space</item>
</style>
<!-- TODO: simply use Dialog as soon as AndroidUtils 2.0.0 is out --> <!-- TODO: simply use Dialog as soon as AndroidUtils 2.0.0 is out -->
<style name="FloatingDialog" parent="Dialog"> <style name="FloatingDialog" parent="Dialog">
<!-- to prevent that keyboard covers dialog --> <!-- to prevent that keyboard covers dialog -->