From 5fab5b9f05fb92f9549e30b045d5286ad8c4c05c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sat, 5 Oct 2019 22:33:56 +0200 Subject: [PATCH] Add Floating Action Button Speed Dial to Android --- android/app/src/main/AndroidManifest.xml | 3 +- .../FloatingActionButtonBackgroundBehavior.kt | 102 ++++++++++++++ .../FloatingActionButtonSpeedDialBehavior.kt | 128 ++++++++++++++++++ .../com/metallic/chiaki/main/MainActivity.kt | 53 ++++++-- .../main/res/drawable/avd_add_to_close.xml | 53 ++++++++ .../main/res/drawable/avd_close_to_add.xml | 51 +++++++ .../src/main/res/drawable/ic_add_close.xml | 29 ++++ .../app/src/main/res/drawable/ic_close.xml | 9 ++ .../app/src/main/res/drawable/ic_register.xml | 9 ++ .../main/res/drawable/selector_add_fab.xml | 24 ++++ .../app/src/main/res/layout/activity_main.xml | 100 ++++++++++++-- android/app/src/main/res/values/colors.xml | 3 + android/app/src/main/res/values/dimens.xml | 1 + android/app/src/main/res/values/strings.xml | 2 + android/app/src/main/res/values/styles.xml | 6 + 15 files changed, 553 insertions(+), 20 deletions(-) create mode 100644 android/app/src/main/java/com/metallic/chiaki/main/FloatingActionButtonBackgroundBehavior.kt create mode 100644 android/app/src/main/java/com/metallic/chiaki/main/FloatingActionButtonSpeedDialBehavior.kt create mode 100644 android/app/src/main/res/drawable/avd_add_to_close.xml create mode 100644 android/app/src/main/res/drawable/avd_close_to_add.xml create mode 100644 android/app/src/main/res/drawable/ic_add_close.xml create mode 100644 android/app/src/main/res/drawable/ic_close.xml create mode 100644 android/app/src/main/res/drawable/ic_register.xml create mode 100644 android/app/src/main/res/drawable/selector_add_fab.xml diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 66f41c5..b3cb4c1 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -13,7 +13,8 @@ android:supportsRtl="true" android:theme="@style/AppTheme"> - + diff --git a/android/app/src/main/java/com/metallic/chiaki/main/FloatingActionButtonBackgroundBehavior.kt b/android/app/src/main/java/com/metallic/chiaki/main/FloatingActionButtonBackgroundBehavior.kt new file mode 100644 index 0000000..5a94ed6 --- /dev/null +++ b/android/app/src/main/java/com/metallic/chiaki/main/FloatingActionButtonBackgroundBehavior.kt @@ -0,0 +1,102 @@ +/* + * This file is part of Chiaki. + * + * Chiaki is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chiaki is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chiaki. If not, see . + */ + +package com.metallic.chiaki.main + +import android.animation.Animator +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.animation.PropertyValuesHolder +import android.content.Context +import android.content.res.Resources +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.animation.addListener +import androidx.core.view.children +import androidx.core.view.isGone +import androidx.core.view.isInvisible +import androidx.core.view.isVisible +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.google.android.material.transformation.ExpandableTransformationBehavior +import com.metallic.chiaki.R + +class FloatingActionButtonBackgroundBehavior @JvmOverloads constructor(context: Context? = null, attrs: AttributeSet? = null) : ExpandableTransformationBehavior(context, attrs) +{ + companion object + { + private const val DURATION = 150L + } + + override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View) + = dependency is FloatingActionButton + + override fun onCreateExpandedStateChangeAnimation(dependency: View, child: View, expanded: Boolean, isAnimating: Boolean): AnimatorSet + = AnimatorSet().also { + it.playTogether(listOf( + if(expanded) + createExpandAnimation(child, isAnimating) + else + createCollapseAnimation(child) + + )) + it.addListener( + onStart = { + if(expanded) + child.isVisible = true + }, + onEnd = { + if(!expanded) + child.isGone = true + } + ) + } + + private fun createExpandAnimation(child: View, currentlyAnimating: Boolean): Animator + { + if(!currentlyAnimating) + child.alpha = 0f + + val animator = ObjectAnimator.ofPropertyValuesHolder( + child, + PropertyValuesHolder.ofFloat(View.ALPHA, 1f) + ).apply { + duration = DURATION + } + + return AnimatorSet().apply { + playTogether(listOf(animator)) + } + } + + private fun createCollapseAnimation(child: View): Animator + { + val animator = ObjectAnimator.ofPropertyValuesHolder( + child, + PropertyValuesHolder.ofFloat(View.ALPHA, 0f) + ).apply { + duration = DURATION + } + + return AnimatorSet().apply { + playTogether(listOf(animator)) + } + } +} diff --git a/android/app/src/main/java/com/metallic/chiaki/main/FloatingActionButtonSpeedDialBehavior.kt b/android/app/src/main/java/com/metallic/chiaki/main/FloatingActionButtonSpeedDialBehavior.kt new file mode 100644 index 0000000..f41bceb --- /dev/null +++ b/android/app/src/main/java/com/metallic/chiaki/main/FloatingActionButtonSpeedDialBehavior.kt @@ -0,0 +1,128 @@ +/* + * This file is part of Chiaki. + * + * Chiaki is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chiaki is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chiaki. If not, see . + */ + +package com.metallic.chiaki.main + +import android.animation.Animator +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.animation.PropertyValuesHolder +import android.content.Context +import android.content.res.Resources +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator +import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.animation.addListener +import androidx.core.view.children +import androidx.core.view.isInvisible +import androidx.core.view.isVisible +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.google.android.material.transformation.ExpandableTransformationBehavior +import com.metallic.chiaki.R + +class FloatingActionButtonSpeedDialBehavior @JvmOverloads constructor(context: Context? = null, attrs: AttributeSet? = null) : ExpandableTransformationBehavior(context, attrs) +{ + companion object + { + private const val DELAY = 30L + private const val DURATION = 150L + } + + override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View) + = dependency is FloatingActionButton && child is ViewGroup + + override fun onCreateExpandedStateChangeAnimation(dependency: View, child: View, expanded: Boolean, isAnimating: Boolean): AnimatorSet + = if(child !is ViewGroup) + AnimatorSet() + else + AnimatorSet().also { + it.playTogether(listOf( + if(expanded) + createExpandAnimation(child, isAnimating) + else + createCollapseAnimation(child) + + )) + it.addListener( + onStart = { + if(expanded) + child.isVisible = true + }, + onEnd = { + if(!expanded) + child.isInvisible = true + } + ) + } + + private fun offset(resources: Resources) = resources.getDimension(R.dimen.floating_action_button_speed_dial_anim_offset) + + private fun createExpandAnimation(child: ViewGroup, currentlyAnimating: Boolean): Animator + { + if(!currentlyAnimating) + { + child.children.forEach { + it.alpha = 0f + it.translationY = this.offset(child.resources) + } + } + + val translationYHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f) + val alphaHolder = PropertyValuesHolder.ofFloat(View.ALPHA, 1f) + + val animators = child.children.mapIndexed { index, view -> + ObjectAnimator.ofPropertyValuesHolder( + view, + translationYHolder, + alphaHolder + ).apply { + duration = DURATION + startDelay = (child.childCount - index - 1) * DELAY + interpolator = DecelerateInterpolator() + } + }.toList() + + return AnimatorSet().apply { + playTogether(animators) + } + } + + private fun createCollapseAnimation(child: ViewGroup): Animator + { + val translationYHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, this.offset(child.resources)) + val alphaHolder = PropertyValuesHolder.ofFloat(View.ALPHA, 0f) + + val animators = child.children.mapIndexed { index, view -> + ObjectAnimator.ofPropertyValuesHolder( + view, + translationYHolder, + alphaHolder + ).apply { + duration = DURATION + startDelay = index * DELAY + interpolator = AccelerateInterpolator() + } + }.toList() + + return AnimatorSet().apply { + playTogether(animators) + } + } +} diff --git a/android/app/src/main/java/com/metallic/chiaki/main/MainActivity.kt b/android/app/src/main/java/com/metallic/chiaki/main/MainActivity.kt index 0362c39..d58f6fd 100644 --- a/android/app/src/main/java/com/metallic/chiaki/main/MainActivity.kt +++ b/android/app/src/main/java/com/metallic/chiaki/main/MainActivity.kt @@ -24,6 +24,7 @@ import android.os.Bundle import android.util.Log import android.view.Menu import android.view.MenuItem +import android.view.View import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.LinearLayoutManager @@ -34,6 +35,7 @@ import com.metallic.chiaki.common.ext.viewModelFactory import com.metallic.chiaki.settings.SettingsActivity import io.reactivex.disposables.CompositeDisposable import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.activity_test_start.* class MainActivity : AppCompatActivity() { @@ -51,13 +53,15 @@ class MainActivity : AppCompatActivity() title = "" setSupportActionBar(toolbar) - addButton.setOnClickListener { - Intent(this, TestStartActivity::class.java).also { - it.putExtra(TestStartActivity.EXTRA_REVEAL_X, addButton.x + addButton.width * 0.5f) - it.putExtra(TestStartActivity.EXTRA_REVEAL_Y, addButton.y + addButton.height * 0.5f) - startActivity(it, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()) - } + floatingActionButton.setOnClickListener { + expandFloatingActionButton(!floatingActionButton.isExpanded) } + floatingActionButtonDialBackground.setOnClickListener { + expandFloatingActionButton(false) + } + + addManualButton.setOnClickListener { addManualConsole() } + addManualLabelButton.setOnClickListener { addManualConsole() } viewModel = ViewModelProviders .of(this, viewModelFactory { MainViewModel(getDatabase(this)) }) @@ -73,24 +77,40 @@ class MainActivity : AppCompatActivity() }) } + private fun expandFloatingActionButton(expand: Boolean) + { + floatingActionButton.isExpanded = expand + floatingActionButton.isActivated = floatingActionButton.isExpanded + } + override fun onDestroy() { super.onDestroy() disposable.dispose() } - override fun onResume() + override fun onStart() { - super.onResume() + super.onStart() viewModel.discoveryManager.resume() } - override fun onPause() + override fun onStop() { - super.onPause() + super.onStop() viewModel.discoveryManager.pause() } + override fun onBackPressed() + { + if(floatingActionButton.isExpanded) + { + expandFloatingActionButton(false) + return + } + super.onBackPressed() + } + override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.main, menu) @@ -125,4 +145,17 @@ class MainActivity : AppCompatActivity() else -> super.onOptionsItemSelected(item) } + + private fun addManualConsole() + { + val parent = addManualButton.parent as View + val parentParent = parent.parent as View + val x = addManualButton.x + parent.x + parentParent.x + addManualButton.width * 0.5f + val y = addManualButton.y + parent.y + parentParent.y + addManualButton.height * 0.5f + Intent(this, TestStartActivity::class.java).also { + it.putExtra(TestStartActivity.EXTRA_REVEAL_X, x) + it.putExtra(TestStartActivity.EXTRA_REVEAL_Y, y) + startActivity(it, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()) + } + } } diff --git a/android/app/src/main/res/drawable/avd_add_to_close.xml b/android/app/src/main/res/drawable/avd_add_to_close.xml new file mode 100644 index 0000000..1616538 --- /dev/null +++ b/android/app/src/main/res/drawable/avd_add_to_close.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/drawable/avd_close_to_add.xml b/android/app/src/main/res/drawable/avd_close_to_add.xml new file mode 100644 index 0000000..9831e19 --- /dev/null +++ b/android/app/src/main/res/drawable/avd_close_to_add.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_add_close.xml b/android/app/src/main/res/drawable/ic_add_close.xml new file mode 100644 index 0000000..903c3b3 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_add_close.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_close.xml b/android/app/src/main/res/drawable/ic_close.xml new file mode 100644 index 0000000..ede4b71 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_close.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_register.xml b/android/app/src/main/res/drawable/ic_register.xml new file mode 100644 index 0000000..538c5bd --- /dev/null +++ b/android/app/src/main/res/drawable/ic_register.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/selector_add_fab.xml b/android/app/src/main/res/drawable/selector_add_fab.xml new file mode 100644 index 0000000..51931e3 --- /dev/null +++ b/android/app/src/main/res/drawable/selector_add_fab.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml index 7cfcfbd..7eb10f7 100644 --- a/android/app/src/main/res/layout/activity_main.xml +++ b/android/app/src/main/res/layout/activity_main.xml @@ -3,15 +3,8 @@ 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:layout_height="match_parent" + android:clipChildren="false"> + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index fded369..f6595cb 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -7,6 +7,9 @@ @android:color/white @android:color/black + #fafafa + #333333 + #22ffffff #88ffffff diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml index 3573e86..e71e7d2 100644 --- a/android/app/src/main/res/values/dimens.xml +++ b/android/app/src/main/res/values/dimens.xml @@ -3,4 +3,5 @@ 48dp 64dp 16dp + 48dp \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 80c81cf..a90d7e4 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -12,4 +12,6 @@ Discover Consoles Automatically Address: %s ID: %s + Register Console + Add Console Manually diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 1d35a0b..7133508 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -12,6 +12,12 @@ true + +