diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt index a06742fbc..9a7953516 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt @@ -92,8 +92,8 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou } } - override suspend fun getContactsForUser(user: UserNgEntity, searchQuery: String?, conversationToken: String?): List { - return apiService.getContacts(authorization = user.getCredentials(), url = ApiUtils.getUrlForContactsSearch(user.baseUrl), shareTypes = ApiUtils.getShareTypesForContactsSearch(), options = ApiUtils.getQueryMapForContactsSearch(searchQuery, conversationToken)).ocs.data.map { + override suspend fun getContactsForUser(user: UserNgEntity, groupConversation: Boolean, searchQuery: String?, conversationToken: String?): List { + return apiService.getContacts(authorization = user.getCredentials(), url = ApiUtils.getUrlForContactsSearch(user.baseUrl), shareTypes = ApiUtils.getShareTypesForContactsSearch(groupConversation), options = ApiUtils.getQueryMapForContactsSearch(searchQuery, conversationToken)).ocs.data.map { val participant = Participant() participant.userId = it.id participant.displayName = it.label diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt index 68117742d..0b8d5eb72 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt @@ -33,7 +33,7 @@ import com.nextcloud.talk.models.json.userprofile.UserProfileOverall import com.nextcloud.talk.newarch.local.models.UserNgEntity interface NextcloudTalkRepository { - suspend fun getContactsForUser(user: UserNgEntity, searchQuery: String?, conversationToken: String?): List + suspend fun getContactsForUser(user: UserNgEntity, groupConversation: Boolean, searchQuery: String?, conversationToken: String?): List suspend fun registerPushWithServerForUser(user: UserNgEntity, options: Map): PushRegistrationOverall suspend fun unregisterPushWithServerForUser(user: UserNgEntity): GenericOverall suspend fun registerPushWithProxyForUser(user: UserNgEntity, options: Map): Any diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/GetContactsUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/GetContactsUseCase.kt index da4e25345..21888f1c3 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/GetContactsUseCase.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/GetContactsUseCase.kt @@ -34,6 +34,6 @@ class GetContactsUseCase constructor( ) : UseCase, Any?>(apiErrorHandler) { override suspend fun run(params: Any?): List { val definitionParameters = params as DefinitionParameters - return nextcloudTalkRepository.getContactsForUser(definitionParameters[0], definitionParameters[1], definitionParameters[2]) + return nextcloudTalkRepository.getContactsForUser(definitionParameters[0], definitionParameters[1], definitionParameters[2], definitionParameters[3]) } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactPresenter.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactPresenter.kt index 1d3d0fea8..85edc18e9 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactPresenter.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactPresenter.kt @@ -51,7 +51,7 @@ open class ContactPresenter(context: Context, onElementClick: ((Page, H private val globalService: GlobalService by inject() override val elementTypes: Collection - get() = listOf(ParticipantElementType.PARTICIPANT.ordinal, ParticipantElementType.PARTICIPANT_SELECTED.ordinal, ParticipantElementType.PARTICIPANT_HEADER.ordinal, ParticipantElementType.PARTICIPANT_FOOTER.ordinal) + get() = listOf(ParticipantElementType.PARTICIPANT.ordinal, ParticipantElementType.PARTICIPANT_SELECTED.ordinal, ParticipantElementType.PARTICIPANT_HEADER.ordinal, ParticipantElementType.PARTICIPANT_FOOTER.ordinal, ParticipantElementType.PARTICIPANT_NEW_GROUP.ordinal, ParticipantElementType.PARTICIPANT_JOIN_VIA_LINK.ordinal) override fun onCreate(parent: ViewGroup, elementType: Int): Holder { return when (elementType) { @@ -64,9 +64,13 @@ open class ContactPresenter(context: Context, onElementClick: ((Page, H ParticipantElementType.PARTICIPANT_HEADER.ordinal -> { Holder(getLayoutInflater().inflate(R.layout.rv_item_title_header, parent, false)) } - else -> { + ParticipantElementType.PARTICIPANT_FOOTER.ordinal -> { Holder(getLayoutInflater().inflate(R.layout.rv_item_participant_rv_footer, parent, false)) } + else -> { + // for join via link and new group + Holder(getLayoutInflater().inflate(R.layout.rv_item_contact, parent, false)) + } } } @@ -120,8 +124,16 @@ open class ContactPresenter(context: Context, onElementClick: ((Page, H } } else if (element.type == ParticipantElementType.PARTICIPANT_HEADER.ordinal) { holder.itemView.titleTextView.text = (element.data as HeaderSource.Data<*, *>).header.toString() - } else { + } else if (element.type == ParticipantElementType.PARTICIPANT_FOOTER.ordinal){ holder.itemView.messageTextView.text = (element.data as FooterSource.Data<*, *>).footer.toString() + } else if (element.type == ParticipantElementType.PARTICIPANT_NEW_GROUP.ordinal) { + val pairData = element.data as Pair<*, *> + holder.itemView.participantNameTextView.text = pairData.first as CharSequence + holder.itemView.avatarImageView.load(Images().getImageWithBackground(context, pairData.second as Int)) + } else { + val pairData = element.data as Pair<*, *> + holder.itemView.participantNameTextView.text = pairData.first as CharSequence + holder.itemView.avatarImageView.load(Images().getImageWithBackground(context, pairData.second as Int)) } } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsUtils.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsUtils.kt index cefeff7fa..41141e6d8 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsUtils.kt @@ -26,5 +26,7 @@ enum class ParticipantElementType { PARTICIPANT, PARTICIPANT_SELECTED, PARTICIPANT_HEADER, - PARTICIPANT_FOOTER + PARTICIPANT_FOOTER, + PARTICIPANT_NEW_GROUP, + PARTICIPANT_JOIN_VIA_LINK } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsView.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsView.kt index ea8d8110d..896191982 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsView.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsView.kt @@ -34,14 +34,12 @@ import androidx.recyclerview.widget.RecyclerView import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider import com.nextcloud.talk.R import com.nextcloud.talk.models.json.participants.Participant +import com.nextcloud.talk.newarch.features.contactsflow.source.FixedListSource import com.nextcloud.talk.newarch.mvvm.BaseView import com.nextcloud.talk.newarch.mvvm.ext.initRecyclerView import com.nextcloud.talk.newarch.utils.ElementPayload import com.nextcloud.talk.utils.bundle.BundleKeys -import com.otaliastudios.elements.Adapter -import com.otaliastudios.elements.Element -import com.otaliastudios.elements.Page -import com.otaliastudios.elements.Presenter +import com.otaliastudios.elements.* import com.uber.autodispose.lifecycle.LifecycleScopeProvider import kotlinx.android.synthetic.main.contacts_list_view.view.* import kotlinx.android.synthetic.main.conversations_list_view.view.recyclerView @@ -59,6 +57,9 @@ class ContactsView(private val bundle: Bundle? = null) : BaseView() { return R.layout.contacts_list_view } + private val isGroupConversation = bundle?.containsKey(BundleKeys.KEY_CONVERSATION_NAME) == true + private val hasToken = bundle?.containsKey(BundleKeys.KEY_CONVERSATION_TOKEN) == true + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup @@ -71,9 +72,11 @@ class ContactsView(private val bundle: Bundle? = null) : BaseView() { // todo - change empty state magic participantsAdapter = Adapter.builder(this) + .addSource(FixedListSource(listOf(Pair(context.getString(R.string.nc_new_group), R.drawable.ic_people_group_white_24px)), ParticipantElementType.PARTICIPANT_NEW_GROUP.ordinal)) + //.addSource(FixedListSource(listOf(Pair(context.getString(R.string.nc_join_via_link), R.drawable.ic_link_white_24px)), ParticipantElementType.PARTICIPANT_JOIN_VIA_LINK.ordinal)) .addSource(ContactsViewSource(data = viewModel.contactsLiveData, elementType = ParticipantElementType.PARTICIPANT.ordinal)) .addSource(ContactsHeaderSource(activity as Context, ParticipantElementType.PARTICIPANT_HEADER.ordinal)) - .addSource(ContactsFooterSource(activity as Context, ParticipantElementType.PARTICIPANT_FOOTER.ordinal)) + .addSource(ContactsViewFooterSource(activity as Context, ParticipantElementType.PARTICIPANT_FOOTER.ordinal)) .addPresenter(ContactPresenter(activity as Context, ::onElementClick)) .addPresenter(Presenter.forLoadingIndicator(activity as Context, R.layout.loading_state)) .addPresenter(Presenter.forEmptyIndicator(activity as Context, R.layout.message_state)) @@ -136,7 +139,7 @@ class ContactsView(private val bundle: Bundle? = null) : BaseView() { } - viewModel.initialize(bundle?.getString(BundleKeys.KEY_CONVERSATION_TOKEN)) + viewModel.initialize(bundle?.getString(BundleKeys.KEY_CONVERSATION_TOKEN), bundle?.containsKey(BundleKeys.KEY_CONVERSATION_NAME) == true) return view } @@ -149,26 +152,46 @@ class ContactsView(private val bundle: Bundle? = null) : BaseView() { private fun onElementClick(page: Page, holder: Presenter.Holder, element: Element) { if (element.data is Participant?) { val participant = element.data as Participant? - val isElementSelected = participant?.selected == true - participant?.let { - if (isElementSelected) { - viewModel.unselectParticipant(it) - } else { - viewModel.selectParticipant(it) - } - it.selected = !isElementSelected - if (element.type == ParticipantElementType.PARTICIPANT_SELECTED.ordinal) { - participantsAdapter.notifyItemRangeChanged(0, participantsAdapter.itemCount, ElementPayload.SELECTION_TOGGLE) - } else { - participantsAdapter.notifyItemChanged(holder.adapterPosition, ElementPayload.SELECTION_TOGGLE) - } + if (isGroupConversation || hasToken) { + val isElementSelected = participant?.selected == true + participant?.let { + if (isElementSelected) { + viewModel.unselectParticipant(it) + } else { + viewModel.selectParticipant(it) + } + it.selected = !isElementSelected + if (element.type == ParticipantElementType.PARTICIPANT_SELECTED.ordinal) { + participantsAdapter.notifyItemRangeChanged(0, participantsAdapter.itemCount, ElementPayload.SELECTION_TOGGLE) + } else { + participantsAdapter.notifyItemChanged(holder.adapterPosition, ElementPayload.SELECTION_TOGGLE) + } + } + } else { + participant?.let { + // create room etc etc + } } + } else if (element.type == ParticipantElementType.PARTICIPANT_NEW_GROUP.ordinal) { + + } else if (element.type == ParticipantElementType.PARTICIPANT_JOIN_VIA_LINK.ordinal) { + } } override fun getTitle(): String? { - return resources?.getString(R.string.nc_select_contacts) + return when { + isGroupConversation -> { + resources?.getString(R.string.nc_select_contacts) + } + hasToken -> { + resources?.getString(R.string.nc_select_new_contacts) + } + else -> { + resources?.getString(R.string.nc_select_contact) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsFooterSource.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsViewFooterSource.kt similarity index 94% rename from app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsFooterSource.kt rename to app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsViewFooterSource.kt index 3a1145ff7..55e5feb6c 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsFooterSource.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsViewFooterSource.kt @@ -29,7 +29,7 @@ import com.otaliastudios.elements.Page import com.otaliastudios.elements.Source import com.otaliastudios.elements.extensions.FooterSource -class ContactsFooterSource(private val context: Context, private val elementType: Int) : FooterSource() { +class ContactsViewFooterSource(private val context: Context, private val elementType: Int) : FooterSource() { private var lastAnchor: Participant? = null override fun dependsOn(source: Source<*>): Boolean { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsViewModel.kt index f69db06e4..78b8798b8 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/ContactsViewModel.kt @@ -45,11 +45,13 @@ class ContactsViewModel constructor( private var searchQuery: String? = null private var conversationToken: String? = null + private var groupConversation: Boolean = false private var initialized = false - fun initialize(conversationToken: String?) { - if (!initialized || conversationToken != this.conversationToken) { + fun initialize(conversationToken: String?, groupConversation: Boolean) { + if (!initialized || conversationToken != this.conversationToken || groupConversation != this.groupConversation) { this.conversationToken = conversationToken + this.groupConversation = groupConversation loadContacts() } } @@ -70,7 +72,7 @@ class ContactsViewModel constructor( } fun loadContacts() { - getContactsUseCase.invoke(viewModelScope, parametersOf(globalService.currentUserLiveData.value, searchQuery, conversationToken), object : + getContactsUseCase.invoke(viewModelScope, parametersOf(globalService.currentUserLiveData.value, groupConversation, searchQuery, conversationToken), object : UseCaseResponse> { override suspend fun onSuccess(result: List) { val sortPriority = mapOf("users" to 0, "groups" to 1, "emails" to 2, "circles" to 0) diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/source/FixedListSource.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/source/FixedListSource.kt new file mode 100644 index 000000000..3ae30f901 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/contactsflow/source/FixedListSource.kt @@ -0,0 +1,41 @@ +/* + * + * * Nextcloud Talk application + * * + * * @author Mario Danic + * * Copyright (C) 2017-2020 Mario Danic + * * + * * This program 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. + * * + * * This program 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 this program. If not, see . + * + */ + +package com.nextcloud.talk.newarch.features.contactsflow.source + +import com.nextcloud.talk.newarch.features.contactsflow.ContactsViewSource +import com.otaliastudios.elements.Source +import com.otaliastudios.elements.extensions.ListSource + +class FixedListSource(list: List, elementType: Int) : ListSource(list, elementType) { + override fun areContentsTheSame(first: Any, second: Any): Boolean { + return true + } + + override fun areItemsTheSame(own: Any, dependency: Source, other: E?): Boolean { + return true + } + + override fun dependsOn(source: Source<*>): Boolean { + return source is ContactsViewSource + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index 1e39ede82..6cb6be679 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -67,16 +67,18 @@ public class ApiUtils { return baseUrl + ocsApiVersion + "/core/autocomplete/get"; } - public static List getShareTypesForContactsSearch() { + public static List getShareTypesForContactsSearch(boolean groupConversation) { List shareTypesList = new ArrayList<>(); // user shareTypesList.add("0"); - // group - shareTypesList.add("1"); - // group - shareTypesList.add("4"); - // remote/circles - shareTypesList.add("7"); + if (groupConversation) { + // group + shareTypesList.add("1"); + // email + shareTypesList.add("4"); + // remote/circles + shareTypesList.add("7"); + } return shareTypesList; } diff --git a/app/src/main/res/menu/menu_contacts.xml b/app/src/main/res/menu/menu_contacts.xml index 1ec3e3ec7..b61e63a9a 100644 --- a/app/src/main/res/menu/menu_contacts.xml +++ b/app/src/main/res/menu/menu_contacts.xml @@ -27,7 +27,7 @@ android:icon="@drawable/ic_search_white_24dp" android:title="@string/nc_search" app:actionViewClass="androidx.appcompat.widget.SearchView" - app:showAsAction="collapseActionView|always" /> + app:showAsAction="collapseActionView|ifRoom" /> + Find participant Find participants + Find new participants Done @@ -339,4 +341,5 @@ M3.27,4.27L19.74,20.74 Search for more participants + New group