From a1b7e1260cdf4956b4a4a155f7138967f26fdb35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Tue, 5 Apr 2022 17:05:27 +0200 Subject: [PATCH 01/55] Add overview for shared items of a conversation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Via the conversation info or the menu entry in the conversation menu a overview of shared items can be opened. Signed-off-by: Tim Krüger --- app/build.gradle | 4 + app/src/main/AndroidManifest.xml | 5 + .../talk/activities/SharedItemsActivity.kt | 44 +++++++++ .../talk/adapters/SharedItemsAdapter.kt | 51 ++++++++++ .../java/com/nextcloud/talk/api/NcApi.java | 7 ++ .../talk/controllers/ChatController.kt | 23 ++++- .../controllers/ConversationInfoController.kt | 14 ++- .../talk/models/json/chat/ChatShareOCS.java | 77 +++++++++++++++ .../models/json/chat/ChatShareOverall.java | 76 +++++++++++++++ .../nextcloud/talk/repositories/SharedItem.kt | 10 ++ .../repositories/SharedItemsRepository.kt | 53 +++++++++++ .../talk/repositories/SharedMediaItems.kt | 7 ++ .../com/nextcloud/talk/utils/ApiUtils.java | 4 + .../talk/viewmodels/SharedItemsViewModel.kt | 95 +++++++++++++++++++ .../main/res/layout/activity_shared_items.xml | 22 +++++ app/src/main/res/layout/attachment_item.xml | 14 +++ .../layout/controller_conversation_info.xml | 25 ++++- app/src/main/res/menu/menu_conversation.xml | 7 +- app/src/main/res/values/strings.xml | 1 + 19 files changed, 529 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOCS.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverall.java create mode 100644 app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt create mode 100644 app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt create mode 100644 app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt create mode 100644 app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt create mode 100644 app/src/main/res/layout/activity_shared_items.xml create mode 100644 app/src/main/res/layout/attachment_item.xml diff --git a/app/build.gradle b/app/build.gradle index 4fa05db26..b8bbb4df1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -332,6 +332,10 @@ dependencies { gplayImplementation 'com.google.android.gms:play-services-base:18.0.1' gplayImplementation "com.google.firebase:firebase-messaging:23.0.0" + + // TODO: Define variable for version + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + // implementation 'androidx.activity:activity-ktx:1.4.0' } task installGitHooks(type: Copy, group: "development") { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index abbf6b73e..aaaeb9430 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -96,6 +96,11 @@ android:name="android.max_aspect" android:value="10" /> + + (BundleKeys.KEY_USER_ENTITY)!! + + binding = ActivitySharedItemsBinding.inflate(layoutInflater) + setContentView(binding.root) + + viewModel = ViewModelProvider( + this, + SharedItemsViewModel.Factory(userEntity, roomToken) + ).get(SharedItemsViewModel::class.java) + + viewModel.media.observe(this) { + Log.d(TAG, "Items received: $it") + val adapter = SharedItemsAdapter() + adapter.items = it.items + adapter.authHeader = it.authHeader + binding.imageRecycler.adapter = adapter + } + } + + private lateinit var binding: ActivitySharedItemsBinding + + private lateinit var viewModel: SharedItemsViewModel +} diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt new file mode 100644 index 000000000..aaaaf274f --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -0,0 +1,51 @@ +package com.nextcloud.talk.adapters + +import android.net.Uri +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.interfaces.DraweeController +import com.facebook.imagepipeline.common.RotationOptions +import com.facebook.imagepipeline.request.ImageRequestBuilder +import com.nextcloud.talk.databinding.AttachmentItemBinding +import com.nextcloud.talk.repositories.SharedItem + +class SharedItemsAdapter : RecyclerView.Adapter() { + + class ViewHolder(val binding: AttachmentItemBinding, itemView: View) : RecyclerView.ViewHolder(itemView) + + var authHeader: Map = emptyMap() + var items: List = emptyList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val binding = AttachmentItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return ViewHolder(binding, binding.root) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + + val currentItem = items[position] + + if (currentItem.previewAvailable) { + val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(currentItem.previewLink)) + .setProgressiveRenderingEnabled(true) + .setRotationOptions(RotationOptions.autoRotate()) + .disableDiskCache() + .setHeaders(authHeader) + .build() + + val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() + .setOldController(holder.binding.image.controller) + .setAutoPlayAnimations(true) + .setImageRequest(imageRequest) + .build() + holder.binding.image.controller = draweeController + } + } + + override fun getItemCount(): Int { + return items.size + } +} diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index 8f177a566..4c4133c60 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -27,6 +27,7 @@ package com.nextcloud.talk.api; import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; import com.nextcloud.talk.models.json.chat.ChatOverall; import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage; +import com.nextcloud.talk.models.json.chat.ChatShareOverall; import com.nextcloud.talk.models.json.conversations.RoomOverall; import com.nextcloud.talk.models.json.conversations.RoomsOverall; import com.nextcloud.talk.models.json.generic.GenericOverall; @@ -338,6 +339,12 @@ public interface NcApi { @Field("actorDisplayName") String actorDisplayName, @Field("replyTo") Integer replyTo); + @GET + Observable> getSharedItems(@Header("Authorization") String authorization, @Url String url, + @Query("objectType") String objectType, + @Nullable @Query("lastKnownMessageId") Integer lastKnownMessageId, + @Nullable @Query("limit") Integer limit); + @GET Observable getMentionAutocompleteSuggestions(@Header("Authorization") String authorization, @Url String url, @Query("search") String query, 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 fca1b0f0c..352d17295 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -46,6 +46,7 @@ import android.os.Build import android.os.Build.VERSION_CODES.O import android.os.Bundle import android.os.Handler +import android.os.Parcelable import android.os.SystemClock import android.os.VibrationEffect import android.os.Vibrator @@ -99,6 +100,7 @@ import com.nextcloud.talk.BuildConfig import com.nextcloud.talk.R import com.nextcloud.talk.activities.CallActivity import com.nextcloud.talk.activities.MainActivity +import com.nextcloud.talk.activities.SharedItemsActivity import com.nextcloud.talk.activities.TakePhotoActivity import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder import com.nextcloud.talk.adapters.messages.IncomingPreviewMessageViewHolder @@ -188,9 +190,7 @@ import java.io.File import java.io.IOException import java.net.HttpURLConnection import java.text.SimpleDateFormat -import java.util.ArrayList import java.util.Date -import java.util.HashMap import java.util.Objects import java.util.concurrent.ExecutionException import javax.inject.Inject @@ -253,6 +253,7 @@ class ChatController(args: Bundle) : var conversationInfoMenuItem: MenuItem? = null var conversationVoiceCallMenuItem: MenuItem? = null var conversationVideoMenuItem: MenuItem? = null + var conversationSharedItemsItem: MenuItem? = null var magicWebSocketInstance: MagicWebSocketInstance? = null @@ -1464,7 +1465,7 @@ class ChatController(args: Bundle) : val bundle = Bundle() bundle.putParcelable(BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap(browserType)) bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(conversationUser)) - bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) + bundle.putString(KEY_ROOM_TOKEN, roomToken) router.pushController( RouterTransaction.with(BrowserForSharingController(bundle)) .pushChangeHandler(VerticalChangeHandler()) @@ -1476,7 +1477,7 @@ class ChatController(args: Bundle) : Log.d(TAG, "showShareLocationScreen") val bundle = Bundle() - bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) + bundle.putString(KEY_ROOM_TOKEN, roomToken) router.pushController( RouterTransaction.with(LocationPickerController(bundle)) .pushChangeHandler(HorizontalChangeHandler()) @@ -1487,7 +1488,7 @@ class ChatController(args: Bundle) : private fun showConversationInfoScreen() { val bundle = Bundle() bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationUser) - bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) + bundle.putString(KEY_ROOM_TOKEN, roomToken) bundle.putBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE, inOneToOneCall()) router.pushController( RouterTransaction.with(ConversationInfoController(bundle)) @@ -2299,6 +2300,7 @@ class ChatController(args: Bundle) : conversationInfoMenuItem = menu.findItem(R.id.conversation_info) conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call) conversationVideoMenuItem = menu.findItem(R.id.conversation_video_call) + conversationSharedItemsItem = menu.findItem(R.id.shared_items) loadAvatarForStatusBar() } @@ -2337,10 +2339,21 @@ class ChatController(args: Bundle) : showConversationInfoScreen() return true } + R.id.shared_items -> { + showSharedItems() + return true + } else -> return super.onOptionsItemSelected(item) } } + private fun showSharedItems() { + val intent = Intent(activity, SharedItemsActivity::class.java) + intent.putExtra(KEY_ROOM_TOKEN, roomToken) + intent.putExtra(KEY_USER_ENTITY, conversationUser as Parcelable) + activity!!.startActivity(intent) + } + private fun handleSystemMessages(chatMessageList: List): List { val chatMessageMap = chatMessageList.map { it.id to it }.toMap().toMutableMap() val chatMessageIterator = chatMessageMap.iterator() diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index cc35573f6..381a2de66 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -27,9 +27,11 @@ package com.nextcloud.talk.controllers import android.annotation.SuppressLint +import android.content.Intent import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.os.Bundle +import android.os.Parcelable import android.text.TextUtils import android.util.Log import android.view.MenuItem @@ -49,6 +51,7 @@ import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.facebook.drawee.backends.pipeline.Fresco import com.nextcloud.talk.R +import com.nextcloud.talk.activities.SharedItemsActivity import com.nextcloud.talk.adapters.items.ParticipantItem import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication @@ -88,11 +91,8 @@ import io.reactivex.schedulers.Schedulers import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -import java.util.ArrayList import java.util.Calendar import java.util.Collections -import java.util.Comparator -import java.util.HashMap import java.util.Locale import javax.inject.Inject @@ -175,10 +175,18 @@ class ConversationInfoController(args: Bundle) : binding.leaveConversationAction.setOnClickListener { leaveConversation() } binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog(null) } binding.addParticipantsAction.setOnClickListener { addParticipants() } + binding.showSharedItemsAction.setOnClickListener { showSharedItems() } fetchRoomInfo() } + private fun showSharedItems() { + val intent = Intent(activity, SharedItemsActivity::class.java) + intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, conversationToken) + intent.putExtra(BundleKeys.KEY_USER_ENTITY, conversationUser as Parcelable) + activity!!.startActivity(intent) + } + override fun onViewBound(view: View) { super.onViewBound(view) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOCS.java new file mode 100644 index 000000000..e869c5ee0 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOCS.java @@ -0,0 +1,77 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.models.json.chat; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; +import com.nextcloud.talk.models.json.generic.GenericOCS; + +import org.parceler.Parcel; + +import java.util.HashMap; +import java.util.Objects; + +@Parcel +@JsonObject +public class ChatShareOCS extends GenericOCS { + @JsonField(name = "data") + public HashMap data; + + public HashMap getData() { + return this.data; + } + + public void setData(HashMap data) { + this.data = data; + } + + public boolean equals(final Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ChatShareOCS)) { + return false; + } + final ChatShareOCS other = (ChatShareOCS) o; + if (!other.canEqual(this)) { + return false; + } + final Object this$data = this.getData(); + final Object other$data = other.getData(); + + return Objects.equals(this$data, other$data); + } + + protected boolean canEqual(final Object other) { + return other instanceof ChatShareOCS; + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $data = this.getData(); + result = result * PRIME + ($data == null ? 43 : $data.hashCode()); + return result; + } + + public String toString() { + return "ChatShareOCS(data=" + this.getData() + ")"; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverall.java new file mode 100644 index 000000000..b489e747a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverall.java @@ -0,0 +1,76 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.models.json.chat; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import org.parceler.Parcel; + +import java.util.Objects; + +@Parcel +@JsonObject +public class ChatShareOverall { + @JsonField(name = "ocs") + public ChatShareOCS ocs; + + public ChatShareOCS getOcs() { + return this.ocs; + } + + public void setOcs(ChatShareOCS ocs) { + this.ocs = ocs; + } + + public boolean equals(final Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ChatShareOverall)) { + return false; + } + final ChatShareOverall other = (ChatShareOverall) o; + if (!other.canEqual(this)) { + return false; + } + final Object this$ocs = this.getOcs(); + final Object other$ocs = other.getOcs(); + + return Objects.equals(this$ocs, other$ocs); + } + + protected boolean canEqual(final Object other) { + return other instanceof ChatShareOverall; + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $ocs = this.getOcs(); + result = result * PRIME + ($ocs == null ? 43 : $ocs.hashCode()); + return result; + } + + public String toString() { + return "ChatShareOverall(ocs=" + this.getOcs() + ")"; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt new file mode 100644 index 000000000..4b956bffa --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt @@ -0,0 +1,10 @@ +package com.nextcloud.talk.repositories + +data class SharedItem( + val id: String, + val name: String, + val mimeType: String, + val link: String, + val previewAvailable: Boolean, + val previewLink: String +) diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt new file mode 100644 index 000000000..63ffce29c --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt @@ -0,0 +1,53 @@ +package com.nextcloud.talk.repositories + +import autodagger.AutoInjector +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication +import com.nextcloud.talk.models.json.chat.ChatShareOverall +import com.nextcloud.talk.utils.ApiUtils +import io.reactivex.Observable +import retrofit2.Response +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class SharedItemsRepository { + + companion object { + private val TAG = SharedItemsRepository::class.simpleName + } + + var parameters: Parameters? = null + + @Inject + lateinit var ncApi: NcApi + + init { + sharedApplication!!.componentApplication.inject(this) + } + + fun media(): Observable>? { + val credentials = ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken) + + return ncApi.getSharedItems( + credentials, + ApiUtils.getUrlForChatSharedItems(1, parameters!!.baseUrl, parameters!!.roomToken), + "media", null, null + ) + } + + fun authHeader(): Map { + return mapOf(Pair("Authorization", ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken))) + } + + fun previewLink(fileId: String?): String { + return ApiUtils.getUrlForFilePreviewWithFileId(parameters!!.baseUrl, fileId, 100) + } + + data class Parameters( + val userName: String, + val userToken: String, + val baseUrl: String, + val roomToken: String + ) +} diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt new file mode 100644 index 000000000..f83a5c6ea --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt @@ -0,0 +1,7 @@ +package com.nextcloud.talk.repositories + +class SharedMediaItems( + val items: List, + val lastSeenId: String, + val authHeader: Map +) diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index 9e3ea3c43..2b6e654c0 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -260,6 +260,10 @@ public class ApiUtils { public static String getUrlForChatMessage(int version, String baseUrl, String token, String messageId) { return getUrlForChat(version, baseUrl, token) + "/" + messageId; } + + public static String getUrlForChatSharedItems(int version, String baseUrl, String token) { + return getUrlForChat(version, baseUrl, token) + "/share"; + } public static String getUrlForSignaling(int version, String baseUrl) { return getUrlForApi(version, baseUrl) + "/signaling"; diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt new file mode 100644 index 000000000..90e53c307 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -0,0 +1,95 @@ +package com.nextcloud.talk.viewmodels + +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.models.json.chat.ChatShareOverall +import com.nextcloud.talk.repositories.SharedItem +import com.nextcloud.talk.repositories.SharedItemsRepository +import com.nextcloud.talk.repositories.SharedMediaItems +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import retrofit2.Response + +class SharedItemsViewModel(private val repository: SharedItemsRepository) : ViewModel() { + + private val _media: MutableLiveData by lazy { + MutableLiveData().also { + loadMediaItems() + } + } + + val media: LiveData + get() = _media + + private fun loadMediaItems() { + + repository.media()?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer> { + + var chatLastGiven: String = "" + val items = mutableListOf() + + override fun onSubscribe(d: Disposable) = Unit + + override fun onNext(response: Response) { + chatLastGiven = response.headers()["x-chat-last-given"]!! + + val mediaItems = response.body()!!.ocs!!.data + mediaItems?.forEach { + val fileParameters = it.value.messageParameters["file"]!! + + val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true) + + items.add( + SharedItem( + fileParameters["id"]!!, fileParameters["name"]!!, + fileParameters["mimetype"]!!, fileParameters["link"]!!, + previewAvailable, + repository.previewLink(fileParameters["id"]) + ) + ) + } + } + + override fun onError(e: Throwable) { + Log.d(TAG, "An error occurred: $e") + } + + override fun onComplete() { + this@SharedItemsViewModel._media.value = + SharedMediaItems(items.asReversed(), chatLastGiven, repository.authHeader()) + } + }) + } + + class Factory(val userEntity: UserEntity, val roomToken: String) : ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(SharedItemsViewModel::class.java)) { + + val repository = SharedItemsRepository() + repository.parameters = SharedItemsRepository.Parameters( + userEntity.userId, + userEntity.token, + userEntity.baseUrl, + roomToken + ) + + return SharedItemsViewModel(repository) as T + } + + throw IllegalArgumentException("Unknown ViewModel class") + } + } + + companion object { + private val TAG = SharedItemsViewModel::class.simpleName + } +} diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml new file mode 100644 index 000000000..c5fc76ea9 --- /dev/null +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -0,0 +1,22 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/attachment_item.xml b/app/src/main/res/layout/attachment_item.xml new file mode 100644 index 000000000..10e2f29a6 --- /dev/null +++ b/app/src/main/res/layout/attachment_item.xml @@ -0,0 +1,14 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/controller_conversation_info.xml b/app/src/main/res/layout/controller_conversation_info.xml index 71d6ef568..77c023d13 100644 --- a/app/src/main/res/layout/controller_conversation_info.xml +++ b/app/src/main/res/layout/controller_conversation_info.xml @@ -129,7 +129,7 @@ android:id="@+id/participants_list_category" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_below="@+id/settings" + android:layout_below="@+id/category_shared_items" android:visibility="gone" apc:cardBackgroundColor="@color/bg_default" apc:cardElevation="0dp" @@ -213,6 +213,29 @@ tools:visibility="visible" /> + + + + + + + + diff --git a/app/src/main/res/menu/menu_conversation.xml b/app/src/main/res/menu/menu_conversation.xml index f9d74ac28..b96a91af2 100644 --- a/app/src/main/res/menu/menu_conversation.xml +++ b/app/src/main/res/menu/menu_conversation.xml @@ -37,8 +37,13 @@ + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 493855699..507d51139 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -512,6 +512,7 @@ Phone Audio output Wired headset + Attachements All From 53a1725606aa2745fdbe8b60d23b4ec843be28a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Tue, 26 Apr 2022 12:13:41 +0200 Subject: [PATCH 02/55] Correct order of items in grid view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- .../talk/viewmodels/SharedItemsViewModel.kt | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index 90e53c307..37bf89d32 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -34,7 +34,7 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View ?.subscribe(object : Observer> { var chatLastGiven: String = "" - val items = mutableListOf() + val items = mutableMapOf() override fun onSubscribe(d: Disposable) = Unit @@ -47,13 +47,11 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true) - items.add( - SharedItem( - fileParameters["id"]!!, fileParameters["name"]!!, - fileParameters["mimetype"]!!, fileParameters["link"]!!, - previewAvailable, - repository.previewLink(fileParameters["id"]) - ) + items[it.value.id] = SharedItem( + fileParameters["id"]!!, fileParameters["name"]!!, + fileParameters["mimetype"]!!, fileParameters["link"]!!, + previewAvailable, + repository.previewLink(fileParameters["id"]) ) } } @@ -64,7 +62,7 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View override fun onComplete() { this@SharedItemsViewModel._media.value = - SharedMediaItems(items.asReversed(), chatLastGiven, repository.authHeader()) + SharedMediaItems(items.toSortedMap().values.toList().reversed(), chatLastGiven, repository.authHeader()) } }) } From 3beda5953c1ecd1f3a3d52dc815ea291cd0c205e Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 26 Apr 2022 20:41:33 +0200 Subject: [PATCH 03/55] use correct icon for shared items of a room Signed-off-by: Andy Scherzinger --- .../res/drawable/ic_folder_multiple_image.xml | 25 +++++++++++++++++++ .../layout/controller_conversation_info.xml | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/ic_folder_multiple_image.xml diff --git a/app/src/main/res/drawable/ic_folder_multiple_image.xml b/app/src/main/res/drawable/ic_folder_multiple_image.xml new file mode 100644 index 000000000..5cbec6e30 --- /dev/null +++ b/app/src/main/res/drawable/ic_folder_multiple_image.xml @@ -0,0 +1,25 @@ + + + + diff --git a/app/src/main/res/layout/controller_conversation_info.xml b/app/src/main/res/layout/controller_conversation_info.xml index 77c023d13..37bd3699e 100644 --- a/app/src/main/res/layout/controller_conversation_info.xml +++ b/app/src/main/res/layout/controller_conversation_info.xml @@ -229,7 +229,7 @@ android:id="@+id/show_shared_items_action" android:layout_width="match_parent" android:layout_height="wrap_content" - apc:mp_icon="@drawable/ic_timer_black_24dp" + apc:mp_icon="@drawable/ic_folder_multiple_image" apc:mp_icon_tint="@color/grey_600" apc:mp_summary="See all shared photos, voice messages, files, etc." apc:mp_title="Shared Items" /> From a40e4111e15837374b68ce2e92e2ae19b7a45a1c Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 26 Apr 2022 20:58:50 +0200 Subject: [PATCH 04/55] replace avatar placeholder with file placeholder Signed-off-by: Andy Scherzinger --- app/src/main/res/layout/attachment_item.xml | 29 +++++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/attachment_item.xml b/app/src/main/res/layout/attachment_item.xml index 10e2f29a6..8e9adf731 100644 --- a/app/src/main/res/layout/attachment_item.xml +++ b/app/src/main/res/layout/attachment_item.xml @@ -1,4 +1,22 @@ - + \ No newline at end of file + fresco:failureImage="@drawable/ic_mimetype_file" + fresco:placeholderImage="@drawable/ic_mimetype_file" + fresco:roundedCornerRadius="4dp" /> From d14f4b38ea7dbf95328a17ad7bfa55279757a1ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Wed, 27 Apr 2022 12:31:24 +0200 Subject: [PATCH 05/55] Check that header 'x-chat-last-given' exists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without that check a null pointer exception occurs if the header is not set. Signed-off-by: Tim Krüger --- .../com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index 37bf89d32..082c0a74d 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -39,7 +39,10 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View override fun onSubscribe(d: Disposable) = Unit override fun onNext(response: Response) { - chatLastGiven = response.headers()["x-chat-last-given"]!! + + if(response.headers()["x-chat-last-given"] != null) { + chatLastGiven = response.headers()["x-chat-last-given"]!! + } val mediaItems = response.body()!!.ocs!!.data mediaItems?.forEach { From df5c0044c10c2597862beeccb4102f0294132215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Wed, 27 Apr 2022 12:38:21 +0200 Subject: [PATCH 06/55] Add app bar to view 'Shared items' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- .../talk/activities/SharedItemsActivity.kt | 22 ++++++++++++-- .../talk/controllers/ChatController.kt | 4 ++- .../controllers/ConversationInfoController.kt | 1 + .../main/res/layout/activity_shared_items.xml | 30 ++++++++++++++++++- app/src/main/res/values/strings.xml | 3 ++ 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 04d3a6a67..9448d87d1 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -2,12 +2,16 @@ package com.nextcloud.talk.activities import android.os.Bundle import android.util.Log +import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider import com.nextcloud.talk.adapters.SharedItemsAdapter import com.nextcloud.talk.databinding.ActivitySharedItemsBinding import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY import com.nextcloud.talk.viewmodels.SharedItemsViewModel class SharedItemsActivity : AppCompatActivity() { @@ -18,12 +22,17 @@ class SharedItemsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val roomToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!! - val userEntity = intent.getParcelableExtra(BundleKeys.KEY_USER_ENTITY)!! + val roomToken = intent.getStringExtra(KEY_ROOM_TOKEN)!! + val conversationName = intent.getStringExtra(KEY_CONVERSATION_NAME) + val userEntity = intent.getParcelableExtra(KEY_USER_ENTITY)!! binding = ActivitySharedItemsBinding.inflate(layoutInflater) + setSupportActionBar(binding.sharedItemsToolbar) setContentView(binding.root) + supportActionBar?.title = conversationName + supportActionBar?.setDisplayHomeAsUpEnabled(true) + viewModel = ViewModelProvider( this, SharedItemsViewModel.Factory(userEntity, roomToken) @@ -38,6 +47,15 @@ class SharedItemsActivity : AppCompatActivity() { } } + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == android.R.id.home) { + onBackPressed() + true + } else { + super.onOptionsItemSelected(item) + } + } + private lateinit var binding: ActivitySharedItemsBinding private lateinit var viewModel: SharedItemsViewModel 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 352d17295..f3e484e15 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -158,6 +158,7 @@ import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.UriUtils import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACTIVE_CONVERSATION +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY @@ -2349,6 +2350,7 @@ class ChatController(args: Bundle) : private fun showSharedItems() { val intent = Intent(activity, SharedItemsActivity::class.java) + intent.putExtra(KEY_CONVERSATION_NAME, currentConversation?.displayName) intent.putExtra(KEY_ROOM_TOKEN, roomToken) intent.putExtra(KEY_USER_ENTITY, conversationUser as Parcelable) activity!!.startActivity(intent) @@ -2415,7 +2417,7 @@ class ChatController(args: Bundle) : bundle.putParcelable(KEY_USER_ENTITY, conversationUser) bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword) bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, conversationUser?.baseUrl) - bundle.putString(BundleKeys.KEY_CONVERSATION_NAME, it.displayName) + bundle.putString(KEY_CONVERSATION_NAME, it.displayName) if (isVoiceOnlyCall) { bundle.putBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, true) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index 381a2de66..c853775a4 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -182,6 +182,7 @@ class ConversationInfoController(args: Bundle) : private fun showSharedItems() { val intent = Intent(activity, SharedItemsActivity::class.java) + intent.putExtra(BundleKeys.KEY_CONVERSATION_NAME, conversation?.displayName) intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, conversationToken) intent.putExtra(BundleKeys.KEY_USER_ENTITY, conversationUser as Parcelable) activity!!.startActivity(intent) diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index c5fc76ea9..de4ca10dd 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -1,4 +1,23 @@ + + + + android:layout_height="wrap_content" + app:layout_constraintTop_toBottomOf="@+id/shared_items_toolbar"> Share contact Permission to read contacts is required + + Shared items + Talk recording from %1$s (%2$s) Hold to record, release to send. From cf91e2390e45d14f923a6df80544ea60ad7afc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Wed, 27 Apr 2022 13:36:58 +0200 Subject: [PATCH 07/55] Fix ktlint issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- .../com/nextcloud/talk/activities/SharedItemsActivity.kt | 1 - .../com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 9448d87d1..2e08f4f65 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -8,7 +8,6 @@ import androidx.lifecycle.ViewModelProvider import com.nextcloud.talk.adapters.SharedItemsAdapter import com.nextcloud.talk.databinding.ActivitySharedItemsBinding import com.nextcloud.talk.models.database.UserEntity -import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index 082c0a74d..25e0667a6 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -40,7 +40,7 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View override fun onNext(response: Response) { - if(response.headers()["x-chat-last-given"] != null) { + if (response.headers()["x-chat-last-given"] != null) { chatLastGiven = response.headers()["x-chat-last-given"]!! } @@ -65,7 +65,11 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View override fun onComplete() { this@SharedItemsViewModel._media.value = - SharedMediaItems(items.toSortedMap().values.toList().reversed(), chatLastGiven, repository.authHeader()) + SharedMediaItems( + items.toSortedMap().values.toList().reversed(), + chatLastGiven, + repository.authHeader() + ) } }) } From 3a5a3cebfb446ae117b01eb32cccad5aa09fa671 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 26 Apr 2022 14:18:40 +0200 Subject: [PATCH 08/55] extract logic for downloading and opening files to helper class this is done because in a next step this logic should also be used by the SharedItemsAdapter extracting is not yet done in a clean way. in a next step some better architecture patterns must be used to separate layers Signed-off-by: Marcel Hibbe --- .../talk/adapters/SharedItemsAdapter.kt | 10 + .../MagicPreviewMessageViewHolder.java | 382 +++--------------- .../nextcloud/talk/utils/FileViewerUtils.kt | 279 +++++++++++++ 3 files changed, 354 insertions(+), 317 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt index aaaaf274f..e1b8ca3c0 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -1,6 +1,7 @@ package com.nextcloud.talk.adapters import android.net.Uri +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -9,11 +10,16 @@ import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.interfaces.DraweeController import com.facebook.imagepipeline.common.RotationOptions import com.facebook.imagepipeline.request.ImageRequestBuilder +import com.nextcloud.talk.activities.SharedItemsActivity import com.nextcloud.talk.databinding.AttachmentItemBinding import com.nextcloud.talk.repositories.SharedItem class SharedItemsAdapter : RecyclerView.Adapter() { + companion object { + private val TAG = SharedItemsAdapter::class.simpleName + } + class ViewHolder(val binding: AttachmentItemBinding, itemView: View) : RecyclerView.ViewHolder(itemView) var authHeader: Map = emptyMap() @@ -42,6 +48,10 @@ class SharedItemsAdapter : RecyclerView.Adapter() .setImageRequest(imageRequest) .build() holder.binding.image.controller = draweeController + + holder.binding.image.setOnClickListener { + Log.d(TAG, "clicked " + currentItem.name) + } } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java index 36d05ed3f..daeb6c3cb 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java @@ -27,13 +27,11 @@ package com.nextcloud.talk.adapters.messages; import android.annotation.SuppressLint; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.net.Uri; -import android.os.Build; import android.os.Handler; import android.util.Base64; import android.util.Log; @@ -45,27 +43,20 @@ import android.widget.ProgressBar; import com.facebook.drawee.view.SimpleDraweeView; import com.google.common.util.concurrent.ListenableFuture; import com.nextcloud.talk.R; -import com.nextcloud.talk.activities.FullScreenImageActivity; -import com.nextcloud.talk.activities.FullScreenMediaActivity; -import com.nextcloud.talk.activities.FullScreenTextViewerActivity; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.components.filebrowser.models.BrowserFile; import com.nextcloud.talk.components.filebrowser.models.DavResponse; import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation; import com.nextcloud.talk.databinding.ReactionsInsideMessageBinding; -import com.nextcloud.talk.jobs.DownloadFileToCacheWorker; -import com.nextcloud.talk.models.database.CapabilitiesUtil; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.chat.ChatMessage; import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet; -import com.nextcloud.talk.utils.AccountUtils; import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DrawableUtils; -import com.nextcloud.talk.utils.bundle.BundleKeys; +import com.nextcloud.talk.utils.FileViewerUtils; import com.stfalcon.chatkit.messages.MessageHolders; import java.io.ByteArrayInputStream; -import java.io.File; import java.io.IOException; import java.util.List; import java.util.concurrent.Callable; @@ -75,10 +66,7 @@ import javax.inject.Inject; import androidx.appcompat.view.ContextThemeWrapper; import androidx.core.content.ContextCompat; -import androidx.core.content.FileProvider; import androidx.emoji.widget.EmojiTextView; -import androidx.work.Data; -import androidx.work.OneTimeWorkRequest; import androidx.work.WorkInfo; import androidx.work.WorkManager; import autodagger.AutoInjector; @@ -114,6 +102,8 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom ReactionsInsideMessageBinding reactionsBinding; + FileViewerUtils fileViewerUtils; + View clickView; ReactionsInterface reactionsInterface; @@ -122,6 +112,7 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom public MagicPreviewMessageViewHolder(View itemView, Object payload) { super(itemView, payload); NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); + fileViewerUtils = new FileViewerUtils(context); } @SuppressLint("SetTextI18n") @@ -138,7 +129,7 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom } else { userAvatar.setVisibility(View.VISIBLE); userAvatar.setOnClickListener(v -> { - if (payload instanceof ProfileBottomSheet){ + if (payload instanceof ProfileBottomSheet) { ((ProfileBottomSheet) payload).showFor(message.actorId, v.getContext()); } }); @@ -179,8 +170,8 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom if (message.getSelectedIndividualHashMap().containsKey(KEY_CONTACT_PHOTO)) { image = getPreviewContactPhoto(); Drawable drawable = getDrawableFromContactDetails( - context, - message.getSelectedIndividualHashMap().get(KEY_CONTACT_PHOTO)); + context, + message.getSelectedIndividualHashMap().get(KEY_CONTACT_PHOTO)); image.getHierarchy().setPlaceholderImage(drawable); } else if (message.getSelectedIndividualHashMap().containsKey(KEY_MIMETYPE)) { String mimetype = message.getSelectedIndividualHashMap().get(KEY_MIMETYPE); @@ -191,7 +182,7 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom fetchFileInformation("/" + message.getSelectedIndividualHashMap().get(KEY_PATH), message.activeUser); } - if(message.activeUser != null && message.activeUser.getUsername() != null && message.activeUser.getBaseUrl() != null){ + if (message.activeUser != null && message.activeUser.getUsername() != null && message.activeUser.getBaseUrl() != null) { String accountString = message.activeUser.getUsername() + "@" + message.activeUser.getBaseUrl() @@ -199,12 +190,7 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom .replace("http://", ""); clickView.setOnClickListener(v -> { - String mimetype = message.getSelectedIndividualHashMap().get(KEY_MIMETYPE); - if (isSupportedForInternalViewer(mimetype) || canBeHandledByExternalApp(mimetype, fileName)) { - openOrDownloadFile(message); - } else { - openFileInFilesApp(message, accountString); - } + fileViewerUtils.openFile(message, progressBar, getMessageText(), image); }); clickView.setOnLongClickListener(l -> { @@ -223,15 +209,21 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom try { for (WorkInfo workInfo : workers.get()) { if (workInfo.getState() == WorkInfo.State.RUNNING || - workInfo.getState() == WorkInfo.State.ENQUEUED) { + workInfo.getState() == WorkInfo.State.ENQUEUED) { progressBar.setVisibility(View.VISIBLE); String mimetype = message.getSelectedIndividualHashMap().get(KEY_MIMETYPE); WorkManager - .getInstance(context) - .getWorkInfoByIdLiveData(workInfo.getId()) - .observeForever(info -> updateViewsByProgress(fileName, mimetype, info)); + .getInstance(context) + .getWorkInfoByIdLiveData(workInfo.getId()) + .observeForever(info -> fileViewerUtils.updateViewsByProgress( + fileName, + mimetype, + info, + progressBar, + getMessageText(), + image)); } } } catch (ExecutionException | InterruptedException e) { @@ -273,9 +265,9 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom Drawable drawable = null; if (!base64.equals("")) { ByteArrayInputStream inputStream = new ByteArrayInputStream( - Base64.decode(base64.getBytes(), Base64.DEFAULT)); + Base64.decode(base64.getBytes(), Base64.DEFAULT)); drawable = Drawable.createFromResourceStream(context.getResources(), - null, inputStream, null, null); + null, inputStream, null, null); try { inputStream.close(); } catch (IOException e) { @@ -287,151 +279,8 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom return drawable; } - public abstract EmojiTextView getMessageText(); - - public abstract ProgressBar getProgressBar(); - - public abstract SimpleDraweeView getImage(); - - public abstract View getPreviewContainer(); - - public abstract View getPreviewContactContainer(); - - public abstract SimpleDraweeView getPreviewContactPhoto(); - - public abstract EmojiTextView getPreviewContactName(); - - public abstract ProgressBar getPreviewContactProgressBar(); - - public abstract ReactionsInsideMessageBinding getReactionsBinding(); - - private void openOrDownloadFile(ChatMessage message) { - String filename = message.getSelectedIndividualHashMap().get(KEY_NAME); - String mimetype = message.getSelectedIndividualHashMap().get(KEY_MIMETYPE); - File file = new File(context.getCacheDir(), filename); - if (file.exists()) { - openFile(filename, mimetype); - } else { - downloadFileToCache(message); - } - } - - public boolean isSupportedForInternalViewer(String mimetype){ - switch (mimetype) { - case "image/png": - case "image/jpeg": - case "image/gif": - case "audio/mpeg": - case "audio/wav": - case "audio/ogg": - case "video/mp4": - case "video/quicktime": - case "video/ogg": - case "text/markdown": - case "text/plain": - return true; - default: - return false; - } - } - - private void openFile(String filename, String mimetype) { - switch (mimetype) { - case "audio/mpeg": - case "audio/wav": - case "audio/ogg": - case "video/mp4": - case "video/quicktime": - case "video/ogg": - openMediaView(filename, mimetype); - break; - case "image/png": - case "image/jpeg": - case "image/gif": - openImageView(filename, mimetype); - break; - case "text/markdown": - case "text/plain": - openTextView(filename, mimetype); - break; - default: - openFileByExternalApp(filename, mimetype); - } - } - - private void openFileByExternalApp(String fileName, String mimetype) { - String path = context.getCacheDir().getAbsolutePath() + "/" + fileName; - File file = new File(path); - Intent intent; - if (Build.VERSION.SDK_INT < 24) { - intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.fromFile(file), mimetype); - intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - } else { - intent = new Intent(); - intent.setAction(Intent.ACTION_VIEW); - Uri pdfURI = FileProvider.getUriForFile(context, context.getPackageName(), file); - intent.setDataAndType(pdfURI, mimetype); - intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - } - - try { - if (intent.resolveActivity(context.getPackageManager()) != null) { - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } else { - Log.e(TAG, "No Application found to open the file. This should have been handled beforehand!"); - } - } catch (Exception e) { - Log.e(TAG, "Error while opening file", e); - } - } - - private boolean canBeHandledByExternalApp(String mimetype, String fileName) { - String path = context.getCacheDir().getAbsolutePath() + "/" + fileName; - File file = new File(path); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.fromFile(file), mimetype); - return intent.resolveActivity(context.getPackageManager()) != null; - } - - private void openImageView(String filename, String mimetype) { - Intent fullScreenImageIntent = new Intent(context, FullScreenImageActivity.class); - fullScreenImageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - fullScreenImageIntent.putExtra("FILE_NAME", filename); - fullScreenImageIntent.putExtra("IS_GIF", isGif(mimetype)); - context.startActivity(fullScreenImageIntent); - } - - private void openFileInFilesApp(ChatMessage message, String accountString) { - if (AccountUtils.INSTANCE.canWeOpenFilesApp(context, accountString)) { - Intent filesAppIntent = new Intent(Intent.ACTION_VIEW, null); - final ComponentName componentName = new ComponentName( - context.getString(R.string.nc_import_accounts_from), - "com.owncloud.android.ui.activity.FileDisplayActivity" - ); - filesAppIntent.setComponent(componentName); - filesAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - filesAppIntent.setPackage(context.getString(R.string.nc_import_accounts_from)); - filesAppIntent.putExtra(BundleKeys.INSTANCE.getKEY_ACCOUNT(), accountString); - filesAppIntent.putExtra( - BundleKeys.INSTANCE.getKEY_FILE_ID(), - message.getSelectedIndividualHashMap().get(KEY_ID) - ); - context.startActivity(filesAppIntent); - } else { - Intent browserIntent = new Intent( - Intent.ACTION_VIEW, - Uri.parse(message.getSelectedIndividualHashMap().get("link")) - ); - browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(browserIntent); - } - } - private void onMessageViewLongClick(ChatMessage message, String accountString) { - if (isSupportedForInternalViewer(message.getSelectedIndividualHashMap().get(KEY_MIMETYPE))) { + if (fileViewerUtils.isSupportedForInternalViewer(message.getSelectedIndividualHashMap().get(KEY_MIMETYPE))) { previewMessageInterface.onPreviewMessageLongClick(message); return; } @@ -452,132 +301,13 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom popupMenu.inflate(R.menu.chat_preview_message_menu); popupMenu.setOnMenuItemClickListener(item -> { - openFileInFilesApp(message, accountString); + fileViewerUtils.openFileInFilesApp(message, accountString); return true; }); popupMenu.show(); } - @SuppressLint("LongLogTag") - private void downloadFileToCache(ChatMessage message) { - - String baseUrl = message.activeUser.getBaseUrl(); - String userId = message.activeUser.getUserId(); - String attachmentFolder = CapabilitiesUtil.getAttachmentFolder(message.activeUser); - - String fileName = message.getSelectedIndividualHashMap().get(KEY_NAME); - String mimetype = message.getSelectedIndividualHashMap().get(KEY_MIMETYPE); - - String size = message.getSelectedIndividualHashMap().get("size"); - - if (size == null) { - size = "-1"; - } - Integer fileSize = Integer.valueOf(size); - - String fileId = message.getSelectedIndividualHashMap().get(KEY_ID); - String path = message.getSelectedIndividualHashMap().get(KEY_PATH); - - // check if download worker is already running - ListenableFuture> workers = WorkManager.getInstance(context).getWorkInfosByTag(fileId); - - try { - for (WorkInfo workInfo : workers.get()) { - if (workInfo.getState() == WorkInfo.State.RUNNING || workInfo.getState() == WorkInfo.State.ENQUEUED) { - Log.d(TAG, "Download worker for " + fileId + " is already running or " + - "scheduled"); - return; - } - } - } catch (ExecutionException | InterruptedException e) { - Log.e(TAG, "Error when checking if worker already exsists", e); - } - - Data data; - OneTimeWorkRequest downloadWorker; - - data = new Data.Builder() - .putString(DownloadFileToCacheWorker.KEY_BASE_URL, baseUrl) - .putString(DownloadFileToCacheWorker.KEY_USER_ID, userId) - .putString(DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, attachmentFolder) - .putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileName) - .putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path) - .putInt(DownloadFileToCacheWorker.KEY_FILE_SIZE, fileSize) - .build(); - - downloadWorker = new OneTimeWorkRequest.Builder(DownloadFileToCacheWorker.class) - .setInputData(data) - .addTag(fileId) - .build(); - - WorkManager.getInstance().enqueue(downloadWorker); - - progressBar.setVisibility(View.VISIBLE); - - WorkManager.getInstance(context).getWorkInfoByIdLiveData(downloadWorker.getId()).observeForever(workInfo -> { - updateViewsByProgress(fileName, mimetype, workInfo); - }); - } - - private void updateViewsByProgress(String fileName, String mimetype, WorkInfo workInfo) { - switch (workInfo.getState()) { - case RUNNING: - int progress = workInfo.getProgress().getInt(DownloadFileToCacheWorker.PROGRESS, -1); - if (progress > -1) { - getMessageText().setText(String.format(context.getResources().getString(R.string.filename_progress), fileName, progress)); - } - break; - - case SUCCEEDED: - if (image.isShown()) { - openFile(fileName, mimetype); - } else { - Log.d(TAG, "file " + fileName + - " was downloaded but it's not opened because view is not shown on screen"); - } - getMessageText().setText(fileName); - progressBar.setVisibility(View.GONE); - break; - - case FAILED: - getMessageText().setText(fileName); - progressBar.setVisibility(View.GONE); - break; - default: - // do nothing - break; - } - } - - private void openMediaView(String filename, String mimetype) { - Intent fullScreenMediaIntent = new Intent(context, FullScreenMediaActivity.class); - fullScreenMediaIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - fullScreenMediaIntent.putExtra("FILE_NAME", filename); - fullScreenMediaIntent.putExtra("AUDIO_ONLY", isAudioOnly(mimetype)); - context.startActivity(fullScreenMediaIntent); - } - - private void openTextView(String filename, String mimetype) { - Intent fullScreenTextViewerIntent = new Intent(context, FullScreenTextViewerActivity.class); - fullScreenTextViewerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - fullScreenTextViewerIntent.putExtra("FILE_NAME", filename); - fullScreenTextViewerIntent.putExtra("IS_MARKDOWN", isMarkdown(mimetype)); - context.startActivity(fullScreenTextViewerIntent); - } - - private boolean isGif(String mimetype) { - return ("image/gif").equals(mimetype); - } - - private boolean isMarkdown(String mimetype) { - return ("text/markdown").equals(mimetype); - } - - private boolean isAudioOnly(String mimetype) { - return mimetype.startsWith("audio"); - } - private void fetchFileInformation(String url, UserEntity activeUser) { Single.fromCallable(new Callable() { @Override @@ -585,34 +315,34 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom return new ReadFilesystemOperation(okHttpClient, activeUser, url, 0); } }).observeOn(Schedulers.io()) - .subscribe(new SingleObserver() { - @Override - public void onSubscribe(@NonNull Disposable d) { - // unused atm - } + .subscribe(new SingleObserver() { + @Override + public void onSubscribe(@NonNull Disposable d) { + // unused atm + } - @Override - public void onSuccess(@NonNull ReadFilesystemOperation readFilesystemOperation) { - DavResponse davResponse = readFilesystemOperation.readRemotePath(); - if (davResponse.data != null) { - List browserFileList = (List) davResponse.data; - if (!browserFileList.isEmpty()) { - new Handler(context.getMainLooper()).post(() -> { - int resourceId = DrawableUtils - .INSTANCE - .getDrawableResourceIdForMimeType(browserFileList.get(0).mimeType); - Drawable drawable = ContextCompat.getDrawable(context, resourceId); - image.getHierarchy().setPlaceholderImage(drawable); - }); - } + @Override + public void onSuccess(@NonNull ReadFilesystemOperation readFilesystemOperation) { + DavResponse davResponse = readFilesystemOperation.readRemotePath(); + if (davResponse.data != null) { + List browserFileList = (List) davResponse.data; + if (!browserFileList.isEmpty()) { + new Handler(context.getMainLooper()).post(() -> { + int resourceId = DrawableUtils + .INSTANCE + .getDrawableResourceIdForMimeType(browserFileList.get(0).mimeType); + Drawable drawable = ContextCompat.getDrawable(context, resourceId); + image.getHierarchy().setPlaceholderImage(drawable); + }); } } + } - @Override - public void onError(@NonNull Throwable e) { - Log.e(TAG, "Error reading file information", e); - } - }); + @Override + public void onError(@NonNull Throwable e) { + Log.e(TAG, "Error reading file information", e); + } + }); } public void assignReactionInterface(ReactionsInterface reactionsInterface) { @@ -622,4 +352,22 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom public void assignPreviewMessageInterface(PreviewMessageInterface previewMessageInterface) { this.previewMessageInterface = previewMessageInterface; } + + public abstract EmojiTextView getMessageText(); + + public abstract ProgressBar getProgressBar(); + + public abstract SimpleDraweeView getImage(); + + public abstract View getPreviewContainer(); + + public abstract View getPreviewContactContainer(); + + public abstract SimpleDraweeView getPreviewContactPhoto(); + + public abstract EmojiTextView getPreviewContactName(); + + public abstract ProgressBar getPreviewContactProgressBar(); + + public abstract ReactionsInsideMessageBinding getReactionsBinding(); } diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt new file mode 100644 index 000000000..99de87bb5 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt @@ -0,0 +1,279 @@ +package com.nextcloud.talk.utils + +import android.annotation.SuppressLint +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.util.Log +import android.view.View +import android.widget.ProgressBar +import androidx.core.content.FileProvider +import androidx.work.Data +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkInfo +import androidx.work.WorkManager +import com.nextcloud.talk.R +import com.nextcloud.talk.activities.FullScreenImageActivity +import com.nextcloud.talk.activities.FullScreenMediaActivity +import com.nextcloud.talk.activities.FullScreenTextViewerActivity +import com.nextcloud.talk.adapters.messages.MagicPreviewMessageViewHolder +import com.nextcloud.talk.jobs.DownloadFileToCacheWorker +import com.nextcloud.talk.models.database.CapabilitiesUtil +import com.nextcloud.talk.models.json.chat.ChatMessage +import com.nextcloud.talk.utils.AccountUtils.canWeOpenFilesApp +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACCOUNT +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_ID +import androidx.emoji.widget.EmojiTextView +import com.facebook.drawee.view.SimpleDraweeView +import java.io.File +import java.util.concurrent.ExecutionException + +class FileViewerUtils(private val context: Context) { + fun openFile(message: ChatMessage, progressBar: ProgressBar, messageText: EmojiTextView, previewImage: SimpleDraweeView) { + val accountString = message.activeUser.username + "@" + + message.activeUser.baseUrl + .replace("https://", "") + .replace("http://", "") + + val fileName = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_NAME]!! + val mimetype = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_MIMETYPE]!! + + if (isSupportedForInternalViewer(mimetype) || canBeHandledByExternalApp(mimetype, fileName)) { + openOrDownloadFile(message, progressBar, messageText, previewImage) + } else { + openFileInFilesApp(message, accountString) + } + } + + private fun canBeHandledByExternalApp(mimetype: String, fileName: String): Boolean { + val path: String = context.cacheDir.absolutePath + "/" + fileName + val file = File(path) + val intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(Uri.fromFile(file), mimetype) + return intent.resolveActivity(context.packageManager) != null + } + + private fun openOrDownloadFile(message: ChatMessage, progressBar: ProgressBar, messageText: EmojiTextView, previewImage: SimpleDraweeView) { + val filename = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_NAME] + val mimetype = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_MIMETYPE] + val file = File(context.cacheDir, filename) + if (file.exists()) { + openFile(filename!!, mimetype!!) + } else { + downloadFileToCache(message, progressBar, messageText, previewImage) + } + } + + private fun openFile(filename: String, mimetype: String) { + when (mimetype) { + "audio/mpeg", "audio/wav", "audio/ogg", "video/mp4", "video/quicktime", "video/ogg" -> openMediaView( + filename, + mimetype + ) + "image/png", "image/jpeg", "image/gif" -> openImageView(filename, mimetype) + "text/markdown", "text/plain" -> openTextView(filename, mimetype) + else -> openFileByExternalApp(filename, mimetype) + } + } + + private fun openFileByExternalApp(fileName: String, mimetype: String) { + val path = context.cacheDir.absolutePath + "/" + fileName + val file = File(path) + val intent: Intent + if (Build.VERSION.SDK_INT < 24) { + intent = Intent(Intent.ACTION_VIEW) + intent.setDataAndType(Uri.fromFile(file), mimetype) + intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY + } else { + intent = Intent() + intent.action = Intent.ACTION_VIEW + val pdfURI = FileProvider.getUriForFile(context, context.packageName, file) + intent.setDataAndType(pdfURI, mimetype) + intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + try { + if (intent.resolveActivity(context.packageManager) != null) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + } else { + Log.e(TAG, "No Application found to open the file. This should have been handled beforehand!") + } + } catch (e: Exception) { + Log.e(TAG, "Error while opening file", e) + } + } + + fun openFileInFilesApp(message: ChatMessage, accountString: String) { + if (canWeOpenFilesApp(context, accountString)) { + val filesAppIntent = Intent(Intent.ACTION_VIEW, null) + val componentName = ComponentName( + context.getString(R.string.nc_import_accounts_from), + "com.owncloud.android.ui.activity.FileDisplayActivity" + ) + filesAppIntent.component = componentName + filesAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + filesAppIntent.setPackage(context.getString(R.string.nc_import_accounts_from)) + filesAppIntent.putExtra(KEY_ACCOUNT, accountString) + filesAppIntent.putExtra( + KEY_FILE_ID, + message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_ID] + ) + context.startActivity(filesAppIntent) + } else { + val browserIntent = Intent( + Intent.ACTION_VIEW, + Uri.parse(message.getSelectedIndividualHashMap()["link"]) + ) + browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(browserIntent) + } + } + + private fun openImageView(filename: String, mimetype: String) { + val fullScreenImageIntent = Intent(context, FullScreenImageActivity::class.java) + fullScreenImageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + fullScreenImageIntent.putExtra("FILE_NAME", filename) + fullScreenImageIntent.putExtra("IS_GIF", isGif(mimetype)) + context.startActivity(fullScreenImageIntent) + } + + private fun openMediaView(filename: String, mimetype: String) { + val fullScreenMediaIntent = Intent(context, FullScreenMediaActivity::class.java) + fullScreenMediaIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + fullScreenMediaIntent.putExtra("FILE_NAME", filename) + fullScreenMediaIntent.putExtra("AUDIO_ONLY", isAudioOnly(mimetype)) + context.startActivity(fullScreenMediaIntent) + } + + private fun openTextView(filename: String, mimetype: String) { + val fullScreenTextViewerIntent = Intent(context, FullScreenTextViewerActivity::class.java) + fullScreenTextViewerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + fullScreenTextViewerIntent.putExtra("FILE_NAME", filename) + fullScreenTextViewerIntent.putExtra("IS_MARKDOWN", isMarkdown(mimetype)) + context.startActivity(fullScreenTextViewerIntent) + } + + fun isSupportedForInternalViewer(mimetype: String?): Boolean { + return when (mimetype) { + "image/png", "image/jpeg", "image/gif", "audio/mpeg", "audio/wav", "audio/ogg", "video/mp4", "video/quicktime", "video/ogg", "text/markdown", "text/plain" -> true + else -> false + } + } + + private fun isGif(mimetype: String): Boolean { + return "image/gif" == mimetype + } + + private fun isMarkdown(mimetype: String): Boolean { + return "text/markdown" == mimetype + } + + private fun isAudioOnly(mimetype: String): Boolean { + return mimetype.startsWith("audio") + } + + @SuppressLint("LongLogTag") + private fun downloadFileToCache(message: ChatMessage, progressBar: ProgressBar, messageText: EmojiTextView, previewImage: SimpleDraweeView) { + val baseUrl = message.activeUser.baseUrl + val userId = message.activeUser.userId + val attachmentFolder = CapabilitiesUtil.getAttachmentFolder(message.activeUser) + val fileName = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_NAME]!! + val mimetype = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_MIMETYPE]!! + var size = message.getSelectedIndividualHashMap()["size"] + if (size == null) { + size = "-1" + } + val fileSize = Integer.valueOf(size) + val fileId = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_ID] + val path = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_PATH] + + // check if download worker is already running + val workers = WorkManager.getInstance(context).getWorkInfosByTag( + fileId!! + ) + try { + for (workInfo in workers.get()) { + if (workInfo.state == WorkInfo.State.RUNNING || workInfo.state == WorkInfo.State.ENQUEUED) { + Log.d(TAG, "Download worker for $fileId is already running or scheduled") + return + } + } + } catch (e: ExecutionException) { + Log.e(TAG, "Error when checking if worker already exsists", e) + } catch (e: InterruptedException) { + Log.e(TAG, "Error when checking if worker already exsists", e) + } + val downloadWorker: OneTimeWorkRequest + val data: Data = Data.Builder() + .putString(DownloadFileToCacheWorker.KEY_BASE_URL, baseUrl) + .putString(DownloadFileToCacheWorker.KEY_USER_ID, userId) + .putString(DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, attachmentFolder) + .putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileName) + .putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path) + .putInt(DownloadFileToCacheWorker.KEY_FILE_SIZE, fileSize) + .build() + downloadWorker = OneTimeWorkRequest.Builder(DownloadFileToCacheWorker::class.java) + .setInputData(data) + .addTag(fileId) + .build() + WorkManager.getInstance().enqueue(downloadWorker) + progressBar.visibility = View.VISIBLE + WorkManager.getInstance(context).getWorkInfoByIdLiveData(downloadWorker.id) + .observeForever { workInfo: WorkInfo? -> + updateViewsByProgress( + fileName, + mimetype, + workInfo!!, + progressBar, + messageText, + previewImage + ) + } + } + + fun updateViewsByProgress( + fileName: String, + mimetype: String, + workInfo: WorkInfo, + progressBar: ProgressBar, + messageText: EmojiTextView, + previewImage: SimpleDraweeView + ) { + when (workInfo.state) { + WorkInfo.State.RUNNING -> { + val progress = workInfo.progress.getInt(DownloadFileToCacheWorker.PROGRESS, -1) + if (progress > -1) { + messageText.text = String.format( + context.resources.getString(R.string.filename_progress), + fileName, + progress + ) + } + } + WorkInfo.State.SUCCEEDED -> { + if (previewImage.isShown) { + openFile(fileName, mimetype) + } else { + Log.d(TAG, "file " + fileName + + " was downloaded but it's not opened because view is not shown on screen" + ) + } + messageText.text = fileName + progressBar.visibility = View.GONE + } + WorkInfo.State.FAILED -> { + messageText.text = fileName + progressBar.visibility = View.GONE + } + else -> { + } + } + } + + companion object { + private val TAG = FileViewerUtils::class.simpleName + } +} \ No newline at end of file From 912bc3c8fe0e68cf8104413fb35c4756c94773be Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 26 Apr 2022 18:09:02 +0200 Subject: [PATCH 09/55] open files from media view Signed-off-by: Marcel Hibbe --- .../talk/adapters/SharedItemsAdapter.kt | 18 +- .../MagicPreviewMessageViewHolder.java | 58 ++--- .../talk/models/json/chat/ChatMessage.java | 3 + .../nextcloud/talk/repositories/SharedItem.kt | 9 +- .../repositories/SharedItemsRepository.kt | 2 + .../nextcloud/talk/utils/FileViewerUtils.kt | 224 +++++++++++++----- .../talk/viewmodels/SharedItemsViewModel.kt | 12 +- 7 files changed, 221 insertions(+), 105 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt index e1b8ca3c0..119e41bef 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -1,18 +1,18 @@ package com.nextcloud.talk.adapters import android.net.Uri -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.interfaces.DraweeController +import com.facebook.drawee.view.SimpleDraweeView import com.facebook.imagepipeline.common.RotationOptions import com.facebook.imagepipeline.request.ImageRequestBuilder -import com.nextcloud.talk.activities.SharedItemsActivity import com.nextcloud.talk.databinding.AttachmentItemBinding import com.nextcloud.talk.repositories.SharedItem +import com.nextcloud.talk.utils.FileViewerUtils class SharedItemsAdapter : RecyclerView.Adapter() { @@ -50,7 +50,19 @@ class SharedItemsAdapter : RecyclerView.Adapter() holder.binding.image.controller = draweeController holder.binding.image.setOnClickListener { - Log.d(TAG, "clicked " + currentItem.name) + val fileViewerUtils = FileViewerUtils(it.context, currentItem.userEntity) + + fileViewerUtils.openFile( + currentItem.id, + currentItem.name, + currentItem.fileSize, + currentItem.path, + currentItem.link, + currentItem.mimeType, + null, + null, + it as SimpleDraweeView + ) } } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java index daeb6c3cb..9d7d40b40 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/MagicPreviewMessageViewHolder.java @@ -41,7 +41,6 @@ import android.widget.PopupMenu; import android.widget.ProgressBar; import com.facebook.drawee.view.SimpleDraweeView; -import com.google.common.util.concurrent.ListenableFuture; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.components.filebrowser.models.BrowserFile; @@ -59,16 +58,14 @@ import com.stfalcon.chatkit.messages.MessageHolders; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.List; +import java.util.Objects; import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; import javax.inject.Inject; import androidx.appcompat.view.ContextThemeWrapper; import androidx.core.content.ContextCompat; import androidx.emoji.widget.EmojiTextView; -import androidx.work.WorkInfo; -import androidx.work.WorkManager; import autodagger.AutoInjector; import io.reactivex.Single; import io.reactivex.SingleObserver; @@ -112,7 +109,6 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom public MagicPreviewMessageViewHolder(View itemView, Object payload) { super(itemView, payload); NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - fileViewerUtils = new FileViewerUtils(context); } @SuppressLint("SetTextI18n") @@ -154,6 +150,8 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom if (message.getMessageType() == ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE) { + fileViewerUtils = new FileViewerUtils(context, message.activeUser); + String fileName = message.getSelectedIndividualHashMap().get(KEY_NAME); getMessageText().setText(fileName); if (message.getSelectedIndividualHashMap().containsKey(KEY_CONTACT_NAME)) { @@ -183,52 +181,26 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom } if (message.activeUser != null && message.activeUser.getUsername() != null && message.activeUser.getBaseUrl() != null) { - String accountString = - message.activeUser.getUsername() + "@" + - message.activeUser.getBaseUrl() - .replace("https://", "") - .replace("http://", ""); - clickView.setOnClickListener(v -> { fileViewerUtils.openFile(message, progressBar, getMessageText(), image); }); clickView.setOnLongClickListener(l -> { - onMessageViewLongClick(message, accountString); + onMessageViewLongClick(message); return true; }); } else { Log.e(TAG, "failed to set click listener because activeUser, username or baseUrl were null"); } + fileViewerUtils.resumeToUpdateViewsByProgress( + Objects.requireNonNull(message.getSelectedIndividualHashMap().get(MagicPreviewMessageViewHolder.KEY_NAME)), + Objects.requireNonNull(message.getSelectedIndividualHashMap().get(MagicPreviewMessageViewHolder.KEY_ID)), + Objects.requireNonNull(message.getSelectedIndividualHashMap().get(MagicPreviewMessageViewHolder.KEY_MIMETYPE)), + progressBar, + getMessageText(), + image); - // check if download worker is already running - String fileId = message.getSelectedIndividualHashMap().get(KEY_ID); - ListenableFuture> workers = WorkManager.getInstance(context).getWorkInfosByTag(fileId); - - try { - for (WorkInfo workInfo : workers.get()) { - if (workInfo.getState() == WorkInfo.State.RUNNING || - workInfo.getState() == WorkInfo.State.ENQUEUED) { - progressBar.setVisibility(View.VISIBLE); - - String mimetype = message.getSelectedIndividualHashMap().get(KEY_MIMETYPE); - - WorkManager - .getInstance(context) - .getWorkInfoByIdLiveData(workInfo.getId()) - .observeForever(info -> fileViewerUtils.updateViewsByProgress( - fileName, - mimetype, - info, - progressBar, - getMessageText(), - image)); - } - } - } catch (ExecutionException | InterruptedException e) { - Log.e(TAG, "Error when checking if worker already exists", e); - } } else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) { getMessageText().setText("GIPHY"); DisplayUtils.setClickableString("GIPHY", "https://giphy.com", getMessageText()); @@ -279,7 +251,7 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom return drawable; } - private void onMessageViewLongClick(ChatMessage message, String accountString) { + private void onMessageViewLongClick(ChatMessage message) { if (fileViewerUtils.isSupportedForInternalViewer(message.getSelectedIndividualHashMap().get(KEY_MIMETYPE))) { previewMessageInterface.onPreviewMessageLongClick(message); return; @@ -301,7 +273,11 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom popupMenu.inflate(R.menu.chat_preview_message_menu); popupMenu.setOnMenuItemClickListener(item -> { - fileViewerUtils.openFileInFilesApp(message, accountString); + if (item.getItemId()== R.id.openInFiles){ + String keyID = message.getSelectedIndividualHashMap().get(KEY_ID); + String link = message.getSelectedIndividualHashMap().get("link"); + fileViewerUtils.openFileInFilesApp(link, keyID); + } return true; }); 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 3057a51ca..e3ac0445b 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 @@ -156,6 +156,9 @@ public class ChatMessage implements MessageContentType, MessageContentType.Image if (MessageDigest.isEqual( Objects.requireNonNull(individualHashMap.get("type")).getBytes(Charsets.UTF_8), ("file").getBytes(Charsets.UTF_8))) { + + // TODO: this selectedIndividualHashMap stuff needs to be analyzed and most likely be refactored! + // it just feels wrong to fill this here inside getImageUrl() selectedIndividualHashMap = individualHashMap; if (!isVoiceMessage()) { if (getActiveUser() != null && getActiveUser().getBaseUrl() != null) { diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt index 4b956bffa..d620c04a1 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItem.kt @@ -1,10 +1,15 @@ package com.nextcloud.talk.repositories +import com.nextcloud.talk.models.database.UserEntity + data class SharedItem( val id: String, val name: String, - val mimeType: String, + val fileSize: Int, + val path: String, val link: String, + val mimeType: String, val previewAvailable: Boolean, - val previewLink: String + val previewLink: String, + val userEntity: UserEntity, ) diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt index 63ffce29c..afb6f80e4 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt @@ -4,6 +4,7 @@ import autodagger.AutoInjector import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication +import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.chat.ChatShareOverall import com.nextcloud.talk.utils.ApiUtils import io.reactivex.Observable @@ -48,6 +49,7 @@ class SharedItemsRepository { val userName: String, val userToken: String, val baseUrl: String, + val userEntity: UserEntity, val roomToken: String ) } diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt index 99de87bb5..b7aabc628 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt @@ -27,23 +27,68 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACCOUNT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_ID import androidx.emoji.widget.EmojiTextView import com.facebook.drawee.view.SimpleDraweeView +import com.nextcloud.talk.models.database.UserEntity import java.io.File import java.util.concurrent.ExecutionException -class FileViewerUtils(private val context: Context) { - fun openFile(message: ChatMessage, progressBar: ProgressBar, messageText: EmojiTextView, previewImage: SimpleDraweeView) { - val accountString = message.activeUser.username + "@" + - message.activeUser.baseUrl - .replace("https://", "") - .replace("http://", "") +class FileViewerUtils(private val context: Context, private val userEntity: UserEntity) { + fun openFile( + message: ChatMessage, + progressBar: ProgressBar?, + messageText: EmojiTextView?, + previewImage: SimpleDraweeView + ) { val fileName = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_NAME]!! val mimetype = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_MIMETYPE]!! + val link = message.getSelectedIndividualHashMap()["link"]!! + val fileId = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_ID]!! + val path = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_PATH]!! + + var size = message.getSelectedIndividualHashMap()["size"] + if (size == null) { + size = "-1" + } + val fileSize = Integer.valueOf(size) + + openFile( + fileId, + fileName, + fileSize, + path, + link, + mimetype, + progressBar, + messageText, + previewImage + ) + } + + fun openFile( + fileId: String, + fileName: String, + fileSize: Int, + path: String, + link: String, + mimetype: String, + progressBar: ProgressBar?, + messageText: EmojiTextView?, + previewImage: SimpleDraweeView + ) { if (isSupportedForInternalViewer(mimetype) || canBeHandledByExternalApp(mimetype, fileName)) { - openOrDownloadFile(message, progressBar, messageText, previewImage) + openOrDownloadFile( + fileName, + fileId, + path, + fileSize, + mimetype, + progressBar, + messageText, + previewImage + ) } else { - openFileInFilesApp(message, accountString) + openFileInFilesApp(link, fileId) } } @@ -55,26 +100,51 @@ class FileViewerUtils(private val context: Context) { return intent.resolveActivity(context.packageManager) != null } - private fun openOrDownloadFile(message: ChatMessage, progressBar: ProgressBar, messageText: EmojiTextView, previewImage: SimpleDraweeView) { - val filename = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_NAME] - val mimetype = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_MIMETYPE] - val file = File(context.cacheDir, filename) + private fun openOrDownloadFile( + fileName: String, + fileId: String, + path: String, + fileSize: Int, + mimetype: String, + progressBar: ProgressBar?, + messageText: EmojiTextView?, + previewImage: SimpleDraweeView + ) { + val file = File(context.cacheDir, fileName) if (file.exists()) { - openFile(filename!!, mimetype!!) + openFileByMimetype(fileName!!, mimetype!!) } else { - downloadFileToCache(message, progressBar, messageText, previewImage) + downloadFileToCache( + fileName, + fileId, + path, + fileSize, + mimetype, + progressBar, + messageText, + previewImage + ) } } - private fun openFile(filename: String, mimetype: String) { + private fun openFileByMimetype(filename: String, mimetype: String) { when (mimetype) { - "audio/mpeg", "audio/wav", "audio/ogg", "video/mp4", "video/quicktime", "video/ogg" -> openMediaView( - filename, - mimetype - ) - "image/png", "image/jpeg", "image/gif" -> openImageView(filename, mimetype) - "text/markdown", "text/plain" -> openTextView(filename, mimetype) - else -> openFileByExternalApp(filename, mimetype) + "audio/mpeg", + "audio/wav", + "audio/ogg", + "video/mp4", + "video/quicktime", + "video/ogg" + -> openMediaView(filename, mimetype) + "image/png", + "image/jpeg", + "image/gif" + -> openImageView(filename, mimetype) + "text/markdown", + "text/plain" + -> openTextView(filename, mimetype) + else + -> openFileByExternalApp(filename, mimetype) } } @@ -106,7 +176,12 @@ class FileViewerUtils(private val context: Context) { } } - fun openFileInFilesApp(message: ChatMessage, accountString: String) { + fun openFileInFilesApp(link: String, keyID: String) { + val accountString = userEntity.username + "@" + + userEntity.baseUrl + .replace("https://", "") + .replace("http://", "") + if (canWeOpenFilesApp(context, accountString)) { val filesAppIntent = Intent(Intent.ACTION_VIEW, null) val componentName = ComponentName( @@ -117,15 +192,12 @@ class FileViewerUtils(private val context: Context) { filesAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) filesAppIntent.setPackage(context.getString(R.string.nc_import_accounts_from)) filesAppIntent.putExtra(KEY_ACCOUNT, accountString) - filesAppIntent.putExtra( - KEY_FILE_ID, - message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_ID] - ) + filesAppIntent.putExtra(KEY_FILE_ID, keyID) context.startActivity(filesAppIntent) } else { val browserIntent = Intent( Intent.ACTION_VIEW, - Uri.parse(message.getSelectedIndividualHashMap()["link"]) + Uri.parse(link) ) browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) context.startActivity(browserIntent) @@ -176,20 +248,16 @@ class FileViewerUtils(private val context: Context) { } @SuppressLint("LongLogTag") - private fun downloadFileToCache(message: ChatMessage, progressBar: ProgressBar, messageText: EmojiTextView, previewImage: SimpleDraweeView) { - val baseUrl = message.activeUser.baseUrl - val userId = message.activeUser.userId - val attachmentFolder = CapabilitiesUtil.getAttachmentFolder(message.activeUser) - val fileName = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_NAME]!! - val mimetype = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_MIMETYPE]!! - var size = message.getSelectedIndividualHashMap()["size"] - if (size == null) { - size = "-1" - } - val fileSize = Integer.valueOf(size) - val fileId = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_ID] - val path = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_PATH] - + private fun downloadFileToCache( + fileName: String, + fileId: String, + path: String, + fileSize: Int, + mimetype: String, + progressBar: ProgressBar?, + messageText: EmojiTextView?, + previewImage: SimpleDraweeView + ) { // check if download worker is already running val workers = WorkManager.getInstance(context).getWorkInfosByTag( fileId!! @@ -208,9 +276,12 @@ class FileViewerUtils(private val context: Context) { } val downloadWorker: OneTimeWorkRequest val data: Data = Data.Builder() - .putString(DownloadFileToCacheWorker.KEY_BASE_URL, baseUrl) - .putString(DownloadFileToCacheWorker.KEY_USER_ID, userId) - .putString(DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, attachmentFolder) + .putString(DownloadFileToCacheWorker.KEY_BASE_URL, userEntity.baseUrl) + .putString(DownloadFileToCacheWorker.KEY_USER_ID, userEntity.userId) + .putString( + DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, + CapabilitiesUtil.getAttachmentFolder(userEntity) + ) .putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileName) .putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path) .putInt(DownloadFileToCacheWorker.KEY_FILE_SIZE, fileSize) @@ -220,7 +291,7 @@ class FileViewerUtils(private val context: Context) { .addTag(fileId) .build() WorkManager.getInstance().enqueue(downloadWorker) - progressBar.visibility = View.VISIBLE + progressBar?.visibility = View.VISIBLE WorkManager.getInstance(context).getWorkInfoByIdLiveData(downloadWorker.id) .observeForever { workInfo: WorkInfo? -> updateViewsByProgress( @@ -234,19 +305,19 @@ class FileViewerUtils(private val context: Context) { } } - fun updateViewsByProgress( + private fun updateViewsByProgress( fileName: String, mimetype: String, workInfo: WorkInfo, - progressBar: ProgressBar, - messageText: EmojiTextView, + progressBar: ProgressBar?, + messageText: EmojiTextView?, previewImage: SimpleDraweeView ) { when (workInfo.state) { WorkInfo.State.RUNNING -> { val progress = workInfo.progress.getInt(DownloadFileToCacheWorker.PROGRESS, -1) if (progress > -1) { - messageText.text = String.format( + messageText?.text = String.format( context.resources.getString(R.string.filename_progress), fileName, progress @@ -255,25 +326,66 @@ class FileViewerUtils(private val context: Context) { } WorkInfo.State.SUCCEEDED -> { if (previewImage.isShown) { - openFile(fileName, mimetype) + openFileByMimetype(fileName, mimetype) } else { - Log.d(TAG, "file " + fileName + + Log.d( + TAG, "file " + fileName + " was downloaded but it's not opened because view is not shown on screen" ) } - messageText.text = fileName - progressBar.visibility = View.GONE + messageText?.text = fileName + progressBar?.visibility = View.GONE } WorkInfo.State.FAILED -> { - messageText.text = fileName - progressBar.visibility = View.GONE + messageText?.text = fileName + progressBar?.visibility = View.GONE } else -> { } } } + fun resumeToUpdateViewsByProgress( + fileName: String, + fileId: String, + mimeType: String, + progressBar: ProgressBar, + messageText: EmojiTextView?, + previewImage: SimpleDraweeView + ) { + val workers = WorkManager.getInstance(context).getWorkInfosByTag(fileId) + + try { + for (workInfo in workers.get()) { + if (workInfo.state == WorkInfo.State.RUNNING || + workInfo.state == WorkInfo.State.ENQUEUED + ) { + progressBar.visibility = View.VISIBLE + WorkManager + .getInstance(context) + .getWorkInfoByIdLiveData(workInfo.id) + .observeForever { info: WorkInfo? -> + updateViewsByProgress( + fileName, + mimeType, + info!!, + progressBar, + messageText, + previewImage + ) + } + } + } + } catch (e: ExecutionException) { + Log.e(TAG, "Error when checking if worker already exists", e) + } catch (e: InterruptedException) { + Log.e(TAG, "Error when checking if worker already exists", e) + } + } + companion object { private val TAG = FileViewerUtils::class.simpleName + + const val KEY_ID = "id" } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index 25e0667a6..183187931 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -51,10 +51,15 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true) items[it.value.id] = SharedItem( - fileParameters["id"]!!, fileParameters["name"]!!, - fileParameters["mimetype"]!!, fileParameters["link"]!!, + fileParameters["id"]!!, + fileParameters["name"]!!, + fileParameters["size"]!!.toInt(), + fileParameters["path"]!!, + fileParameters["link"]!!, + fileParameters["mimetype"]!!, previewAvailable, - repository.previewLink(fileParameters["id"]) + repository.previewLink(fileParameters["id"]), + repository.parameters!!.userEntity ) } } @@ -84,6 +89,7 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View userEntity.userId, userEntity.token, userEntity.baseUrl, + userEntity, roomToken ) From e1703aac24a426b89d16c55429658fcb03db219f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Wed, 27 Apr 2022 15:26:28 +0200 Subject: [PATCH 10/55] Remove dark theme for app bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- app/src/main/res/layout/activity_shared_items.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index de4ca10dd..0fdd7a77d 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -29,9 +29,7 @@ android:id="@+id/shared_items_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" - android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" - app:layout_constraintTop_toTopOf="parent" - app:popupTheme="@style/ThemeOverlay.AppCompat.Dark" /> + app:layout_constraintTop_toTopOf="parent" /> Date: Wed, 27 Apr 2022 17:17:34 +0200 Subject: [PATCH 11/55] add fallback images for mimetypes if no preview image was found Signed-off-by: Marcel Hibbe --- .../talk/adapters/SharedItemsAdapter.kt | 50 +++++++++++++------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt index 119e41bef..2ef4331e5 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -10,6 +10,7 @@ import com.facebook.drawee.interfaces.DraweeController import com.facebook.drawee.view.SimpleDraweeView import com.facebook.imagepipeline.common.RotationOptions import com.facebook.imagepipeline.request.ImageRequestBuilder +import com.nextcloud.talk.R import com.nextcloud.talk.databinding.AttachmentItemBinding import com.nextcloud.talk.repositories.SharedItem import com.nextcloud.talk.utils.FileViewerUtils @@ -48,23 +49,42 @@ class SharedItemsAdapter : RecyclerView.Adapter() .setImageRequest(imageRequest) .build() holder.binding.image.controller = draweeController - - holder.binding.image.setOnClickListener { - val fileViewerUtils = FileViewerUtils(it.context, currentItem.userEntity) - - fileViewerUtils.openFile( - currentItem.id, - currentItem.name, - currentItem.fileSize, - currentItem.path, - currentItem.link, - currentItem.mimeType, - null, - null, - it as SimpleDraweeView - ) + } else { + when (currentItem.mimeType) { + "video/mp4", + "video/quicktime", + "video/ogg" + -> holder.binding.image.setImageResource(R.drawable.ic_mimetype_video) + "audio/mpeg", + "audio/wav", + "audio/ogg", + -> holder.binding.image.setImageResource(R.drawable.ic_mimetype_audio) + "image/png", + "image/jpeg", + "image/gif" + -> holder.binding.image.setImageResource(R.drawable.ic_mimetype_image) + "text/markdown", + "text/plain" + -> holder.binding.image.setImageResource(R.drawable.ic_mimetype_text) + else + -> holder.binding.image.setImageResource(R.drawable.ic_mimetype_file) } } + holder.binding.image.setOnClickListener { + val fileViewerUtils = FileViewerUtils(it.context, currentItem.userEntity) + + fileViewerUtils.openFile( + currentItem.id, + currentItem.name, + currentItem.fileSize, + currentItem.path, + currentItem.link, + currentItem.mimeType, + null, + null, + it as SimpleDraweeView + ) + } } override fun getItemCount(): Int { From cad7b4cb3d7d1a7face3f961f0943bbeb1fca2a5 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 27 Apr 2022 19:40:55 +0200 Subject: [PATCH 12/55] add tabLayout for file types (WIP) quick&dirty, needs to be improved and might contain bugs Signed-off-by: Marcel Hibbe --- .../talk/activities/SharedItemsActivity.kt | 53 +++++++++++++++++++ .../talk/adapters/SharedItemsAdapter.kt | 3 ++ .../repositories/SharedItemsRepository.kt | 4 +- .../talk/viewmodels/SharedItemsViewModel.kt | 6 +-- .../main/res/layout/activity_shared_items.xml | 14 ++++- 5 files changed, 74 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 2e08f4f65..1ef8bff34 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -5,8 +5,10 @@ import android.util.Log import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProvider +import com.google.android.material.tabs.TabLayout import com.nextcloud.talk.adapters.SharedItemsAdapter import com.nextcloud.talk.databinding.ActivitySharedItemsBinding +import com.nextcloud.talk.databinding.ItemReactionsTabBinding import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN @@ -32,20 +34,71 @@ class SharedItemsActivity : AppCompatActivity() { supportActionBar?.title = conversationName supportActionBar?.setDisplayHomeAsUpEnabled(true) + initTabs() + viewModel = ViewModelProvider( this, SharedItemsViewModel.Factory(userEntity, roomToken) ).get(SharedItemsViewModel::class.java) + updateItems("media") + viewModel.media.observe(this) { Log.d(TAG, "Items received: $it") val adapter = SharedItemsAdapter() adapter.items = it.items adapter.authHeader = it.authHeader binding.imageRecycler.adapter = adapter + adapter.notifyDataSetChanged() } } + fun updateItems(type: String){ + viewModel.loadMediaItems(type) + } + + private fun initTabs() { + val tabAudio: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabAudio.text = "audio" + binding.sharedItemsTabs.addTab(tabAudio) + + val tabDeckcard: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabDeckcard.text = "deckcard" + binding.sharedItemsTabs.addTab(tabDeckcard) + + val tabFile: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabFile.text = "files" + binding.sharedItemsTabs.addTab(tabFile) + + val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabLocation.text = "locations" + binding.sharedItemsTabs.addTab(tabLocation) + + val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabMedia.text = "media" + binding.sharedItemsTabs.addTab(tabMedia) + + val tabVoice: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabVoice.text = "voice" + binding.sharedItemsTabs.addTab(tabVoice) + + val tabOther: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabOther.text = "other" + binding.sharedItemsTabs.addTab(tabOther) + + binding.sharedItemsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + updateItems(tab.text.toString()) + } + + override fun onTabUnselected(tab: TabLayout.Tab) { + } + + override fun onTabReselected(tab: TabLayout.Tab) { + } + }) + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { return if (item.itemId == android.R.id.home) { onBackPressed() diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt index 2ef4331e5..19e147871 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -49,6 +49,9 @@ class SharedItemsAdapter : RecyclerView.Adapter() .setImageRequest(imageRequest) .build() holder.binding.image.controller = draweeController + + // } else if () { TODO check if voice message etc.. + } else { when (currentItem.mimeType) { "video/mp4", diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt index afb6f80e4..3a17b0502 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt @@ -27,13 +27,13 @@ class SharedItemsRepository { sharedApplication!!.componentApplication.inject(this) } - fun media(): Observable>? { + fun media(type: String): Observable>? { val credentials = ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken) return ncApi.getSharedItems( credentials, ApiUtils.getUrlForChatSharedItems(1, parameters!!.baseUrl, parameters!!.roomToken), - "media", null, null + type, null, null ) } diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index 183187931..e8de645a2 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -20,16 +20,16 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View private val _media: MutableLiveData by lazy { MutableLiveData().also { - loadMediaItems() + loadMediaItems("media") } } val media: LiveData get() = _media - private fun loadMediaItems() { + fun loadMediaItems(type: String) { - repository.media()?.subscribeOn(Schedulers.io()) + repository.media(type)?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe(object : Observer> { diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index 0fdd7a77d..4261b7d7c 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -31,10 +31,22 @@ android:layout_height="?attr/actionBarSize" app:layout_constraintTop_toTopOf="parent" /> + + + app:layout_constraintTop_toBottomOf="@+id/shared_items_tabs"> Date: Wed, 27 Apr 2022 23:25:06 +0200 Subject: [PATCH 13/55] properly theme bars Signed-off-by: Andy Scherzinger --- .../talk/activities/SharedItemsActivity.kt | 15 +++++++++++++++ app/src/main/res/layout/activity_shared_items.xml | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 1ef8bff34..fdf6e98ae 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -3,13 +3,17 @@ package com.nextcloud.talk.activities import android.os.Bundle import android.util.Log import android.view.MenuItem +import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.res.ResourcesCompat import androidx.lifecycle.ViewModelProvider import com.google.android.material.tabs.TabLayout +import com.nextcloud.talk.R import com.nextcloud.talk.adapters.SharedItemsAdapter import com.nextcloud.talk.databinding.ActivitySharedItemsBinding import com.nextcloud.talk.databinding.ItemReactionsTabBinding import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY @@ -31,6 +35,17 @@ class SharedItemsActivity : AppCompatActivity() { setSupportActionBar(binding.sharedItemsToolbar) setContentView(binding.root) + DisplayUtils.applyColorToStatusBar( + this, + ResourcesCompat.getColor( + resources, R.color.appbar, null + ) + ) + DisplayUtils.applyColorToNavigationBar( + this.window, + ResourcesCompat.getColor(resources, R.color.bg_default, null) + ) + supportActionBar?.title = conversationName supportActionBar?.setDisplayHomeAsUpEnabled(true) diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index 4261b7d7c..31cc3beaf 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -29,6 +29,13 @@ android:id="@+id/shared_items_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:background="@color/appbar" + android:theme="?attr/actionBarPopupTheme" + app:layout_scrollFlags="enterAlwaysCollapsed|noScroll" + app:navigationIconTint="@color/fontAppbar" + app:popupTheme="@style/appActionBarPopupMenu" + app:titleTextColor="@color/fontAppbar" + tools:title="@string/nc_app_product_name" app:layout_constraintTop_toTopOf="parent" /> Date: Wed, 27 Apr 2022 23:29:03 +0200 Subject: [PATCH 14/55] set correct background color (curerntly creates a new lint warning) Signed-off-by: Andy Scherzinger --- app/src/main/res/layout/activity_shared_items.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index 31cc3beaf..368d96f25 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -23,6 +23,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@color/bg_default" tools:context=".activities.SharedItemsActivity"> Date: Wed, 27 Apr 2022 23:33:32 +0200 Subject: [PATCH 15/55] visually add tab bar to the toolbar Signed-off-by: Andy Scherzinger --- app/src/main/res/layout/activity_shared_items.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index 368d96f25..a762ff69c 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -3,7 +3,9 @@ ~ Nextcloud Talk application ~ ~ @author Tim Krüger + ~ @author Andy Scherzinger ~ Copyright (C) 2022 Tim Krüger + ~ Copyright (C) 2022 Andy Scherzinger ~ ~ 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 @@ -44,9 +46,10 @@ android:layout_width="wrap_content" android:layout_height="@dimen/min_size_clickable_area" android:layout_marginBottom="8dp" - app:layout_constraintTop_toBottomOf="@id/shared_items_toolbar" + android:background="@color/appbar" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/shared_items_toolbar" app:tabGravity="fill" app:tabMode="scrollable" /> From 5854a93b0d47083c61f6c17a2a3cb2dac35e9d71 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 27 Apr 2022 23:47:46 +0200 Subject: [PATCH 16/55] remove unused string resources Signed-off-by: Andy Scherzinger --- app/src/main/res/values/strings.xml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8d06e03e2..fd83b64a3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -89,7 +89,6 @@ The server version is too old and not supported by this version of the Android app The server version is very old and will not be supported in the next release! Warning - Add Only current account can be reauthorized Talk app is not installed on the server you tried to authenticate against Your already existing account was updated, instead of adding a new one @@ -118,7 +117,6 @@ Lock %1$s with Android screen lock or supported biometric method screen_lock Screen lock inactivity timeout - None screen_lock_timeout Screen security Prevents screenshots in the recent list and inside the app @@ -205,8 +203,6 @@ INCOMING RINGING Connecting… - Calling… - Incoming call from Guest New public conversation Public conversations let you invite people from outside through a specially crafted link. @@ -338,7 +334,6 @@ Join a conversation or start a new one Say hi to your friends and colleagues! - Hello %s characters limit has been hit @@ -379,14 +374,6 @@ The meeting will start soon Not set - - No connection - Bad response - Timeout - Empty response - Unknown error - Unauthorized - Allow guests Could not leave conversation You need to promote a new moderator before you can leave %1$s. From cc2e0348e5d01d65a410ab180bdc4a4b1e41f8c0 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 28 Apr 2022 10:40:23 +0200 Subject: [PATCH 17/55] fix to show also files Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/activities/SharedItemsActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index fdf6e98ae..00639f997 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -82,11 +82,11 @@ class SharedItemsActivity : AppCompatActivity() { binding.sharedItemsTabs.addTab(tabDeckcard) val tabFile: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabFile.text = "files" + tabFile.text = "file" binding.sharedItemsTabs.addTab(tabFile) val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabLocation.text = "locations" + tabLocation.text = "location" binding.sharedItemsTabs.addTab(tabLocation) val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab() From 0ba74b1a303b49b6dccefc1460d474233b2763a9 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 28 Apr 2022 10:48:00 +0200 Subject: [PATCH 18/55] replace hardcoded value for file previews request maximum_file_preview_size instead hardcoded 100px for file previews but somehow this his no effect because many file previews are not found on sermo?! Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/repositories/SharedItemsRepository.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt index 3a17b0502..c6d26df2c 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt @@ -1,6 +1,7 @@ package com.nextcloud.talk.repositories import autodagger.AutoInjector +import com.nextcloud.talk.R import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication @@ -42,7 +43,10 @@ class SharedItemsRepository { } fun previewLink(fileId: String?): String { - return ApiUtils.getUrlForFilePreviewWithFileId(parameters!!.baseUrl, fileId, 100) + return ApiUtils.getUrlForFilePreviewWithFileId( + parameters!!.baseUrl, + fileId, + sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size)) } data class Parameters( From b22b600352899d8909abdf26d2590830637ad917 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 28 Apr 2022 10:58:27 +0200 Subject: [PATCH 19/55] initially load audio Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/activities/SharedItemsActivity.kt | 4 +--- .../nextcloud/talk/viewmodels/SharedItemsViewModel.kt | 9 +++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 00639f997..96334101b 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -53,11 +53,9 @@ class SharedItemsActivity : AppCompatActivity() { viewModel = ViewModelProvider( this, - SharedItemsViewModel.Factory(userEntity, roomToken) + SharedItemsViewModel.Factory(userEntity, roomToken, "audio") ).get(SharedItemsViewModel::class.java) - updateItems("media") - viewModel.media.observe(this) { Log.d(TAG, "Items received: $it") val adapter = SharedItemsAdapter() diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index e8de645a2..a64c49d2e 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -16,11 +16,11 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import retrofit2.Response -class SharedItemsViewModel(private val repository: SharedItemsRepository) : ViewModel() { +class SharedItemsViewModel(private val repository: SharedItemsRepository, val initialType: String) : ViewModel() { private val _media: MutableLiveData by lazy { MutableLiveData().also { - loadMediaItems("media") + loadMediaItems(initialType) } } @@ -79,7 +79,8 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View }) } - class Factory(val userEntity: UserEntity, val roomToken: String) : ViewModelProvider.Factory { + class Factory(val userEntity: UserEntity, val roomToken: String, private val initialType: String) : ViewModelProvider + .Factory { override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(SharedItemsViewModel::class.java)) { @@ -93,7 +94,7 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository) : View roomToken ) - return SharedItemsViewModel(repository) as T + return SharedItemsViewModel(repository, initialType) as T } throw IllegalArgumentException("Unknown ViewModel class") From fa90155195e3332244da4985c278c692756310b5 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 28 Apr 2022 11:40:03 +0200 Subject: [PATCH 20/55] hide location and deck for now because other fileParameters need to be handled Signed-off-by: Marcel Hibbe --- .../talk/activities/SharedItemsActivity.kt | 12 +++---- .../talk/viewmodels/SharedItemsViewModel.kt | 31 +++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 96334101b..6f1a80494 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -75,17 +75,17 @@ class SharedItemsActivity : AppCompatActivity() { tabAudio.text = "audio" binding.sharedItemsTabs.addTab(tabAudio) - val tabDeckcard: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabDeckcard.text = "deckcard" - binding.sharedItemsTabs.addTab(tabDeckcard) + // val tabDeckcard: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabDeckcard.text = "deckcard" + // binding.sharedItemsTabs.addTab(tabDeckcard) val tabFile: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabFile.text = "file" binding.sharedItemsTabs.addTab(tabFile) - val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabLocation.text = "location" - binding.sharedItemsTabs.addTab(tabLocation) + // val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabLocation.text = "location" + // binding.sharedItemsTabs.addTab(tabLocation) val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabMedia.text = "media" diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index a64c49d2e..78d1faa6a 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -46,21 +46,26 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository, val in val mediaItems = response.body()!!.ocs!!.data mediaItems?.forEach { - val fileParameters = it.value.messageParameters["file"]!! + if (it.value.messageParameters.containsKey("file")) { + val fileParameters = it.value.messageParameters["file"]!! - val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true) + val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true) + + items[it.value.id] = SharedItem( + fileParameters["id"]!!, + fileParameters["name"]!!, + fileParameters["size"]!!.toInt(), + fileParameters["path"]!!, + fileParameters["link"]!!, + fileParameters["mimetype"]!!, + previewAvailable, + repository.previewLink(fileParameters["id"]), + repository.parameters!!.userEntity + ) + } else { + Log.w(TAG, "location and deckcard are not yet supported") + } - items[it.value.id] = SharedItem( - fileParameters["id"]!!, - fileParameters["name"]!!, - fileParameters["size"]!!.toInt(), - fileParameters["path"]!!, - fileParameters["link"]!!, - fileParameters["mimetype"]!!, - previewAvailable, - repository.previewLink(fileParameters["id"]), - repository.parameters!!.userEntity - ) } } From 0e75337caf5b0df63b0174ce6a1015628020b401 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 28 Apr 2022 12:16:31 +0200 Subject: [PATCH 21/55] show items other than "media" as list (WIP) extract hardcoding Signed-off-by: Marcel Hibbe --- .../talk/activities/SharedItemsActivity.kt | 56 ++++++++++++++----- .../talk/viewmodels/SharedItemsViewModel.kt | 2 +- .../main/res/layout/activity_shared_items.xml | 3 +- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 6f1a80494..8d9fe305b 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -3,15 +3,15 @@ package com.nextcloud.talk.activities import android.os.Bundle import android.util.Log import android.view.MenuItem -import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.tabs.TabLayout import com.nextcloud.talk.R import com.nextcloud.talk.adapters.SharedItemsAdapter import com.nextcloud.talk.databinding.ActivitySharedItemsBinding -import com.nextcloud.talk.databinding.ItemReactionsTabBinding import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME @@ -20,13 +20,16 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY import com.nextcloud.talk.viewmodels.SharedItemsViewModel class SharedItemsActivity : AppCompatActivity() { - companion object { - private val TAG = SharedItemsActivity::class.simpleName - } + + private lateinit var binding: ActivitySharedItemsBinding + private lateinit var viewModel: SharedItemsViewModel + private lateinit var currentTab: String override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + currentTab = TAB_AUDIO + val roomToken = intent.getStringExtra(KEY_ROOM_TOKEN)!! val conversationName = intent.getStringExtra(KEY_CONVERSATION_NAME) val userEntity = intent.getParcelableExtra(KEY_USER_ENTITY)!! @@ -53,7 +56,7 @@ class SharedItemsActivity : AppCompatActivity() { viewModel = ViewModelProvider( this, - SharedItemsViewModel.Factory(userEntity, roomToken, "audio") + SharedItemsViewModel.Factory(userEntity, roomToken, currentTab) ).get(SharedItemsViewModel::class.java) viewModel.media.observe(this) { @@ -62,46 +65,64 @@ class SharedItemsActivity : AppCompatActivity() { adapter.items = it.items adapter.authHeader = it.authHeader binding.imageRecycler.adapter = adapter + + if (currentTab == "media") { + val layoutManager = GridLayoutManager(this, 4) + binding.imageRecycler.layoutManager = layoutManager + } else { + val layoutManager = LinearLayoutManager(this) + layoutManager.orientation = LinearLayoutManager.VERTICAL + binding.imageRecycler.layoutManager = layoutManager + } + adapter.notifyDataSetChanged() } } - fun updateItems(type: String){ + fun updateItems(type: String) { + currentTab = type viewModel.loadMediaItems(type) } private fun initTabs() { val tabAudio: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabAudio.tag = TAB_AUDIO tabAudio.text = "audio" binding.sharedItemsTabs.addTab(tabAudio) - // val tabDeckcard: TabLayout.Tab = binding.sharedItemsTabs.newTab() - // tabDeckcard.text = "deckcard" - // binding.sharedItemsTabs.addTab(tabDeckcard) + // val tabDeckCard: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabDeckCard.tag = TAB_DECKCARD + // tabDeckCard.text = "deckcard" + // binding.sharedItemsTabs.addTab(tabDeckCard) val tabFile: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabFile.tag = TAB_FILE tabFile.text = "file" binding.sharedItemsTabs.addTab(tabFile) // val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabLocation.tag = TAB_LOCATION // tabLocation.text = "location" // binding.sharedItemsTabs.addTab(tabLocation) val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabMedia.tag = TAB_MEDIA tabMedia.text = "media" binding.sharedItemsTabs.addTab(tabMedia) val tabVoice: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabVoice.tag = TAB_VOICE tabVoice.text = "voice" binding.sharedItemsTabs.addTab(tabVoice) val tabOther: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabOther.tag = TAB_OTHER tabOther.text = "other" binding.sharedItemsTabs.addTab(tabOther) binding.sharedItemsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab) { - updateItems(tab.text.toString()) + updateItems(tab.tag as String) } override fun onTabUnselected(tab: TabLayout.Tab) { @@ -121,7 +142,14 @@ class SharedItemsActivity : AppCompatActivity() { } } - private lateinit var binding: ActivitySharedItemsBinding - - private lateinit var viewModel: SharedItemsViewModel + companion object { + private val TAG = SharedItemsActivity::class.simpleName + const val TAB_AUDIO = "audio" + const val TAB_FILE = "file" + const val TAB_MEDIA = "media" + const val TAB_VOICE = "voice" + const val TAB_LOCATION = "location" + const val TAB_DECKCARD = "deckcard" + const val TAB_OTHER = "other" + } } diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index 78d1faa6a..6a90ef67c 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -16,7 +16,7 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import retrofit2.Response -class SharedItemsViewModel(private val repository: SharedItemsRepository, val initialType: String) : ViewModel() { +class SharedItemsViewModel(private val repository: SharedItemsRepository, private val initialType: String) : ViewModel() { private val _media: MutableLiveData by lazy { MutableLiveData().also { diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index a762ff69c..0af20136c 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -63,8 +63,7 @@ android:id="@+id/image_recycler" android:layout_width="match_parent" android:layout_height="match_parent" - app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" - app:spanCount="4" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" tools:listitem="@layout/attachment_item" /> From 52b257d027d2b88da1fe96ea074c2affd785ae0e Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 28 Apr 2022 12:19:58 +0200 Subject: [PATCH 22/55] define other order of tabs Signed-off-by: Marcel Hibbe --- .../talk/activities/SharedItemsActivity.kt | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 8d9fe305b..f538dd500 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -28,7 +28,7 @@ class SharedItemsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - currentTab = TAB_AUDIO + currentTab = TAB_MEDIA val roomToken = intent.getStringExtra(KEY_ROOM_TOKEN)!! val conversationName = intent.getStringExtra(KEY_CONVERSATION_NAME) @@ -85,36 +85,36 @@ class SharedItemsActivity : AppCompatActivity() { } private fun initTabs() { - val tabAudio: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabAudio.tag = TAB_AUDIO - tabAudio.text = "audio" - binding.sharedItemsTabs.addTab(tabAudio) - - // val tabDeckCard: TabLayout.Tab = binding.sharedItemsTabs.newTab() - // tabDeckCard.tag = TAB_DECKCARD - // tabDeckCard.text = "deckcard" - // binding.sharedItemsTabs.addTab(tabDeckCard) + val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabMedia.tag = TAB_MEDIA + tabMedia.text = "media" + binding.sharedItemsTabs.addTab(tabMedia) val tabFile: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabFile.tag = TAB_FILE tabFile.text = "file" binding.sharedItemsTabs.addTab(tabFile) - // val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() - // tabLocation.tag = TAB_LOCATION - // tabLocation.text = "location" - // binding.sharedItemsTabs.addTab(tabLocation) - - val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabMedia.tag = TAB_MEDIA - tabMedia.text = "media" - binding.sharedItemsTabs.addTab(tabMedia) + val tabAudio: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabAudio.tag = TAB_AUDIO + tabAudio.text = "audio" + binding.sharedItemsTabs.addTab(tabAudio) val tabVoice: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabVoice.tag = TAB_VOICE tabVoice.text = "voice" binding.sharedItemsTabs.addTab(tabVoice) + // val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabLocation.tag = TAB_LOCATION + // tabLocation.text = "location" + // binding.sharedItemsTabs.addTab(tabLocation) + + // val tabDeckCard: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabDeckCard.tag = TAB_DECKCARD + // tabDeckCard.text = "deckcard" + // binding.sharedItemsTabs.addTab(tabDeckCard) + val tabOther: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabOther.tag = TAB_OTHER tabOther.text = "other" From b304e8facb258c3a3ae5fef43a595f39e1d14aa7 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 28 Apr 2022 13:44:13 +0200 Subject: [PATCH 23/55] make shared items tabs translatable Signed-off-by: Andy Scherzinger --- .../talk/activities/SharedItemsActivity.kt | 12 +++++++----- app/src/main/res/values/strings.xml | 13 ++++++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index f538dd500..827d16f3d 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -87,22 +87,22 @@ class SharedItemsActivity : AppCompatActivity() { private fun initTabs() { val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabMedia.tag = TAB_MEDIA - tabMedia.text = "media" + tabMedia.setText(R.string.shared_items_media) binding.sharedItemsTabs.addTab(tabMedia) val tabFile: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabFile.tag = TAB_FILE - tabFile.text = "file" + tabFile.setText(R.string.shared_items_file) binding.sharedItemsTabs.addTab(tabFile) val tabAudio: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabAudio.tag = TAB_AUDIO - tabAudio.text = "audio" + tabAudio.setText(R.string.shared_items_audio) binding.sharedItemsTabs.addTab(tabAudio) val tabVoice: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabVoice.tag = TAB_VOICE - tabVoice.text = "voice" + tabVoice.setText(R.string.shared_items_voice) binding.sharedItemsTabs.addTab(tabVoice) // val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() @@ -117,7 +117,7 @@ class SharedItemsActivity : AppCompatActivity() { val tabOther: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabOther.tag = TAB_OTHER - tabOther.text = "other" + tabOther.setText(R.string.shared_items_other) binding.sharedItemsTabs.addTab(tabOther) binding.sharedItemsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { @@ -126,9 +126,11 @@ class SharedItemsActivity : AppCompatActivity() { } override fun onTabUnselected(tab: TabLayout.Tab) { + // unused atm } override fun onTabReselected(tab: TabLayout.Tab) { + // unused atm } }) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fd83b64a3..f2249b863 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -488,6 +488,7 @@ Invalid password Do you want to reauthorize or delete this account? + Take a photo Switch camera Re-take photo @@ -497,12 +498,22 @@ Send Error taking picture Taking a photo is not possible without permissions + + Bluetooth Speaker Phone Audio output Wired headset - Attachements + + + Media + File + Audio + Voice + Other + + Attachments All From 745fc651170fb93fbdb5b54a523e1d755ced80b6 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 28 Apr 2022 13:52:29 +0200 Subject: [PATCH 24/55] make tab labels non-allCaps Signed-off-by: Andy Scherzinger --- app/src/main/res/layout/activity_shared_items.xml | 3 ++- app/src/main/res/values/styles.xml | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index 0af20136c..486b79e57 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -51,7 +51,8 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/shared_items_toolbar" app:tabGravity="fill" - app:tabMode="scrollable" /> + app:tabMode="scrollable" + app:tabTextAppearance="@style/TextAppearanceTab" /> bold + + + From 29f5d73f05271aa5d5072035d777069efff15f37 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 28 Apr 2022 14:29:07 +0200 Subject: [PATCH 25/55] add adapter + design for list view Signed-off-by: Marcel Hibbe --- .../talk/activities/SharedItemsActivity.kt | 23 +++-- .../talk/adapters/SharedItemsAdapter.kt | 2 - .../talk/adapters/SharedItemsListAdapter.kt | 94 +++++++++++++++++++ .../main/res/layout/attachment_list_item.xml | 72 ++++++++++++++ app/src/main/res/values/dimens.xml | 1 + 5 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt create mode 100644 app/src/main/res/layout/attachment_list_item.xml diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 827d16f3d..d611f575f 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -11,6 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.tabs.TabLayout import com.nextcloud.talk.R import com.nextcloud.talk.adapters.SharedItemsAdapter +import com.nextcloud.talk.adapters.SharedItemsListAdapter import com.nextcloud.talk.databinding.ActivitySharedItemsBinding import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.utils.DisplayUtils @@ -61,21 +62,29 @@ class SharedItemsActivity : AppCompatActivity() { viewModel.media.observe(this) { Log.d(TAG, "Items received: $it") - val adapter = SharedItemsAdapter() - adapter.items = it.items - adapter.authHeader = it.authHeader - binding.imageRecycler.adapter = adapter - if (currentTab == "media") { + if (currentTab == TAB_MEDIA) { + val adapter = SharedItemsAdapter() + adapter.items = it.items + adapter.authHeader = it.authHeader + binding.imageRecycler.adapter = adapter + val layoutManager = GridLayoutManager(this, 4) binding.imageRecycler.layoutManager = layoutManager + + adapter.notifyDataSetChanged() } else { + val adapter = SharedItemsListAdapter() + adapter.items = it.items + adapter.authHeader = it.authHeader + binding.imageRecycler.adapter = adapter + val layoutManager = LinearLayoutManager(this) layoutManager.orientation = LinearLayoutManager.VERTICAL binding.imageRecycler.layoutManager = layoutManager - } - adapter.notifyDataSetChanged() + adapter.notifyDataSetChanged() + } } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt index 19e147871..e9031f99b 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -50,8 +50,6 @@ class SharedItemsAdapter : RecyclerView.Adapter() .build() holder.binding.image.controller = draweeController - // } else if () { TODO check if voice message etc.. - } else { when (currentItem.mimeType) { "video/mp4", diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt new file mode 100644 index 000000000..6ed597a22 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt @@ -0,0 +1,94 @@ +package com.nextcloud.talk.adapters + +import android.net.Uri +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.interfaces.DraweeController +import com.facebook.imagepipeline.common.RotationOptions +import com.facebook.imagepipeline.request.ImageRequestBuilder +import com.nextcloud.talk.R +import com.nextcloud.talk.databinding.AttachmentListItemBinding +import com.nextcloud.talk.repositories.SharedItem +import com.nextcloud.talk.utils.FileViewerUtils + +class SharedItemsListAdapter : RecyclerView.Adapter() { + + companion object { + private val TAG = SharedItemsListAdapter::class.simpleName + } + + class ViewHolder(val binding: AttachmentListItemBinding, itemView: View) : RecyclerView.ViewHolder(itemView) + + var authHeader: Map = emptyMap() + var items: List = emptyList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val binding = AttachmentListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return ViewHolder(binding, binding.root) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + + val currentItem = items[position] + + holder.binding.fileName.text = currentItem.name + + if (currentItem.previewAvailable) { + val imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(currentItem.previewLink)) + .setProgressiveRenderingEnabled(true) + .setRotationOptions(RotationOptions.autoRotate()) + .disableDiskCache() + .setHeaders(authHeader) + .build() + + val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() + .setOldController(holder.binding.fileImage.controller) + .setAutoPlayAnimations(true) + .setImageRequest(imageRequest) + .build() + holder.binding.fileImage.controller = draweeController + } else { + when (currentItem.mimeType) { + "video/mp4", + "video/quicktime", + "video/ogg" + -> holder.binding.fileImage.setImageResource(R.drawable.ic_mimetype_video) + "audio/mpeg", + "audio/wav", + "audio/ogg", + -> holder.binding.fileImage.setImageResource(R.drawable.ic_mimetype_audio) + "image/png", + "image/jpeg", + "image/gif" + -> holder.binding.fileImage.setImageResource(R.drawable.ic_mimetype_image) + "text/markdown", + "text/plain" + -> holder.binding.fileImage.setImageResource(R.drawable.ic_mimetype_text) + else + -> holder.binding.fileImage.setImageResource(R.drawable.ic_mimetype_file) + } + } + holder.binding.fileItem.setOnClickListener { + val fileViewerUtils = FileViewerUtils(it.context, currentItem.userEntity) + + fileViewerUtils.openFile( + currentItem.id, + currentItem.name, + currentItem.fileSize, + currentItem.path, + currentItem.link, + currentItem.mimeType, + null, + null, + holder.binding.fileImage + ) + } + } + + override fun getItemCount(): Int { + return items.size + } +} diff --git a/app/src/main/res/layout/attachment_list_item.xml b/app/src/main/res/layout/attachment_list_item.xml new file mode 100644 index 000000000..59e790a21 --- /dev/null +++ b/app/src/main/res/layout/attachment_list_item.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index fd5f467b9..a37207455 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -35,6 +35,7 @@ 40dp 30dp 96dp + 40dp 14sp 6dp From 9edffc5e64d5192e8bb513be29837d4d6c6602c2 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 28 Apr 2022 15:42:55 +0200 Subject: [PATCH 26/55] add progress bar for media items Signed-off-by: Marcel Hibbe --- .../talk/adapters/SharedItemsAdapter.kt | 2 +- app/src/main/res/layout/attachment_item.xml | 50 ++++++++++++++----- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt index e9031f99b..07cf78af6 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -81,7 +81,7 @@ class SharedItemsAdapter : RecyclerView.Adapter() currentItem.path, currentItem.link, currentItem.mimeType, - null, + holder.binding.progressBar, null, it as SimpleDraweeView ) diff --git a/app/src/main/res/layout/attachment_item.xml b/app/src/main/res/layout/attachment_item.xml index 8e9adf731..fdc89bfc5 100644 --- a/app/src/main/res/layout/attachment_item.xml +++ b/app/src/main/res/layout/attachment_item.xml @@ -2,7 +2,9 @@ ~ Nextcloud Talk application ~ ~ @author Tim Krüger + ~ @author Marcel Hibbe ~ Copyright (C) 2022 Tim Krüger + ~ Copyright (C) 2022 Marcel Hibbe ~ ~ 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 @@ -17,17 +19,41 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - + android:layout_height="wrap_content" + android:layout_marginStart="2dp" + android:layout_marginEnd="2dp" + android:adjustViewBounds="true" + app:layout_alignSelf="flex_start" + app:layout_flexGrow="1" + app:layout_wrapBefore="true"> + + + + + + + From d92f5546e9bc4ad870aa6cde6412928599da6174 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 28 Apr 2022 15:56:23 +0200 Subject: [PATCH 27/55] add progress bar for other items than media Signed-off-by: Marcel Hibbe --- .../talk/adapters/SharedItemsListAdapter.kt | 2 +- .../main/res/layout/attachment_list_item.xml | 50 +++++++++++++------ 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt index 6ed597a22..7768d59bb 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt @@ -81,7 +81,7 @@ class SharedItemsListAdapter : RecyclerView.Adapter - + + + + + + Date: Thu, 28 Apr 2022 17:09:26 +0200 Subject: [PATCH 28/55] Load shared items while scrolling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- .../talk/activities/SharedItemsActivity.kt | 26 ++-- .../repositories/SharedItemsRepository.kt | 6 +- .../talk/repositories/SharedMediaItems.kt | 6 +- .../talk/viewmodels/SharedItemsViewModel.kt | 125 +++++++++++------- .../main/res/layout/activity_shared_items.xml | 28 ++-- app/src/main/res/layout/attachment_item.xml | 63 +++++---- 6 files changed, 148 insertions(+), 106 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index d611f575f..f9661d72a 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -8,6 +8,7 @@ import androidx.core.content.res.ResourcesCompat import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.google.android.material.tabs.TabLayout import com.nextcloud.talk.R import com.nextcloud.talk.adapters.SharedItemsAdapter @@ -60,7 +61,7 @@ class SharedItemsActivity : AppCompatActivity() { SharedItemsViewModel.Factory(userEntity, roomToken, currentTab) ).get(SharedItemsViewModel::class.java) - viewModel.media.observe(this) { + viewModel.sharedItems.observe(this) { Log.d(TAG, "Items received: $it") if (currentTab == TAB_MEDIA) { @@ -71,8 +72,6 @@ class SharedItemsActivity : AppCompatActivity() { val layoutManager = GridLayoutManager(this, 4) binding.imageRecycler.layoutManager = layoutManager - - adapter.notifyDataSetChanged() } else { val adapter = SharedItemsListAdapter() adapter.items = it.items @@ -82,15 +81,22 @@ class SharedItemsActivity : AppCompatActivity() { val layoutManager = LinearLayoutManager(this) layoutManager.orientation = LinearLayoutManager.VERTICAL binding.imageRecycler.layoutManager = layoutManager - - adapter.notifyDataSetChanged() } } + + binding.imageRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (!recyclerView.canScrollVertically(1) && newState == RecyclerView.SCROLL_STATE_IDLE) { + viewModel.loadNextItems() + } + } + }) } fun updateItems(type: String) { currentTab = type - viewModel.loadMediaItems(type) + viewModel.loadItems(type) } private fun initTabs() { @@ -134,13 +140,9 @@ class SharedItemsActivity : AppCompatActivity() { updateItems(tab.tag as String) } - override fun onTabUnselected(tab: TabLayout.Tab) { - // unused atm - } + override fun onTabUnselected(tab: TabLayout.Tab) = Unit - override fun onTabReselected(tab: TabLayout.Tab) { - // unused atm - } + override fun onTabReselected(tab: TabLayout.Tab) = Unit }) } diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt index c6d26df2c..d908c9ee4 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt @@ -29,12 +29,16 @@ class SharedItemsRepository { } fun media(type: String): Observable>? { + return media(type, null) + } + + fun media(type: String, lastKnownMessageId: Int?): Observable>? { val credentials = ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken) return ncApi.getSharedItems( credentials, ApiUtils.getUrlForChatSharedItems(1, parameters!!.baseUrl, parameters!!.roomToken), - type, null, null + type, lastKnownMessageId, 28 ) } diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt index f83a5c6ea..1e3b56bae 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt @@ -1,7 +1,9 @@ package com.nextcloud.talk.repositories class SharedMediaItems( - val items: List, - val lastSeenId: String, + val type: String, + val items: MutableList, + var lastSeenId: Int?, + var moreItemsExisting: Boolean, val authHeader: Map ) diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index 6a90ef67c..39af56a00 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -16,76 +16,109 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import retrofit2.Response -class SharedItemsViewModel(private val repository: SharedItemsRepository, private val initialType: String) : ViewModel() { +class SharedItemsViewModel(private val repository: SharedItemsRepository, private val initialType: String) : + ViewModel() { - private val _media: MutableLiveData by lazy { + private val _sharedItems: MutableLiveData by lazy { MutableLiveData().also { - loadMediaItems(initialType) + loadItems(initialType) } } - val media: LiveData - get() = _media + val sharedItems: LiveData + get() = _sharedItems - fun loadMediaItems(type: String) { + fun loadNextItems() { + val currentSharedItems = sharedItems.value!! + if (currentSharedItems.moreItemsExisting) { + repository.media(currentSharedItems.type, currentSharedItems.lastSeenId)?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(observer(currentSharedItems.type, false)) + } + } + + fun loadItems(type: String) { repository.media(type)?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer> { + ?.subscribe(observer(type, true)) + } - var chatLastGiven: String = "" - val items = mutableMapOf() + private fun observer(type: String, initModel: Boolean): Observer> { + return object : Observer> { - override fun onSubscribe(d: Disposable) = Unit + var chatLastGiven: Int? = null + val items = mutableMapOf() - override fun onNext(response: Response) { + override fun onSubscribe(d: Disposable) = Unit - if (response.headers()["x-chat-last-given"] != null) { - chatLastGiven = response.headers()["x-chat-last-given"]!! - } + override fun onNext(response: Response) { - val mediaItems = response.body()!!.ocs!!.data - mediaItems?.forEach { - if (it.value.messageParameters.containsKey("file")) { - val fileParameters = it.value.messageParameters["file"]!! - - val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true) - - items[it.value.id] = SharedItem( - fileParameters["id"]!!, - fileParameters["name"]!!, - fileParameters["size"]!!.toInt(), - fileParameters["path"]!!, - fileParameters["link"]!!, - fileParameters["mimetype"]!!, - previewAvailable, - repository.previewLink(fileParameters["id"]), - repository.parameters!!.userEntity - ) - } else { - Log.w(TAG, "location and deckcard are not yet supported") - } - - } + if (response.headers()["x-chat-last-given"] != null) { + chatLastGiven = response.headers()["x-chat-last-given"]!!.toInt() } - override fun onError(e: Throwable) { - Log.d(TAG, "An error occurred: $e") - } + val mediaItems = response.body()!!.ocs!!.data + mediaItems?.forEach { + if (it.value.messageParameters.containsKey("file")) { + val fileParameters = it.value.messageParameters["file"]!! - override fun onComplete() { - this@SharedItemsViewModel._media.value = + val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true) + + items[it.value.id] = SharedItem( + fileParameters["id"]!!, + fileParameters["name"]!!, + fileParameters["size"]!!.toInt(), + fileParameters["path"]!!, + fileParameters["link"]!!, + fileParameters["mimetype"]!!, + previewAvailable, + repository.previewLink(fileParameters["id"]), + repository.parameters!!.userEntity + ) + } else { + Log.w(TAG, "location and deckcard are not yet supported") + } + + } + } + + override fun onError(e: Throwable) { + Log.d(TAG, "An error occurred: $e") + } + + override fun onComplete() { + + val sortedMutableItems = items.toSortedMap().values.toList().reversed().toMutableList() + val moreItemsExisting = items.count() == 28 + + if (initModel) { + this@SharedItemsViewModel._sharedItems.value = SharedMediaItems( - items.toSortedMap().values.toList().reversed(), + type, + sortedMutableItems, chatLastGiven, + moreItemsExisting, + repository.authHeader() + ) + } else { + val oldItems = this@SharedItemsViewModel._sharedItems.value!!.items + this@SharedItemsViewModel._sharedItems.value = + SharedMediaItems( + type, + (oldItems.toMutableList() + sortedMutableItems) as MutableList, + chatLastGiven, + moreItemsExisting, repository.authHeader() ) } - }) + } + } } - class Factory(val userEntity: UserEntity, val roomToken: String, private val initialType: String) : ViewModelProvider - .Factory { + class Factory(val userEntity: UserEntity, val roomToken: String, private val initialType: String) : + ViewModelProvider + .Factory { override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(SharedItemsViewModel::class.java)) { diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index 486b79e57..d6ababc24 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -1,5 +1,4 @@ - - - + android:layout_height="wrap_content"> - + app:layout_flexGrow="1" + app:layout_wrapBefore="true"> - - + + + + + \ No newline at end of file From 6b671191e9b3599fdd93eb3518be031264b0d46a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Thu, 28 Apr 2022 17:14:10 +0200 Subject: [PATCH 29/55] Fix ktlint warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- .../talk/adapters/SharedItemsAdapter.kt | 1 - .../talk/repositories/SharedItemsRepository.kt | 3 ++- .../nextcloud/talk/utils/FileViewerUtils.kt | 18 ++++++++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt index 07cf78af6..c18ec7b61 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -49,7 +49,6 @@ class SharedItemsAdapter : RecyclerView.Adapter() .setImageRequest(imageRequest) .build() holder.binding.image.controller = draweeController - } else { when (currentItem.mimeType) { "video/mp4", diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt index d908c9ee4..900946724 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt @@ -50,7 +50,8 @@ class SharedItemsRepository { return ApiUtils.getUrlForFilePreviewWithFileId( parameters!!.baseUrl, fileId, - sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size)) + sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size) + ) } data class Parameters( diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt index b7aabc628..4b3638956 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt @@ -10,10 +10,12 @@ import android.util.Log import android.view.View import android.widget.ProgressBar import androidx.core.content.FileProvider +import androidx.emoji.widget.EmojiTextView import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.WorkInfo import androidx.work.WorkManager +import com.facebook.drawee.view.SimpleDraweeView import com.nextcloud.talk.R import com.nextcloud.talk.activities.FullScreenImageActivity import com.nextcloud.talk.activities.FullScreenMediaActivity @@ -21,13 +23,11 @@ import com.nextcloud.talk.activities.FullScreenTextViewerActivity import com.nextcloud.talk.adapters.messages.MagicPreviewMessageViewHolder import com.nextcloud.talk.jobs.DownloadFileToCacheWorker import com.nextcloud.talk.models.database.CapabilitiesUtil +import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.utils.AccountUtils.canWeOpenFilesApp import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACCOUNT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_ID -import androidx.emoji.widget.EmojiTextView -import com.facebook.drawee.view.SimpleDraweeView -import com.nextcloud.talk.models.database.UserEntity import java.io.File import java.util.concurrent.ExecutionException @@ -230,7 +230,12 @@ class FileViewerUtils(private val context: Context, private val userEntity: User fun isSupportedForInternalViewer(mimetype: String?): Boolean { return when (mimetype) { - "image/png", "image/jpeg", "image/gif", "audio/mpeg", "audio/wav", "audio/ogg", "video/mp4", "video/quicktime", "video/ogg", "text/markdown", "text/plain" -> true + "image/png", "image/jpeg", + "image/gif", "audio/mpeg", + "audio/wav", "audio/ogg", + "video/mp4", "video/quicktime", + "video/ogg", "text/markdown", + "text/plain" -> true else -> false } } @@ -329,7 +334,8 @@ class FileViewerUtils(private val context: Context, private val userEntity: User openFileByMimetype(fileName, mimetype) } else { Log.d( - TAG, "file " + fileName + + TAG, + "file " + fileName + " was downloaded but it's not opened because view is not shown on screen" ) } @@ -388,4 +394,4 @@ class FileViewerUtils(private val context: Context, private val userEntity: User const val KEY_ID = "id" } -} \ No newline at end of file +} From 000184a338f5042e06a4944edfa592bdb4b508b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Thu, 28 Apr 2022 17:37:22 +0200 Subject: [PATCH 30/55] Remove lint error for using 'Hashmap#forEach' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Call requires API level 24 (current min is 21): java.util.HashMap#forEach Signed-off-by: Tim Krüger --- .../java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index 39af56a00..9c4e2b532 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -59,7 +59,7 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository, privat } val mediaItems = response.body()!!.ocs!!.data - mediaItems?.forEach { + for (it in mediaItems) { if (it.value.messageParameters.containsKey("file")) { val fileParameters = it.value.messageParameters["file"]!! @@ -79,7 +79,6 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository, privat } else { Log.w(TAG, "location and deckcard are not yet supported") } - } } From 6f277cbe92e4a9b3759763fb727d2732c4aa0a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Thu, 28 Apr 2022 17:57:30 +0200 Subject: [PATCH 31/55] Fix new introduced SpotBugs error and warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- .../com/nextcloud/talk/models/json/chat/ChatShareOCS.java | 5 ++--- .../nextcloud/talk/models/json/chat/ChatShareOverall.java | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOCS.java index e869c5ee0..6f0191037 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOCS.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOCS.java @@ -30,7 +30,7 @@ import java.util.Objects; @Parcel @JsonObject -public class ChatShareOCS extends GenericOCS { +public class ChatShareOCS { @JsonField(name = "data") public HashMap data; @@ -67,8 +67,7 @@ public class ChatShareOCS extends GenericOCS { final int PRIME = 59; int result = 1; final Object $data = this.getData(); - result = result * PRIME + ($data == null ? 43 : $data.hashCode()); - return result; + return result * PRIME + ($data == null ? 43 : $data.hashCode()); } public String toString() { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverall.java index b489e747a..ce97b53e6 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverall.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverall.java @@ -66,8 +66,7 @@ public class ChatShareOverall { final int PRIME = 59; int result = 1; final Object $ocs = this.getOcs(); - result = result * PRIME + ($ocs == null ? 43 : $ocs.hashCode()); - return result; + return result * PRIME + ($ocs == null ? 43 : $ocs.hashCode()); } public String toString() { From f5d70dab0c09435c77346415b09f580ebfa30102 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 28 Apr 2022 18:36:34 +0200 Subject: [PATCH 32/55] mediaItems can be null Signed-off-by: Andy Scherzinger --- .../talk/viewmodels/SharedItemsViewModel.kt | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index 9c4e2b532..cddf49407 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -59,25 +59,28 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository, privat } val mediaItems = response.body()!!.ocs!!.data - for (it in mediaItems) { - if (it.value.messageParameters.containsKey("file")) { - val fileParameters = it.value.messageParameters["file"]!! + if (mediaItems != null) { + for (it in mediaItems) { + if (it.value.messageParameters.containsKey("file")) { + val fileParameters = it.value.messageParameters["file"]!! - val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true) + val previewAvailable = + "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true) - items[it.value.id] = SharedItem( - fileParameters["id"]!!, - fileParameters["name"]!!, - fileParameters["size"]!!.toInt(), - fileParameters["path"]!!, - fileParameters["link"]!!, - fileParameters["mimetype"]!!, - previewAvailable, - repository.previewLink(fileParameters["id"]), - repository.parameters!!.userEntity - ) - } else { - Log.w(TAG, "location and deckcard are not yet supported") + items[it.value.id] = SharedItem( + fileParameters["id"]!!, + fileParameters["name"]!!, + fileParameters["size"]!!.toInt(), + fileParameters["path"]!!, + fileParameters["link"]!!, + fileParameters["mimetype"]!!, + previewAvailable, + repository.previewLink(fileParameters["id"]), + repository.parameters!!.userEntity + ) + } else { + Log.w(TAG, "location and deckcard are not yet supported") + } } } } From 941d35e03fb526e9b06e354343a32ae79ef5ce16 Mon Sep 17 00:00:00 2001 From: drone Date: Thu, 28 Apr 2022 17:23:36 +0000 Subject: [PATCH 33/55] Drone: update Lint results to reflect reduced error/warning count [skip ci] Signed-off-by: drone --- scripts/analysis/lint-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index bf6bfba1f..5a424f167 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 1 error and 156 warnings + Lint Report: 1 error and 150 warnings From 48c7ccb4b09670d584ea8e1375d7aeec25e1c9fe Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 28 Apr 2022 21:02:19 +0200 Subject: [PATCH 34/55] improve detekt score Signed-off-by: Andy Scherzinger --- .../talk/activities/SharedItemsActivity.kt | 3 +- .../talk/adapters/SharedItemsAdapter.kt | 8 +- .../talk/adapters/SharedItemsListAdapter.kt | 8 +- .../MagicPreviewMessageViewHolder.java | 13 +-- .../repositories/SharedItemsRepository.kt | 12 ++- .../nextcloud/talk/utils/FileViewerUtils.kt | 94 ++++++++++--------- .../talk/viewmodels/SharedItemsViewModel.kt | 3 +- 7 files changed, 76 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index f9661d72a..b42d8a743 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -70,7 +70,7 @@ class SharedItemsActivity : AppCompatActivity() { adapter.authHeader = it.authHeader binding.imageRecycler.adapter = adapter - val layoutManager = GridLayoutManager(this, 4) + val layoutManager = GridLayoutManager(this, SPAN_COUNT) binding.imageRecycler.layoutManager = layoutManager } else { val adapter = SharedItemsListAdapter() @@ -164,5 +164,6 @@ class SharedItemsActivity : AppCompatActivity() { const val TAB_LOCATION = "location" const val TAB_DECKCARD = "deckcard" const val TAB_OTHER = "other" + const val SPAN_COUNT: Int = 4 } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt index c18ec7b61..6a685df17 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -80,9 +80,11 @@ class SharedItemsAdapter : RecyclerView.Adapter() currentItem.path, currentItem.link, currentItem.mimeType, - holder.binding.progressBar, - null, - it as SimpleDraweeView + FileViewerUtils.ProgressUi( + holder.binding.progressBar, + null, + it as SimpleDraweeView + ) ) } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt index 7768d59bb..431dd8fe9 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt @@ -81,9 +81,11 @@ class SharedItemsListAdapter : RecyclerView.Adapter { - fileViewerUtils.openFile(message, progressBar, getMessageText(), image); - }); + clickView.setOnClickListener(v -> + fileViewerUtils.openFile( + message, + new FileViewerUtils.ProgressUi(progressBar, getMessageText(), image) + ) + ); clickView.setOnLongClickListener(l -> { onMessageViewLongClick(message); @@ -197,9 +200,7 @@ public abstract class MagicPreviewMessageViewHolder extends MessageHolders.Incom Objects.requireNonNull(message.getSelectedIndividualHashMap().get(MagicPreviewMessageViewHolder.KEY_NAME)), Objects.requireNonNull(message.getSelectedIndividualHashMap().get(MagicPreviewMessageViewHolder.KEY_ID)), Objects.requireNonNull(message.getSelectedIndividualHashMap().get(MagicPreviewMessageViewHolder.KEY_MIMETYPE)), - progressBar, - getMessageText(), - image); + new FileViewerUtils.ProgressUi(progressBar, getMessageText(), image)); } else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) { getMessageText().setText("GIPHY"); diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt index 900946724..b6dc0d4af 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt @@ -15,10 +15,6 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class SharedItemsRepository { - companion object { - private val TAG = SharedItemsRepository::class.simpleName - } - var parameters: Parameters? = null @Inject @@ -38,7 +34,9 @@ class SharedItemsRepository { return ncApi.getSharedItems( credentials, ApiUtils.getUrlForChatSharedItems(1, parameters!!.baseUrl, parameters!!.roomToken), - type, lastKnownMessageId, 28 + type, + lastKnownMessageId, + BATCH_SIZE ) } @@ -61,4 +59,8 @@ class SharedItemsRepository { val userEntity: UserEntity, val roomToken: String ) + + companion object { + const val BATCH_SIZE: Int = 28 + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt index 4b3638956..a2785bafb 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package com.nextcloud.talk.utils import android.annotation.SuppressLint @@ -35,9 +55,7 @@ class FileViewerUtils(private val context: Context, private val userEntity: User fun openFile( message: ChatMessage, - progressBar: ProgressBar?, - messageText: EmojiTextView?, - previewImage: SimpleDraweeView + progressUi: ProgressUi ) { val fileName = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_NAME]!! val mimetype = message.getSelectedIndividualHashMap()[MagicPreviewMessageViewHolder.KEY_MIMETYPE]!! @@ -59,9 +77,7 @@ class FileViewerUtils(private val context: Context, private val userEntity: User path, link, mimetype, - progressBar, - messageText, - previewImage + progressUi ) } @@ -72,9 +88,7 @@ class FileViewerUtils(private val context: Context, private val userEntity: User path: String, link: String, mimetype: String, - progressBar: ProgressBar?, - messageText: EmojiTextView?, - previewImage: SimpleDraweeView + progressUi: ProgressUi ) { if (isSupportedForInternalViewer(mimetype) || canBeHandledByExternalApp(mimetype, fileName)) { openOrDownloadFile( @@ -83,9 +97,7 @@ class FileViewerUtils(private val context: Context, private val userEntity: User path, fileSize, mimetype, - progressBar, - messageText, - previewImage + progressUi ) } else { openFileInFilesApp(link, fileId) @@ -106,9 +118,7 @@ class FileViewerUtils(private val context: Context, private val userEntity: User path: String, fileSize: Int, mimetype: String, - progressBar: ProgressBar?, - messageText: EmojiTextView?, - previewImage: SimpleDraweeView + progressUi: ProgressUi ) { val file = File(context.cacheDir, fileName) if (file.exists()) { @@ -120,9 +130,7 @@ class FileViewerUtils(private val context: Context, private val userEntity: User path, fileSize, mimetype, - progressBar, - messageText, - previewImage + progressUi ) } } @@ -148,11 +156,12 @@ class FileViewerUtils(private val context: Context, private val userEntity: User } } + @Suppress("Detekt.TooGenericExceptionCaught") private fun openFileByExternalApp(fileName: String, mimetype: String) { val path = context.cacheDir.absolutePath + "/" + fileName val file = File(path) val intent: Intent - if (Build.VERSION.SDK_INT < 24) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { intent = Intent(Intent.ACTION_VIEW) intent.setDataAndType(Uri.fromFile(file), mimetype) intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY @@ -259,14 +268,10 @@ class FileViewerUtils(private val context: Context, private val userEntity: User path: String, fileSize: Int, mimetype: String, - progressBar: ProgressBar?, - messageText: EmojiTextView?, - previewImage: SimpleDraweeView + progressUi: ProgressUi ) { // check if download worker is already running - val workers = WorkManager.getInstance(context).getWorkInfosByTag( - fileId!! - ) + val workers = WorkManager.getInstance(context).getWorkInfosByTag(fileId!!) try { for (workInfo in workers.get()) { if (workInfo.state == WorkInfo.State.RUNNING || workInfo.state == WorkInfo.State.ENQUEUED) { @@ -296,16 +301,14 @@ class FileViewerUtils(private val context: Context, private val userEntity: User .addTag(fileId) .build() WorkManager.getInstance().enqueue(downloadWorker) - progressBar?.visibility = View.VISIBLE + progressUi.progressBar?.visibility = View.VISIBLE WorkManager.getInstance(context).getWorkInfoByIdLiveData(downloadWorker.id) .observeForever { workInfo: WorkInfo? -> updateViewsByProgress( fileName, mimetype, workInfo!!, - progressBar, - messageText, - previewImage + progressUi ) } } @@ -314,15 +317,13 @@ class FileViewerUtils(private val context: Context, private val userEntity: User fileName: String, mimetype: String, workInfo: WorkInfo, - progressBar: ProgressBar?, - messageText: EmojiTextView?, - previewImage: SimpleDraweeView + progressUi: ProgressUi ) { when (workInfo.state) { WorkInfo.State.RUNNING -> { val progress = workInfo.progress.getInt(DownloadFileToCacheWorker.PROGRESS, -1) if (progress > -1) { - messageText?.text = String.format( + progressUi.messageText?.text = String.format( context.resources.getString(R.string.filename_progress), fileName, progress @@ -330,7 +331,7 @@ class FileViewerUtils(private val context: Context, private val userEntity: User } } WorkInfo.State.SUCCEEDED -> { - if (previewImage.isShown) { + if (progressUi.previewImage.isShown) { openFileByMimetype(fileName, mimetype) } else { Log.d( @@ -339,12 +340,12 @@ class FileViewerUtils(private val context: Context, private val userEntity: User " was downloaded but it's not opened because view is not shown on screen" ) } - messageText?.text = fileName - progressBar?.visibility = View.GONE + progressUi.messageText?.text = fileName + progressUi.progressBar?.visibility = View.GONE } WorkInfo.State.FAILED -> { - messageText?.text = fileName - progressBar?.visibility = View.GONE + progressUi.messageText?.text = fileName + progressUi.progressBar?.visibility = View.GONE } else -> { } @@ -355,9 +356,7 @@ class FileViewerUtils(private val context: Context, private val userEntity: User fileName: String, fileId: String, mimeType: String, - progressBar: ProgressBar, - messageText: EmojiTextView?, - previewImage: SimpleDraweeView + progressUi: ProgressUi ) { val workers = WorkManager.getInstance(context).getWorkInfosByTag(fileId) @@ -366,7 +365,7 @@ class FileViewerUtils(private val context: Context, private val userEntity: User if (workInfo.state == WorkInfo.State.RUNNING || workInfo.state == WorkInfo.State.ENQUEUED ) { - progressBar.visibility = View.VISIBLE + progressUi.progressBar?.visibility = View.VISIBLE WorkManager .getInstance(context) .getWorkInfoByIdLiveData(workInfo.id) @@ -375,9 +374,7 @@ class FileViewerUtils(private val context: Context, private val userEntity: User fileName, mimeType, info!!, - progressBar, - messageText, - previewImage + progressUi ) } } @@ -389,9 +386,14 @@ class FileViewerUtils(private val context: Context, private val userEntity: User } } + data class ProgressUi( + val progressBar: ProgressBar?, + val messageText: EmojiTextView?, + val previewImage: SimpleDraweeView + ) + companion object { private val TAG = FileViewerUtils::class.simpleName - const val KEY_ID = "id" } } diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index cddf49407..e0335df3a 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -92,7 +92,7 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository, privat override fun onComplete() { val sortedMutableItems = items.toSortedMap().values.toList().reversed().toMutableList() - val moreItemsExisting = items.count() == 28 + val moreItemsExisting = items.count() == BATCH_SIZE if (initModel) { this@SharedItemsViewModel._sharedItems.value = @@ -143,5 +143,6 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository, privat companion object { private val TAG = SharedItemsViewModel::class.simpleName + const val BATCH_SIZE: Int = 28 } } From eaab88776530f8baa55fc6d497d9a932261c9e3d Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 28 Apr 2022 21:18:29 +0200 Subject: [PATCH 35/55] refactor duplicated code Signed-off-by: Andy Scherzinger --- .../nextcloud/talk/activities/MainActivity.kt | 50 ++++++++----------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index 06be3d52d..c3ca78e90 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -118,42 +118,32 @@ class MainActivity : BaseActivity(), ActionBarProvider { if (userUtils.anyUserExists()) { setDefaultRootController() } else { - if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) { - router!!.pushController( - RouterTransaction.with( - WebViewLoginController(resources.getString(R.string.weblogin_url), false) - ) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) - } else { - router!!.setRoot( - RouterTransaction.with(ServerSelectionController()) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) - } + launchLoginScreen() } } else { - if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) { - router!!.pushController( - RouterTransaction.with( - WebViewLoginController(resources.getString(R.string.weblogin_url), false) - ) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) - } else { - router!!.setRoot( - RouterTransaction.with(ServerSelectionController()) - .pushChangeHandler(HorizontalChangeHandler()) - .popChangeHandler(HorizontalChangeHandler()) - ) - } + launchLoginScreen() } } } + private fun launchLoginScreen() { + if (!TextUtils.isEmpty(resources.getString(R.string.weblogin_url))) { + router!!.pushController( + RouterTransaction.with( + WebViewLoginController(resources.getString(R.string.weblogin_url), false) + ) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + } else { + router!!.setRoot( + RouterTransaction.with(ServerSelectionController()) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + } + } + override fun onStart() { Log.d(TAG, "onStart: Activity: " + System.identityHashCode(this).toString()) From 5c662427f8a44de9b24f6c7b41be40fd990c25ea Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Thu, 28 Apr 2022 21:29:54 +0200 Subject: [PATCH 36/55] improve detekt score Signed-off-by: Andy Scherzinger --- .../talk/adapters/SharedItemsAdapter.kt | 4 +- .../talk/adapters/SharedItemsListAdapter.kt | 10 +--- .../nextcloud/talk/utils/FileViewerUtils.kt | 54 +++++++++---------- 3 files changed, 27 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt index 6a685df17..f5ca1ffff 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -74,9 +74,7 @@ class SharedItemsAdapter : RecyclerView.Adapter() val fileViewerUtils = FileViewerUtils(it.context, currentItem.userEntity) fileViewerUtils.openFile( - currentItem.id, - currentItem.name, - currentItem.fileSize, + FileViewerUtils.FileInfo(currentItem.id, currentItem.name, currentItem.fileSize), currentItem.path, currentItem.link, currentItem.mimeType, diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt index 431dd8fe9..455ab2609 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt @@ -75,17 +75,11 @@ class SharedItemsListAdapter : RecyclerView.Adapter updateViewsByProgress( - fileName, + fileInfo.fileName, mimetype, workInfo!!, progressUi @@ -392,6 +380,12 @@ class FileViewerUtils(private val context: Context, private val userEntity: User val previewImage: SimpleDraweeView ) + data class FileInfo( + val fileId: String, + val fileName: String, + val fileSize: Int + ) + companion object { private val TAG = FileViewerUtils::class.simpleName const val KEY_ID = "id" From 962e43857ec4930839629249f101fe024c6dfc96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Fri, 29 Apr 2022 10:04:56 +0200 Subject: [PATCH 37/55] Check for spreed capability "rich-object-list-media" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the capability is not available the shared items can't shown. Signed-off-by: Tim Krüger --- .../java/com/nextcloud/talk/controllers/ChatController.kt | 7 ++++++- .../talk/controllers/ConversationInfoController.kt | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) 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 f3e484e15..8541eaa56 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -2301,7 +2301,12 @@ class ChatController(args: Bundle) : conversationInfoMenuItem = menu.findItem(R.id.conversation_info) conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call) conversationVideoMenuItem = menu.findItem(R.id.conversation_video_call) - conversationSharedItemsItem = menu.findItem(R.id.shared_items) + + if(CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "rich-object-list-media")){ + conversationSharedItemsItem = menu.findItem(R.id.shared_items) + } else { + menu.removeItem(R.id.shared_items) + } loadAvatarForStatusBar() } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index c853775a4..8fa50f086 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -175,7 +175,12 @@ class ConversationInfoController(args: Bundle) : binding.leaveConversationAction.setOnClickListener { leaveConversation() } binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog(null) } binding.addParticipantsAction.setOnClickListener { addParticipants() } - binding.showSharedItemsAction.setOnClickListener { showSharedItems() } + + if (CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "rich-object-list-media")) { + binding.showSharedItemsAction.setOnClickListener { showSharedItems() } + } else { + binding.categorySharedItems.visibility = View.GONE + } fetchRoomInfo() } From 863638af713cf89fc005746dd0f2fe33093413e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Fri, 29 Apr 2022 10:17:58 +0200 Subject: [PATCH 38/55] Remove redundant 'Shared items' string in conversation info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- app/src/main/res/layout/controller_conversation_info.xml | 5 ++--- app/src/main/res/values/strings.xml | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/controller_conversation_info.xml b/app/src/main/res/layout/controller_conversation_info.xml index 37bd3699e..269449006 100644 --- a/app/src/main/res/layout/controller_conversation_info.xml +++ b/app/src/main/res/layout/controller_conversation_info.xml @@ -223,7 +223,7 @@ android:animateLayoutChanges="true" apc:cardBackgroundColor="@color/bg_default" apc:cardElevation="0dp" - apc:mpc_title="Shared Items"> + apc:mpc_title="@string/nc_shared_items"> + apc:mp_title="@string/nc_shared_items_description" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f2249b863..ba1df70e9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -419,6 +419,7 @@ Shared items + Images, files, voice messages… Talk recording from %1$s (%2$s) From 925d839ccd5f9a84157ed2d0902e732940337af9 Mon Sep 17 00:00:00 2001 From: drone Date: Fri, 29 Apr 2022 08:27:47 +0000 Subject: [PATCH 39/55] Drone: update Lint results to reflect reduced error/warning count [skip ci] Signed-off-by: drone --- scripts/analysis/lint-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 5a424f167..8c4822eac 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 1 error and 150 warnings + Lint Report: 1 error and 149 warnings From fd08ad965af1e33b782da00dc04d2b1c2b807af7 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 29 Apr 2022 10:30:28 +0200 Subject: [PATCH 40/55] add filesize and date to listview for shared items Signed-off-by: Marcel Hibbe --- .../talk/adapters/SharedItemsListAdapter.kt | 10 ++++ .../nextcloud/talk/repositories/SharedItem.kt | 1 + .../talk/viewmodels/SharedItemsViewModel.kt | 1 + .../main/res/layout/attachment_list_item.xml | 49 +++++++++++++++---- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt index 455ab2609..60619fca6 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt @@ -1,6 +1,7 @@ package com.nextcloud.talk.adapters import android.net.Uri +import android.text.format.Formatter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -12,12 +13,14 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder import com.nextcloud.talk.R import com.nextcloud.talk.databinding.AttachmentListItemBinding import com.nextcloud.talk.repositories.SharedItem +import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.FileViewerUtils class SharedItemsListAdapter : RecyclerView.Adapter() { companion object { private val TAG = SharedItemsListAdapter::class.simpleName + private const val ONE_SECOND_IN_MILLIS = 1000 } class ViewHolder(val binding: AttachmentListItemBinding, itemView: View) : RecyclerView.ViewHolder(itemView) @@ -35,6 +38,13 @@ class SharedItemsListAdapter : RecyclerView.Adapter - + android:orientation="vertical" + android:layout_toEndOf="@id/preview_container"> + + + + + + + + + + + + From e2735e8cb286c06c07e444ff698224ba232e2b24 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 29 Apr 2022 10:52:39 +0200 Subject: [PATCH 41/55] improve list view design for shared items Signed-off-by: Marcel Hibbe --- .../main/res/layout/attachment_list_item.xml | 17 +++++++++++++---- app/src/main/res/values/dimens.xml | 3 ++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/attachment_list_item.xml b/app/src/main/res/layout/attachment_list_item.xml index 4a2088aaa..7f1d2513f 100644 --- a/app/src/main/res/layout/attachment_list_item.xml +++ b/app/src/main/res/layout/attachment_list_item.xml @@ -43,10 +43,11 @@ @@ -101,7 +108,9 @@ android:id="@+id/file_date" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="@dimen/margin_between_elements" + android:layout_marginStart="@dimen/standard_quarter_margin" + android:textColor="@color/textColorMaxContrast" + android:textSize="12sp" tools:text="date"/> diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index a37207455..e4dc99eb7 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -35,7 +35,6 @@ 40dp 30dp 96dp - 40dp 14sp 6dp @@ -71,4 +70,6 @@ 48dp 40dp 2dp + + 50dp From 704ea904493514b053809d140b72e0030c9a2865 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 29 Apr 2022 11:49:31 +0200 Subject: [PATCH 42/55] align all secondary text line sizes to 14sp Signed-off-by: Andy Scherzinger --- app/src/main/res/layout/attachment_list_item.xml | 14 +++++++------- app/src/main/res/layout/rv_item_browser_file.xml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/layout/attachment_list_item.xml b/app/src/main/res/layout/attachment_list_item.xml index 7f1d2513f..d12c99730 100644 --- a/app/src/main/res/layout/attachment_list_item.xml +++ b/app/src/main/res/layout/attachment_list_item.xml @@ -80,7 +80,7 @@ android:textAlignment="viewStart" android:textAppearance="@style/ListItem" android:textSize="@dimen/two_line_primary_text_size" - tools:text="Filename" /> + tools:text="Filename.md" /> + android:textSize="14sp" + tools:text="11 KB" /> @@ -110,8 +110,8 @@ android:layout_height="wrap_content" android:layout_marginStart="@dimen/standard_quarter_margin" android:textColor="@color/textColorMaxContrast" - android:textSize="12sp" - tools:text="date"/> + android:textSize="14sp" + tools:text="04-05-2022 21:16"/> diff --git a/app/src/main/res/layout/rv_item_browser_file.xml b/app/src/main/res/layout/rv_item_browser_file.xml index 769049bee..dfc50383f 100644 --- a/app/src/main/res/layout/rv_item_browser_file.xml +++ b/app/src/main/res/layout/rv_item_browser_file.xml @@ -73,7 +73,7 @@ android:singleLine="true" android:textAlignment="viewStart" android:textColor="@color/textColorMaxContrast" - android:textSize="12sp" + android:textSize="14sp" tools:text="3 minutes ago" /> Date: Fri, 29 Apr 2022 11:20:43 +0200 Subject: [PATCH 43/55] show mimetype icons from DrawableUtils Signed-off-by: Marcel Hibbe --- .../talk/adapters/SharedItemsAdapter.kt | 25 ++++--------------- .../talk/adapters/SharedItemsListAdapter.kt | 25 ++++--------------- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt index f5ca1ffff..7783e5189 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt @@ -4,15 +4,16 @@ import android.net.Uri import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.interfaces.DraweeController import com.facebook.drawee.view.SimpleDraweeView import com.facebook.imagepipeline.common.RotationOptions import com.facebook.imagepipeline.request.ImageRequestBuilder -import com.nextcloud.talk.R import com.nextcloud.talk.databinding.AttachmentItemBinding import com.nextcloud.talk.repositories.SharedItem +import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType import com.nextcloud.talk.utils.FileViewerUtils class SharedItemsAdapter : RecyclerView.Adapter() { @@ -50,25 +51,9 @@ class SharedItemsAdapter : RecyclerView.Adapter() .build() holder.binding.image.controller = draweeController } else { - when (currentItem.mimeType) { - "video/mp4", - "video/quicktime", - "video/ogg" - -> holder.binding.image.setImageResource(R.drawable.ic_mimetype_video) - "audio/mpeg", - "audio/wav", - "audio/ogg", - -> holder.binding.image.setImageResource(R.drawable.ic_mimetype_audio) - "image/png", - "image/jpeg", - "image/gif" - -> holder.binding.image.setImageResource(R.drawable.ic_mimetype_image) - "text/markdown", - "text/plain" - -> holder.binding.image.setImageResource(R.drawable.ic_mimetype_text) - else - -> holder.binding.image.setImageResource(R.drawable.ic_mimetype_file) - } + val drawableResourceId = getDrawableResourceIdForMimeType(currentItem.mimeType) + val drawable = ContextCompat.getDrawable(holder.binding.image.context, drawableResourceId) + holder.binding.image.hierarchy.setPlaceholderImage(drawable) } holder.binding.image.setOnClickListener { val fileViewerUtils = FileViewerUtils(it.context, currentItem.userEntity) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt index 60619fca6..b8d52a1cb 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt @@ -5,15 +5,16 @@ import android.text.format.Formatter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.interfaces.DraweeController import com.facebook.imagepipeline.common.RotationOptions import com.facebook.imagepipeline.request.ImageRequestBuilder -import com.nextcloud.talk.R import com.nextcloud.talk.databinding.AttachmentListItemBinding import com.nextcloud.talk.repositories.SharedItem import com.nextcloud.talk.utils.DateUtils +import com.nextcloud.talk.utils.DrawableUtils import com.nextcloud.talk.utils.FileViewerUtils class SharedItemsListAdapter : RecyclerView.Adapter() { @@ -61,25 +62,9 @@ class SharedItemsListAdapter : RecyclerView.Adapter holder.binding.fileImage.setImageResource(R.drawable.ic_mimetype_video) - "audio/mpeg", - "audio/wav", - "audio/ogg", - -> holder.binding.fileImage.setImageResource(R.drawable.ic_mimetype_audio) - "image/png", - "image/jpeg", - "image/gif" - -> holder.binding.fileImage.setImageResource(R.drawable.ic_mimetype_image) - "text/markdown", - "text/plain" - -> holder.binding.fileImage.setImageResource(R.drawable.ic_mimetype_text) - else - -> holder.binding.fileImage.setImageResource(R.drawable.ic_mimetype_file) - } + val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(currentItem.mimeType) + val drawable = ContextCompat.getDrawable(holder.binding.fileImage.context, drawableResourceId) + holder.binding.fileImage.hierarchy.setPlaceholderImage(drawable) } holder.binding.fileItem.setOnClickListener { val fileViewerUtils = FileViewerUtils(it.context, currentItem.userEntity) From a91ff9e920f63889847a6a7368841d397c4f7dfe Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 29 Apr 2022 11:53:48 +0200 Subject: [PATCH 44/55] make tabbar full width improve design hide "Other" tab Signed-off-by: Marcel Hibbe --- .../com/nextcloud/talk/activities/SharedItemsActivity.kt | 8 ++++---- app/src/main/res/layout/activity_shared_items.xml | 9 ++++----- app/src/main/res/layout/attachment_list_item.xml | 1 - 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index b42d8a743..5149ef2eb 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -130,10 +130,10 @@ class SharedItemsActivity : AppCompatActivity() { // tabDeckCard.text = "deckcard" // binding.sharedItemsTabs.addTab(tabDeckCard) - val tabOther: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabOther.tag = TAB_OTHER - tabOther.setText(R.string.shared_items_other) - binding.sharedItemsTabs.addTab(tabOther) + // val tabOther: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabOther.tag = TAB_OTHER + // tabOther.setText(R.string.shared_items_other) + // binding.sharedItemsTabs.addTab(tabOther) binding.sharedItemsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab) { diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index d6ababc24..cbab88d8d 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -42,15 +42,13 @@ @@ -61,6 +59,7 @@ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@+id/shared_items_tabs" + android:layout_marginTop="@dimen/double_margin_between_elements" tools:listitem="@layout/attachment_item" /> diff --git a/app/src/main/res/layout/attachment_list_item.xml b/app/src/main/res/layout/attachment_list_item.xml index d12c99730..541d323ac 100644 --- a/app/src/main/res/layout/attachment_list_item.xml +++ b/app/src/main/res/layout/attachment_list_item.xml @@ -24,7 +24,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="@dimen/standard_margin" - android:layout_marginTop="@dimen/standard_half_margin" android:layout_marginEnd="@dimen/standard_margin" android:layout_marginBottom="@dimen/standard_half_margin" android:orientation="vertical"> From b49a1bbbdeb39a6aa0a67d75e62d503ad7267f25 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 29 Apr 2022 12:26:30 +0200 Subject: [PATCH 45/55] fix klint warnings Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/controllers/ChatController.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 8541eaa56..b34dd9094 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -1681,7 +1681,7 @@ class ChatController(args: Bundle) : " sessionId: " + currentConversation?.sessionId ) - if (! validSessionId()) { + if (!validSessionId()) { var apiVersion = 1 // FIXME Fix API checking with guests? if (conversationUser != null) { @@ -2302,7 +2302,7 @@ class ChatController(args: Bundle) : conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call) conversationVideoMenuItem = menu.findItem(R.id.conversation_video_call) - if(CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "rich-object-list-media")){ + if (CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser, "rich-object-list-media")) { conversationSharedItemsItem = menu.findItem(R.id.shared_items) } else { menu.removeItem(R.id.shared_items) From aa5e34b3f90e15cb00769710874b0d23ec460e95 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 29 Apr 2022 12:27:10 +0200 Subject: [PATCH 46/55] fix to align progress bar in center of files Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/attachment_list_item.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/res/layout/attachment_list_item.xml b/app/src/main/res/layout/attachment_list_item.xml index 541d323ac..d20fb94da 100644 --- a/app/src/main/res/layout/attachment_list_item.xml +++ b/app/src/main/res/layout/attachment_list_item.xml @@ -33,7 +33,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="2dp" - android:layout_marginEnd="2dp" + android:layout_marginEnd="@dimen/standard_margin" android:adjustViewBounds="true" app:layout_alignSelf="flex_start" app:layout_flexGrow="1" @@ -46,7 +46,6 @@ android:layout_height="@dimen/mediatab_file_icon_size" android:padding="4dp" android:src="@drawable/ic_mimetype_file" - android:layout_marginEnd="@dimen/standard_margin" app:layout_constraintTop_toTopOf="parent" app:placeholderImageScaleType="fitCenter" fresco:actualImageScaleType="centerCrop" From b22ed7399d6c46b93a356b74cd5f4f772e388609 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 29 Apr 2022 13:21:30 +0200 Subject: [PATCH 47/55] fix to close media view when press back button when starting media tab from conversation info, the back button opened media tab again. to fix this FLAG_ACTIVITY_CLEAR_TOP was set as flag Signed-off-by: Marcel Hibbe --- app/src/main/AndroidManifest.xml | 9 ++++----- .../talk/controllers/ConversationInfoController.kt | 2 ++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aaaeb9430..9e52ddd26 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -96,11 +96,6 @@ android:name="android.max_aspect" android:value="10" /> - - + + diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt index 8fa50f086..b60ed878a 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationInfoController.kt @@ -187,6 +187,8 @@ class ConversationInfoController(args: Bundle) : private fun showSharedItems() { val intent = Intent(activity, SharedItemsActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.putExtra(BundleKeys.KEY_CONVERSATION_NAME, conversation?.displayName) intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, conversationToken) intent.putExtra(BundleKeys.KEY_USER_ENTITY, conversationUser as Parcelable) From 721797c078e9749b11944c13804b84b71ffb02bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Fri, 29 Apr 2022 13:41:54 +0200 Subject: [PATCH 48/55] Hide tabs without content in shared items view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- .../talk/activities/SharedItemsActivity.kt | 97 +++++++++++-------- .../java/com/nextcloud/talk/api/NcApi.java | 7 ++ .../json/chat/ChatShareOverviewOCS.java | 78 +++++++++++++++ .../json/chat/ChatShareOverviewOverall.java | 75 ++++++++++++++ .../talk/repositories/SharedItemType.kt | 18 ++++ .../repositories/SharedItemsRepository.kt | 18 +++- .../talk/repositories/SharedMediaItems.kt | 2 +- .../com/nextcloud/talk/utils/ApiUtils.java | 4 + .../talk/viewmodels/SharedItemsViewModel.kt | 51 +++++++++- 9 files changed, 299 insertions(+), 51 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverviewOCS.java create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverviewOverall.java create mode 100644 app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 5149ef2eb..00a60c0b7 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -15,6 +15,7 @@ import com.nextcloud.talk.adapters.SharedItemsAdapter import com.nextcloud.talk.adapters.SharedItemsListAdapter import com.nextcloud.talk.databinding.ActivitySharedItemsBinding import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.repositories.SharedItemType import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN @@ -25,12 +26,12 @@ class SharedItemsActivity : AppCompatActivity() { private lateinit var binding: ActivitySharedItemsBinding private lateinit var viewModel: SharedItemsViewModel - private lateinit var currentTab: String + private lateinit var currentTab: SharedItemType override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - currentTab = TAB_MEDIA + currentTab = SharedItemType.MEDIA val roomToken = intent.getStringExtra(KEY_ROOM_TOKEN)!! val conversationName = intent.getStringExtra(KEY_CONVERSATION_NAME) @@ -54,17 +55,19 @@ class SharedItemsActivity : AppCompatActivity() { supportActionBar?.title = conversationName supportActionBar?.setDisplayHomeAsUpEnabled(true) - initTabs() - viewModel = ViewModelProvider( this, SharedItemsViewModel.Factory(userEntity, roomToken, currentTab) ).get(SharedItemsViewModel::class.java) + viewModel.sharedItemType.observe(this) { + initTabs(it) + } + viewModel.sharedItems.observe(this) { Log.d(TAG, "Items received: $it") - if (currentTab == TAB_MEDIA) { + if (currentTab == SharedItemType.MEDIA) { val adapter = SharedItemsAdapter() adapter.items = it.items adapter.authHeader = it.authHeader @@ -94,50 +97,65 @@ class SharedItemsActivity : AppCompatActivity() { }) } - fun updateItems(type: String) { + fun updateItems(type: SharedItemType) { currentTab = type viewModel.loadItems(type) } - private fun initTabs() { - val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabMedia.tag = TAB_MEDIA - tabMedia.setText(R.string.shared_items_media) - binding.sharedItemsTabs.addTab(tabMedia) + private fun initTabs(sharedItemTypes: Set) { - val tabFile: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabFile.tag = TAB_FILE - tabFile.setText(R.string.shared_items_file) - binding.sharedItemsTabs.addTab(tabFile) + if(sharedItemTypes.contains(SharedItemType.MEDIA)) { + val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabMedia.tag = SharedItemType.MEDIA + tabMedia.setText(R.string.shared_items_media) + binding.sharedItemsTabs.addTab(tabMedia) + } - val tabAudio: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabAudio.tag = TAB_AUDIO - tabAudio.setText(R.string.shared_items_audio) - binding.sharedItemsTabs.addTab(tabAudio) + if(sharedItemTypes.contains(SharedItemType.FILE)) { + val tabFile: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabFile.tag = SharedItemType.FILE + tabFile.setText(R.string.shared_items_file) + binding.sharedItemsTabs.addTab(tabFile) + } - val tabVoice: TabLayout.Tab = binding.sharedItemsTabs.newTab() - tabVoice.tag = TAB_VOICE - tabVoice.setText(R.string.shared_items_voice) - binding.sharedItemsTabs.addTab(tabVoice) + if(sharedItemTypes.contains(SharedItemType.AUDIO)) { + val tabAudio: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabAudio.tag = SharedItemType.AUDIO + tabAudio.setText(R.string.shared_items_audio) + binding.sharedItemsTabs.addTab(tabAudio) + } - // val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() - // tabLocation.tag = TAB_LOCATION - // tabLocation.text = "location" - // binding.sharedItemsTabs.addTab(tabLocation) + if(sharedItemTypes.contains(SharedItemType.VOICE)) { + val tabVoice: TabLayout.Tab = binding.sharedItemsTabs.newTab() + tabVoice.tag = SharedItemType.VOICE + tabVoice.setText(R.string.shared_items_voice) + binding.sharedItemsTabs.addTab(tabVoice) + } - // val tabDeckCard: TabLayout.Tab = binding.sharedItemsTabs.newTab() - // tabDeckCard.tag = TAB_DECKCARD - // tabDeckCard.text = "deckcard" - // binding.sharedItemsTabs.addTab(tabDeckCard) + // if(sharedItemTypes.contains(SharedItemType.LOCATION)) { + // val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabLocation.tag = SharedItemType.LOCATION + // tabLocation.text = "location" + // binding.sharedItemsTabs.addTab(tabLocation) + // } - // val tabOther: TabLayout.Tab = binding.sharedItemsTabs.newTab() - // tabOther.tag = TAB_OTHER - // tabOther.setText(R.string.shared_items_other) - // binding.sharedItemsTabs.addTab(tabOther) + // if(sharedItemTypes.contains(SharedItemType.DECKCARD)) { + // val tabDeckCard: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabDeckCard.tag = SharedItemType.DECKCARD + // tabDeckCard.text = "deckcard" + // binding.sharedItemsTabs.addTab(tabDeckCard) + // } + + // if(sharedItemTypes.contains(SharedItemType.OTHER)) { + // val tabOther: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabOther.tag = SharedItemType.OTHER + // tabOther.setText(R.string.shared_items_other) + // binding.sharedItemsTabs.addTab(tabOther) + // } binding.sharedItemsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab) { - updateItems(tab.tag as String) + updateItems(tab.tag as SharedItemType) } override fun onTabUnselected(tab: TabLayout.Tab) = Unit @@ -157,13 +175,6 @@ class SharedItemsActivity : AppCompatActivity() { companion object { private val TAG = SharedItemsActivity::class.simpleName - const val TAB_AUDIO = "audio" - const val TAB_FILE = "file" - const val TAB_MEDIA = "media" - const val TAB_VOICE = "voice" - const val TAB_LOCATION = "location" - const val TAB_DECKCARD = "deckcard" - const val TAB_OTHER = "other" const val SPAN_COUNT: Int = 4 } } diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index 4c4133c60..857eabecc 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -28,6 +28,7 @@ import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; import com.nextcloud.talk.models.json.chat.ChatOverall; import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage; import com.nextcloud.talk.models.json.chat.ChatShareOverall; +import com.nextcloud.talk.models.json.chat.ChatShareOverviewOverall; import com.nextcloud.talk.models.json.conversations.RoomOverall; import com.nextcloud.talk.models.json.conversations.RoomsOverall; import com.nextcloud.talk.models.json.generic.GenericOverall; @@ -345,6 +346,12 @@ public interface NcApi { @Nullable @Query("lastKnownMessageId") Integer lastKnownMessageId, @Nullable @Query("limit") Integer limit); + @GET + Observable> getSharedItemsOverview(@Header("Authorization") String authorization, + @Url String url, + @Nullable @Query("limit") Integer limit); + + @GET Observable getMentionAutocompleteSuggestions(@Header("Authorization") String authorization, @Url String url, @Query("search") String query, diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverviewOCS.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverviewOCS.java new file mode 100644 index 000000000..c6ae81b62 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverviewOCS.java @@ -0,0 +1,78 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.models.json.chat; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import org.parceler.Parcel; + +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + +import androidx.annotation.NonNull; + +@Parcel +@JsonObject +public class ChatShareOverviewOCS { + @JsonField(name = "data") + public HashMap> data; + + public HashMap> getData() { + return this.data; + } + + public void setData(HashMap> data) { + this.data = data; + } + + public boolean equals(final Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ChatShareOverviewOCS)) { + return false; + } + final ChatShareOverviewOCS other = (ChatShareOverviewOCS) o; + if (!other.canEqual(this)) { + return false; + } + final Object this$data = this.getData(); + final Object other$data = other.getData(); + + return Objects.equals(this$data, other$data); + } + + protected boolean canEqual(final Object other) { + return other instanceof ChatShareOverviewOCS; + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $data = this.getData(); + return result * PRIME + ($data == null ? 43 : $data.hashCode()); + } + + public String toString() { + return "ChatShareOverviewOCS(data=" + this.getData() + ")"; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverviewOverall.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverviewOverall.java new file mode 100644 index 000000000..086a765d7 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatShareOverviewOverall.java @@ -0,0 +1,75 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.models.json.chat; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +import org.parceler.Parcel; + +import java.util.Objects; + +@Parcel +@JsonObject +public class ChatShareOverviewOverall { + @JsonField(name = "ocs") + public ChatShareOverviewOCS ocs; + + public ChatShareOverviewOCS getOcs() { + return this.ocs; + } + + public void setOcs(ChatShareOverviewOCS ocs) { + this.ocs = ocs; + } + + public boolean equals(final Object o) { + if (o == this) { + return true; + } + if (!(o instanceof ChatShareOverviewOverall)) { + return false; + } + final ChatShareOverviewOverall other = (ChatShareOverviewOverall) o; + if (!other.canEqual(this)) { + return false; + } + final Object this$ocs = this.getOcs(); + final Object other$ocs = other.getOcs(); + + return Objects.equals(this$ocs, other$ocs); + } + + protected boolean canEqual(final Object other) { + return other instanceof ChatShareOverviewOverall; + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $ocs = this.getOcs(); + return result * PRIME + ($ocs == null ? 43 : $ocs.hashCode()); + } + + public String toString() { + return "ChatShareOverviewOverall(ocs=" + this.getOcs() + ")"; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt new file mode 100644 index 000000000..641ba72e6 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt @@ -0,0 +1,18 @@ +package com.nextcloud.talk.repositories + +import java.util.Locale + +enum class SharedItemType { + + AUDIO, + FILE, + MEDIA, + VOICE, + LOCATION, + DECKCARD, + OTHER; + + companion object { + fun typeFor(name: String) = valueOf(name.uppercase(Locale.ROOT)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt index b6dc0d4af..f34cd5497 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemsRepository.kt @@ -7,9 +7,11 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.chat.ChatShareOverall +import com.nextcloud.talk.models.json.chat.ChatShareOverviewOverall import com.nextcloud.talk.utils.ApiUtils import io.reactivex.Observable import retrofit2.Response +import java.util.Locale import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -24,22 +26,32 @@ class SharedItemsRepository { sharedApplication!!.componentApplication.inject(this) } - fun media(type: String): Observable>? { + fun media(type: SharedItemType): Observable>? { return media(type, null) } - fun media(type: String, lastKnownMessageId: Int?): Observable>? { + fun media(type: SharedItemType, lastKnownMessageId: Int?): Observable>? { val credentials = ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken) return ncApi.getSharedItems( credentials, ApiUtils.getUrlForChatSharedItems(1, parameters!!.baseUrl, parameters!!.roomToken), - type, + type.toString().lowercase(Locale.ROOT), lastKnownMessageId, BATCH_SIZE ) } + fun availableTypes(): Observable>? { + val credentials = ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken) + + return ncApi.getSharedItemsOverview( + credentials, + ApiUtils.getUrlForChatSharedItemsOverview(1, parameters!!.baseUrl, parameters!!.roomToken), + 1 + ) + } + fun authHeader(): Map { return mapOf(Pair("Authorization", ApiUtils.getCredentials(parameters!!.userName, parameters!!.userToken))) } diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt index 1e3b56bae..91f5fcf10 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedMediaItems.kt @@ -1,7 +1,7 @@ package com.nextcloud.talk.repositories class SharedMediaItems( - val type: String, + val type: SharedItemType, val items: MutableList, var lastSeenId: Int?, var moreItemsExisting: Boolean, diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index 2b6e654c0..2199191f6 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -265,6 +265,10 @@ public class ApiUtils { return getUrlForChat(version, baseUrl, token) + "/share"; } + public static String getUrlForChatSharedItemsOverview(int version, String baseUrl, String token) { + return getUrlForChatSharedItems(version, baseUrl, token) + "/overview"; + } + public static String getUrlForSignaling(int version, String baseUrl) { return getUrlForApi(version, baseUrl) + "/signaling"; } diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt index 8e3b76602..6a935a438 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/SharedItemsViewModel.kt @@ -7,7 +7,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.chat.ChatShareOverall +import com.nextcloud.talk.models.json.chat.ChatShareOverviewOverall import com.nextcloud.talk.repositories.SharedItem +import com.nextcloud.talk.repositories.SharedItemType import com.nextcloud.talk.repositories.SharedItemsRepository import com.nextcloud.talk.repositories.SharedMediaItems import io.reactivex.Observer @@ -16,15 +18,24 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import retrofit2.Response -class SharedItemsViewModel(private val repository: SharedItemsRepository, private val initialType: String) : +class SharedItemsViewModel(private val repository: SharedItemsRepository, private val initialType: SharedItemType) : ViewModel() { + private val _sharedItemType: MutableLiveData> by lazy { + MutableLiveData>().also { + availableTypes() + } + } + private val _sharedItems: MutableLiveData by lazy { MutableLiveData().also { loadItems(initialType) } } + val sharedItemType: LiveData> + get() = _sharedItemType + val sharedItems: LiveData get() = _sharedItems @@ -38,13 +49,13 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository, privat } } - fun loadItems(type: String) { + fun loadItems(type: SharedItemType) { repository.media(type)?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe(observer(type, true)) } - private fun observer(type: String, initModel: Boolean): Observer> { + private fun observer(type: SharedItemType, initModel: Boolean): Observer> { return object : Observer> { var chatLastGiven: Int? = null @@ -119,7 +130,39 @@ class SharedItemsViewModel(private val repository: SharedItemsRepository, privat } } - class Factory(val userEntity: UserEntity, val roomToken: String, private val initialType: String) : + private fun availableTypes() { + repository.availableTypes()?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer> { + + val types = mutableSetOf() + + override fun onSubscribe(d: Disposable) = Unit + + override fun onNext(response: Response) { + val typeMap = response.body()!!.ocs!!.data + for (it in typeMap) { + if (it.value.size > 0) { + try { + types += SharedItemType.typeFor(it.key) + } catch (e: IllegalArgumentException) { + Log.w(TAG, "Server responds an unknown shared item type: ${it.key}") + } + } + } + } + + override fun onError(e: Throwable) { + Log.d(TAG, "An error occurred: $e") + } + + override fun onComplete() { + this@SharedItemsViewModel._sharedItemType.value = types + } + }) + } + + class Factory(val userEntity: UserEntity, val roomToken: String, private val initialType: SharedItemType) : ViewModelProvider .Factory { From 58acf3edc890e28a7e930a46934e48add8a77983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Fri, 29 Apr 2022 13:44:11 +0200 Subject: [PATCH 49/55] Rename 'SharedItemsAdapter' to 'SharedItemsGridAdapter' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- .../java/com/nextcloud/talk/activities/SharedItemsActivity.kt | 4 ++-- .../{SharedItemsAdapter.kt => SharedItemsGridAdapter.kt} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename app/src/main/java/com/nextcloud/talk/adapters/{SharedItemsAdapter.kt => SharedItemsGridAdapter.kt} (95%) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 00a60c0b7..436af7f9b 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -11,7 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.tabs.TabLayout import com.nextcloud.talk.R -import com.nextcloud.talk.adapters.SharedItemsAdapter +import com.nextcloud.talk.adapters.SharedItemsGridAdapter import com.nextcloud.talk.adapters.SharedItemsListAdapter import com.nextcloud.talk.databinding.ActivitySharedItemsBinding import com.nextcloud.talk.models.database.UserEntity @@ -68,7 +68,7 @@ class SharedItemsActivity : AppCompatActivity() { Log.d(TAG, "Items received: $it") if (currentTab == SharedItemType.MEDIA) { - val adapter = SharedItemsAdapter() + val adapter = SharedItemsGridAdapter() adapter.items = it.items adapter.authHeader = it.authHeader binding.imageRecycler.adapter = adapter diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt similarity index 95% rename from app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt rename to app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt index 7783e5189..0001cdd6b 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt @@ -16,10 +16,10 @@ import com.nextcloud.talk.repositories.SharedItem import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType import com.nextcloud.talk.utils.FileViewerUtils -class SharedItemsAdapter : RecyclerView.Adapter() { +class SharedItemsGridAdapter : RecyclerView.Adapter() { companion object { - private val TAG = SharedItemsAdapter::class.simpleName + private val TAG = SharedItemsGridAdapter::class.simpleName } class ViewHolder(val binding: AttachmentItemBinding, itemView: View) : RecyclerView.ViewHolder(itemView) From afe61b7aa1f1e7250f2efd9e132d0c9735ea7624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Fri, 29 Apr 2022 14:03:59 +0200 Subject: [PATCH 50/55] Unify naming of shared item layouts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- .../com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt | 6 +++--- .../com/nextcloud/talk/adapters/SharedItemsListAdapter.kt | 6 +++--- app/src/main/res/layout/activity_shared_items.xml | 2 +- .../layout/{attachment_item.xml => shared_item_grid.xml} | 0 .../{attachment_list_item.xml => shared_item_list.xml} | 0 5 files changed, 7 insertions(+), 7 deletions(-) rename app/src/main/res/layout/{attachment_item.xml => shared_item_grid.xml} (100%) rename app/src/main/res/layout/{attachment_list_item.xml => shared_item_list.xml} (100%) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt index 0001cdd6b..480836214 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt @@ -11,7 +11,7 @@ import com.facebook.drawee.interfaces.DraweeController import com.facebook.drawee.view.SimpleDraweeView import com.facebook.imagepipeline.common.RotationOptions import com.facebook.imagepipeline.request.ImageRequestBuilder -import com.nextcloud.talk.databinding.AttachmentItemBinding +import com.nextcloud.talk.databinding.SharedItemGridBinding import com.nextcloud.talk.repositories.SharedItem import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType import com.nextcloud.talk.utils.FileViewerUtils @@ -22,13 +22,13 @@ class SharedItemsGridAdapter : RecyclerView.Adapter = emptyMap() var items: List = emptyList() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val binding = AttachmentItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = SharedItemGridBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ViewHolder(binding, binding.root) } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt index b8d52a1cb..e8daa988c 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt @@ -11,7 +11,7 @@ import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.interfaces.DraweeController import com.facebook.imagepipeline.common.RotationOptions import com.facebook.imagepipeline.request.ImageRequestBuilder -import com.nextcloud.talk.databinding.AttachmentListItemBinding +import com.nextcloud.talk.databinding.SharedItemListBinding import com.nextcloud.talk.repositories.SharedItem import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DrawableUtils @@ -24,13 +24,13 @@ class SharedItemsListAdapter : RecyclerView.Adapter = emptyMap() var items: List = emptyList() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val binding = AttachmentListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = SharedItemListBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ViewHolder(binding, binding.root) } diff --git a/app/src/main/res/layout/activity_shared_items.xml b/app/src/main/res/layout/activity_shared_items.xml index cbab88d8d..ed76e8e2c 100644 --- a/app/src/main/res/layout/activity_shared_items.xml +++ b/app/src/main/res/layout/activity_shared_items.xml @@ -60,7 +60,7 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@+id/shared_items_tabs" android:layout_marginTop="@dimen/double_margin_between_elements" - tools:listitem="@layout/attachment_item" /> + tools:listitem="@layout/shared_item_grid" /> \ No newline at end of file diff --git a/app/src/main/res/layout/attachment_item.xml b/app/src/main/res/layout/shared_item_grid.xml similarity index 100% rename from app/src/main/res/layout/attachment_item.xml rename to app/src/main/res/layout/shared_item_grid.xml diff --git a/app/src/main/res/layout/attachment_list_item.xml b/app/src/main/res/layout/shared_item_list.xml similarity index 100% rename from app/src/main/res/layout/attachment_list_item.xml rename to app/src/main/res/layout/shared_item_list.xml From a64e22c8206204f6a8e13d3d16a4fec84af6eb14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Fri, 29 Apr 2022 14:09:36 +0200 Subject: [PATCH 51/55] Fix ktlin issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- .../talk/activities/SharedItemsActivity.kt | 32 +++++++++---------- .../talk/repositories/SharedItemType.kt | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt index 436af7f9b..76fb75600 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/SharedItemsActivity.kt @@ -104,28 +104,28 @@ class SharedItemsActivity : AppCompatActivity() { private fun initTabs(sharedItemTypes: Set) { - if(sharedItemTypes.contains(SharedItemType.MEDIA)) { + if (sharedItemTypes.contains(SharedItemType.MEDIA)) { val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabMedia.tag = SharedItemType.MEDIA tabMedia.setText(R.string.shared_items_media) binding.sharedItemsTabs.addTab(tabMedia) } - if(sharedItemTypes.contains(SharedItemType.FILE)) { + if (sharedItemTypes.contains(SharedItemType.FILE)) { val tabFile: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabFile.tag = SharedItemType.FILE tabFile.setText(R.string.shared_items_file) binding.sharedItemsTabs.addTab(tabFile) } - if(sharedItemTypes.contains(SharedItemType.AUDIO)) { + if (sharedItemTypes.contains(SharedItemType.AUDIO)) { val tabAudio: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabAudio.tag = SharedItemType.AUDIO tabAudio.setText(R.string.shared_items_audio) binding.sharedItemsTabs.addTab(tabAudio) } - if(sharedItemTypes.contains(SharedItemType.VOICE)) { + if (sharedItemTypes.contains(SharedItemType.VOICE)) { val tabVoice: TabLayout.Tab = binding.sharedItemsTabs.newTab() tabVoice.tag = SharedItemType.VOICE tabVoice.setText(R.string.shared_items_voice) @@ -133,24 +133,24 @@ class SharedItemsActivity : AppCompatActivity() { } // if(sharedItemTypes.contains(SharedItemType.LOCATION)) { - // val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() - // tabLocation.tag = SharedItemType.LOCATION - // tabLocation.text = "location" - // binding.sharedItemsTabs.addTab(tabLocation) + // val tabLocation: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabLocation.tag = SharedItemType.LOCATION + // tabLocation.text = "location" + // binding.sharedItemsTabs.addTab(tabLocation) // } // if(sharedItemTypes.contains(SharedItemType.DECKCARD)) { - // val tabDeckCard: TabLayout.Tab = binding.sharedItemsTabs.newTab() - // tabDeckCard.tag = SharedItemType.DECKCARD - // tabDeckCard.text = "deckcard" - // binding.sharedItemsTabs.addTab(tabDeckCard) + // val tabDeckCard: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabDeckCard.tag = SharedItemType.DECKCARD + // tabDeckCard.text = "deckcard" + // binding.sharedItemsTabs.addTab(tabDeckCard) // } // if(sharedItemTypes.contains(SharedItemType.OTHER)) { - // val tabOther: TabLayout.Tab = binding.sharedItemsTabs.newTab() - // tabOther.tag = SharedItemType.OTHER - // tabOther.setText(R.string.shared_items_other) - // binding.sharedItemsTabs.addTab(tabOther) + // val tabOther: TabLayout.Tab = binding.sharedItemsTabs.newTab() + // tabOther.tag = SharedItemType.OTHER + // tabOther.setText(R.string.shared_items_other) + // binding.sharedItemsTabs.addTab(tabOther) // } binding.sharedItemsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { diff --git a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt index 641ba72e6..e263ae8cc 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/SharedItemType.kt @@ -15,4 +15,4 @@ enum class SharedItemType { companion object { fun typeFor(name: String) = valueOf(name.uppercase(Locale.ROOT)) } -} \ No newline at end of file +} From 734f33caba234d01d800f34ddf8edb85b69f9955 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 29 Apr 2022 14:24:14 +0200 Subject: [PATCH 52/55] add fallback image for mimetype if drawee request fails Signed-off-by: Marcel Hibbe --- .../talk/adapters/SharedItemsGridAdapter.kt | 26 ++++++++++++++++--- .../talk/adapters/SharedItemsListAdapter.kt | 25 +++++++++++++++--- app/src/main/res/layout/shared_item_grid.xml | 3 --- app/src/main/res/layout/shared_item_list.xml | 3 --- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt index 480836214..78beacb1d 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt @@ -1,18 +1,23 @@ package com.nextcloud.talk.adapters import android.net.Uri +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.controller.BaseControllerListener +import com.facebook.drawee.controller.ControllerListener import com.facebook.drawee.interfaces.DraweeController import com.facebook.drawee.view.SimpleDraweeView import com.facebook.imagepipeline.common.RotationOptions +import com.facebook.imagepipeline.image.ImageInfo import com.facebook.imagepipeline.request.ImageRequestBuilder import com.nextcloud.talk.databinding.SharedItemGridBinding import com.nextcloud.talk.repositories.SharedItem +import com.nextcloud.talk.utils.DrawableUtils import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType import com.nextcloud.talk.utils.FileViewerUtils @@ -44,16 +49,22 @@ class SharedItemsGridAdapter : RecyclerView.Adapter = object : BaseControllerListener() { + override fun onFailure(id: String, e: Throwable) { + Log.w(TAG, "Failed to load image. A static mimetype image will be used", e) + setStaticMimetypeImage(currentItem, holder) + } + } + val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() .setOldController(holder.binding.image.controller) .setAutoPlayAnimations(true) .setImageRequest(imageRequest) + .setControllerListener(listener) .build() holder.binding.image.controller = draweeController } else { - val drawableResourceId = getDrawableResourceIdForMimeType(currentItem.mimeType) - val drawable = ContextCompat.getDrawable(holder.binding.image.context, drawableResourceId) - holder.binding.image.hierarchy.setPlaceholderImage(drawable) + setStaticMimetypeImage(currentItem, holder) } holder.binding.image.setOnClickListener { val fileViewerUtils = FileViewerUtils(it.context, currentItem.userEntity) @@ -72,6 +83,15 @@ class SharedItemsGridAdapter : RecyclerView.Adapter = object : BaseControllerListener() { + override fun onFailure(id: String, e: Throwable) { + Log.w(TAG, "Failed to load image. A static mimetype image will be used", e) + setStaticMimetypeImage(currentItem, holder) + } + } + val draweeController: DraweeController = Fresco.newDraweeControllerBuilder() .setOldController(holder.binding.fileImage.controller) .setAutoPlayAnimations(true) .setImageRequest(imageRequest) + .setControllerListener(listener) .build() holder.binding.fileImage.controller = draweeController } else { - val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(currentItem.mimeType) - val drawable = ContextCompat.getDrawable(holder.binding.fileImage.context, drawableResourceId) - holder.binding.fileImage.hierarchy.setPlaceholderImage(drawable) + setStaticMimetypeImage(currentItem, holder) } holder.binding.fileItem.setOnClickListener { val fileViewerUtils = FileViewerUtils(it.context, currentItem.userEntity) @@ -79,6 +89,15 @@ class SharedItemsListAdapter : RecyclerView.Adapter From d96a6903294ddb0e4179934e514529fcfb89f93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Kr=C3=BCger?= Date: Fri, 29 Apr 2022 15:05:00 +0200 Subject: [PATCH 53/55] Allow one more lint warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tim Krüger --- scripts/analysis/lint-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 8c4822eac..5a424f167 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 1 error and 149 warnings + Lint Report: 1 error and 150 warnings From e0d1f161062ee0ddad349236910952927d462173 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 29 Apr 2022 15:24:42 +0200 Subject: [PATCH 54/55] show progress bar also when user left and re-entered the media tab screen how to test: 1. click on file with big file size that was not clicked before (so it's not in the cache) 2. leave media tab view or just select other tab. 3. go back to the clicked file 4. --> see that progress bar is still spinning (otherwise it's already finished and a click should oen the file directly without showing the progress bar) Signed-off-by: Marcel Hibbe --- .../talk/adapters/SharedItemsGridAdapter.kt | 18 ++++++++++++------ .../talk/adapters/SharedItemsListAdapter.kt | 18 +++++++++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt index 78beacb1d..352512e9d 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt @@ -18,7 +18,6 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder import com.nextcloud.talk.databinding.SharedItemGridBinding import com.nextcloud.talk.repositories.SharedItem import com.nextcloud.talk.utils.DrawableUtils -import com.nextcloud.talk.utils.DrawableUtils.getDrawableResourceIdForMimeType import com.nextcloud.talk.utils.FileViewerUtils class SharedItemsGridAdapter : RecyclerView.Adapter() { @@ -66,9 +65,10 @@ class SharedItemsGridAdapter : RecyclerView.Adapter() { @@ -76,17 +77,28 @@ class SharedItemsListAdapter : RecyclerView.Adapter Date: Fri, 29 Apr 2022 15:32:54 +0200 Subject: [PATCH 55/55] fix klint warning Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt | 3 ++- .../java/com/nextcloud/talk/adapters/SharedItemsListAdapter.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt index 352512e9d..20f723a9c 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/SharedItemsGridAdapter.kt @@ -77,7 +77,8 @@ class SharedItemsGridAdapter : RecyclerView.Adapter