From 268730e71b4f6d9c2f6d2282fc6665311418ce61 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 15 May 2019 19:44:06 +0200 Subject: [PATCH] Home: start reworking UX [WIP] --- vector/src/main/AndroidManifest.xml | 1 + .../features/home/HomeActivity.kt | 22 ++-- .../features/home/HomeDrawerFragment.kt | 14 ++- .../features/home/HomeNavigator.kt | 33 +++--- .../features/home/HomePermalinkHandler.kt | 4 +- .../features/home/LoadingFragment.kt | 49 ++++++++ .../features/home/group/GroupListFragment.kt | 6 + .../features/home/group/GroupListViewModel.kt | 33 ++++-- .../features/home/group/GroupSummaryItem.kt | 8 +- .../home/group/SelectedGroupFragment.kt | 111 ++++++++++++++++++ .../room/detail/LoadingRoomDetailFragment.kt | 47 -------- .../home/room/detail/RoomDetailActivity.kt | 57 +++++++++ .../home/room/detail/RoomDetailFragment.kt | 43 +++---- .../home/room/list/RoomListFragment.kt | 38 +++--- .../home/room/list/RoomListViewModel.kt | 6 +- .../main/res/layout/activity_room_detail.xml | 8 ++ .../main/res/layout/fragment_group_list.xml | 3 +- .../main/res/layout/fragment_home_drawer.xml | 61 ++++++++-- ...g_room_detail.xml => fragment_loading.xml} | 0 .../main/res/layout/fragment_room_list.xml | 52 ++------ .../res/layout/fragment_selected_group.xml | 38 ++++++ vector/src/main/res/layout/item_group.xml | 40 +++++-- .../res/menu/selected_group_navigation.xml | 25 ++++ 23 files changed, 498 insertions(+), 201 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotredesign/features/home/LoadingFragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupFragment.kt delete mode 100644 vector/src/main/java/im/vector/riotredesign/features/home/room/detail/LoadingRoomDetailFragment.kt create mode 100644 vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActivity.kt create mode 100644 vector/src/main/res/layout/activity_room_detail.xml rename vector/src/main/res/layout/{fragment_loading_room_detail.xml => fragment_loading.xml} (100%) create mode 100644 vector/src/main/res/layout/fragment_selected_group.xml create mode 100644 vector/src/main/res/menu/selected_group_navigation.xml diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 591b1885..5d8d3dc7 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -44,6 +44,7 @@ android:label="@string/title_activity_emoji_reaction_picker" /> + { // TODO better UI if (it) { @@ -115,30 +110,27 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { override fun configure(toolbar: Toolbar) { setSupportActionBar(toolbar) supportActionBar?.setHomeButtonEnabled(true) - supportActionBar?.setDisplayHomeAsUpEnabled(true) - val drawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, 0, 0) - drawerLayout.addDrawerListener(drawerToggle) - drawerToggle.syncState() + supportActionBar?.setDisplayUseLogoEnabled(true) } override fun getMenuRes() = R.menu.home override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - android.R.id.home -> { + android.R.id.home -> { drawerLayout.openDrawer(GravityCompat.START) return true } - R.id.sliding_menu_settings -> { + R.id.sliding_menu_settings -> { startActivity(VectorSettingsActivity.getIntent(this, "TODO")) return true } - R.id.sliding_menu_sign_out -> { + R.id.sliding_menu_sign_out -> { SignOutUiWorker(this).perform(Matrix.getInstance().currentSession!!) return true } // TODO Temporary code here to create a room - R.id.tmp_menu_create_room -> { + R.id.tmp_menu_create_room -> { // Start Activity for now startActivity(Intent(this, RoomDirectoryActivity::class.java)) return true diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt index 4458d456..9566e881 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeDrawerFragment.kt @@ -17,11 +17,12 @@ package im.vector.riotredesign.features.home import android.os.Bundle +import im.vector.matrix.android.api.Matrix import im.vector.riotredesign.R import im.vector.riotredesign.core.extensions.replaceChildFragment import im.vector.riotredesign.core.platform.VectorBaseFragment import im.vector.riotredesign.features.home.group.GroupListFragment -import im.vector.riotredesign.features.home.room.list.RoomListFragment +import kotlinx.android.synthetic.main.fragment_home_drawer.* class HomeDrawerFragment : VectorBaseFragment() { @@ -38,9 +39,14 @@ class HomeDrawerFragment : VectorBaseFragment() { super.onActivityCreated(savedInstanceState) if (savedInstanceState == null) { val groupListFragment = GroupListFragment.newInstance() - replaceChildFragment(groupListFragment, R.id.groupListFragmentContainer) - val roomListFragment = RoomListFragment.newInstance() - replaceChildFragment(roomListFragment, R.id.roomListFragmentContainer) + replaceChildFragment(groupListFragment, R.id.homeDrawerGroupListContainer) + } + val session = Matrix.getInstance().currentSession ?: return + val user = session.getUser(session.sessionParams.credentials.userId) + if (user != null) { + AvatarRenderer.render(user.avatarUrl, user.userId, user.displayName, homeDrawerHeaderAvatarView) + homeDrawerUsernameView.text = user.displayName + homeDrawerUserIdView.text = user.userId } } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt index ad3cd4cf..d435cd60 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomeNavigator.kt @@ -18,11 +18,13 @@ package im.vector.riotredesign.features.home import androidx.core.view.GravityCompat import androidx.fragment.app.FragmentManager +import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.riotredesign.R -import im.vector.riotredesign.core.extensions.addFragmentToBackstack import im.vector.riotredesign.core.extensions.replaceFragment +import im.vector.riotredesign.features.home.group.SelectedGroupFragment +import im.vector.riotredesign.features.home.group.SelectedGroupParams +import im.vector.riotredesign.features.home.room.detail.RoomDetailActivity import im.vector.riotredesign.features.home.room.detail.RoomDetailArgs -import im.vector.riotredesign.features.home.room.detail.RoomDetailFragment import kotlinx.android.synthetic.main.activity_home.* import timber.log.Timber @@ -32,22 +34,25 @@ class HomeNavigator { private var rootRoomId: String? = null + fun openSelectedGroup(groupSummary: GroupSummary) { + Timber.v("Open selected group ${groupSummary.groupId}") + activity?.let { + val args = SelectedGroupParams(groupSummary.groupId, groupSummary.displayName, groupSummary.avatarUrl) + val selectedGroupFragment = SelectedGroupFragment.newInstance(args) + it.drawerLayout?.closeDrawer(GravityCompat.START) + it.replaceFragment(selectedGroupFragment, R.id.homeDetailFragmentContainer) + } + } + fun openRoomDetail(roomId: String, - eventId: String?, - addToBackstack: Boolean = false) { - Timber.v("Open room detail $roomId - $eventId - $addToBackstack") + eventId: String?) { + Timber.v("Open room detail $roomId - $eventId") activity?.let { //TODO enable eventId permalink. It doesn't work enough at the moment. - val args = RoomDetailArgs(roomId) - val roomDetailFragment = RoomDetailFragment.newInstance(args) it.drawerLayout?.closeDrawer(GravityCompat.START) - if (addToBackstack) { - it.addFragmentToBackstack(roomDetailFragment, R.id.homeDetailFragmentContainer, roomId) - } else { - rootRoomId = roomId - clearBackStack(it.supportFragmentManager) - it.replaceFragment(roomDetailFragment, R.id.homeDetailFragmentContainer) - } + val args = RoomDetailArgs(roomId) + val roomDetailIntent = RoomDetailActivity.newIntent(it, args) + it.startActivity(roomDetailIntent) } } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/HomePermalinkHandler.kt b/vector/src/main/java/im/vector/riotredesign/features/home/HomePermalinkHandler.kt index 143ddb57..f2a42a33 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/HomePermalinkHandler.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/HomePermalinkHandler.kt @@ -34,10 +34,10 @@ class HomePermalinkHandler(private val navigator: HomeNavigator) { val permalinkData = PermalinkParser.parse(deepLink) when (permalinkData) { is PermalinkData.EventLink -> { - navigator.openRoomDetail(permalinkData.roomIdOrAlias, permalinkData.eventId, true) + navigator.openRoomDetail(permalinkData.roomIdOrAlias, permalinkData.eventId) } is PermalinkData.RoomLink -> { - navigator.openRoomDetail(permalinkData.roomIdOrAlias, null, true) + navigator.openRoomDetail(permalinkData.roomIdOrAlias, null) } is PermalinkData.GroupLink -> { navigator.openGroupDetail(permalinkData.groupId) diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/LoadingFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/LoadingFragment.kt new file mode 100644 index 00000000..de5eca71 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/home/LoadingFragment.kt @@ -0,0 +1,49 @@ +/* + * + * * 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 + +import android.graphics.drawable.AnimationDrawable +import android.os.Bundle +import android.view.View +import im.vector.riotredesign.R +import im.vector.riotredesign.core.platform.VectorBaseFragment +import kotlinx.android.synthetic.main.fragment_loading.* + +class LoadingFragment : VectorBaseFragment() { + + companion object { + + fun newInstance(): LoadingFragment { + return LoadingFragment() + } + } + + override fun getLayoutResId() = R.layout.fragment_loading + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val background = animatedLogoImageView.background + if (background is AnimationDrawable) { + background.start() + } + } + + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt index 5ed550a2..884b7a61 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListFragment.kt @@ -22,9 +22,11 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.fragmentViewModel import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.riotredesign.R +import im.vector.riotredesign.core.extensions.observeEvent import im.vector.riotredesign.core.platform.StateView import im.vector.riotredesign.core.platform.VectorBaseFragment import im.vector.riotredesign.features.home.HomeModule +import im.vector.riotredesign.features.home.HomeNavigator import kotlinx.android.synthetic.main.fragment_group_list.* import org.koin.android.ext.android.inject import org.koin.android.scope.ext.android.bindScope @@ -39,6 +41,7 @@ class GroupListFragment : VectorBaseFragment(), GroupSummaryController.Callback } private val viewModel: GroupListViewModel by fragmentViewModel() + private val homeNavigator by inject() private val groupController by inject() override fun getLayoutResId() = R.layout.fragment_group_list @@ -50,6 +53,9 @@ class GroupListFragment : VectorBaseFragment(), GroupSummaryController.Callback stateView.contentView = epoxyRecyclerView epoxyRecyclerView.setController(groupController) viewModel.subscribe { renderState(it) } + viewModel.openGroupLiveData.observeEvent(this) { + homeNavigator.openSelectedGroup(it) + } } private fun renderState(state: GroupListViewState) { diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt index 1cd23802..bbe042df 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupListViewModel.kt @@ -16,17 +16,25 @@ package im.vector.riotredesign.features.home.group +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import arrow.core.Option import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.rx.rx import im.vector.riotredesign.core.platform.VectorViewModel +import im.vector.riotredesign.core.resources.StringProvider +import im.vector.riotredesign.core.utils.LiveEvent import org.koin.android.ext.android.get +const val ALL_COMMUNITIES_GROUP_ID = "ALL_COMMUNITIES_GROUP_ID" + class GroupListViewModel(initialState: GroupListViewState, private val selectedGroupHolder: SelectedGroupStore, - private val session: Session + private val session: Session, + private val stringProvider: StringProvider ) : VectorViewModel(initialState) { companion object : MvRxViewModelFactory { @@ -35,10 +43,15 @@ class GroupListViewModel(initialState: GroupListViewState, override fun create(viewModelContext: ViewModelContext, state: GroupListViewState): GroupListViewModel? { val currentSession = viewModelContext.activity.get() val selectedGroupHolder = viewModelContext.activity.get() - return GroupListViewModel(state, selectedGroupHolder, currentSession) + val stringProvider = viewModelContext.activity.get() + return GroupListViewModel(state, selectedGroupHolder, currentSession, stringProvider) } } + private val _openGroupLiveData = MutableLiveData>() + val openGroupLiveData: LiveData> + get() = _openGroupLiveData + init { observeGroupSummaries() observeState() @@ -46,8 +59,8 @@ class GroupListViewModel(initialState: GroupListViewState, private fun observeState() { subscribe { - val selectedGroup = Option.fromNullable(it.selectedGroup) - selectedGroupHolder.post(selectedGroup) + val optionGroup = Option.fromNullable(it.selectedGroup) + selectedGroupHolder.post(optionGroup) } } @@ -62,15 +75,21 @@ class GroupListViewModel(initialState: GroupListViewState, private fun handleSelectGroup(action: GroupListActions.SelectGroup) = withState { state -> if (state.selectedGroup?.groupId != action.groupSummary.groupId) { setState { copy(selectedGroup = action.groupSummary) } - } else { - setState { copy(selectedGroup = null) } + _openGroupLiveData.postValue(LiveEvent(action.groupSummary)) } } - private fun observeGroupSummaries() { session .rx().liveGroupSummaries() + .map { + val myUser = session.getUser(session.sessionParams.credentials.userId) + val allCommunityGroup = GroupSummary( + groupId = ALL_COMMUNITIES_GROUP_ID, + displayName = "All Communities", + avatarUrl = myUser?.avatarUrl ?: "") + listOf(allCommunityGroup) + it + } .execute { async -> copy(asyncGroups = async) } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt index eef0e865..2bb1f81e 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/group/GroupSummaryItem.kt @@ -16,13 +16,14 @@ package im.vector.riotredesign.features.home.group +import android.view.ViewGroup import android.widget.ImageView +import android.widget.TextView import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.riotredesign.R import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder import im.vector.riotredesign.core.epoxy.VectorEpoxyModel -import im.vector.riotredesign.core.platform.CheckableFrameLayout import im.vector.riotredesign.features.home.AvatarRenderer @EpoxyModelClass(layout = R.layout.item_group) @@ -36,14 +37,15 @@ abstract class GroupSummaryItem : VectorEpoxyModel() { override fun bind(holder: Holder) { super.bind(holder) - holder.rootView.isSelected = selected holder.rootView.setOnClickListener { listener?.invoke() } + holder.groupNameView.text = groupName AvatarRenderer.render(avatarUrl, groupId, groupName.toString(), holder.avatarImageView) } class Holder : VectorEpoxyHolder() { val avatarImageView by bind(R.id.groupAvatarImageView) - val rootView by bind(R.id.itemGroupLayout) + val groupNameView by bind(R.id.groupNameView) + val rootView by bind(R.id.itemGroupLayout) } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupFragment.kt new file mode 100644 index 00000000..add2ef6f --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/home/group/SelectedGroupFragment.kt @@ -0,0 +1,111 @@ +/* + * + * * 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.group + +import android.graphics.drawable.Drawable +import android.os.Bundle +import android.os.Parcelable +import com.airbnb.mvrx.args +import com.bumptech.glide.request.target.SimpleTarget +import com.bumptech.glide.request.transition.Transition +import im.vector.riotredesign.R +import im.vector.riotredesign.core.extensions.replaceChildFragment +import im.vector.riotredesign.core.glide.GlideApp +import im.vector.riotredesign.core.platform.ToolbarConfigurable +import im.vector.riotredesign.core.platform.VectorBaseFragment +import im.vector.riotredesign.features.home.AvatarRenderer +import im.vector.riotredesign.features.home.room.list.RoomListFragment +import im.vector.riotredesign.features.home.room.list.RoomListParams +import kotlinx.android.parcel.Parcelize +import kotlinx.android.synthetic.main.fragment_selected_group.* + +@Parcelize +data class SelectedGroupParams( + val groupId: String, + val groupName: String, + val groupAvatar: String +) : Parcelable + +class SelectedGroupFragment : VectorBaseFragment() { + + private val selectedGroupParams: SelectedGroupParams by args() + + override fun getLayoutResId(): Int { + return R.layout.fragment_selected_group + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + if (savedInstanceState == null) { + updateSelectedFragment(RoomListFragment.DisplayMode.HOME) + toolbar.setTitle(RoomListFragment.DisplayMode.HOME.titleRes) + } + setupBottomNavigationView() + setupToolbar() + } + + private fun setupToolbar() { + val parentActivity = vectorBaseActivity + if (parentActivity is ToolbarConfigurable) { + parentActivity.configure(toolbar) + } + val toolbarLogoTarget = object : SimpleTarget() { + override fun onResourceReady(resource: Drawable, transition: Transition?) { + toolbar.logo = resource + } + } + AvatarRenderer.render( + requireContext(), + GlideApp.with(this), + selectedGroupParams.groupAvatar, + selectedGroupParams.groupId, + selectedGroupParams.groupName, + toolbarLogoTarget + ) + } + + private fun setupBottomNavigationView() { + bottomNavigationView.setOnNavigationItemSelectedListener { + val displayMode = when { + it.itemId == R.id.bottom_action_people -> RoomListFragment.DisplayMode.PEOPLE + it.itemId == R.id.bottom_action_rooms -> RoomListFragment.DisplayMode.ROOMS + else -> RoomListFragment.DisplayMode.HOME + } + updateSelectedFragment(displayMode) + toolbar.setTitle(displayMode.titleRes) + true + } + } + + private fun updateSelectedFragment(displayMode: RoomListFragment.DisplayMode) { + val roomListParams = RoomListParams(displayMode) + val roomListFragment = RoomListFragment.newInstance(roomListParams) + replaceChildFragment(roomListFragment, R.id.roomListContainer) + } + + companion object { + + fun newInstance(args: SelectedGroupParams): SelectedGroupFragment { + return SelectedGroupFragment().apply { + setArguments(args) + } + } + + } +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/LoadingRoomDetailFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/LoadingRoomDetailFragment.kt deleted file mode 100644 index eefb6566..00000000 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/LoadingRoomDetailFragment.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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.detail - -import android.graphics.drawable.AnimationDrawable -import android.os.Bundle -import android.view.View -import im.vector.riotredesign.R -import im.vector.riotredesign.core.platform.VectorBaseFragment -import kotlinx.android.synthetic.main.fragment_loading_room_detail.* - -class LoadingRoomDetailFragment : VectorBaseFragment() { - - companion object { - - fun newInstance(): LoadingRoomDetailFragment { - return LoadingRoomDetailFragment() - } - } - - override fun getLayoutResId() = R.layout.fragment_loading_room_detail - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val background = animatedLogoImageView.background - if (background is AnimationDrawable) { - background.start() - } - } - - -} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActivity.kt new file mode 100644 index 00000000..fd79e8c1 --- /dev/null +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailActivity.kt @@ -0,0 +1,57 @@ +/* + * + * * 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.detail + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import im.vector.riotredesign.R +import im.vector.riotredesign.core.extensions.replaceFragment +import im.vector.riotredesign.core.platform.VectorBaseActivity + +class RoomDetailActivity : VectorBaseActivity() { + + override fun getLayoutRes(): Int { + return R.layout.activity_room_detail + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (savedInstanceState == null) { + val roomDetailArgs: RoomDetailArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS) + ?: return + val roomDetailFragment = RoomDetailFragment.newInstance(roomDetailArgs) + replaceFragment(roomDetailFragment, R.id.roomDetailContainer) + } + } + + companion object { + + private const val EXTRA_ROOM_DETAIL_ARGS = "EXTRA_ROOM_DETAIL_ARGS" + + fun newIntent(context: Context, roomDetailArgs: RoomDetailArgs): Intent { + return Intent(context, RoomDetailActivity::class.java).apply { + putExtra(EXTRA_ROOM_DETAIL_ARGS, roomDetailArgs) + } + } + + + } + +} \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt index 0278607a..dbc7408a 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt @@ -64,7 +64,6 @@ import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer import im.vector.riotredesign.core.extensions.hideKeyboard import im.vector.riotredesign.core.extensions.observeEvent import im.vector.riotredesign.core.glide.GlideApp -import im.vector.riotredesign.core.platform.ToolbarConfigurable import im.vector.riotredesign.core.platform.VectorBaseFragment import im.vector.riotredesign.core.utils.* import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandPresenter @@ -170,7 +169,6 @@ class RoomDetailFragment : actionViewModel = ViewModelProviders.of(requireActivity()).get(ActionsHandler::class.java) bindScope(getOrCreateScope(HomeModule.ROOM_DETAIL_SCOPE)) setupRecyclerView() - setupToolbar() setupComposer() setupAttachmentButton() setupInviteView() @@ -194,7 +192,7 @@ class RoomDetailFragment : if (resultCode == RESULT_OK && data != null) { when (requestCode) { REQUEST_FILES_REQUEST_CODE, TAKE_IMAGE_REQUEST_CODE -> handleMediaIntent(data) - REACTION_SELECT_REQUEST_CODE -> { + REACTION_SELECT_REQUEST_CODE -> { val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID) ?: return val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT) @@ -213,13 +211,6 @@ class RoomDetailFragment : // PRIVATE METHODS ***************************************************************************** - private fun setupToolbar() { - val parentActivity = vectorBaseActivity - if (parentActivity is ToolbarConfigurable) { - parentActivity.configure(toolbar) - } - } - private fun setupRecyclerView() { val epoxyVisibilityTracker = EpoxyVisibilityTracker() epoxyVisibilityTracker.attach(recyclerView) @@ -362,24 +353,24 @@ class RoomDetailFragment : private fun onSendChoiceClicked(dialogListItem: DialogListItem) { Timber.v("On send choice clicked: $dialogListItem") when (dialogListItem) { - is DialogListItem.SendFile -> { + is DialogListItem.SendFile -> { // launchFileIntent } - is DialogListItem.SendVoice -> { + is DialogListItem.SendVoice -> { //launchAudioRecorderIntent() } - is DialogListItem.SendSticker -> { + is DialogListItem.SendSticker -> { //startStickerPickerActivity() } is DialogListItem.TakePhotoVideo -> if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { // launchCamera() } - is DialogListItem.TakePhoto -> + is DialogListItem.TakePhoto -> if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA)) { openCamera(requireActivity(), CAMERA_VALUE_TITLE, TAKE_IMAGE_REQUEST_CODE) } - is DialogListItem.TakeVideo -> + is DialogListItem.TakeVideo -> if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA)) { // launchNativeVideoRecorder() } @@ -426,20 +417,20 @@ class RoomDetailFragment : private fun renderSendMessageResult(sendMessageResult: SendMessageResult) { when (sendMessageResult) { is SendMessageResult.MessageSent, - is SendMessageResult.SlashCommandHandled -> { + is SendMessageResult.SlashCommandHandled -> { // Clear composer composerEditText.text = null } - is SendMessageResult.SlashCommandError -> { + is SendMessageResult.SlashCommandError -> { displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command)) } - is SendMessageResult.SlashCommandUnknown -> { + is SendMessageResult.SlashCommandUnknown -> { displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command)) } - is SendMessageResult.SlashCommandResultOk -> { + is SendMessageResult.SlashCommandResultOk -> { // Ignore } - is SendMessageResult.SlashCommandResultError -> { + is SendMessageResult.SlashCommandResultError -> { displayCommandError(sendMessageResult.throwable.localizedMessage) } is SendMessageResult.SlashCommandNotImplemented -> { @@ -537,22 +528,22 @@ class RoomDetailFragment : it?.getContentIfNotHandled()?.let { actionData -> when (actionData.actionId) { - MessageMenuViewModel.ACTION_ADD_REACTION -> { + MessageMenuViewModel.ACTION_ADD_REACTION -> { val eventId = actionData.data?.toString() ?: return startActivityForResult(EmojiReactionPickerActivity.intent(requireContext(), eventId), REACTION_SELECT_REQUEST_CODE) } - MessageMenuViewModel.ACTION_COPY -> { + MessageMenuViewModel.ACTION_COPY -> { //I need info about the current selected message :/ copyToClipboard(requireContext(), actionData.data?.toString() ?: "", false) val snack = Snackbar.make(view!!, requireContext().getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT) snack.view.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.notification_accent_color)) snack.show() } - MessageMenuViewModel.ACTION_DELETE -> { + MessageMenuViewModel.ACTION_DELETE -> { val eventId = actionData.data?.toString() ?: return roomDetailViewModel.process(RoomDetailActions.RedactAction(eventId, context?.getString(R.string.event_redacted_by_user_reason))) } - MessageMenuViewModel.ACTION_SHARE -> { + MessageMenuViewModel.ACTION_SHARE -> { //TODO current data communication is too limited //Need to now the media type actionData.data?.toString()?.let { @@ -595,13 +586,13 @@ class RoomDetailFragment : .setPositiveButton(R.string.ok) { dialog, id -> dialog.cancel() } .show() } - MessageMenuViewModel.ACTION_QUICK_REACT -> { + MessageMenuViewModel.ACTION_QUICK_REACT -> { //eventId,ClickedOn,Opposite (actionData.data as? Triple)?.let { (eventId, clickedOn, opposite) -> roomDetailViewModel.process(RoomDetailActions.UpdateQuickReactAction(eventId, clickedOn, opposite)) } } - else -> { + else -> { Toast.makeText(context, "Action ${actionData.actionId} not implemented", Toast.LENGTH_LONG).show() } } 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 5a43a21e..794c011d 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 @@ -17,8 +17,8 @@ package im.vector.riotredesign.features.home.room.list import android.os.Bundle -import android.text.Editable -import android.text.TextWatcher +import android.os.Parcelable +import androidx.annotation.StringRes import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete @@ -29,21 +29,35 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.riotredesign.R import im.vector.riotredesign.core.epoxy.LayoutManagerStateRestorer import im.vector.riotredesign.core.extensions.observeEvent -import im.vector.riotredesign.core.extensions.setupAsSearch import im.vector.riotredesign.core.platform.StateView import im.vector.riotredesign.core.platform.VectorBaseFragment import im.vector.riotredesign.features.home.HomeModule import im.vector.riotredesign.features.home.HomeNavigator +import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_room_list.* import org.koin.android.ext.android.inject import org.koin.android.scope.ext.android.bindScope import org.koin.android.scope.ext.android.getOrCreateScope +@Parcelize +data class RoomListParams( + val displayMode: RoomListFragment.DisplayMode +) : Parcelable + + class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback { + enum class DisplayMode(@StringRes val titleRes: Int) { + HOME(R.string.bottom_action_home), + PEOPLE(R.string.bottom_action_people), + ROOMS(R.string.bottom_action_rooms) + } + companion object { - fun newInstance(): RoomListFragment { - return RoomListFragment() + fun newInstance(roomListParams: RoomListParams): RoomListFragment { + return RoomListFragment().apply { + setArguments(roomListParams) + } } } @@ -57,7 +71,6 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback { super.onActivityCreated(savedInstanceState) bindScope(getOrCreateScope(HomeModule.ROOM_LIST_SCOPE)) setupRecyclerView() - setupFilterView() roomListViewModel.subscribe { renderState(it) } roomListViewModel.openRoomLiveData.observeEvent(this) { homeNavigator.openRoomDetail(it, null) @@ -74,19 +87,6 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback { epoxyRecyclerView.setController(roomController) } - private fun setupFilterView() { - filterRoomView.setupAsSearch() - filterRoomView.addTextChangedListener(object : TextWatcher { - override fun afterTextChanged(s: Editable?) = Unit - - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit - - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - roomListViewModel.accept(RoomListActions.FilterRooms(s)) - } - }) - } - private fun renderState(state: RoomListViewState) { when (state.asyncRooms) { is Incomplete -> renderLoading() diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt index b9476ce5..fd9c19d3 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/list/RoomListViewModel.kt @@ -30,6 +30,7 @@ import im.vector.matrix.android.api.session.room.model.tag.RoomTag import im.vector.matrix.rx.rx import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.core.utils.LiveEvent +import im.vector.riotredesign.features.home.group.ALL_COMMUNITIES_GROUP_ID import im.vector.riotredesign.features.home.group.SelectedGroupStore import im.vector.riotredesign.features.home.room.VisibleRoomStore import io.reactivex.Observable @@ -118,7 +119,7 @@ class RoomListViewModel(initialState: RoomListViewState, val filteredDirectRooms = filteredRooms .filter { it.isDirect } .filter { - if (selectedGroup == null) { + if (selectedGroup == null || selectedGroup.groupId == ALL_COMMUNITIES_GROUP_ID) { true } else { it.otherMemberIds @@ -130,7 +131,8 @@ class RoomListViewModel(initialState: RoomListViewState, val filteredGroupRooms = filteredRooms .filter { !it.isDirect } .filter { - selectedGroup?.roomIds?.contains(it.roomId) ?: true + selectedGroup?.groupId == ALL_COMMUNITIES_GROUP_ID + || selectedGroup?.roomIds?.contains(it.roomId) ?: true } buildRoomSummaries(filteredDirectRooms + filteredGroupRooms) } diff --git a/vector/src/main/res/layout/activity_room_detail.xml b/vector/src/main/res/layout/activity_room_detail.xml new file mode 100644 index 00000000..1dae010e --- /dev/null +++ b/vector/src/main/res/layout/activity_room_detail.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_group_list.xml b/vector/src/main/res/layout/fragment_group_list.xml index a075d511..810fe3e4 100644 --- a/vector/src/main/res/layout/fragment_group_list.xml +++ b/vector/src/main/res/layout/fragment_group_list.xml @@ -4,8 +4,7 @@ + android:layout_height="match_parent"> - + + + + + + + + + + + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/homeDrawerHeader" /> \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_loading_room_detail.xml b/vector/src/main/res/layout/fragment_loading.xml similarity index 100% rename from vector/src/main/res/layout/fragment_loading_room_detail.xml rename to vector/src/main/res/layout/fragment_loading.xml diff --git a/vector/src/main/res/layout/fragment_room_list.xml b/vector/src/main/res/layout/fragment_room_list.xml index 6355db77..19cfdd59 100644 --- a/vector/src/main/res/layout/fragment_room_list.xml +++ b/vector/src/main/res/layout/fragment_room_list.xml @@ -1,53 +1,15 @@ - - + - - - - - - - \ No newline at end of file + diff --git a/vector/src/main/res/layout/fragment_selected_group.xml b/vector/src/main/res/layout/fragment_selected_group.xml new file mode 100644 index 00000000..e2cd454d --- /dev/null +++ b/vector/src/main/res/layout/fragment_selected_group.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_group.xml b/vector/src/main/res/layout/item_group.xml index 7dec77be..cfc230c9 100644 --- a/vector/src/main/res/layout/item_group.xml +++ b/vector/src/main/res/layout/item_group.xml @@ -1,22 +1,48 @@ - + android:padding="16dp"> - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/vector/src/main/res/menu/selected_group_navigation.xml b/vector/src/main/res/menu/selected_group_navigation.xml new file mode 100644 index 00000000..e0491950 --- /dev/null +++ b/vector/src/main/res/menu/selected_group_navigation.xml @@ -0,0 +1,25 @@ + + + + + + + + + +