Added FloatingActionMenuButton so that users can trigger a money transfer without having to long click on an account transaction

This commit is contained in:
dankl 2020-01-03 17:57:05 +01:00 committed by dankito
parent 941746c18b
commit 87985469bf
15 changed files with 304 additions and 15 deletions

View File

@ -7,10 +7,12 @@ ext {
kotlinVersion = '1.3.41' kotlinVersion = '1.3.41'
javaUtilsVersion = '1.0.8' javaUtilsVersion = '1.0.9'
androidUtilsVersion = '1.1.0' androidUtilsVersion = '1.1.0'
clansFloatingActionButtonVersion = '1.6.4'
javaFxUtilsVersion = '1.0.3' javaFxUtilsVersion = '1.0.3'
junitVersion = '4.12' junitVersion = '4.12'

View File

@ -53,6 +53,8 @@ dependencies {
// TODO: try to get rid of this import // TODO: try to get rid of this import
implementation project(':fints4javaLib') implementation project(':fints4javaLib')
implementation "com.github.clans:fab:$clansFloatingActionButtonVersion"
api "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" api "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
api "net.dankito.utils:android-utils:$androidUtilsVersion", { api "net.dankito.utils:android-utils:$androidUtilsVersion", {

View File

@ -1,18 +1,19 @@
package net.dankito.banking.fints4java.android package net.dankito.banking.fints4java.android
import android.os.Bundle import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.design.widget.NavigationView import android.support.design.widget.NavigationView
import android.support.v4.widget.DrawerLayout import android.support.v4.widget.DrawerLayout
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar import android.support.v7.widget.Toolbar
import android.view.Menu import android.view.Menu
import androidx.navigation.findNavController import androidx.navigation.findNavController
import com.github.clans.fab.FloatingActionMenu
import kotlinx.android.synthetic.main.action_view_account_menu_item.view.*
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
import net.dankito.banking.fints4java.android.ui.dialogs.AddAccountDialog import net.dankito.banking.fints4java.android.ui.dialogs.AddAccountDialog
import net.dankito.banking.fints4java.android.ui.dialogs.EnterAtcDialog import net.dankito.banking.fints4java.android.ui.dialogs.EnterAtcDialog
import net.dankito.banking.fints4java.android.ui.dialogs.EnterTanDialog import net.dankito.banking.fints4java.android.ui.dialogs.EnterTanDialog
import net.dankito.banking.mapper.fints4javaModelMapper import net.dankito.banking.fints4java.android.ui.views.MainActivityFloatingActionMenuButton
import net.dankito.banking.ui.BankingClientCallback import net.dankito.banking.ui.BankingClientCallback
import net.dankito.banking.ui.model.Account import net.dankito.banking.ui.model.Account
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
@ -27,6 +28,9 @@ class MainActivity : AppCompatActivity() {
// private lateinit var appBarConfiguration: AppBarConfiguration // private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var floatingActionMenuButton: MainActivityFloatingActionMenuButton
val presenter = MainWindowPresenter(Base64ServiceAndroid(), object : BankingClientCallback { val presenter = MainWindowPresenter(Base64ServiceAndroid(), object : BankingClientCallback {
override fun enterTan(account: Account, tanChallenge: TanChallenge): EnterTanResult { override fun enterTan(account: Account, tanChallenge: TanChallenge): EnterTanResult {
@ -52,11 +56,6 @@ class MainActivity : AppCompatActivity() {
val toolbar: Toolbar = findViewById(R.id.toolbar) val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
val fab: FloatingActionButton = findViewById(R.id.fab)
fab.setOnClickListener { view ->
AddAccountDialog().show(this, presenter)
}
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view) val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment) val navController = findNavController(R.id.nav_host_fragment)
@ -71,11 +70,13 @@ class MainActivity : AppCompatActivity() {
// ) // )
// //
// setupActionBarWithNavController(navController, appBarConfiguration) // setupActionBarWithNavController(navController, appBarConfiguration)
// navView.setupWithNavController(navController) // navigationView.setupWithNavController(navController)
val floatingActionMenu = findViewById<FloatingActionMenu>(R.id.floatingActionMenu)
floatingActionMenuButton = MainActivityFloatingActionMenuButton(floatingActionMenu, presenter)
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu) menuInflater.inflate(R.menu.menu_main, menu)
return true return true
@ -86,6 +87,24 @@ class MainActivity : AppCompatActivity() {
// return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp() // return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
// } // }
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if(floatingActionMenuButton.handlesTouch(event)) { // close menu when menu is opened and touch is outside floatingActionMenuButton
return true
}
return super.dispatchTouchEvent(event)
}
override fun onBackPressed() {
if (floatingActionMenuButton.handlesBackButtonPress()) { // close menu when menu is opened and back button gets pressed
return
}
super.onBackPressed()
}
private fun getTanFromUserOffUiThread(account: Account, tanChallenge: TanChallenge): EnterTanResult { private fun getTanFromUserOffUiThread(account: Account, tanChallenge: TanChallenge): EnterTanResult {
val enteredTan = AtomicReference<EnterTanResult>(null) val enteredTan = AtomicReference<EnterTanResult>(null)
val tanEnteredLatch = CountDownLatch(1) val tanEnteredLatch = CountDownLatch(1)
@ -107,7 +126,6 @@ class MainActivity : AppCompatActivity() {
val tanEnteredLatch = CountDownLatch(1) val tanEnteredLatch = CountDownLatch(1)
runOnUiThread { runOnUiThread {
// TODO: don't create a fints4javaModelMapper instance here, let MainWindowPresenter do the job
EnterAtcDialog().show(tanMedium, this@MainActivity, false) { enteredResult -> EnterAtcDialog().show(tanMedium, this@MainActivity, false) { enteredResult ->
result.set(enteredResult) result.set(enteredResult)
tanEnteredLatch.countDown() tanEnteredLatch.countDown()

View File

@ -0,0 +1,78 @@
package net.dankito.banking.fints4java.android.ui.views
import android.os.Bundle
import android.view.MotionEvent
import com.github.clans.fab.FloatingActionMenu
import net.dankito.utils.android.extensions.isTouchInsideView
open class FloatingActionMenuButton(protected val floatingActionMenu: FloatingActionMenu) {
companion object {
private const val IS_OPENED_EXTRA_NAME = "IS_OPENED"
}
private var isClosingMenu = false
init {
setup()
}
protected open fun setup() {
floatingActionMenu.setClosedOnTouchOutside(true)
floatingActionMenu.setOnMenuToggleListener { isClosingMenu = false }
}
protected open fun executeAndCloseMenu(action: () -> Unit) {
action() // first execute action and then close menu as when action sets menu items visibility closeMenu() would otherwise overwrite this value
closeMenu()
}
protected open fun closeMenu() {
isClosingMenu = true // as closing is animated it takes till animation end till floatingActionMenu.isOpened is set to true
floatingActionMenu.close(true)
}
open fun handlesBackButtonPress(): Boolean {
if(floatingActionMenu.isOpened) {
closeMenu()
return true
}
return false
}
open fun handlesTouch(event: MotionEvent): Boolean {
if(floatingActionMenu.isOpened) { // if menu is opened and user clicked somewhere else in the view, close menu
if(floatingActionMenu.isTouchInsideView(event) == false) {
closeMenu()
return true
}
}
return false
}
open fun saveInstanceState(outState: Bundle?) {
outState?.let {
outState.putBoolean(IS_OPENED_EXTRA_NAME, floatingActionMenu.isOpened && isClosingMenu == false)
}
}
open fun restoreInstanceState(savedInstanceState: Bundle?) {
savedInstanceState?.let {
if(savedInstanceState.getBoolean(IS_OPENED_EXTRA_NAME, false)) {
floatingActionMenu.open(false)
}
}
}
}

View File

@ -0,0 +1,39 @@
package net.dankito.banking.fints4java.android.ui.views
import android.support.v7.app.AppCompatActivity
import com.github.clans.fab.FloatingActionMenu
import kotlinx.android.synthetic.main.view_floating_action_button_main.view.*
import net.dankito.banking.fints4java.android.ui.MainWindowPresenter
import net.dankito.banking.fints4java.android.ui.dialogs.AddAccountDialog
import net.dankito.banking.fints4java.android.ui.dialogs.TransferMoneyDialog
open class MainActivityFloatingActionMenuButton(floatingActionMenu: FloatingActionMenu, protected val presenter: MainWindowPresenter)
: FloatingActionMenuButton(floatingActionMenu) {
init {
setupButtons(floatingActionMenu)
}
private fun setupButtons(floatingActionMenu: FloatingActionMenu) {
(floatingActionMenu.context as? AppCompatActivity)?.let { activity ->
floatingActionMenu.fabAddAccount.setOnClickListener {
executeAndCloseMenu { AddAccountDialog().show(activity, presenter) }
}
val fabTransferMoney = floatingActionMenu.fabTransferMoney
fabTransferMoney.isEnabled = false
floatingActionMenu.setOnMenuToggleListener {
if (floatingActionMenu.isOpened) {
fabTransferMoney.isEnabled = presenter.accounts.isNotEmpty()
}
}
fabTransferMoney.setOnClickListener {
executeAndCloseMenu { TransferMoneyDialog().show(activity, presenter, null) }
}
}
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/overshoot">
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:duration="400" />
<translate
android:fromYDelta="30%p"
android:toYDelta="0"
android:duration="300" />
</set>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/anticipate_interpolator">
<alpha
android:fromAlpha="1"
android:toAlpha="0"
android:duration="200" />
<translate
android:fromYDelta="0"
android:toYDelta="30%p"
android:duration="200" />
</set>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/fabLabelBackgroundColor"/>
<padding
android:left="16dp"
android:top="4dp"
android:right="16dp"
android:bottom="4dp"/>
<corners
android:radius="2dp"/>
</shape>

View File

@ -22,12 +22,15 @@
<include layout="@layout/content_main" /> <include layout="@layout/content_main" />
<android.support.design.widget.FloatingActionButton <include
android:id="@+id/fab" layout="@layout/view_floating_action_button_main"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin" android:layout_marginRight="@dimen/fab_margin"
app:srcCompat="@drawable/ic_add_white_48dp" /> android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="@dimen/fab_margin_bottom_without_toolbar"
app:layout_behavior="@string/move_upward_behaviour"
/>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<com.github.clans.fab.FloatingActionMenu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fab="http://schemas.android.com/apk/res-auto"
android:id="@+id/floatingActionMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:menu_labels_style="@style/MenuLabelsStyle"
fab:menu_shadowColor="#444"
fab:menu_colorNormal="?attr/colorAccent"
fab:menu_colorPressed="?attr/colorPrimary"
fab:menu_colorRipple="?attr/colorPrimaryDark"
fab:menu_animationDelayPerItem="50">
<com.github.clans.fab.FloatingActionButton
android:id="@+id/fabTransferMoney"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/MenuButtonsStyle"
fab:fab_label="@string/floating_action_menu_transfer_money"
/>
<com.github.clans.fab.FloatingActionButton
android:id="@+id/fabAddAccount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/MenuButtonsStyle"
fab:fab_label="@string/floating_action_menu_add_account"
/>
</com.github.clans.fab.FloatingActionMenu>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Theme">
<attr name="fabTextColor" format="reference"/>
<attr name="fabLabelBackgroundColor" format="reference"/>
<attr name="fabShadowColor" format="reference"/>
</declare-styleable>
</resources>

View File

@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="primaryTextColor_Dark">#BABABA</color>
<color name="primaryTextColor_Light">#000000</color>
<color name="backgroundColor_Dark">#303030</color>
<color name="backgroundColor_Light">#FFFFFF</color>
<color name="positiveAmount">#00FF00</color> <color name="positiveAmount">#00FF00</color>
<color name="negativeAmount">#FF0000</color> <color name="negativeAmount">#FF0000</color>
@ -9,4 +15,10 @@
<color name="list_item_bank_info_bank_supported">#ff669900</color> <color name="list_item_bank_info_bank_supported">#ff669900</color>
<color name="list_item_bank_info_bank_not_supported">#ffcc0000</color> <color name="list_item_bank_info_bank_not_supported">#ffcc0000</color>
<color name="fabTextColor">#FFFFFF</color>
<!-- <color name="fabLabelBackgroundColor">@color/black_semi_transparent</color>-->
<color name="fabLabelBackgroundColor">@color/colorAccent</color>
<color name="fabShadowColor">#444</color>
</resources> </resources>

View File

@ -5,6 +5,8 @@
<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="fab_margin">16dp</dimen> <dimen name="fab_margin">16dp</dimen>
<dimen name="fab_margin_bottom_without_toolbar">16dp</dimen>
<dimen name="fab_margin_bottom_with_toolbar">42dp</dimen>
<dimen name="list_item_account_transaction_height">100dp</dimen> <dimen name="list_item_account_transaction_height">100dp</dimen>
<dimen name="list_item_account_transaction_padding">4dp</dimen> <dimen name="list_item_account_transaction_padding">4dp</dimen>

View File

@ -17,6 +17,9 @@
<string name="nav_header_subtitle">android.studio@android.com</string> <string name="nav_header_subtitle">android.studio@android.com</string>
<string name="nav_header_desc">Navigation header</string> <string name="nav_header_desc">Navigation header</string>
<string name="floating_action_menu_add_account">Account</string>
<string name="floating_action_menu_transfer_money">Transfer money</string>
<string name="menu_home">Home</string> <string name="menu_home">Home</string>
<string name="menu_main_update_transactions">Update transactions</string> <string name="menu_main_update_transactions">Update transactions</string>

View File

@ -6,6 +6,10 @@
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
<item name="fabTextColor">@color/fabTextColor</item>
<item name="fabLabelBackgroundColor">@color/fabLabelBackgroundColor</item>
<item name="fabShadowColor">@color/fabShadowColor</item>
</style> </style>
<style name="AppTheme.NoActionBar"> <style name="AppTheme.NoActionBar">
@ -17,4 +21,40 @@
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<!-- Floating Action Button -->
<style name="MenuLabelsStyle">
<item name="android:background">@drawable/fab_label_background</item>
<item name="android:textColor">?attr/fabTextColor</item>
<item name="android:textSize">14sp</item>
<item name="android:maxLines">2</item>
<item name="android:ellipsize">end</item>
<item name="android:gravity">center_vertical</item>
<item name="menu_shadowColor">?attr/fabShadowColor</item>
<item name="menu_colorNormal">?attr/colorPrimary</item>
<item name="menu_colorPressed">?attr/colorAccent</item>
<item name="menu_colorRipple">?attr/colorPrimaryDark</item>
<item name="fab_showAnimation">@anim/jump_from_down</item>
<item name="fab_hideAnimation">@anim/jump_to_down</item>
</style>
<style name="MenuButtonsStyle">
<item name="android:textColor">?attr/fabTextColor</item>
<item name="fab_size">mini</item>
<item name="fab_shadowColor">?attr/fabShadowColor</item>
<item name="fab_colorNormal">?attr/colorAccent</item>
<item name="fab_colorPressed">?attr/colorPrimary</item>
<item name="fab_colorRipple">?attr/colorPrimaryDark</item>
<item name="fab_showAnimation">@anim/jump_from_down</item>
<item name="fab_hideAnimation">@anim/jump_to_down</item>
<item name="android:src">@drawable/fab_add</item>
</style>
</resources> </resources>