diff --git a/vector/src/main/java/im/vector/riotredesign/core/animations/SimpleTransitionListener.kt b/vector/src/main/java/im/vector/riotredesign/core/animations/SimpleTransitionListener.kt
new file mode 100644
index 00000000..0e644990
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/animations/SimpleTransitionListener.kt
@@ -0,0 +1,41 @@
+/*
+ * 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 androidx.transition.Transition
+
+open class SimpleTransitionListener : Transition.TransitionListener {
+ override fun onTransitionEnd(transition: Transition) {
+ // No op
+ }
+
+ override fun onTransitionResume(transition: Transition) {
+ // No op
+ }
+
+ override fun onTransitionPause(transition: Transition) {
+ // No op
+ }
+
+ override fun onTransitionCancel(transition: Transition) {
+ // No op
+ }
+
+ override fun onTransitionStart(transition: Transition) {
+ // No op
+ }
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotredesign/core/animations/VectorFullTransitionSet.kt b/vector/src/main/java/im/vector/riotredesign/core/animations/VectorFullTransitionSet.kt
new file mode 100644
index 00000000..666e6ced
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/core/animations/VectorFullTransitionSet.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.content.Context
+import android.util.AttributeSet
+import androidx.transition.ChangeBounds
+import androidx.transition.ChangeTransform
+import androidx.transition.Fade
+import androidx.transition.TransitionSet
+
+class VectorFullTransitionSet : TransitionSet {
+
+ constructor() {
+ init()
+ }
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+ init()
+ }
+
+ private fun init() {
+ ordering = ORDERING_TOGETHER
+ addTransition(Fade(Fade.OUT))
+ .addTransition(ChangeBounds())
+ .addTransition(ChangeTransform())
+ .addTransition(Fade(Fade.IN))
+ }
+
+}
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 19b031cf..247ab070 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,7 +16,6 @@
package im.vector.riotredesign.features.home.room.list
-import android.animation.Animator
import android.os.Bundle
import android.os.Parcelable
import androidx.annotation.StringRes
@@ -25,18 +24,16 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.airbnb.mvrx.*
-import com.google.android.material.floatingactionbutton.FloatingActionButton
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 im.vector.riotredesign.features.home.room.list.widget.FabMenuView
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_room_list.*
import org.koin.android.ext.android.inject
@@ -47,11 +44,7 @@ data class RoomListParams(
) : Parcelable
-class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, OnBackPressed {
-
- lateinit var fabButton: FloatingActionButton
-
- private var isFabMenuOpened = false
+class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, OnBackPressed, FabMenuView.Listener {
enum class DisplayMode(@StringRes val titleRes: Int) {
HOME(R.string.bottom_action_home),
@@ -82,21 +75,16 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, O
navigator.openRoom(it)
}
- isFabMenuOpened = false
+ createChatFabMenu.listener = this
}
private fun setupCreateRoomButton() {
- fabButton = when (roomListParams.displayMode) {
- DisplayMode.HOME -> createRoomButton
- DisplayMode.PEOPLE -> createChatRoomButton
- else -> createGroupRoomButton
+ when (roomListParams.displayMode) {
+ DisplayMode.HOME -> createChatFabMenu.isVisible = true
+ DisplayMode.PEOPLE -> createChatRoomButton.isVisible = true
+ else -> createGroupRoomButton.isVisible = true
}
- fabButton.isVisible = true
-
- createRoomButton.setOnClickListener {
- toggleFabMenu()
- }
createChatRoomButton.setOnClickListener {
createDirectChat()
}
@@ -104,93 +92,34 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, O
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) {
- fabButton.removeCallbacks(showFabRunnable)
+ createChatFabMenu.removeCallbacks(showFabRunnable)
when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> {
- fabButton.postDelayed(showFabRunnable, 1000)
+ createChatFabMenu.postDelayed(showFabRunnable, 1000)
}
RecyclerView.SCROLL_STATE_DRAGGING,
RecyclerView.SCROLL_STATE_SETTLING -> {
- fabButton.hide()
+ when (roomListParams.displayMode) {
+ DisplayMode.HOME -> createChatFabMenu.hide()
+ DisplayMode.PEOPLE -> createChatRoomButton.hide()
+ else -> createGroupRoomButton.hide()
+ }
}
}
}
})
}
- 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() {
+ override fun openRoomDirectory() {
navigator.openRoomDirectory()
}
- private fun createDirectChat() {
+ override fun createDirectChat() {
vectorBaseActivity.notImplemented("creating direct chat")
}
@@ -206,7 +135,13 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, O
}
private val showFabRunnable = Runnable {
- fabButton.show()
+ if (isAdded) {
+ when (roomListParams.displayMode) {
+ DisplayMode.HOME -> createChatFabMenu.show()
+ DisplayMode.PEOPLE -> createChatRoomButton.show()
+ else -> createGroupRoomButton.show()
+ }
+ }
}
private fun renderState(state: RoomListViewState) {
@@ -278,8 +213,7 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, O
}
override fun onBackPressed(): Boolean {
- if (isFabMenuOpened) {
- toggleFabMenu()
+ if (createChatFabMenu.onBackPressed()) {
return true
}
diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/widget/FabMenuView.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/widget/FabMenuView.kt
new file mode 100644
index 00000000..525efb06
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/widget/FabMenuView.kt
@@ -0,0 +1,160 @@
+/*
+ * 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.features.home.room.list.widget
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.view.isVisible
+import androidx.transition.ChangeTransform
+import androidx.transition.Transition
+import androidx.transition.TransitionManager
+import im.vector.riotredesign.R
+import im.vector.riotredesign.core.animations.ANIMATION_DURATION_SHORT
+import im.vector.riotredesign.core.animations.SimpleTransitionListener
+import im.vector.riotredesign.core.animations.VectorFullTransitionSet
+import kotlinx.android.synthetic.main.merge_fab_menu_view.view.*
+
+class FabMenuView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+ var listener: Listener? = null
+
+ private var isFabMenuOpened = false
+
+ init {
+ inflate(context, R.layout.merge_fab_menu_view, this)
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+
+ // Collapse
+ ConstraintSet().also {
+ it.clone(context, R.layout.constraint_set_fab_menu_close)
+ it.applyTo(this)
+ }
+
+ createRoomItemChat.isVisible = false
+ createRoomItemChatLabel.isVisible = false
+ createRoomItemGroup.isVisible = false
+ createRoomItemGroupLabel.isVisible = false
+ // Collapse end
+
+
+ createRoomButton.setOnClickListener {
+ toggleFabMenu()
+ }
+
+ listOf(createRoomItemChat, createRoomItemChatLabel)
+ .forEach {
+ it.setOnClickListener {
+ closeFabMenu()
+ listener?.createDirectChat()
+ }
+ }
+ listOf(createRoomItemGroup, createRoomItemGroupLabel)
+ .forEach {
+ it.setOnClickListener {
+ closeFabMenu()
+ listener?.openRoomDirectory()
+ }
+ }
+
+ createRoomTouchGuard.setOnClickListener {
+ closeFabMenu()
+ }
+ }
+
+ fun show() {
+ createRoomButton.show()
+ }
+
+ fun hide() {
+ createRoomButton.hide()
+ }
+
+ private fun openFabMenu() {
+ if (isFabMenuOpened) {
+ return
+ }
+
+ toggleFabMenu()
+ }
+
+ private fun closeFabMenu() {
+ if (!isFabMenuOpened) {
+ return
+ }
+
+ toggleFabMenu()
+ }
+
+ private fun toggleFabMenu() {
+ isFabMenuOpened = !isFabMenuOpened
+
+ TransitionManager.beginDelayedTransition(parent as? ViewGroup ?: this,
+ VectorFullTransitionSet().apply {
+ duration = ANIMATION_DURATION_SHORT
+ ChangeTransform()
+ addListener(object : SimpleTransitionListener() {
+ override fun onTransitionEnd(transition: Transition) {
+ // Hide the view after the transition for a better visual effect
+ createRoomItemChat.isVisible = isFabMenuOpened
+ createRoomItemChatLabel.isVisible = isFabMenuOpened
+ createRoomItemGroup.isVisible = isFabMenuOpened
+ createRoomItemGroupLabel.isVisible = isFabMenuOpened
+ }
+ })
+ })
+
+ if (isFabMenuOpened) {
+ // Animate manually the rotation for a better effect
+ createRoomButton.animate().setDuration(ANIMATION_DURATION_SHORT).rotation(135f)
+
+
+ ConstraintSet().also {
+ it.clone(context, R.layout.constraint_set_fab_menu_open)
+ it.applyTo(this)
+ }
+ } else {
+ createRoomButton.animate().setDuration(ANIMATION_DURATION_SHORT).rotation(0f)
+
+ ConstraintSet().also {
+ it.clone(context, R.layout.constraint_set_fab_menu_close)
+ it.applyTo(this)
+ }
+ }
+ }
+
+ fun onBackPressed(): Boolean {
+ if (isFabMenuOpened) {
+ closeFabMenu()
+ return true
+ }
+
+ return false
+ }
+
+ interface Listener {
+ fun createDirectChat()
+ fun openRoomDirectory()
+ }
+
+}
\ No newline at end of file
diff --git a/vector/src/main/res/drawable/vector_label_background.xml b/vector/src/main/res/drawable/vector_label_background.xml
new file mode 100644
index 00000000..6e0f1c85
--- /dev/null
+++ b/vector/src/main/res/drawable/vector_label_background.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml b/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml
index a2c79905..fd9d5d27 100644
--- a/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml
+++ b/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml
@@ -159,6 +159,5 @@
app:layout_constraintStart_toEndOf="@+id/composer_avatar_view"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/lorem/random" />
-
\ No newline at end of file
diff --git a/vector/src/main/res/layout/constraint_set_fab_menu_close.xml b/vector/src/main/res/layout/constraint_set_fab_menu_close.xml
new file mode 100644
index 00000000..23b1ede5
--- /dev/null
+++ b/vector/src/main/res/layout/constraint_set_fab_menu_close.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/layout/constraint_set_fab_menu_open.xml b/vector/src/main/res/layout/constraint_set_fab_menu_open.xml
new file mode 100644
index 00000000..8aac13b6
--- /dev/null
+++ b/vector/src/main/res/layout/constraint_set_fab_menu_open.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/layout/fragment_room_list.xml b/vector/src/main/res/layout/fragment_room_list.xml
index c785b7ad..8701d79e 100644
--- a/vector/src/main/res/layout/fragment_room_list.xml
+++ b/vector/src/main/res/layout/fragment_room_list.xml
@@ -1,6 +1,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/vector/src/main/res/layout/merge_fab_menu_view.xml b/vector/src/main/res/layout/merge_fab_menu_view.xml
new file mode 100644
index 00000000..18db4683
--- /dev/null
+++ b/vector/src/main/res/layout/merge_fab_menu_view.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vector/src/main/res/values/dimens.xml b/vector/src/main/res/values/dimens.xml
index 8b19873d..886cde2e 100644
--- a/vector/src/main/res/values/dimens.xml
+++ b/vector/src/main/res/values/dimens.xml
@@ -27,7 +27,4 @@
20dp
4dp
-
- 76dp
- 146dp
\ No newline at end of file
diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml
index de308c3d..3554d619 100644
--- a/vector/src/main/res/values/strings_riotX.xml
+++ b/vector/src/main/res/values/strings_riotX.xml
@@ -37,4 +37,7 @@
"This room can't be previewed"
"The preview of world-readable room is not supported yet in RiotX"
+ "Rooms"
+ "Direct Messages"
+
\ No newline at end of file
diff --git a/vector/src/main/res/values/styles_riot.xml b/vector/src/main/res/values/styles_riot.xml
index b3b987c0..b3ce18f1 100644
--- a/vector/src/main/res/values/styles_riot.xml
+++ b/vector/src/main/res/values/styles_riot.xml
@@ -265,4 +265,17 @@
- @id/messageMemberNameView
- @id/messageBottomInfo
+
+
+
\ No newline at end of file