diff --git a/vector/src/main/java/im/vector/riotredesign/core/animations/Constants.kt b/vector/src/main/java/im/vector/riotredesign/core/animations/Constants.kt
new file mode 100644
index 0000000000..4d9c59aa7d
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/animations/Constants.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.animations
+
+const val ANIMATION_DURATION_SHORT = 200L
diff --git a/vector/src/main/java/im/vector/riotredesign/core/animations/SimpleAnimatorListener.kt b/vector/src/main/java/im/vector/riotredesign/core/animations/SimpleAnimatorListener.kt
new file mode 100644
index 0000000000..f1aad8f186
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/animations/SimpleAnimatorListener.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotredesign.core.animations
+
+import android.animation.Animator
+
+open class SimpleAnimatorListener : Animator.AnimatorListener {
+ override fun onAnimationRepeat(animation: Animator?) {
+ // No op
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ // No op
+ }
+
+ override fun onAnimationCancel(animation: Animator?) {
+ // No op
+ }
+
+ override fun onAnimationStart(animation: Animator?) {
+ // No op
+ }
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt
index 7b64bb39c8..538666e5c1 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt
@@ -138,8 +138,9 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
}
private fun recursivelyDispatchOnBackPressed(fm: FragmentManager): Boolean {
- if (fm.backStackEntryCount == 0)
- return false
+ // if (fm.backStackEntryCount == 0)
+ // return false
+
val reverseOrder = fm.fragments.filter { it is OnBackPressed }.reversed()
for (f in reverseOrder) {
val handledByChildFragments = recursivelyDispatchOnBackPressed(f.childFragmentManager)
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt
index 7e13e66546..19b031cf3c 100644
--- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListFragment.kt
@@ -16,6 +16,7 @@
package im.vector.riotredesign.features.home.room.list
+import android.animation.Animator
import android.os.Bundle
import android.os.Parcelable
import androidx.annotation.StringRes
@@ -29,8 +30,11 @@ import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotredesign.R
+import im.vector.riotredesign.core.animations.ANIMATION_DURATION_SHORT
+import im.vector.riotredesign.core.animations.SimpleAnimatorListener
import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer
import im.vector.riotredesign.core.extensions.observeEvent
+import im.vector.riotredesign.core.platform.OnBackPressed
import im.vector.riotredesign.core.platform.StateView
import im.vector.riotredesign.core.platform.VectorBaseFragment
import kotlinx.android.parcel.Parcelize
@@ -43,10 +47,12 @@ data class RoomListParams(
) : Parcelable
-class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback {
+class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, OnBackPressed {
lateinit var fabButton: FloatingActionButton
+ private var isFabMenuOpened = false
+
enum class DisplayMode(@StringRes val titleRes: Int) {
HOME(R.string.bottom_action_home),
PEOPLE(R.string.bottom_action_people),
@@ -75,6 +81,8 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback {
roomListViewModel.openRoomLiveData.observeEvent(this) {
navigator.openRoom(it)
}
+
+ isFabMenuOpened = false
}
private fun setupCreateRoomButton() {
@@ -87,17 +95,30 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback {
fabButton.isVisible = true
createRoomButton.setOnClickListener {
- // TODO Is it the expected action?
- navigator.openRoomDirectory()
+ toggleFabMenu()
}
createChatRoomButton.setOnClickListener {
- // TODO Is it the expected action?
- navigator.openRoomDirectory()
+ createDirectChat()
}
createGroupRoomButton.setOnClickListener {
- navigator.openRoomDirectory()
+ openRoomDirectory()
}
+ createRoomItemChat.setOnClickListener {
+ toggleFabMenu()
+ createDirectChat()
+ }
+ createRoomItemGroup.setOnClickListener {
+ toggleFabMenu()
+ openRoomDirectory()
+ }
+
+ createRoomTouchGuard.setOnClickListener {
+ toggleFabMenu()
+ }
+
+ createRoomTouchGuard.isClickable = false
+
// Hide FAB when list is scrolling
epoxyRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
@@ -116,6 +137,63 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback {
})
}
+ private fun toggleFabMenu() {
+ isFabMenuOpened = !isFabMenuOpened
+
+ if (isFabMenuOpened) {
+ createRoomItemChat.isVisible = true
+ createRoomItemGroup.isVisible = true
+
+ createRoomButton.animate()
+ .setDuration(ANIMATION_DURATION_SHORT)
+ .rotation(135f)
+ createRoomItemChat.animate()
+ .setDuration(ANIMATION_DURATION_SHORT)
+ .translationY(-resources.getDimension(R.dimen.fab_menu_offset_1))
+ createRoomItemGroup.animate()
+ .setDuration(ANIMATION_DURATION_SHORT)
+ .translationY(-resources.getDimension(R.dimen.fab_menu_offset_2))
+ createRoomTouchGuard.animate()
+ .setDuration(ANIMATION_DURATION_SHORT)
+ .alpha(0.6f)
+ .setListener(null)
+ createRoomTouchGuard.isClickable = true
+ } else {
+ createRoomButton.animate()
+ .setDuration(ANIMATION_DURATION_SHORT)
+ .rotation(0f)
+ createRoomItemChat.animate()
+ .setDuration(ANIMATION_DURATION_SHORT)
+ .translationY(0f)
+ createRoomItemGroup.animate()
+ .setDuration(ANIMATION_DURATION_SHORT)
+ .translationY(0f)
+ createRoomTouchGuard.animate()
+ .setDuration(ANIMATION_DURATION_SHORT)
+ .alpha(0f)
+ .setListener(object : SimpleAnimatorListener() {
+ override fun onAnimationCancel(animation: Animator?) {
+ animation?.removeListener(this)
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ // Use isFabMenuOpened because it may have been open meanwhile
+ createRoomItemChat.isVisible = isFabMenuOpened
+ createRoomItemGroup.isVisible = isFabMenuOpened
+ }
+ })
+ createRoomTouchGuard.isClickable = false
+ }
+ }
+
+ private fun openRoomDirectory() {
+ navigator.openRoomDirectory()
+ }
+
+ private fun createDirectChat() {
+ vectorBaseActivity.notImplemented("creating direct chat")
+ }
+
private fun setupRecyclerView() {
val layoutManager = LinearLayoutManager(context)
val stateRestorer = LayoutManagerStateRestorer(layoutManager).register()
@@ -199,6 +277,15 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback {
stateView.state = StateView.State.Error(message)
}
+ override fun onBackPressed(): Boolean {
+ if (isFabMenuOpened) {
+ toggleFabMenu()
+ return true
+ }
+
+ return super.onBackPressed()
+ }
+
// RoomSummaryController.Callback **************************************************************
override fun onRoomSelected(room: RoomSummary) {
diff --git a/vector/src/main/res/layout/fragment_room_list.xml b/vector/src/main/res/layout/fragment_room_list.xml
index b94aceab66..c785b7adbc 100644
--- a/vector/src/main/res/layout/fragment_room_list.xml
+++ b/vector/src/main/res/layout/fragment_room_list.xml
@@ -15,6 +15,45 @@
android:layout_height="match_parent" />
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/values/dimens.xml b/vector/src/main/res/values/dimens.xml
index 90443bc5f9..8b19873d7c 100644
--- a/vector/src/main/res/values/dimens.xml
+++ b/vector/src/main/res/values/dimens.xml
@@ -26,4 +26,8 @@
16dp
20dp
4dp
+
+
+ 76dp
+ 146dp
\ No newline at end of file