Implement empty presenter & Lots of other nice improvements

This commit is contained in:
Mario Danic 2020-01-04 02:05:05 +01:00
parent 5a473faba9
commit c18623de8e
No known key found for this signature in database
GPG Key ID: CDE0BBD2738C4CC0
12 changed files with 129 additions and 37 deletions

View File

@ -154,9 +154,9 @@ android {
}
ext {
work_version = "2.3.0-alpha02"
work_version = '2.3.0-beta02'
koin_version = "2.1.0-alpha-1"
lifecycle_version = "2.2.0-rc01"
lifecycle_version = '2.2.0-rc03'
coil_version = "0.9.1"
room_version = "2.2.3"
}
@ -257,12 +257,12 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.14.3'
implementation 'com.squareup.okhttp3:logging-interceptor:3.14.3'
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.2'
implementation 'com.squareup.retrofit2:retrofit:2.7.1'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.7.1'
implementation 'com.github.aurae.retrofit2:converter-logansquare:1.4.1'
implementation group: 'joda-time', name: 'joda-time', version: '2.10.3'
implementation 'com.bluelinelabs:logansquare:1.3.7'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.10.0.pr1'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.10.1'
kapt 'com.bluelinelabs:logansquare-compiler:1.3.7'
compileOnly 'javax.annotation:jsr250-api:1.0'
@ -277,8 +277,8 @@ dependencies {
annotationProcessor "org.projectlombok:lombok:1.18.10"
kapt "org.projectlombok:lombok:1.18.10"
implementation 'com.jakewharton:butterknife:10.2.0'
kapt 'com.jakewharton:butterknife-compiler:10.2.0'
implementation 'com.jakewharton:butterknife:10.2.1'
kapt 'com.jakewharton:butterknife-compiler:10.2.1'
implementation 'com.github.HITGIF:TextFieldBoxes:1.4.5'
implementation 'eu.davidea:flexible-adapter:5.1.0'
implementation 'eu.davidea:flexible-adapter-ui:1.0.0'
@ -315,13 +315,13 @@ dependencies {
implementation 'org.parceler:parceler-api:1.1.12'
kapt 'org.parceler:parceler:1.1.12'
testImplementation 'junit:junit:4.12'
testImplementation 'junit:junit:4.13'
testImplementation 'org.mockito:mockito-core:3.0.0'
testImplementation 'org.powermock:powermock-core:2.0.2'
testImplementation 'org.powermock:powermock-module-junit4:2.0.2'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.2'
androidTestImplementation('androidx.test.espresso:espresso-core:3.3.0-alpha02', {
androidTestImplementation('androidx.test.espresso:espresso-core:3.3.0-alpha03', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'

View File

@ -39,9 +39,11 @@ import java.util.*
@Data
@JsonObject(serializeNullCollectionElements = true, serializeNullObjects = true)
class Conversation {
@JsonIgnore
var databaseId: String? = null
@JsonIgnore
@NonNull
var internalUserId: Long? = null
var databaseUserId: Long? = null
@JsonField(name = ["id"])
var conversationId: String? = null
@JsonField(name = ["token"])

View File

@ -0,0 +1,37 @@
/*
*
* * 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.newarch.data.presenters
import android.content.Context
import com.otaliastudios.elements.Element
import com.otaliastudios.elements.Page
import com.otaliastudios.elements.extensions.EmptyPresenter
class AdvancedEmptyPresenter(context: Context, layout: Int, private val onViewClick: (() -> Unit)? = null) : EmptyPresenter(context, layout) {
override fun onBind(page: Page, holder: Holder, element: Element<Void>, payloads: List<Any>) {
super.onBind(page, holder, element, payloads)
holder.itemView.setOnClickListener {
onViewClick?.invoke()
}
}
}

View File

@ -20,7 +20,7 @@
*
*/
package com.nextcloud.talk.adapters
package com.nextcloud.talk.newarch.features.conversationsList
import android.content.Context
import android.graphics.drawable.Drawable

View File

@ -0,0 +1,49 @@
/*
*
* * 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.newarch.features.conversationsList
import androidx.lifecycle.LiveData
import com.nextcloud.talk.models.json.conversations.Conversation
import com.otaliastudios.elements.Element
import com.otaliastudios.elements.Page
import com.otaliastudios.elements.Source
import com.otaliastudios.elements.extensions.MainSource
class ConversationsListSource<T: Conversation>(private val data: LiveData<List<T>>, private val elementType: Int = 0, loadingIndicatorsEnabled: Boolean = true, errorIndicatorEnabled: Boolean = true, emptyIndicatorEnabled: Boolean = true) : MainSource<T>(loadingIndicatorsEnabled, errorIndicatorEnabled, emptyIndicatorEnabled) {
override fun onPageOpened(page: Page, dependencies: List<Element<*>>) {
super.onPageOpened(page, dependencies)
if (page.previous() == null) {
postResult(page, data)
}
}
override fun dependsOn(source: Source<*>): Boolean {
return false
}
override fun areItemsTheSame(first: T, second: T): Boolean {
return first.databaseId == second.databaseId
}
}

View File

@ -22,7 +22,6 @@ package com.nextcloud.talk.newarch.features.conversationsList
import android.os.Bundle
import android.view.*
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.observe
import butterknife.OnClick
@ -36,13 +35,13 @@ 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.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.data.presenters.AdvancedEmptyPresenter
import com.nextcloud.talk.newarch.mvvm.ext.initRecyclerView
import com.nextcloud.talk.utils.ConductorRemapping
import com.nextcloud.talk.utils.DisplayUtils
@ -147,19 +146,19 @@ class ConversationsListView : BaseView() {
val view = super.onCreateView(inflater, container)
val adapter = Adapter.builder(this)
.addSource(Source.fromLiveData(viewModel.conversationsLiveData))
.addSource(ConversationsListSource(viewModel.conversationsLiveData))
.addPresenter(ConversationsPresenter(context, ::onElementClick, ::onElementLongClick))
.addPresenter(Presenter.forLoadingIndicator(context, R.layout.loading_state))
.addPresenter(Presenter.forEmptyIndicator(context, R.layout.message_state))
.addPresenter(AdvancedEmptyPresenter(context, R.layout.message_state, ::openNewConversationScreen))
.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.apply {
recyclerView.initRecyclerView(SmoothScrollLinearLayoutManager(activity), adapter, false)
swipeRefreshLayoutView.setOnRefreshListener {
view.swipeRefreshLayoutView.isRefreshing = false
viewModel.loadConversations()

View File

@ -182,7 +182,7 @@ class ConversationsListViewModel constructor(
val mutableList = result.toMutableList()
val internalUserId = globalService.currentUserLiveData.value!!.id
mutableList.forEach {
it.internalUserId = internalUserId
it.databaseUserId = internalUserId
}
conversationsRepository.saveConversationsForUser(

View File

@ -42,7 +42,7 @@ class ParticipantMapConverter {
return null
}
return LoganSquare.parse(value, HashMap::class.java) as java.util.HashMap<String, Participant>?
return LoganSquare.parseMap(value, Participant::class.java) as HashMap<String, Participant>?
}
}

View File

@ -118,7 +118,8 @@ data class ConversationEntity(
fun ConversationEntity.toConversation(): Conversation {
val conversation = Conversation()
conversation.internalUserId = this.userId
conversation.databaseId = this.id
conversation.databaseUserId = this.userId
conversation.conversationId = this.conversationId
conversation.type = this.type
conversation.token = this.token
@ -148,8 +149,8 @@ fun ConversationEntity.toConversation(): Conversation {
}
fun Conversation.toConversationEntity(): ConversationEntity {
val conversationEntity = ConversationEntity(this.internalUserId.toString() + "@" + this.token)
conversationEntity.userId = this.internalUserId
val conversationEntity = ConversationEntity(this.databaseUserId.toString() + "@" + this.token)
conversationEntity.userId = this.databaseUserId
conversationEntity.conversationId = this.conversationId
conversationEntity.token = this.token
conversationEntity.name = this.name

View File

@ -25,6 +25,7 @@ package com.nextcloud.talk.newarch.services.shortcuts
import android.content.Context
import android.content.Intent
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.core.app.Person
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
@ -38,6 +39,7 @@ import coil.transform.CircleCropTransformation
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.MainActivity
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository
import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.newarch.local.models.getCredentials
@ -47,6 +49,7 @@ import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.bundle.BundleKeys
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.lang.Exception
import kotlin.math.abs
@ -91,6 +94,7 @@ class ShortcutService constructor(private var context: Context,
.setAlwaysBadged()
.build())
var iconImage: Drawable? = null
for ((index, conversation) in conversations.withIndex()) {
val intent = Intent(context, MainActivity::class.java)
intent.action = BundleKeys.KEY_OPEN_CONVERSATION
@ -99,28 +103,31 @@ class ShortcutService constructor(private var context: Context,
val persons = mutableListOf<Person>()
conversation.participants?.forEach {
val hashMap = it.value as HashMap<*, *>
val key = it.key
val participant = it.value
val personBuilder = Person.Builder()
personBuilder.setName(hashMap["name"].toString())
personBuilder.setName(participant.name.toString())
personBuilder.setBot(false)
// we need a key for each of the users
/*val isGuest = hashMap["type"]?.equals(Participant.ParticipantType.GUEST) == true
|| hashMap["type"]?.equals(Participant.ParticipantType.GUEST_AS_MODERATOR) == true
|| hashMap["type"]?.equals(Participant.ParticipantType.USER_FOLLOWING_LINK) == true
val isGuest = participant.type == Participant.ParticipantType.GUEST || participant.type == Participant.ParticipantType.GUEST_AS_MODERATOR || participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK
val avatarUrl = if (isGuest) ApiUtils.getUrlForAvatarWithNameForGuests(user.baseUrl, hashMap["name"].toString(), R.dimen.avatar_size_big)
else ApiUtils.getUrlForAvatarWithName(user.baseUrl, hashMap["userId"].toString(), R.dimen.avatar_size_big)
val avatarUrl = if (isGuest) ApiUtils.getUrlForAvatarWithNameForGuests(user.baseUrl, participant.name, R.dimen.avatar_size_big)
else ApiUtils.getUrlForAvatarWithName(user.baseUrl, it.key, R.dimen.avatar_size_big)
iconImage = Coil.get(avatarUrl) {
addHeader("Authorization", user.getCredentials())
transformations(CircleCropTransformation())
try {
iconImage = Coil.get(avatarUrl) {
addHeader("Authorization", user.getCredentials())
transformations(CircleCropTransformation())
}
personBuilder.setIcon(IconCompat.createWithBitmap((iconImage as BitmapDrawable).bitmap))
} catch (e: Exception) {
// No icon, that's fine for now
}
personBuilder.setIcon(IconCompat.createWithBitmap((iconImage as BitmapDrawable).bitmap))*/
persons.add(personBuilder.build())
}
var iconImage = images.getImageForConversation(context, conversation)
iconImage = images.getImageForConversation(context, conversation)
if (iconImage == null) {
iconImage = Coil.get(ApiUtils.getUrlForAvatarWithName(user.baseUrl, conversation.name, R.dimen.avatar_size_big)) {

View File

@ -49,8 +49,6 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:layout_anchor="@id/recyclerView"
app:layout_anchorGravity="bottom|end"
app:backgroundTint="@color/colorPrimary"
app:srcCompat="@drawable/ic_add_white_24px"
app:tint="@color/white" />

View File

@ -33,8 +33,7 @@
<item
android:id="@+id/action_settings"
android:icon="@drawable/ic_settings_white_24dp"
android:title="@string/nc_settings"
android:visible="@bool/value_true"
app:showAsAction="ifRoom" />