From 9d6feca3f9dd18be2e93c671823c8468905eb511 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 3 Jan 2020 22:58:01 +0100 Subject: [PATCH] Lots of progress on reworked conversations --- .idea/misc.xml | 1 + .idea/modules.xml | 9 - app/build.gradle | 54 ++-- app/gplay.gradle | 2 +- .../talk/adapters/ConversationPresenter.kt | 172 ++++++++++ .../talk/adapters/ConversationsSource.kt | 30 ++ .../talk/adapters/items/ConversationItem.kt | 13 +- .../MentionAutocompleteCallback.java | 2 +- .../talk/controllers/base/BaseController.kt | 2 +- .../ConversationsListView.kt | 295 +++--------------- .../ConversationsListViewModel.kt | 7 +- .../layout/controller_conversations_rv.xml | 7 - app/src/main/res/layout/loading_state.xml | 37 +++ app/src/main/res/layout/message_state.xml | 48 +++ app/src/main/res/layout/view_states.xml | 67 ---- app/src/main/res/values/strings.xml | 2 + 16 files changed, 380 insertions(+), 368 deletions(-) delete mode 100644 .idea/modules.xml create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/ConversationPresenter.kt create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/ConversationsSource.kt create mode 100644 app/src/main/res/layout/loading_state.xml create mode 100644 app/src/main/res/layout/message_state.xml delete mode 100644 app/src/main/res/layout/view_states.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 7bfef59df..69139282f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,6 @@ + diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index a4d92409c..000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 914790766..98ab86851 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,7 +19,7 @@ */ apply plugin: 'com.android.application' -apply plugin: 'findbugs' +//apply plugin: 'findbugs' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' @@ -82,9 +82,26 @@ android { } } - dataBinding { - enabled = true - } + /*buildFeatures { + // Determines whether to enable support for Jetpack Compose. + compose = false + // Determines whether to generate a BuildConfig class. + buildConfig = true + // Determines whether to support View Binding. + // Note that the viewBinding.enabled property is now deprecated. + viewBinding = true + // Determines whether to support Data Binding. + // Note that the dataBinding.enabled property is now deprecated. + dataBinding = true + // Determines whether to generate binder classes for your AIDL files. + aidl = true + // Determines whether to support RenderScript. + renderScript = true + // Determines whether to support injecting custom variables into the module’s R class. + resValues = true + // Determines whether to support shader AOT compilation. + shaders = true + }*/ androidExtensions { experimental = true @@ -134,27 +151,6 @@ android { htmlOutput file("$project.buildDir/reports/lint/lint.html") disable 'MissingTranslation' } - - task findbugs(type: FindBugs) { - ignoreFailures = false - effort = "max" - reportLevel = "medium" - classes = fileTree("$project.buildDir/intermediates/javac/gplayDebug/classes/com/nextcloud") - excludeFilter = file("${project.rootDir}/findbugs-filter.xml") - source = fileTree('src/main/java') - pluginClasspath = project.configurations.findbugsPlugins - classpath = files() - include '**/*.java' - exclude '**/gen/**' - - reports { - xml.enabled = false - html.enabled = true - html { - destination = file("$project.buildDir/reports/findbugs/findbugs.html") - } - } - } } ext { @@ -242,7 +238,7 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' - implementation 'androidx.biometric:biometric:1.0.0' + implementation 'androidx.biometric:biometric:1.0.1' implementation "androidx.lifecycle:lifecycle-extensions:2.1.0" implementation 'androidx.multidex:multidex:2.0.1' @@ -278,7 +274,7 @@ dependencies { kapt 'io.requery:requery-processor:1.5.1' implementation 'net.orange-box.storebox:storebox-lib:1.4.0' compileOnly 'org.projectlombok:lombok:1.18.10' - annotationProcessor 'org.projectlombok:lombok:1.18.10' + annotationProcessor "org.projectlombok:lombok:1.18.10" kapt "org.projectlombok:lombok:1.18.10" implementation 'com.jakewharton:butterknife:10.2.0' @@ -287,6 +283,7 @@ dependencies { implementation 'eu.davidea:flexible-adapter:5.1.0' implementation 'eu.davidea:flexible-adapter-ui:1.0.0' implementation 'eu.davidea:flexible-adapter-livedata:1.0.0-b3' + implementation 'com.otaliastudios:elements:0.3.7' implementation 'org.webrtc:google-webrtc:1.0.23295' implementation 'com.yarolegovich:lovely-dialog:1.1.0' implementation 'com.yarolegovich:lovelyinput:1.0.9' @@ -327,9 +324,6 @@ dependencies { androidTestImplementation('androidx.test.espresso:espresso-core:3.3.0-alpha02', { exclude group: 'com.android.support', module: 'support-annotations' }) - findbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.10.0' - findbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.7' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0' implementation 'com.github.Kennyc1012:BottomSheet:2.4.1' - implementation 'com.google.firebase:firebase-messaging:20.1.0' } diff --git a/app/gplay.gradle b/app/gplay.gradle index d51f37891..a613566ec 100644 --- a/app/gplay.gradle +++ b/app/gplay.gradle @@ -19,6 +19,6 @@ */ dependencies { - implementation "androidx.work:work-gcm:2.3.0-beta01" + implementation "androidx.work:work-gcm:2.3.0-beta02" implementation "com.google.firebase:firebase-messaging:20.1.0" } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/ConversationPresenter.kt b/app/src/main/java/com/nextcloud/talk/adapters/ConversationPresenter.kt new file mode 100644 index 000000000..31d60fe55 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/ConversationPresenter.kt @@ -0,0 +1,172 @@ +/* + * + * * 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.adapters + +import android.content.Context +import android.graphics.drawable.Drawable +import android.text.TextUtils +import android.text.format.DateUtils +import android.view.View +import android.view.ViewGroup +import coil.api.load +import coil.transform.CircleCropTransformation +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.newarch.local.models.getCredentials +import com.nextcloud.talk.newarch.services.GlobalService +import com.nextcloud.talk.newarch.utils.Images +import com.nextcloud.talk.utils.ApiUtils +import com.otaliastudios.elements.Element +import com.otaliastudios.elements.Page +import com.otaliastudios.elements.Presenter +import kotlinx.android.synthetic.main.rv_item_conversation_with_last_message.view.* +import org.koin.core.KoinComponent +import org.koin.core.inject + +open class ConversationsPresenter(context: Context, onElementClick: ((Page, Holder, Element) -> Unit)?) : Presenter(context, onElementClick), KoinComponent { + private val globalService: GlobalService by inject() + + override val elementTypes: Collection + get() = listOf(0) + + override fun onCreate(parent: ViewGroup, elementType: Int): Holder { + return Holder(getLayoutInflater().inflate(R.layout.rv_item_conversation_with_last_message, parent, false)) + } + + override fun onBind(page: Page, holder: Holder, element: Element, payloads: List) { + super.onBind(page, holder, element, payloads) + val conversation = element.data + val user = globalService.currentUserLiveData.value + + user?.let { user -> + conversation?.let { conversation -> + val appContext = NextcloudTalkApplication.sharedApplication!!.applicationContext + + if (conversation.changing) { + holder.itemView.actionProgressBar!!.visibility = View.VISIBLE + } else { + holder.itemView.actionProgressBar!!.visibility = View.GONE + } + + holder.itemView.dialogName!!.text = conversation.displayName + + if (conversation.unreadMessages > 0) { + holder.itemView.dialogUnreadBubble!!.visibility = View.VISIBLE + if (conversation.unreadMessages < 100) { + holder.itemView.dialogUnreadBubble!!.text = conversation.unreadMessages.toLong() + .toString() + } else { + holder.itemView.dialogUnreadBubble!!.text = context.getString(R.string.nc_99_plus) + } + + if (conversation.unreadMention) { + holder.itemView.dialogUnreadBubble!!.background = + context.getDrawable(R.drawable.bubble_circle_unread_mention) + } else { + holder.itemView.dialogUnreadBubble!!.background = + context.getDrawable(R.drawable.bubble_circle_unread) + } + } else { + holder.itemView.dialogUnreadBubble!!.visibility = View.GONE + } + + if (conversation.hasPassword) { + holder.itemView.passwordProtectedRoomImageView!!.visibility = View.VISIBLE + } else { + holder.itemView.passwordProtectedRoomImageView!!.visibility = View.GONE + } + + if (conversation.favorite) { + holder.itemView.favoriteConversationImageView!!.visibility = View.VISIBLE + } else { + holder.itemView.favoriteConversationImageView!!.visibility = View.GONE + } + + if (conversation.lastMessage != null) { + holder.itemView.dialogDate!!.visibility = View.VISIBLE + holder.itemView.dialogDate!!.text = DateUtils.getRelativeTimeSpanString( + conversation.lastActivity * 1000L, + System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE + ) + + if (!TextUtils.isEmpty( + conversation.lastMessage!!.systemMessage + ) || Conversation.ConversationType.SYSTEM_CONVERSATION == conversation.type + ) { + holder.itemView.dialogLastMessage!!.text = conversation.lastMessage!!.text + } else { + var authorDisplayName = "" + conversation.lastMessage!!.activeUser = user + val text: String + if (conversation.lastMessage!! + .messageType == ChatMessage.MessageType.REGULAR_TEXT_MESSAGE && (!(Conversation.ConversationType.ONE_TO_ONE_CONVERSATION).equals( + conversation.type) || conversation.lastMessage!!.actorId == user.userId) + ) { + if (conversation.lastMessage!!.actorId == user.userId) { + text = String.format( + appContext.getString(R.string.nc_formatted_message_you), + conversation.lastMessage!!.lastMessageDisplayText + ) + } else { + authorDisplayName = if (!TextUtils.isEmpty(conversation.lastMessage!!.actorDisplayName)) + conversation.lastMessage!!.actorDisplayName + else if ("guests" == conversation.lastMessage!!.actorType) + appContext.getString(R.string.nc_guest) + else + "" + text = String.format( + appContext.getString(R.string.nc_formatted_message), + authorDisplayName, + conversation.lastMessage!!.lastMessageDisplayText + ) + } + } else { + text = conversation.lastMessage!!.lastMessageDisplayText + } + + holder.itemView.dialogLastMessage.text = text + } + } else { + holder.itemView.dialogDate.visibility = View.GONE + holder.itemView.dialogLastMessage.setText(R.string.nc_no_messages_yet) + } + + val conversationDrawable: Drawable? = Images().getImageForConversation(context, conversation) + + conversationDrawable?.let { + holder.itemView.dialogAvatar.load(conversationDrawable) + }?: run { + holder.itemView.dialogAvatar.load(ApiUtils.getUrlForAvatarWithName( + user.baseUrl, + conversation.name, R.dimen.avatar_size)) + { + addHeader("Authorization", user.getCredentials()) + transformations(CircleCropTransformation()) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/adapters/ConversationsSource.kt b/app/src/main/java/com/nextcloud/talk/adapters/ConversationsSource.kt new file mode 100644 index 000000000..62adbbcb7 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/ConversationsSource.kt @@ -0,0 +1,30 @@ +/* + * + * * 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.adapters + +import androidx.lifecycle.LiveData +import com.nextcloud.talk.models.json.conversations.Conversation +import com.otaliastudios.elements.extensions.LiveDataSource + +class ConversationsSource(data: LiveData>, elementType: Int) : LiveDataSource(data, elementType) { +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt index a107c10df..17ee5da1e 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt @@ -24,7 +24,9 @@ import android.content.Context import android.graphics.drawable.Drawable import android.text.TextUtils import android.text.format.DateUtils +import android.util.Log import android.view.View +import androidx.recyclerview.widget.RecyclerView import coil.api.load import coil.transform.CircleCropTransformation import com.nextcloud.talk.R @@ -212,11 +214,20 @@ class ConversationItem( addHeader("Authorization", user.getCredentials()) transformations(CircleCropTransformation()) } - } } + override fun onViewAttached(adapter: FlexibleAdapter>?, holder: ConversationItemViewHolder?, position: Int) { + super.onViewAttached(adapter, holder, position) + Log.d("MAriO", model.displayName!!) + } + + override fun onViewDetached(adapter: FlexibleAdapter>?, holder: ConversationItemViewHolder?, position: Int) { + super.onViewDetached(adapter, holder, position) + Log.d("MAriO DETACH", model.displayName!!) + } + override fun filter(constraint: String): Boolean { return model.displayName != null && Pattern.compile( constraint, Pattern.CASE_INSENSITIVE or Pattern.LITERAL diff --git a/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java b/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java index 5e51fe5da..a259a2c5b 100644 --- a/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java +++ b/app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java @@ -54,7 +54,7 @@ public class MentionAutocompleteCallback implements AutocompleteCallback = ControllerScopeProvider.from(this) + open val scopeProvider: LifecycleScopeProvider<*> = ControllerScopeProvider.from(this) val appPreferences: AppPreferences by inject() val context: Context by inject() diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt index b2633f1f1..29175e896 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListView.kt @@ -20,63 +20,44 @@ package com.nextcloud.talk.newarch.features.conversationsList -import android.app.SearchManager -import android.content.Context -import android.content.Intent -import android.os.Build import android.os.Bundle -import android.text.InputType import android.view.* -import android.view.inputmethod.EditorInfo import androidx.appcompat.widget.SearchView -import androidx.appcompat.widget.SearchView.OnQueryTextListener -import androidx.core.view.MenuItemCompat -import androidx.core.view.isVisible -import androidx.lifecycle.Observer +import androidx.lifecycle.observe import butterknife.OnClick -import com.afollestad.materialdialogs.LayoutMode.WRAP_CONTENT -import com.afollestad.materialdialogs.MaterialDialog -import com.afollestad.materialdialogs.bottomsheets.BottomSheet import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.bluelinelabs.conductor.changehandler.TransitionChangeHandlerCompat import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler import com.nextcloud.talk.R import com.nextcloud.talk.R.drawable -import com.nextcloud.talk.adapters.items.ConversationItem +import com.nextcloud.talk.adapters.ConversationsPresenter import com.nextcloud.talk.controllers.ContactsController import com.nextcloud.talk.controllers.SettingsController import com.nextcloud.talk.controllers.bottomsheet.items.BasicListItemWithImage -import com.nextcloud.talk.controllers.bottomsheet.items.listItemsWithImage import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.newarch.conversationsList.mvp.BaseView -import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListViewNetworkState.* import com.nextcloud.talk.newarch.mvvm.ext.initRecyclerView import com.nextcloud.talk.utils.ConductorRemapping -import com.nextcloud.talk.utils.DisplayUtils -import com.nextcloud.talk.utils.ShareUtils import com.nextcloud.talk.utils.animations.SharedElementTransition import com.nextcloud.talk.utils.bundle.BundleKeys -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemClickListener -import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemLongClickListener +import com.otaliastudios.elements.* +import com.uber.autodispose.lifecycle.LifecycleScopeProvider import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager -import eu.davidea.flexibleadapter.items.IFlexible import kotlinx.android.synthetic.main.controller_conversations_rv.view.* -import kotlinx.android.synthetic.main.fast_scroller.view.* -import kotlinx.android.synthetic.main.view_states.view.* +import kotlinx.android.synthetic.main.message_state.view.* import org.koin.android.ext.android.inject import org.parceler.Parcels import java.util.* -class ConversationsListView : BaseView(), OnQueryTextListener, - OnItemClickListener, OnItemLongClickListener { +class ConversationsListView : BaseView() { + + override val scopeProvider: LifecycleScopeProvider<*> = ControllerScopeProvider.from(this) private lateinit var viewModel: ConversationsListViewModel val factory: ConversationListViewModelFactory by inject() - private val recyclerViewAdapter = FlexibleAdapter(mutableListOf(), this, true) - private var searchItem: MenuItem? = null private var settingsItem: MenuItem? = null private var searchView: SearchView? = null @@ -88,19 +69,11 @@ class ConversationsListView : BaseView(), OnQueryTextListener, super.onCreateOptionsMenu(menu, inflater) inflater.inflate(R.menu.menu_conversation_plus_filter, menu) searchItem = menu.findItem(R.id.action_search) - initSearchView() } override fun onPrepareOptionsMenu(menu: Menu) { super.onPrepareOptionsMenu(menu) - if (recyclerViewAdapter.hasFilter()) { - searchItem?.expandActionView() - searchView?.setQuery(viewModel.searchQuery.value, false) - recyclerViewAdapter.filterItems() - } - settingsItem = menu.findItem(R.id.action_settings) - searchItem?.isVisible = searchItem?.isVisible == false && !recyclerViewAdapter.isEmpty viewModel.loadAvatar() } @@ -128,7 +101,7 @@ class ConversationsListView : BaseView(), OnQueryTextListener, } } - private fun initSearchView() { + /*private fun initSearchView() { val searchManager = activity!!.getSystemService(Context.SEARCH_SERVICE) as SearchManager searchView = MenuItemCompat.getActionView(searchItem) as SearchView searchView!!.maxWidth = Integer.MAX_VALUE @@ -154,7 +127,7 @@ class ConversationsListView : BaseView(), OnQueryTextListener, override fun onQueryTextChange(newText: String?): Boolean { return onQueryTextSubmit(newText) - } + }*/ override fun onCreateView( inflater: LayoutInflater, @@ -164,114 +137,58 @@ class ConversationsListView : BaseView(), OnQueryTextListener, actionBar?.show() viewModel = viewModelProvider(factory).get(ConversationsListViewModel::class.java) - val view = super.onCreateView(inflater, container) + val adapter = Adapter.builder(this) + .addSource(Source.fromLiveData(viewModel.conversationsLiveData)) + .addPresenter(ConversationsPresenter(context, ::onElementClick)) + .addPresenter(Presenter.forLoadingIndicator(context, R.layout.loading_state)) + .addPresenter(Presenter.forEmptyIndicator(context, R.layout.message_state)) + .addPresenter(Presenter.forErrorIndicator(context, R.layout.message_state) { view, throwable -> + view.messageStateTextView.setText(R.string.nc_oops) + view.messageStateImageView.setImageDrawable(context.getDrawable(drawable.ic_announcement_white_24dp)) + }) + .into(view.recyclerView) + view.recyclerView.initRecyclerView(SmoothScrollLinearLayoutManager(activity), adapter, false) + view.apply { - recyclerView.initRecyclerView( - SmoothScrollLinearLayoutManager(activity), recyclerViewAdapter, false) - recyclerViewAdapter.fastScroller = fast_scroller + recyclerView.initRecyclerView(SmoothScrollLinearLayoutManager(activity), adapter, false) + swipeRefreshLayoutView.setOnRefreshListener { view.swipeRefreshLayoutView.isRefreshing = false viewModel.loadConversations() } + swipeRefreshLayoutView.setColorSchemeResources(R.color.colorPrimary) - fast_scroller.setBubbleTextCreator { position -> - var displayName = - (recyclerViewAdapter.getItem(position) as ConversationItem).model.displayName - - if (displayName!!.length > 8) { - displayName = displayName.substring(0, 4) + "..." - } - - displayName - } } - viewModel.apply { - currentUserAvatar.observe(this@ConversationsListView, Observer { value -> - settingsItem?.icon = value - }) - - conversationsLiveData.observe(this@ConversationsListView, Observer { - val isListEmpty = it.isNullOrEmpty() - - if (isListEmpty) { - view.stateWithMessageView?.errorStateTextView?.text = - resources?.getText(R.string.nc_conversations_empty) - view.stateWithMessageView?.errorStateImageView?.setImageResource(drawable.ic_logo) - } - - view.stateWithMessageView?.visibility = if (isListEmpty && networkStateLiveData.value != LOADING) View.VISIBLE else View.GONE - - if (view.floatingActionButton?.isShown == false) { - view.floatingActionButton?.show() - } - - searchItem?.isVisible = searchItem?.isVisible == false && !isListEmpty - - val newConversations = mutableListOf() - for (conversation in it) { - newConversations.add( - ConversationItem( - conversation, globalService.currentUserLiveData.value!!, - activity!! - ) - ) - } - - recyclerViewAdapter.updateDataSet( - newConversations as List>, false - ) - - }) - - networkStateLiveData.observe(this@ConversationsListView, Observer { value -> - when (value) { - LOADING -> { - view.post { - view.loadingStateView?.visibility = View.VISIBLE - view.recyclerView?.visibility = View.GONE - view.stateWithMessageView?.visibility = View.GONE - view.floatingActionButton?.visibility = View.GONE - } - } - LOADED -> { - // awesome, but we delegate the magic stuff to the data handler - view.post { - view.loadingStateView?.visibility = View.GONE - view.recyclerView?.visibility = View.VISIBLE - view.stateWithMessageView?.visibility = if (recyclerViewAdapter.isEmpty) View.VISIBLE else View.GONE - view.floatingActionButton?.visibility = View.VISIBLE - if (view.floatingActionButton?.isShown == false) { - view.floatingActionButton?.show() - } - } - } - FAILED -> { - // probably offline, so what? :) - view.post { - view.loadingStateView?.visibility = View.GONE - view.recyclerView?.visibility = View.VISIBLE - view.floatingActionButton?.visibility = View.GONE - view.stateWithMessageView?.visibility = if (recyclerViewAdapter.isEmpty) View.VISIBLE else View.GONE - } - } - else -> { - // We should not be here - } - } - }) - - searchQuery.observe(this@ConversationsListView, Observer { - recyclerViewAdapter.setFilter(it) - recyclerViewAdapter.filterItems(500) - }) + viewModel.avatar.observe(this@ConversationsListView) { avatar -> + settingsItem?.icon = avatar } return view } + private fun onElementClick(page: Page, holder: Presenter.Holder, element: Element) { + val conversation = element.data + val user = viewModel.globalService.currentUserLiveData.value + + user?.let { user -> + conversation?.let { conversation -> + val bundle = Bundle() + bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, user) + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation.token) + bundle.putString(BundleKeys.KEY_ROOM_ID, conversation.conversationId) + bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, Parcels.wrap(conversation)) + ConductorRemapping.remapChatController( + router, user.id!!, conversation.token!!, + bundle, false + ) + } + + } + } + override fun getLayoutId(): Int { return R.layout.controller_conversations_rv } @@ -281,13 +198,6 @@ class ConversationsListView : BaseView(), OnQueryTextListener, openNewConversationScreen() } - @OnClick(R.id.stateWithMessageView) - fun onStateWithMessageViewClick() { - if (view?.floatingActionButton?.isVisible == true) { - openNewConversationScreen() - } - } - private fun openNewConversationScreen() { val bundle = Bundle() bundle.putBoolean(BundleKeys.KEY_NEW_CONVERSATION, true) @@ -298,31 +208,6 @@ class ConversationsListView : BaseView(), OnQueryTextListener, ) } - private fun getShareIntentForConversation(conversation: Conversation): Intent { - val sendIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra( - Intent.EXTRA_SUBJECT, - String.format( - context.getString(R.string.nc_share_subject), - context.getString(R.string.nc_app_name) - ) - ) - - // TODO, make sure we ask for password if needed - putExtra( - Intent.EXTRA_TEXT, ShareUtils.getStringForIntent( - context, null, conversation - ) - ) - - type = "text/plain" - } - - // TODO filter our own app once we're there - return Intent.createChooser(sendIntent, context.getString(R.string.nc_share_link)) - } - private fun getConversationMenuItemsForConversation(conversation: Conversation): MutableList { val items = mutableListOf() @@ -381,88 +266,4 @@ class ConversationsListView : BaseView(), OnQueryTextListener, super.onRestoreViewState(view, savedViewState) viewModel.loadConversations() } - - override fun onItemLongClick(position: Int) { - val clickedItem = recyclerViewAdapter.getItem(position) - clickedItem?.let { - val conversation = (it as ConversationItem).model - - activity?.let { activity -> - MaterialDialog(activity, BottomSheet(WRAP_CONTENT)).show { - cornerRadius(res = R.dimen.corner_radius) - title(text = conversation.displayName) - listItemsWithImage(getConversationMenuItemsForConversation(conversation) - ) { dialog, - index, item -> - - when (item.iconRes) { - drawable.ic_star_border_black_24dp -> { - viewModel.changeFavoriteValueForConversation(conversation, false) - - } - drawable.ic_star_black_24dp -> { - viewModel.changeFavoriteValueForConversation(conversation, true) - } - drawable.ic_share_black_24dp -> { - startActivity(getShareIntentForConversation(conversation)) - } - drawable.ic_exit_to_app_black_24dp -> { - MaterialDialog(activity).show { - title(R.string.nc_leave) - message(R.string.nc_leave_message) - positiveButton(R.string.nc_simple_leave) { dialog -> - viewModel.leaveConversation(conversation) - } - negativeButton(R.string.nc_cancel) - icon(drawable.ic_exit_to_app_black_24dp) - } - } - drawable.ic_delete_grey600_24dp -> { - MaterialDialog(activity).show { - title(R.string.nc_delete) - message(text = conversation.deleteWarningMessage) - positiveButton(R.string.nc_delete_call) { dialog -> - viewModel.deleteConversation(conversation) - } - negativeButton(R.string.nc_cancel) - icon( - drawable = DisplayUtils.getTintedDrawable( - resources!!, drawable - .ic_delete_grey600_24dp, R.color.nc_darkRed - ) - ) - } - } - else -> { - - } - } - } - } - } - } - } - - override fun onItemClick( - view: View?, - position: Int - ): Boolean { - val clickedItem = recyclerViewAdapter.getItem(position) - if (clickedItem != null) { - val conversationItem = clickedItem as ConversationItem - val conversation = conversationItem.model - - val bundle = Bundle() - bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationItem.user) - bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation.token) - bundle.putString(BundleKeys.KEY_ROOM_ID, conversation.conversationId) - bundle.putParcelable(BundleKeys.KEY_ACTIVE_CONVERSATION, Parcels.wrap(conversation)) - ConductorRemapping.remapChatController( - router, conversationItem.user.id!!, conversation.token!!, - bundle, false - ) - } - - return true - } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt index 7f4aca2d0..d55d6af38 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt @@ -60,9 +60,8 @@ class ConversationsListViewModel constructor( private val conversationsLoadingLock = ReentrantLock() var messageData: String? = null - val searchQuery = MutableLiveData() val networkStateLiveData: MutableLiveData = MutableLiveData(ConversationsListViewNetworkState.LOADING) - val currentUserAvatar: MutableLiveData = MutableLiveData(DisplayUtils.getRoundedDrawable(context.getDrawable(R.drawable.ic_settings_white_24dp))) + val avatar: MutableLiveData = MutableLiveData(DisplayUtils.getRoundedDrawable(context.getDrawable(R.drawable.ic_settings_white_24dp))) val conversationsLiveData = Transformations.switchMap(globalService.currentUserLiveData) { if (networkStateLiveData.value != ConversationsListViewNetworkState.LOADING) { networkStateLiveData.postValue(ConversationsListViewNetworkState.LOADING) @@ -163,12 +162,12 @@ class ConversationsListViewModel constructor( operationUser?.let { viewModelScope.launch { - val url = ApiUtils.getUrlForAvatarWithNameAndPixels(it.baseUrl, it.userId, 512) + val url = ApiUtils.getUrlForAvatarWithNameAndPixels(it.baseUrl, it.userId, 256) val drawable = Coil.get((url)) { addHeader("Authorization", it.getCredentials()) transformations(CircleCropTransformation()) } - currentUserAvatar.postValue(drawable) + avatar.postValue(drawable) } } } diff --git a/app/src/main/res/layout/controller_conversations_rv.xml b/app/src/main/res/layout/controller_conversations_rv.xml index 0db8ed335..b1208e7dc 100644 --- a/app/src/main/res/layout/controller_conversations_rv.xml +++ b/app/src/main/res/layout/controller_conversations_rv.xml @@ -26,11 +26,6 @@ android:layout_height="match_parent" android:animateLayoutChanges="true"> - - - - diff --git a/app/src/main/res/layout/loading_state.xml b/app/src/main/res/layout/loading_state.xml new file mode 100644 index 000000000..c353c525f --- /dev/null +++ b/app/src/main/res/layout/loading_state.xml @@ -0,0 +1,37 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/message_state.xml b/app/src/main/res/layout/message_state.xml new file mode 100644 index 000000000..b5e798480 --- /dev/null +++ b/app/src/main/res/layout/message_state.xml @@ -0,0 +1,48 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_states.xml b/app/src/main/res/layout/view_states.xml deleted file mode 100644 index 7a2f7c38e..000000000 --- a/app/src/main/res/layout/view_states.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fe0e15dbf..8077eec0e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -316,6 +316,8 @@ Unknown error Unauthorized + Ooops, something went wrong. + General Allow guests Could not leave conversation