mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-19 10:45:13 +01:00
Lots of progress on reworked conversations
This commit is contained in:
parent
af04c95bab
commit
9d6feca3f9
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/talk-android.iml" filepath="$PROJECT_DIR$/talk-android.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'findbugs'
|
//apply plugin: 'findbugs'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
@ -82,9 +82,26 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dataBinding {
|
/*buildFeatures {
|
||||||
enabled = true
|
// 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 {
|
androidExtensions {
|
||||||
experimental = true
|
experimental = true
|
||||||
@ -134,27 +151,6 @@ android {
|
|||||||
htmlOutput file("$project.buildDir/reports/lint/lint.html")
|
htmlOutput file("$project.buildDir/reports/lint/lint.html")
|
||||||
disable 'MissingTranslation'
|
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 {
|
ext {
|
||||||
@ -242,7 +238,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
|
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.lifecycle:lifecycle-extensions:2.1.0"
|
||||||
|
|
||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
@ -278,7 +274,7 @@ dependencies {
|
|||||||
kapt 'io.requery:requery-processor:1.5.1'
|
kapt 'io.requery:requery-processor:1.5.1'
|
||||||
implementation 'net.orange-box.storebox:storebox-lib:1.4.0'
|
implementation 'net.orange-box.storebox:storebox-lib:1.4.0'
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.10'
|
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"
|
kapt "org.projectlombok:lombok:1.18.10"
|
||||||
|
|
||||||
implementation 'com.jakewharton:butterknife:10.2.0'
|
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:5.1.0'
|
||||||
implementation 'eu.davidea:flexible-adapter-ui:1.0.0'
|
implementation 'eu.davidea:flexible-adapter-ui:1.0.0'
|
||||||
implementation 'eu.davidea:flexible-adapter-livedata:1.0.0-b3'
|
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 'org.webrtc:google-webrtc:1.0.23295'
|
||||||
implementation 'com.yarolegovich:lovely-dialog:1.1.0'
|
implementation 'com.yarolegovich:lovely-dialog:1.1.0'
|
||||||
implementation 'com.yarolegovich:lovelyinput:1.0.9'
|
implementation 'com.yarolegovich:lovelyinput:1.0.9'
|
||||||
@ -327,9 +324,6 @@ dependencies {
|
|||||||
androidTestImplementation('androidx.test.espresso:espresso-core:3.3.0-alpha02', {
|
androidTestImplementation('androidx.test.espresso:espresso-core:3.3.0-alpha02', {
|
||||||
exclude group: 'com.android.support', module: 'support-annotations'
|
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 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
|
||||||
implementation 'com.github.Kennyc1012:BottomSheet:2.4.1'
|
implementation 'com.github.Kennyc1012:BottomSheet:2.4.1'
|
||||||
implementation 'com.google.firebase:firebase-messaging:20.1.0'
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
dependencies {
|
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"
|
implementation "com.google.firebase:firebase-messaging:20.1.0"
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* * Nextcloud Talk application
|
||||||
|
* *
|
||||||
|
* * @author Mario Danic
|
||||||
|
* * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.com>
|
||||||
|
* *
|
||||||
|
* * 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<Conversation>) -> Unit)?) : Presenter<Conversation>(context, onElementClick), KoinComponent {
|
||||||
|
private val globalService: GlobalService by inject()
|
||||||
|
|
||||||
|
override val elementTypes: Collection<Int>
|
||||||
|
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<Conversation>, payloads: List<Any>) {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* * Nextcloud Talk application
|
||||||
|
* *
|
||||||
|
* * @author Mario Danic
|
||||||
|
* * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.com>
|
||||||
|
* *
|
||||||
|
* * 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<List<Conversation>>, elementType: Int) : LiveDataSource<Conversation>(data, elementType) {
|
||||||
|
}
|
@ -24,7 +24,9 @@ import android.content.Context
|
|||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import coil.api.load
|
import coil.api.load
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
@ -212,9 +214,18 @@ class ConversationItem(
|
|||||||
addHeader("Authorization", user.getCredentials())
|
addHeader("Authorization", user.getCredentials())
|
||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onViewAttached(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?, holder: ConversationItemViewHolder?, position: Int) {
|
||||||
|
super.onViewAttached(adapter, holder, position)
|
||||||
|
Log.d("MAriO", model.displayName!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewDetached(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?, holder: ConversationItemViewHolder?, position: Int) {
|
||||||
|
super.onViewDetached(adapter, holder, position)
|
||||||
|
Log.d("MAriO DETACH", model.displayName!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun filter(constraint: String): Boolean {
|
override fun filter(constraint: String): Boolean {
|
||||||
|
@ -54,7 +54,7 @@ public class MentionAutocompleteCallback implements AutocompleteCallback<Mention
|
|||||||
if (range == null) return false;
|
if (range == null) return false;
|
||||||
int start = range[0];
|
int start = range[0];
|
||||||
int end = range[1];
|
int end = range[1];
|
||||||
String replacement = item.getLabel();
|
String replacement = item.label;
|
||||||
|
|
||||||
StringBuilder replacementStringBuilder = new StringBuilder(item.getLabel());
|
StringBuilder replacementStringBuilder = new StringBuilder(item.getLabel());
|
||||||
for (EmojiRange emojiRange : EmojiUtils.emojis(replacement)) {
|
for (EmojiRange emojiRange : EmojiUtils.emojis(replacement)) {
|
||||||
|
@ -47,7 +47,7 @@ import java.util.*
|
|||||||
|
|
||||||
abstract class BaseController : ButterKnifeController(), ComponentCallbacks {
|
abstract class BaseController : ButterKnifeController(), ComponentCallbacks {
|
||||||
|
|
||||||
val scopeProvider: LifecycleScopeProvider<*> = ControllerScopeProvider.from(this)
|
open val scopeProvider: LifecycleScopeProvider<*> = ControllerScopeProvider.from(this)
|
||||||
|
|
||||||
val appPreferences: AppPreferences by inject()
|
val appPreferences: AppPreferences by inject()
|
||||||
val context: Context by inject()
|
val context: Context by inject()
|
||||||
|
@ -20,63 +20,44 @@
|
|||||||
|
|
||||||
package com.nextcloud.talk.newarch.features.conversationsList
|
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.os.Bundle
|
||||||
import android.text.InputType
|
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.inputmethod.EditorInfo
|
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
import androidx.lifecycle.observe
|
||||||
import androidx.core.view.MenuItemCompat
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import butterknife.OnClick
|
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.RouterTransaction
|
||||||
|
import com.bluelinelabs.conductor.autodispose.ControllerScopeProvider
|
||||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||||
import com.bluelinelabs.conductor.changehandler.TransitionChangeHandlerCompat
|
import com.bluelinelabs.conductor.changehandler.TransitionChangeHandlerCompat
|
||||||
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
|
import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
|
||||||
import com.nextcloud.talk.R
|
import com.nextcloud.talk.R
|
||||||
import com.nextcloud.talk.R.drawable
|
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.ContactsController
|
||||||
import com.nextcloud.talk.controllers.SettingsController
|
import com.nextcloud.talk.controllers.SettingsController
|
||||||
import com.nextcloud.talk.controllers.bottomsheet.items.BasicListItemWithImage
|
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.models.json.conversations.Conversation
|
||||||
import com.nextcloud.talk.newarch.conversationsList.mvp.BaseView
|
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.newarch.mvvm.ext.initRecyclerView
|
||||||
import com.nextcloud.talk.utils.ConductorRemapping
|
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.animations.SharedElementTransition
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import com.otaliastudios.elements.*
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemClickListener
|
import com.uber.autodispose.lifecycle.LifecycleScopeProvider
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemLongClickListener
|
|
||||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
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.controller_conversations_rv.view.*
|
||||||
import kotlinx.android.synthetic.main.fast_scroller.view.*
|
import kotlinx.android.synthetic.main.message_state.view.*
|
||||||
import kotlinx.android.synthetic.main.view_states.view.*
|
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.parceler.Parcels
|
import org.parceler.Parcels
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ConversationsListView : BaseView(), OnQueryTextListener,
|
class ConversationsListView : BaseView() {
|
||||||
OnItemClickListener, OnItemLongClickListener {
|
|
||||||
|
override val scopeProvider: LifecycleScopeProvider<*> = ControllerScopeProvider.from(this)
|
||||||
|
|
||||||
private lateinit var viewModel: ConversationsListViewModel
|
private lateinit var viewModel: ConversationsListViewModel
|
||||||
val factory: ConversationListViewModelFactory by inject()
|
val factory: ConversationListViewModelFactory by inject()
|
||||||
|
|
||||||
private val recyclerViewAdapter = FlexibleAdapter(mutableListOf(), this, true)
|
|
||||||
|
|
||||||
private var searchItem: MenuItem? = null
|
private var searchItem: MenuItem? = null
|
||||||
private var settingsItem: MenuItem? = null
|
private var settingsItem: MenuItem? = null
|
||||||
private var searchView: SearchView? = null
|
private var searchView: SearchView? = null
|
||||||
@ -88,19 +69,11 @@ class ConversationsListView : BaseView(), OnQueryTextListener,
|
|||||||
super.onCreateOptionsMenu(menu, inflater)
|
super.onCreateOptionsMenu(menu, inflater)
|
||||||
inflater.inflate(R.menu.menu_conversation_plus_filter, menu)
|
inflater.inflate(R.menu.menu_conversation_plus_filter, menu)
|
||||||
searchItem = menu.findItem(R.id.action_search)
|
searchItem = menu.findItem(R.id.action_search)
|
||||||
initSearchView()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||||
super.onPrepareOptionsMenu(menu)
|
super.onPrepareOptionsMenu(menu)
|
||||||
if (recyclerViewAdapter.hasFilter()) {
|
|
||||||
searchItem?.expandActionView()
|
|
||||||
searchView?.setQuery(viewModel.searchQuery.value, false)
|
|
||||||
recyclerViewAdapter.filterItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
settingsItem = menu.findItem(R.id.action_settings)
|
settingsItem = menu.findItem(R.id.action_settings)
|
||||||
searchItem?.isVisible = searchItem?.isVisible == false && !recyclerViewAdapter.isEmpty
|
|
||||||
viewModel.loadAvatar()
|
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
|
val searchManager = activity!!.getSystemService(Context.SEARCH_SERVICE) as SearchManager
|
||||||
searchView = MenuItemCompat.getActionView(searchItem) as SearchView
|
searchView = MenuItemCompat.getActionView(searchItem) as SearchView
|
||||||
searchView!!.maxWidth = Integer.MAX_VALUE
|
searchView!!.maxWidth = Integer.MAX_VALUE
|
||||||
@ -154,7 +127,7 @@ class ConversationsListView : BaseView(), OnQueryTextListener,
|
|||||||
|
|
||||||
override fun onQueryTextChange(newText: String?): Boolean {
|
override fun onQueryTextChange(newText: String?): Boolean {
|
||||||
return onQueryTextSubmit(newText)
|
return onQueryTextSubmit(newText)
|
||||||
}
|
}*/
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
@ -164,114 +137,58 @@ class ConversationsListView : BaseView(), OnQueryTextListener,
|
|||||||
actionBar?.show()
|
actionBar?.show()
|
||||||
|
|
||||||
viewModel = viewModelProvider(factory).get(ConversationsListViewModel::class.java)
|
viewModel = viewModelProvider(factory).get(ConversationsListViewModel::class.java)
|
||||||
|
|
||||||
val view = super.onCreateView(inflater, container)
|
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 {
|
view.apply {
|
||||||
recyclerView.initRecyclerView(
|
recyclerView.initRecyclerView(SmoothScrollLinearLayoutManager(activity), adapter, false)
|
||||||
SmoothScrollLinearLayoutManager(activity), recyclerViewAdapter, false)
|
|
||||||
recyclerViewAdapter.fastScroller = fast_scroller
|
|
||||||
swipeRefreshLayoutView.setOnRefreshListener {
|
swipeRefreshLayoutView.setOnRefreshListener {
|
||||||
view.swipeRefreshLayoutView.isRefreshing = false
|
view.swipeRefreshLayoutView.isRefreshing = false
|
||||||
viewModel.loadConversations()
|
viewModel.loadConversations()
|
||||||
}
|
}
|
||||||
|
|
||||||
swipeRefreshLayoutView.setColorSchemeResources(R.color.colorPrimary)
|
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.avatar.observe(this@ConversationsListView) { avatar ->
|
||||||
}
|
settingsItem?.icon = avatar
|
||||||
}
|
|
||||||
|
|
||||||
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<ConversationItem>()
|
|
||||||
for (conversation in it) {
|
|
||||||
newConversations.add(
|
|
||||||
ConversationItem(
|
|
||||||
conversation, globalService.currentUserLiveData.value!!,
|
|
||||||
activity!!
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
recyclerViewAdapter.updateDataSet(
|
|
||||||
newConversations as List<IFlexible<ConversationItem.ConversationItemViewHolder>>, 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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onElementClick(page: Page, holder: Presenter.Holder, element: Element<Conversation>) {
|
||||||
|
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 {
|
override fun getLayoutId(): Int {
|
||||||
return R.layout.controller_conversations_rv
|
return R.layout.controller_conversations_rv
|
||||||
}
|
}
|
||||||
@ -281,13 +198,6 @@ class ConversationsListView : BaseView(), OnQueryTextListener,
|
|||||||
openNewConversationScreen()
|
openNewConversationScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnClick(R.id.stateWithMessageView)
|
|
||||||
fun onStateWithMessageViewClick() {
|
|
||||||
if (view?.floatingActionButton?.isVisible == true) {
|
|
||||||
openNewConversationScreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openNewConversationScreen() {
|
private fun openNewConversationScreen() {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putBoolean(BundleKeys.KEY_NEW_CONVERSATION, true)
|
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<BasicListItemWithImage> {
|
private fun getConversationMenuItemsForConversation(conversation: Conversation): MutableList<BasicListItemWithImage> {
|
||||||
val items = mutableListOf<BasicListItemWithImage>()
|
val items = mutableListOf<BasicListItemWithImage>()
|
||||||
|
|
||||||
@ -381,88 +266,4 @@ class ConversationsListView : BaseView(), OnQueryTextListener,
|
|||||||
super.onRestoreViewState(view, savedViewState)
|
super.onRestoreViewState(view, savedViewState)
|
||||||
viewModel.loadConversations()
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -60,9 +60,8 @@ class ConversationsListViewModel constructor(
|
|||||||
private val conversationsLoadingLock = ReentrantLock()
|
private val conversationsLoadingLock = ReentrantLock()
|
||||||
|
|
||||||
var messageData: String? = null
|
var messageData: String? = null
|
||||||
val searchQuery = MutableLiveData<String>()
|
|
||||||
val networkStateLiveData: MutableLiveData<ConversationsListViewNetworkState> = MutableLiveData(ConversationsListViewNetworkState.LOADING)
|
val networkStateLiveData: MutableLiveData<ConversationsListViewNetworkState> = MutableLiveData(ConversationsListViewNetworkState.LOADING)
|
||||||
val currentUserAvatar: MutableLiveData<Drawable> = MutableLiveData(DisplayUtils.getRoundedDrawable(context.getDrawable(R.drawable.ic_settings_white_24dp)))
|
val avatar: MutableLiveData<Drawable> = MutableLiveData(DisplayUtils.getRoundedDrawable(context.getDrawable(R.drawable.ic_settings_white_24dp)))
|
||||||
val conversationsLiveData = Transformations.switchMap(globalService.currentUserLiveData) {
|
val conversationsLiveData = Transformations.switchMap(globalService.currentUserLiveData) {
|
||||||
if (networkStateLiveData.value != ConversationsListViewNetworkState.LOADING) {
|
if (networkStateLiveData.value != ConversationsListViewNetworkState.LOADING) {
|
||||||
networkStateLiveData.postValue(ConversationsListViewNetworkState.LOADING)
|
networkStateLiveData.postValue(ConversationsListViewNetworkState.LOADING)
|
||||||
@ -163,12 +162,12 @@ class ConversationsListViewModel constructor(
|
|||||||
|
|
||||||
operationUser?.let {
|
operationUser?.let {
|
||||||
viewModelScope.launch {
|
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)) {
|
val drawable = Coil.get((url)) {
|
||||||
addHeader("Authorization", it.getCredentials())
|
addHeader("Authorization", it.getCredentials())
|
||||||
transformations(CircleCropTransformation())
|
transformations(CircleCropTransformation())
|
||||||
}
|
}
|
||||||
currentUserAvatar.postValue(drawable)
|
avatar.postValue(drawable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,6 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:animateLayoutChanges="true">
|
android:animateLayoutChanges="true">
|
||||||
|
|
||||||
<include
|
|
||||||
layout="@layout/view_states"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/swipeRefreshLayoutView"
|
android:id="@+id/swipeRefreshLayoutView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -59,6 +54,4 @@
|
|||||||
app:srcCompat="@drawable/ic_add_white_24px"
|
app:srcCompat="@drawable/ic_add_white_24px"
|
||||||
app:tint="@color/white" />
|
app:tint="@color/white" />
|
||||||
|
|
||||||
<include layout="@layout/fast_scroller" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
37
app/src/main/res/layout/loading_state.xml
Normal file
37
app/src/main/res/layout/loading_state.xml
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ /*
|
||||||
|
~ * Nextcloud Talk application
|
||||||
|
~ *
|
||||||
|
~ * @author Mario Danic
|
||||||
|
~ * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.com>
|
||||||
|
~ *
|
||||||
|
~ * 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 <http://www.gnu.org/licenses/>.
|
||||||
|
~ */
|
||||||
|
-->
|
||||||
|
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/loadingStateView"
|
||||||
|
android:layout_width="@dimen/item_height"
|
||||||
|
android:layout_height="@dimen/item_height"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_margin="@dimen/activity_horizontal_margin"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:indeterminateTint="@color/colorPrimary"
|
||||||
|
android:indeterminateTintMode="src_in" />
|
||||||
|
</RelativeLayout>
|
48
app/src/main/res/layout/message_state.xml
Normal file
48
app/src/main/res/layout/message_state.xml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ /*
|
||||||
|
~ * Nextcloud Talk application
|
||||||
|
~ *
|
||||||
|
~ * @author Mario Danic
|
||||||
|
~ * Copyright (C) 2017-2020 Mario Danic <mario@lovelyhq.com>
|
||||||
|
~ *
|
||||||
|
~ * 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 <http://www.gnu.org/licenses/>.
|
||||||
|
~ */
|
||||||
|
-->
|
||||||
|
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent" android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/messageStateImageView"
|
||||||
|
android:layout_width="@dimen/item_height"
|
||||||
|
android:layout_height="@dimen/item_height"
|
||||||
|
android:layout_above="@id/messageStateTextView"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:src="@drawable/ic_logo"
|
||||||
|
android:tint="@color/colorPrimary"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/messageStateTextView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:text="@string/nc_conversations_empty"
|
||||||
|
android:textSize="20sp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -1,67 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Nextcloud Talk application
|
|
||||||
~
|
|
||||||
~ @author Mario Danic
|
|
||||||
~ Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
|
|
||||||
~
|
|
||||||
~ 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 <http://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
>
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/loadingStateView"
|
|
||||||
android:layout_width="@dimen/item_height"
|
|
||||||
android:layout_height="@dimen/item_height"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_margin="@dimen/activity_horizontal_margin"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:indeterminateTint="@color/colorPrimary"
|
|
||||||
android:indeterminateTintMode="src_in"
|
|
||||||
android:visibility="gone"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@+id/stateWithMessageView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:visibility="gone"
|
|
||||||
>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/errorStateImageView"
|
|
||||||
android:layout_width="@dimen/item_height"
|
|
||||||
android:layout_height="@dimen/item_height"
|
|
||||||
android:layout_above="@id/errorStateTextView"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:src="@drawable/ic_announcement_white_24dp"
|
|
||||||
android:tint="@color/colorPrimary"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/errorStateTextView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textSize="20sp"
|
|
||||||
/>
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
@ -316,6 +316,8 @@
|
|||||||
<string name="nc_not_defined_error">Unknown error</string>
|
<string name="nc_not_defined_error">Unknown error</string>
|
||||||
<string name="nc_unauthorized_error">Unauthorized</string>
|
<string name="nc_unauthorized_error">Unauthorized</string>
|
||||||
|
|
||||||
|
<string name="nc_oops">Ooops, something went wrong.</string>
|
||||||
|
|
||||||
<string name="nc_general_settings">General</string>
|
<string name="nc_general_settings">General</string>
|
||||||
<string name="nc_allow_guests">Allow guests</string>
|
<string name="nc_allow_guests">Allow guests</string>
|
||||||
<string name="nc_last_moderator_title">Could not leave conversation</string>
|
<string name="nc_last_moderator_title">Could not leave conversation</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user