diff --git a/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json b/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json index acfe41a3f..8aa661eec 100644 --- a/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json +++ b/app/schemas/com.nextcloud.talk.newarch.local.db.TalkDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "26585a95894baebb7d811a80f3811b85", + "identityHash": "0bb77d35d80f74e97eea4a5415d9a102", "entities": [ { "tableName": "conversations", @@ -15,7 +15,7 @@ "notNull": true }, { - "fieldPath": "user", + "fieldPath": "userId", "columnName": "user_id", "affinity": "INTEGER", "notNull": false @@ -198,7 +198,7 @@ }, { "tableName": "messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `conversation_id` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `actor_id` TEXT, `actor_type` TEXT, `actor_display_name` TEXT, `timestamp` INTEGER NOT NULL, `message` TEXT, `system_message_type` TEXT, PRIMARY KEY(`id`), FOREIGN KEY(`conversation_id`) REFERENCES `conversations`(`id`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `conversation_id` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `actor_id` TEXT, `actor_type` TEXT, `actor_display_name` TEXT, `timestamp` INTEGER NOT NULL, `message` TEXT, `replyable` INTEGER NOT NULL, `system_message_type` TEXT, PRIMARY KEY(`id`), FOREIGN KEY(`conversation_id`) REFERENCES `conversations`(`id`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)", "fields": [ { "fieldPath": "id", @@ -207,7 +207,7 @@ "notNull": true }, { - "fieldPath": "conversation", + "fieldPath": "conversationId", "columnName": "conversation_id", "affinity": "TEXT", "notNull": true @@ -248,6 +248,12 @@ "affinity": "TEXT", "notNull": false }, + { + "fieldPath": "replyable", + "columnName": "replyable", + "affinity": "INTEGER", + "notNull": true + }, { "fieldPath": "systemMessageType", "columnName": "system_message_type", @@ -369,7 +375,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '26585a95894baebb7d811a80f3811b85')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0bb77d35d80f74e97eea4a5415d9a102')" ] } } \ 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 92a44d7ba..5175e0a16 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 @@ -34,6 +34,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType.ONE_TO_ONE_CONVERSATION import com.nextcloud.talk.newarch.local.models.UserNgEntity +import com.nextcloud.talk.newarch.local.models.getCredentials import com.nextcloud.talk.utils.ApiUtils import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -67,14 +68,14 @@ class ConversationItem( && model.unreadMention == comparedConversation.unreadMention && model.objectType == comparedConversation.objectType && model.changing == comparedConversation.changing - && user.id == inItem.user.id) + && inItem.user.id == other.user.id) } return false } override fun hashCode(): Int { return Objects.hash( - model.conversationId, model.token, + model.token, user.id ) } @@ -245,6 +246,7 @@ class ConversationItem( model.name, R.dimen.avatar_size ) ) { + addHeader("Authorization", user.getCredentials()) transformations(CircleCropTransformation()) } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.kt index 4c43d36c5..7687ff923 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MentionAutocompleteItem.kt @@ -27,6 +27,7 @@ import coil.transform.CircleCropTransformation import com.nextcloud.talk.R import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.newarch.local.models.UserNgEntity +import com.nextcloud.talk.newarch.local.models.getCredentials import com.nextcloud.talk.utils.ApiUtils import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -112,6 +113,7 @@ class MentionAutocompleteItem( } holder.avatarImageView!!.load(avatarUrl) { + addHeader("Authorization", currentUser.getCredentials()) transformations(CircleCropTransformation()) } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt index f178a4140..afe4747e1 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt @@ -59,6 +59,7 @@ import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.RoomsOverall import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.ParticipantsOverall +import com.nextcloud.talk.newarch.utils.getCredentials import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DoNotDisturbUtils import com.nextcloud.talk.utils.bundle.BundleKeys @@ -393,6 +394,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr currentConversation!!.name, R.dimen.avatar_size_very_big ) ) { + addHeader("Authorization", userBeingCalled.getCredentials()) transformations(CircleCropTransformation()) listener(onSuccess = { data, dataSource -> GlobalScope.launch { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index ab2ae2c2b..b72ef9f62 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -105,7 +105,6 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter .OnMessageLongClickListener<IMessage>, MessageHolders.ContentChecker<IMessage> { val ncApi: NcApi by inject() - val userUtils: UserUtils by inject() @BindView(R.id.messagesListView) @JvmField @@ -306,7 +305,7 @@ class ChatController(args: Bundle) : BaseController(), MessagesListAdapter imageLoader, context, ApiUtils.getUrlForAvatarWithNameAndPixels( conversationUser?.baseUrl, currentConversation?.name, avatarSize / 2 - ), null, target, null, + ), conversationUser, target, null, CircleCropTransformation() ) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt index 0f1284c88..8a79ed2dd 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt @@ -256,10 +256,10 @@ class SettingsController : BaseController() { } private fun removeCurrentAccount() { - val user = usersRepository.getActiveUser() - user!!.status = UserStatus.PENDING_DELETE GlobalScope.launch { val job = async { + val user = usersRepository.getActiveUser() + user!!.status = UserStatus.PENDING_DELETE usersRepository.updateUser(user) val accountRemovalWork = OneTimeWorkRequest.Builder(AccountRemovalWorker::class.java) .build() @@ -274,13 +274,14 @@ class SettingsController : BaseController() { onAttach(view!!) } } else { - router.setRoot(RouterTransaction.with( - ServerSelectionController() - ) - .pushChangeHandler(VerticalChangeHandler()) - .popChangeHandler(VerticalChangeHandler()) - ) - + withContext(Dispatchers.Main) { + router.setRoot(RouterTransaction.with( + ServerSelectionController() + ) + .pushChangeHandler(VerticalChangeHandler()) + .popChangeHandler(VerticalChangeHandler()) + ) + } } } } @@ -293,7 +294,7 @@ class SettingsController : BaseController() { } GlobalScope.launch { - var hasMultipleUsers: Boolean = false + var hasMultipleUsers = false val job = async { currentUser = usersRepository.getActiveUser() hasMultipleUsers = usersRepository.getUsers().size > 0 diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt index 7bdda773e..1b59def44 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/SwitchAccountController.kt @@ -37,12 +37,21 @@ import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.nextcloud.talk.R import com.nextcloud.talk.adapters.items.AdvancedUserItem import com.nextcloud.talk.controllers.base.BaseController +import com.nextcloud.talk.models.ImportAccount +import com.nextcloud.talk.models.json.participants.Participant +import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository +import com.nextcloud.talk.newarch.local.models.UserNgEntity +import com.nextcloud.talk.newarch.local.models.other.UserStatus import com.nextcloud.talk.utils.AccountUtils import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.database.user.UserUtils import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import java.net.CookieManager import java.util.* @@ -53,7 +62,7 @@ class SwitchAccountController : BaseController { internal var recyclerView: RecyclerView? = null val cookieManager: CookieManager by inject() - val userUtils: UserUtils by inject() + val usersRepository: UsersRepository by inject() @JvmField @BindView(R.id.swipe_refresh_layout) @@ -75,30 +84,13 @@ class SwitchAccountController : BaseController { private val onSwitchItemClickListener = FlexibleAdapter.OnItemClickListener { view, position -> if (userItems.size > position) { val userEntity = (userItems[position] as AdvancedUserItem).entity - /*userUtils!!.createOrUpdateUser(null, null, null, null, null, true, null, userEntity!!.getId(), null, null, null) - .`as`(AutoDispose.autoDisposable<Any>(scopeProvider)) - .subscribe(object : Observer<UserEntity> { - override fun onSubscribe(d: Disposable) { - - } - - override fun onNext(userEntity: UserEntity) { - cookieManager!!.cookieStore.removeAll() - - userUtils!!.disableAllUsersWithoutId(userEntity.getId()) - if (activity != null) { - activity!!.runOnUiThread { router.popCurrentController() } - } - } - - override fun onError(e: Throwable) { - - } - - override fun onComplete() { - - } - })*/ + GlobalScope.launch { + usersRepository.setUserAsActiveWithId(userEntity!!.id!!) + cookieManager.cookieStore.removeAll() + withContext(Dispatchers.Main) { + router.popCurrentController() + } + } } true @@ -130,7 +122,69 @@ class SwitchAccountController : BaseController { return inflater.inflate(R.layout.controller_generic_rv, container, false) } - override fun onViewBound(view: View) { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup + ): View { + swipeRefreshLayout?.isEnabled = false + actionBar?.show() + + adapter = FlexibleAdapter(userItems, activity, false) + GlobalScope.launch { + val users = usersRepository.getUsers() + var userEntity: UserNgEntity + var participant: Participant + + if (isAccountImport) { + var account: Account + var importAccount: ImportAccount + for (accountObject in AccountUtils.findAccounts(users)) { + account = accountObject + importAccount = AccountUtils.getInformationFromAccount(account) + + participant = Participant() + participant.name = importAccount.username + participant.userId = importAccount.username + userEntity = UserNgEntity(-1, "!", "!", importAccount.baseUrl) + userItems.add(AdvancedUserItem(participant, userEntity, account)) + } + + adapter!!.addListener(onImportItemClickListener) + withContext(Dispatchers.Main) { + adapter!!.updateDataSet(userItems, false) + } + + + } else { + for (userEntityObject in users) { + userEntity = userEntityObject + if (userEntity.status != UserStatus.ACTIVE) { + participant = Participant() + participant.name = userEntity.displayName + + val userId: String + + if (userEntity.userId != null) { + userId = userEntity.userId + } else { + userId = userEntity.username + } + participant.userId = userId + userItems.add(AdvancedUserItem(participant, userEntity, null)) + } + } + + adapter!!.addListener(onSwitchItemClickListener) + withContext(Dispatchers.Main) { + adapter!!.updateDataSet(userItems, false) + } + + } + + } + return super.onCreateView(inflater, container) + } + override fun onViewBound(view: View) { super.onViewBound(view) swipeRefreshLayout!!.isEnabled = false diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 8b194963b..3e1c927e1 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -486,7 +486,7 @@ class NotificationWorker( } val request = Images().getRequestForUrl( - Coil.loader(), context!!, avatarUrl!!, null, + Coil.loader(), context!!, avatarUrl!!, signatureVerification!!.userEntity, target, null, CircleCropTransformation() ) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java index 7e691d61a..1db353524 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java @@ -44,6 +44,7 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import lombok.Data; diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt index ded1fca50..c89382bb5 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt @@ -29,6 +29,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.converters.* import com.nextcloud.talk.models.json.participants.Participant +import com.nextcloud.talk.newarch.local.models.ConversationEntity import com.nextcloud.talk.newarch.local.models.UserNgEntity import lombok.Data import org.parceler.Parcel @@ -42,8 +43,6 @@ class Conversation { @JsonIgnore @NonNull var internalUserId: Long? = null - @JsonIgnore - var internalId: Long? = null @JsonField(name = ["id"]) var conversationId: String? = null @JsonField(name = ["token"]) diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt index 4626fd5cf..3b0719125 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt @@ -20,9 +20,7 @@ package com.nextcloud.talk.newarch.data.repository.offline -import androidx.lifecycle.LiveData -import androidx.lifecycle.Transformations -import androidx.lifecycle.map +import androidx.lifecycle.* import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository import com.nextcloud.talk.newarch.local.dao.ConversationsDao @@ -58,14 +56,11 @@ class ConversationsRepositoryImpl(val conversationsDao: ConversationsDao) : } override fun getConversationsForUser(userId: Long): LiveData<List<Conversation>> { - return Transformations.distinctUntilChanged(conversationsDao - .getConversationsForUser(userId) - .map { data -> - data.map { - it.toConversation() - } - } - ) + return conversationsDao.getConversationsForUser(userId).distinctUntilChanged().map { data -> + data.map { + it.toConversation() + } + } } override suspend fun getConversationForUserWithToken(userId: Long, token: String): Conversation? { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt index 0a85a0ae2..b2285eea6 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/UsersRepositoryImpl.kt @@ -21,13 +21,15 @@ package com.nextcloud.talk.newarch.data.repository.offline import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations +import androidx.lifecycle.distinctUntilChanged import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository import com.nextcloud.talk.newarch.local.dao.UsersDao import com.nextcloud.talk.newarch.local.models.UserNgEntity -class UsersRepositoryImpl(val usersDao: UsersDao) : UsersRepository { +class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository { override fun getActiveUserLiveData(): LiveData<UserNgEntity> { - return usersDao.getActiveUserLiveData() + return usersDao.getActiveUserLiveData().distinctUntilChanged() } override fun getActiveUser(): UserNgEntity { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt index c7e9d8944..c3f1c6350 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt @@ -55,18 +55,26 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou return apiService.getConversation(userEntity.getCredentials(), conversationToken) } + override suspend fun joinConversationForUser(userNgEntity: UserNgEntity, conversationToken: String, conversationPassword: String?): RoomOverall { + return apiService.joinConversation(userNgEntity.getCredentials(), ApiUtils.getUrlForSettingMyselfAsActiveParticipant(userNgEntity.baseUrl, conversationToken), conversationPassword) + } + + override suspend fun exitConversationForUser(userNgEntity: UserNgEntity, conversationToken: String): GenericOverall { + return apiService.exitConversation(userNgEntity.getCredentials(), ApiUtils.getUrlForSettingMyselfAsActiveParticipant(userNgEntity.baseUrl, conversationToken)) + } + override suspend fun setFavoriteValueForConversation( user: UserNgEntity, conversation: Conversation, favorite: Boolean ): GenericOverall { - if (favorite) { - return apiService.addConversationToFavorites( + return if (favorite) { + apiService.addConversationToFavorites( user.getCredentials(), ApiUtils.getUrlForConversationFavorites(user.baseUrl, conversation.token) ) } else { - return apiService.removeConversationFromFavorites( + apiService.removeConversationFromFavorites( user.getCredentials(), ApiUtils.getUrlForConversationFavorites(user.baseUrl, conversation.token) ) diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt index ade26c6af..58ddd8d1e 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/source/remote/ApiService.kt @@ -23,6 +23,7 @@ package com.nextcloud.talk.newarch.data.source.remote import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.conversations.RoomsOverall import com.nextcloud.talk.models.json.generic.GenericOverall +import io.reactivex.Observable import retrofit2.http.* interface ApiService { @@ -63,4 +64,14 @@ interface ApiService { @GET suspend fun getConversation(@Header("Authorization") authorization: String, @Url url: String): RoomOverall + + @FormUrlEncoded + @POST + suspend fun joinConversation(@Header("Authorization") authorization: String, + @Url url: String, @Field("password") password: String?): RoomOverall + + @DELETE + suspend fun exitConversation(@Header("Authorization") authorization: String, + @Url url: String): GenericOverall + } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt index 6b62830d4..d33a1342a 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/online/NextcloudTalkRepository.kt @@ -47,4 +47,15 @@ interface NextcloudTalkRepository { userEntity: UserNgEntity, conversationToken: String ): RoomOverall + + suspend fun joinConversationForUser( + userNgEntity: UserNgEntity, + conversationToken: String, + conversationPassword: String? + ): RoomOverall + + suspend fun exitConversationForUser( + userNgEntity: UserNgEntity, + conversationToken: String + ): GenericOverall } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/ExitConversationUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/ExitConversationUseCase.kt new file mode 100644 index 000000000..2cb0bcbd1 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/ExitConversationUseCase.kt @@ -0,0 +1,40 @@ +/* + * 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/>. + */ + +package com.nextcloud.talk.newarch.domain.usecases + +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler +import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository +import com.nextcloud.talk.newarch.domain.usecases.base.UseCase +import org.koin.core.parameter.DefinitionParameters + +class ExitConversationUseCase constructor( + private val nextcloudTalkRepository: NextcloudTalkRepository, + apiErrorHandler: ApiErrorHandler? +) : UseCase<GenericOverall, Any?>(apiErrorHandler) { + + override suspend fun run(params: Any?): GenericOverall { + val definitionParameters = params as DefinitionParameters + return nextcloudTalkRepository.exitConversationForUser( + definitionParameters[0], + definitionParameters[1]) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/JoinConversationUseCase.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/JoinConversationUseCase.kt new file mode 100644 index 000000000..727c137ea --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/usecases/JoinConversationUseCase.kt @@ -0,0 +1,42 @@ +/* + * 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/>. + */ + +package com.nextcloud.talk.newarch.domain.usecases + +import com.nextcloud.talk.models.json.conversations.RoomOverall +import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler +import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository +import com.nextcloud.talk.newarch.domain.usecases.base.UseCase +import org.koin.core.parameter.DefinitionParameters + +class JoinConversationUseCase constructor( + private val nextcloudTalkRepository: NextcloudTalkRepository, + apiErrorHandler: ApiErrorHandler? +) : UseCase<RoomOverall, Any?>(apiErrorHandler) { + + override suspend fun run(params: Any?): RoomOverall { + val definitionParameters = params as DefinitionParameters + return nextcloudTalkRepository.joinConversationForUser( + definitionParameters[0], + definitionParameters[1], + definitionParameters[2] + ) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatView.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatView.kt index d238b7952..c95057bd1 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatView.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatView.kt @@ -355,7 +355,8 @@ class ChatView : BaseView(), MessageHolders.ContentChecker<IMessage>, MessagesLi } } - if (url.startsWith(viewModel.user.baseUrl) && url.contains("index.php/core/preview?fileId=")) { + val needsAuthBasedOnUrl = url.contains("index.php/core/preview?fileId=") || url.contains("index.php/avatar/") + if (url.startsWith(viewModel.user.baseUrl) && needsAuthBasedOnUrl) { addHeader("Authorization", viewModel.user.getCredentials()) } @@ -384,7 +385,7 @@ class ChatView : BaseView(), MessageHolders.ContentChecker<IMessage>, MessagesLi imageLoader, context, ApiUtils.getUrlForAvatarWithNameAndPixels( viewModel.user.baseUrl, it.name, avatarSize / 2 - ), null, target, this, + ), viewModel.user, target, this, CircleCropTransformation() ) diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModel.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModel.kt index fca682c01..682f209cf 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModel.kt @@ -2,16 +2,37 @@ package com.nextcloud.talk.newarch.features.chat import android.app.Application import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Transformations import androidx.lifecycle.viewModelScope +import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.conversations.RoomOverall +import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.newarch.conversationsList.mvp.BaseViewModel +import com.nextcloud.talk.newarch.data.model.ErrorModel import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository +import com.nextcloud.talk.newarch.domain.repository.offline.MessagesRepository +import com.nextcloud.talk.newarch.domain.usecases.ExitConversationUseCase +import com.nextcloud.talk.newarch.domain.usecases.JoinConversationUseCase +import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse +import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListViewState +import com.nextcloud.talk.newarch.local.models.MessageEntity import com.nextcloud.talk.newarch.local.models.UserNgEntity import kotlinx.coroutines.launch +import org.koin.core.parameter.parametersOf -class ChatViewModel constructor(application: Application, private val conversationsRepository: ConversationsRepository) : BaseViewModel<ChatView>(application) { +class ChatViewModel constructor(application: Application, + private val joinConversationUseCase: JoinConversationUseCase, + private val exitConversationUseCase: ExitConversationUseCase, + private val conversationsRepository: ConversationsRepository, + private val messagesRepository: MessagesRepository) : BaseViewModel<ChatView>(application) { lateinit var user: UserNgEntity val conversation: MutableLiveData<Conversation?> = MutableLiveData() + val messagesLiveData = Transformations.switchMap(conversation) { + it?.let { + messagesRepository.getMessagesWithUserForConversation(it.conversationId!!) + } + } var conversationPassword: String? = null @@ -23,6 +44,38 @@ class ChatViewModel constructor(application: Application, private val conversati } } + suspend fun joinConversation() { + joinConversationUseCase.invoke(viewModelScope, parametersOf( + user, + conversation.value!!.token, + conversationPassword + ), + object : UseCaseResponse<RoomOverall> { + override suspend fun onSuccess(result: RoomOverall) { + conversationsRepository.saveConversationsForUser(user.id!!, listOf(result.ocs.data)) + } + + override fun onError(errorModel: ErrorModel?) { + // what do we do on error + } + }) + } + + suspend fun exitConversation() { + exitConversationUseCase.invoke(backgroundScope, parametersOf( + user, + conversation.value!!.token + ), + object : UseCaseResponse<GenericOverall> { + override suspend fun onSuccess(result: GenericOverall) { + } + + override fun onError(errorModel: ErrorModel?) { + // what do we do on error + } + }) + } + fun sendMessage(message: CharSequence) { } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModelFactory.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModelFactory.kt index 6de298fbd..333ea41b2 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModelFactory.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/chat/ChatViewModelFactory.kt @@ -4,15 +4,21 @@ import android.app.Application import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository +import com.nextcloud.talk.newarch.domain.repository.offline.MessagesRepository +import com.nextcloud.talk.newarch.domain.usecases.ExitConversationUseCase +import com.nextcloud.talk.newarch.domain.usecases.JoinConversationUseCase class ChatViewModelFactory constructor( private val application: Application, - private val conversationsRepository: ConversationsRepository + private val joinConversationUseCase: JoinConversationUseCase, + private val exitConversationUseCase: ExitConversationUseCase, + private val conversationsRepository: ConversationsRepository, + private val messagesRepository: MessagesRepository ) : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { return ChatViewModel( - application, conversationsRepository + application, joinConversationUseCase, exitConversationUseCase, conversationsRepository, messagesRepository ) as T } } 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 14f267795..95fe9740d 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 @@ -26,12 +26,15 @@ import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle import android.text.InputType +import android.util.Log 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.lifecycle.MutableLiveData import androidx.lifecycle.Observer +import androidx.lifecycle.distinctUntilChanged import androidx.recyclerview.widget.RecyclerView.ViewHolder import butterknife.OnClick import coil.ImageLoader @@ -74,11 +77,12 @@ import java.util.* class ConversationsListView : BaseView(), OnQueryTextListener, OnItemClickListener, OnItemLongClickListener { - lateinit var viewModel: ConversationsListViewModel + private lateinit var viewModel: ConversationsListViewModel val factory: ConversationListViewModelFactory by inject() - val imageLoader: ImageLoader by inject() + private val imageLoader: ImageLoader by inject() + private val viewState: MutableLiveData<ConversationsListViewState> = MutableLiveData(LOADING) - private val recyclerViewAdapter = FlexibleAdapter(mutableListOf(), null, true) + private val recyclerViewAdapter = FlexibleAdapter(mutableListOf(), null, false) private var searchItem: MenuItem? = null private var settingsItem: MenuItem? = null @@ -178,9 +182,12 @@ class ConversationsListView : BaseView(), OnQueryTextListener, searchView!!.imeOptions = imeOptions searchView!!.queryHint = resources?.getString(R.string.nc_search) searchView!!.setSearchableInfo(searchManager.getSearchableInfo(activity!!.componentName)) - searchView!!.setOnQueryTextListener(this) + } + override fun onRestoreViewState(view: View, savedViewState: Bundle) { + super.onRestoreViewState(view, savedViewState) + viewModel.loadConversations() } override fun onQueryTextSubmit(query: String?): Boolean { @@ -208,71 +215,15 @@ class ConversationsListView : BaseView(), OnQueryTextListener, loadAvatar() }) - viewState.observe(this@ConversationsListView, Observer { value -> - when (value) { - LOADING -> { - view?.swipeRefreshLayoutView?.isEnabled = false - view?.loadingStateView?.visibility = View.VISIBLE - view?.stateWithMessageView?.visibility = View.GONE - view?.dataStateView?.visibility = View.GONE - view?.floatingActionButton?.visibility = View.GONE - searchItem?.isVisible = false - } - LOADED -> { - view?.swipeRefreshLayoutView?.isEnabled = true - view?.swipeRefreshLayoutView?.post { - view?.swipeRefreshLayoutView?.isRefreshing = false - } - view?.loadingStateView?.visibility = View.GONE - view?.stateWithMessageView?.visibility = View.GONE - view?.dataStateView?.visibility = View.VISIBLE - view?.floatingActionButton?.visibility = View.VISIBLE - searchItem?.isVisible = true - } - LOADED_EMPTY, FAILED -> { - view?.swipeRefreshLayoutView?.post { - view?.swipeRefreshLayoutView?.isRefreshing = false - } - view?.swipeRefreshLayoutView?.isEnabled = true - view?.loadingStateView?.visibility = View.GONE - view?.dataStateView?.visibility = View.GONE - searchItem?.isVisible = false - - if (value.equals(FAILED)) { - view?.stateWithMessageView?.errorStateTextView?.text = messageData - if (messageData.equals( - context.resources.getString(R.string.nc_no_connection_error) - ) - ) { - view?.stateWithMessageView?.errorStateImageView?.setImageResource( - drawable.ic_signal_wifi_off_white_24dp - ) - } else { - view?.stateWithMessageView?.errorStateImageView?.setImageResource( - drawable.ic_announcement_white_24dp - ) - } - view?.floatingActionButton?.visibility = View.GONE - } else { - view?.floatingActionButton?.visibility = View.VISIBLE - view?.stateWithMessageView?.errorStateTextView?.text = - resources?.getText(R.string.nc_conversations_empty) - view?.stateWithMessageView?.errorStateImageView?.setImageResource(drawable.ic_logo) - } - - view?.stateWithMessageView?.visibility = View.VISIBLE - } - else -> { - // We should not be here - } - } - }) - conversationsLiveData.observe(this@ConversationsListView, Observer { if (it.isEmpty()) { - viewState.value = LOADED_EMPTY + if (viewState.value != LOADED_EMPTY) { + viewState.value = LOADED_EMPTY + } } else { - viewState.value = LOADED + if (viewState.value != LOADED) { + viewState.value = LOADED + } } val newConversations = mutableListOf<ConversationItem>() @@ -297,6 +248,67 @@ class ConversationsListView : BaseView(), OnQueryTextListener, }) } + viewState.observe(this@ConversationsListView, Observer { value -> + when (value) { + LOADING -> { + view?.swipeRefreshLayoutView?.isEnabled = false + view?.loadingStateView?.visibility = View.VISIBLE + view?.stateWithMessageView?.visibility = View.GONE + view?.dataStateView?.visibility = View.GONE + view?.floatingActionButton?.visibility = View.GONE + searchItem?.isVisible = false + } + LOADED -> { + view?.swipeRefreshLayoutView?.isEnabled = true + view?.swipeRefreshLayoutView?.post { + view?.swipeRefreshLayoutView?.isRefreshing = false + } + view?.loadingStateView?.visibility = View.GONE + view?.stateWithMessageView?.visibility = View.GONE + view?.dataStateView?.visibility = View.VISIBLE + view?.floatingActionButton?.visibility = View.VISIBLE + searchItem?.isVisible = true + } + LOADED_EMPTY, FAILED -> { + view?.swipeRefreshLayoutView?.post { + view?.swipeRefreshLayoutView?.isRefreshing = false + } + view?.swipeRefreshLayoutView?.isEnabled = true + view?.loadingStateView?.visibility = View.GONE + view?.dataStateView?.visibility = View.GONE + searchItem?.isVisible = false + + if (value.equals(FAILED)) { + view?.stateWithMessageView?.errorStateTextView?.text = viewModel.messageData + if (viewModel.messageData.equals( + context.resources.getString(R.string.nc_no_connection_error) + ) + ) { + view?.stateWithMessageView?.errorStateImageView?.setImageResource( + drawable.ic_signal_wifi_off_white_24dp + ) + } else { + view?.stateWithMessageView?.errorStateImageView?.setImageResource( + drawable.ic_announcement_white_24dp + ) + } + view?.floatingActionButton?.visibility = View.GONE + } else { + view?.floatingActionButton?.visibility = View.VISIBLE + view?.stateWithMessageView?.errorStateTextView?.text = + resources?.getText(R.string.nc_conversations_empty) + view?.stateWithMessageView?.errorStateImageView?.setImageResource(drawable.ic_logo) + } + + view?.stateWithMessageView?.visibility = View.VISIBLE + } + else -> { + // We should not be here + } + } + }) + + return super.onCreateView(inflater, container) } @@ -311,7 +323,7 @@ class ConversationsListView : BaseView(), OnQueryTextListener, @OnClick(R.id.stateWithMessageView) fun onStateWithMessageViewClick() { - if (viewModel.viewState.equals(LOADED_EMPTY)) { + if (viewState.value!! == LOADED_EMPTY) { openNewConversationScreen() } } 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 3e4889b2d..f8096e2ef 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 @@ -41,7 +41,6 @@ import com.nextcloud.talk.newarch.domain.usecases.GetConversationsUseCase import com.nextcloud.talk.newarch.domain.usecases.LeaveConversationUseCase import com.nextcloud.talk.newarch.domain.usecases.SetConversationFavoriteValueUseCase import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse -import com.nextcloud.talk.newarch.features.conversationsList.ConversationsListViewState.LOADING import com.nextcloud.talk.newarch.local.models.UserNgEntity import com.nextcloud.talk.utils.ShareUtils import kotlinx.coroutines.launch @@ -57,14 +56,11 @@ class ConversationsListViewModel constructor( usersRepository: UsersRepository ) : BaseViewModel<ConversationsListView>(application) { - val viewState = MutableLiveData(LOADING) var messageData: String? = null val searchQuery = MutableLiveData<String>() val currentUserLiveData: LiveData<UserNgEntity> = usersRepository.getActiveUserLiveData() val conversationsLiveData = Transformations.switchMap(currentUserLiveData) { - if (viewState.value != LOADING) { - viewState.value = LOADING - } + loadConversations() conversationsRepository.getConversationsForUser(it.id!!) } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewState.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewState.kt index aefa288e7..4bd9f7029 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewState.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewState.kt @@ -21,6 +21,7 @@ package com.nextcloud.talk.newarch.features.conversationsList enum class ConversationsListViewState { + INITIAL_LOAD, LOADING, LOADED_EMPTY, LOADED, diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/models/ConversationEntity.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/models/ConversationEntity.kt index 740d93d91..458d0e5fb 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/models/ConversationEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/models/ConversationEntity.kt @@ -20,6 +20,7 @@ package com.nextcloud.talk.newarch.local.models +import android.util.Log import androidx.room.* import androidx.room.ForeignKey.CASCADE import com.nextcloud.talk.models.json.chat.ChatMessage @@ -42,7 +43,7 @@ import java.util.* ) data class ConversationEntity( @PrimaryKey @ColumnInfo(name = "id") var id: String, - @ColumnInfo(name = "user_id") var user: Long? = null, + @ColumnInfo(name = "user_id") var userId: Long? = null, @ColumnInfo(name = "conversation_id") var conversationId: String? = null, @ColumnInfo(name = "token") var token: String? = null, @ColumnInfo(name = "name") var name: String? = null, @@ -74,11 +75,52 @@ data class ConversationEntity( @ColumnInfo(name = "last_read_message") var lastReadMessageId: Long = 0, @ColumnInfo(name = "modified_at") var modifiedAt: Long? = null, @ColumnInfo(name = "changing") var changing: Boolean = false -) +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ConversationEntity + + if (id != other.id) return false + if (userId != other.userId) return false + if (conversationId != other.conversationId) return false + if (token != other.token) return false + if (name != other.name) return false + if (displayName != other.displayName) return false + if (type != other.type) return false + if (count != other.count) return false + if (numberOfGuests != other.numberOfGuests) return false + if (participantsCount != other.participantsCount) return false + if (participantType != other.participantType) return false + if (hasPassword != other.hasPassword) return false + if (sessionId != other.sessionId) return false + if (favorite != other.favorite) return false + if (lastActivity != other.lastActivity) return false + if (unreadMessages != other.unreadMessages) return false + if (unreadMention != other.unreadMention) return false + if (lastMessage?.internalMessageId != other.lastMessage?.internalMessageId) return false + if (objectType != other.objectType) return false + if (notificationLevel != other.notificationLevel) return false + if (conversationReadOnlyState != other.conversationReadOnlyState) return false + if (lobbyState != other.lobbyState) return false + if (lobbyTimer != other.lobbyTimer) return false + if (lastReadMessageId != other.lastReadMessageId) return false + if (changing != other.changing) return false + + return true + } + + override fun hashCode(): Int { + var result = userId?.hashCode() ?: 0 + result = 31 * result + (token?.hashCode() ?: 0) + return result + } +} fun ConversationEntity.toConversation(): Conversation { val conversation = Conversation() - conversation.internalUserId = this.user + conversation.internalUserId = this.userId conversation.conversationId = this.conversationId conversation.type = this.type conversation.token = this.token @@ -111,7 +153,7 @@ fun ConversationEntity.toConversation(): Conversation { fun Conversation.toConversationEntity(): ConversationEntity { val conversationEntity = ConversationEntity(this.internalUserId.toString() + "@" + this.token) - conversationEntity.user = this.internalUserId + conversationEntity.userId = this.internalUserId conversationEntity.conversationId = this.conversationId conversationEntity.token = this.token conversationEntity.name = this.name diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/models/MessageEntity.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/models/MessageEntity.kt index f24a5bdce..2b0f65656 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/models/MessageEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/models/MessageEntity.kt @@ -39,7 +39,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType ) data class MessageEntity( @PrimaryKey @ColumnInfo(name = "id") var id: String, - @ColumnInfo(name = "conversation_id") var conversation: String, + @ColumnInfo(name = "conversation_id") var conversationId: String, @ColumnInfo(name = "message_id") var messageId: Long = 0, @ColumnInfo(name = "actor_id") var actorId: String? = null, @ColumnInfo(name = "actor_type") var actorType: String? = null, @@ -56,7 +56,7 @@ data class MessageEntity( fun MessageEntity.toChatMessage(): ChatMessage { val chatMessage = ChatMessage() chatMessage.internalMessageId = this.id - chatMessage.internalConversationId = this.conversation + chatMessage.internalConversationId = this.conversationId chatMessage.jsonMessageId = this.messageId chatMessage.actorType = this.actorType chatMessage.actorId = this.actorId diff --git a/app/src/main/java/com/nextcloud/talk/newarch/utils/ConversationsManager.kt b/app/src/main/java/com/nextcloud/talk/newarch/utils/ConversationsManager.kt new file mode 100644 index 000000000..e8fcf0a69 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/utils/ConversationsManager.kt @@ -0,0 +1,99 @@ +/* + * 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/>. + */ + +package com.nextcloud.talk.newarch.utils + +import androidx.lifecycle.LiveData +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.conversations.RoomOverall +import com.nextcloud.talk.newarch.data.model.ErrorModel +import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository +import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository +import com.nextcloud.talk.newarch.domain.usecases.GetConversationUseCase +import com.nextcloud.talk.newarch.domain.usecases.JoinConversationUseCase +import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse +import com.nextcloud.talk.newarch.local.models.UserNgEntity +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import okhttp3.OkHttpClient +import org.koin.core.KoinComponent +import org.koin.core.parameter.parametersOf +import java.net.CookieManager + +class ConversationsManager constructor(usersRepository: UsersRepository, + cookieManager: CookieManager, + okHttpClient: OkHttpClient, + private val conversationsRepository: ConversationsRepository, + private val joinConversationUseCase: JoinConversationUseCase, + private val getConversationUseCase: GetConversationUseCase): KoinComponent { + private val applicationScope = CoroutineScope(Dispatchers.Default) + private val previousUser: UserNgEntity? = null + private val currentUserLiveData: LiveData<UserNgEntity> = usersRepository.getActiveUserLiveData() + private var currentConversation: Conversation? = null + + init { + currentUserLiveData.observeForever { user -> + cookieManager.cookieStore.removeAll() + okHttpClient.dispatcher().cancelAll() + currentConversation = null + } + } + + suspend fun getConversation(conversationToken: String) { + val currentUser = currentUserLiveData.value + getConversationUseCase.invoke(applicationScope, parametersOf( + currentUser, + conversationToken + ), + object : UseCaseResponse<RoomOverall> { + override suspend fun onSuccess(result: RoomOverall) { + currentUser?.let { + conversationsRepository.saveConversationsForUser(it.id!!, listOf(result.ocs.data)) + } + } + + override fun onError(errorModel: ErrorModel?) { + // what do we do on error + } + }) + } + + suspend fun joinConversation(conversationToken: String, conversationPassword: String?, conversationsManagerInterface: ConversationsManagerInterface) { + val currentUser = currentUserLiveData.value + joinConversationUseCase.invoke(applicationScope, parametersOf( + currentUser, + conversationToken, + conversationPassword + ), + object : UseCaseResponse<RoomOverall> { + override suspend fun onSuccess(result: RoomOverall) { + currentUser?.let { + conversationsRepository.saveConversationsForUser(it.id!!, listOf(result.ocs.data)) + currentConversation = conversationsRepository.getConversationForUserWithToken(it.id!!, result.ocs!!.data!!.token!!) + conversationsManagerInterface.joinedConversationForUser(it, currentConversation) + } + } + + override fun onError(errorModel: ErrorModel?) { + // what do we do on error + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/utils/ConversationsManagerInterface.kt b/app/src/main/java/com/nextcloud/talk/newarch/utils/ConversationsManagerInterface.kt new file mode 100644 index 000000000..d4db24f4f --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/utils/ConversationsManagerInterface.kt @@ -0,0 +1,28 @@ +/* + * 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/>. + */ + +package com.nextcloud.talk.newarch.utils + +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.newarch.local.models.UserNgEntity + +interface ConversationsManagerInterface { + fun joinedConversationForUser(userNgEntity: UserNgEntity, conversation: Conversation?) +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/newarch/utils/Images.kt b/app/src/main/java/com/nextcloud/talk/newarch/utils/Images.kt index 690dd6df8..519452ec0 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/utils/Images.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/utils/Images.kt @@ -51,9 +51,8 @@ class Images { target(target) } - if (userEntity != null && url.startsWith(userEntity.baseUrl) && url.contains( - "index.php/core/preview?fileId=" - ) + if (userEntity != null && url.startsWith(userEntity.baseUrl) && (url.contains( + "index.php/core/preview?fileId=") || url.contains("index.php/avatar/")) ) { addHeader("Authorization", userEntity.getCredentials()) } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/utils/LiveDataTransformations.kt b/app/src/main/java/com/nextcloud/talk/newarch/utils/LiveDataTransformations.kt new file mode 100644 index 000000000..0f1bcb0b2 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/newarch/utils/LiveDataTransformations.kt @@ -0,0 +1,62 @@ +package com.nextcloud.talk.newarch.utils + +import android.util.Log +import androidx.annotation.MainThread +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.Observer +import java.util.* +import kotlin.collections.ArrayList + +class LiveDataTransformations { + + /** + * Creates a new [LiveData] object that does not emit a value until the source LiveData + * value has been changed. The value is considered changed if `equals()` yields + * `false`. + * + * @param source the input [LiveData] + * @param <X> the generic type parameter of `source` + * @return a new [LiveData] of type `X` + </X> */ + @MainThread + fun <X> distinctUntilChangedForArray(source: LiveData<X>): LiveData<X> { + val outputLiveData = MediatorLiveData<X>() + outputLiveData.addSource(source, object : Observer<X> { + + var mFirstTime = true + + override fun onChanged(currentValue: X?) { + val previousValue = outputLiveData.value + if (mFirstTime && currentValue != null + || (previousValue == null && currentValue != null)) { + mFirstTime = false + outputLiveData.value = currentValue + } else if (previousValue != null) { + if (currentValue == null) { + outputLiveData.value = currentValue + } else { + val previousArray: Array<X> = (previousValue as ArrayList<X>).toArray() as Array<X> + val currentArray: Array<X> = (currentValue as ArrayList<X>).toArray() as Array<X> + + if (previousArray.size != currentArray.size) { + outputLiveData.value = currentValue + } else { + var counter = 0 + for(element in previousArray) { + if (!element!!.equals(currentArray[counter])) { + outputLiveData.value = currentValue + return + } + + counter++ + } + } + } + } + } + }) + + return outputLiveData + } +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.kt index 4f5e2cfd6..8307bb335 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.kt @@ -262,7 +262,7 @@ object DisplayUtils { ) } - val request = Images().getRequestForUrl(Coil.loader(), context, url, null, target, null, CircleCropTransformation()) + val request = Images().getRequestForUrl(Coil.loader(), context, url, conversationUser, target, null, CircleCropTransformation()) Coil.loader().load(request) }