mirror of
https://github.com/nextcloud/talk-android
synced 2025-03-06 06:15:12 +00:00
Impl "Add to Notes Action"
- Note to self option should only appear if conversation is available - Added ic_edit_note_24_xml - Implemented MVVM functions for cleaner data flow - Added the option to the XML - Works for Voice Messages - Works for Files(and Gifs) + captions - Works for GeoLocation Messages - Added SnackBar Signed-off-by: Julius Linus <julius.linus@nextcloud.com>
This commit is contained in:
parent
5b85731097
commit
d45277beaf
@ -2682,8 +2682,9 @@ class ChatActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun uploadFile(fileUri: String, isVoiceMessage: Boolean, caption: String = "") {
|
||||
private fun uploadFile(fileUri: String, isVoiceMessage: Boolean, caption: String = "", token: String = "") {
|
||||
var metaData = ""
|
||||
var room = ""
|
||||
|
||||
if (!participantPermissions.hasChatPermission()) {
|
||||
Log.w(TAG, "uploading file(s) is forbidden because of missing attendee permissions")
|
||||
@ -2698,11 +2699,13 @@ class ChatActivity :
|
||||
metaData = "{\"caption\":\"$caption\"}"
|
||||
}
|
||||
|
||||
if (token == "") room = roomToken else room = token
|
||||
|
||||
try {
|
||||
require(fileUri.isNotEmpty())
|
||||
UploadAndShareFilesWorker.upload(
|
||||
fileUri,
|
||||
roomToken,
|
||||
room,
|
||||
currentConversation?.displayName!!,
|
||||
metaData
|
||||
)
|
||||
@ -4171,6 +4174,82 @@ class ChatActivity :
|
||||
}
|
||||
}
|
||||
|
||||
fun shareToNotes(message: ChatMessage, roomToken: String) {
|
||||
val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1))
|
||||
val type = message.getCalculateMessageType()
|
||||
var shareUri: Uri? = null
|
||||
var data: HashMap<String?, String?>?
|
||||
var metaData: String = ""
|
||||
var objectId: String = ""
|
||||
if (message.hasFileAttachment()) {
|
||||
val filename = message.selectedIndividualHashMap!!["name"]
|
||||
path = applicationContext.cacheDir.absolutePath + "/" + filename
|
||||
shareUri = FileProvider.getUriForFile(
|
||||
this,
|
||||
BuildConfig.APPLICATION_ID,
|
||||
File(path)
|
||||
)
|
||||
|
||||
this.grantUriPermission(
|
||||
applicationContext.packageName,
|
||||
shareUri,
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
)
|
||||
} else if (message.hasGeoLocation()) {
|
||||
data = message.messageParameters?.get("object")
|
||||
objectId = data?.get("id")!!
|
||||
val name = data.get("name")!!
|
||||
val lat = data.get("latitude")!!
|
||||
val lon = data.get("longitude")!!
|
||||
metaData =
|
||||
"{\"type\":\"geo-location\",\"id\":\"geo:$lat,$lon\",\"latitude\":\"$lat\"," +
|
||||
"\"longitude\":\"$lon\",\"name\":\"$name\"}"
|
||||
}
|
||||
|
||||
when (type) {
|
||||
ChatMessage.MessageType.VOICE_MESSAGE -> {
|
||||
uploadFile(shareUri.toString(), true, token = roomToken)
|
||||
Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE -> {
|
||||
val caption = if (message.message != "{file}") message.message else ""
|
||||
if (null != shareUri) {
|
||||
try {
|
||||
context.contentResolver.openInputStream(shareUri)?.close()
|
||||
uploadFile(shareUri.toString(), false, caption!!, roomToken)
|
||||
Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show()
|
||||
} catch (e: java.lang.Exception) {
|
||||
Log.w(TAG, "File corresponding to the uri does not exist " + shareUri.toString())
|
||||
downloadFileToCache(message, false) {
|
||||
uploadFile(shareUri.toString(), false, caption!!, roomToken)
|
||||
Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ChatMessage.MessageType.SINGLE_NC_GEOLOCATION_MESSAGE -> {
|
||||
chatViewModel.shareLocationToNotes(
|
||||
credentials!!,
|
||||
ApiUtils.getUrlToSendLocation(apiVersion, conversationUser!!.baseUrl, roomToken),
|
||||
"geo-location",
|
||||
objectId,
|
||||
metaData
|
||||
)
|
||||
Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
ChatMessage.MessageType.REGULAR_TEXT_MESSAGE -> {
|
||||
chatViewModel.shareToNotes(
|
||||
credentials!!,
|
||||
ApiUtils.getUrlForChat(apiVersion, conversationUser!!.baseUrl, roomToken),
|
||||
message.message!!,
|
||||
conversationUser!!.displayName!!
|
||||
)
|
||||
Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
fun openInFilesApp(message: ChatMessage) {
|
||||
val keyID = message.selectedIndividualHashMap!![PreviewMessageViewHolder.KEY_ID]
|
||||
val link = message.selectedIndividualHashMap!!["link"]
|
||||
|
@ -22,6 +22,7 @@ package com.nextcloud.talk.chat.data
|
||||
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.reminder.Reminder
|
||||
import io.reactivex.Observable
|
||||
@ -32,4 +33,18 @@ interface ChatRepository {
|
||||
fun setReminder(user: User, roomToken: String, messageId: String, timeStamp: Int): Observable<Reminder>
|
||||
fun getReminder(user: User, roomToken: String, messageId: String): Observable<Reminder>
|
||||
fun deleteReminder(user: User, roomToken: String, messageId: String): Observable<GenericOverall>
|
||||
fun shareToNotes(
|
||||
credentials: String,
|
||||
url: String,
|
||||
message: String,
|
||||
displayName: String
|
||||
): Observable<GenericOverall> // last two fields are false
|
||||
fun checkForNoteToSelf(credentials: String, url: String, includeStatus: Boolean): Observable<RoomsOverall>
|
||||
fun shareLocationToNotes(
|
||||
credentials: String,
|
||||
url: String,
|
||||
objectType: String,
|
||||
objectId: String,
|
||||
metadata: String
|
||||
): Observable<GenericOverall>
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ package com.nextcloud.talk.chat.data
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.reminder.Reminder
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
@ -83,4 +84,40 @@ class ChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
override fun shareToNotes(
|
||||
credentials: String,
|
||||
url: String,
|
||||
message: String,
|
||||
displayName: String
|
||||
): Observable<GenericOverall> {
|
||||
return ncApi.sendChatMessage(
|
||||
credentials,
|
||||
url,
|
||||
message,
|
||||
displayName,
|
||||
null,
|
||||
false
|
||||
).map {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkForNoteToSelf(
|
||||
credentials: String,
|
||||
url: String,
|
||||
includeStatus: Boolean
|
||||
): Observable<RoomsOverall> {
|
||||
return ncApi.getRooms(credentials, url, includeStatus).map { it }
|
||||
}
|
||||
|
||||
override fun shareLocationToNotes(
|
||||
credentials: String,
|
||||
url: String,
|
||||
objectType: String,
|
||||
objectId: String,
|
||||
metadata: String
|
||||
): Observable<GenericOverall> {
|
||||
return ncApi.sendLocation(credentials, url, objectType, objectId, metadata).map { it }
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,10 @@ import androidx.lifecycle.ViewModel
|
||||
import com.nextcloud.talk.chat.data.ChatRepository
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
import com.nextcloud.talk.models.json.conversations.RoomsOverall
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.reminder.Reminder
|
||||
import com.nextcloud.talk.utils.ConversationUtils
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
@ -49,6 +51,13 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
|
||||
val getReminderExistState: LiveData<ViewState>
|
||||
get() = _getReminderExistState
|
||||
|
||||
object NoteToSelfNotAvaliableState : ViewState
|
||||
open class NoteToSelfAvaliableState(val roomToken: String) : ViewState
|
||||
|
||||
private val _getNoteToSelfAvaliability: MutableLiveData<ViewState> = MutableLiveData(NoteToSelfNotAvaliableState)
|
||||
val getNoteToSelfAvaliability: LiveData<ViewState>
|
||||
get() = _getNoteToSelfAvaliability
|
||||
|
||||
open class GetRoomSuccessState(val conversationModel: ConversationModel) : ViewState
|
||||
|
||||
private val _getRoomViewState: MutableLiveData<ViewState> = MutableLiveData(GetRoomStartState)
|
||||
@ -117,6 +126,58 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
|
||||
})
|
||||
}
|
||||
|
||||
fun shareToNotes(credentials: String, url: String, message: String, displayName: String) {
|
||||
repository.shareToNotes(credentials, url, message, displayName)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(genericOverall: GenericOverall) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.d(TAG, "Error when sharing to notes $e")
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun checkForNoteToSelf(credentials: String, baseUrl: String, includeStatus: Boolean) {
|
||||
repository.checkForNoteToSelf(credentials, baseUrl, includeStatus).subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(CheckForNoteToSelfObserver())
|
||||
}
|
||||
|
||||
fun shareLocationToNotes(credentials: String, url: String, objectType: String, objectId: String, metadata: String) {
|
||||
repository.shareLocationToNotes(credentials, url, objectType, objectId, metadata)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(genericOverall: GenericOverall) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "Error when sharing location to notes $e")
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
inner class GetRoomObserver : Observer<ConversationModel> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
@ -192,6 +253,36 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository)
|
||||
}
|
||||
}
|
||||
|
||||
inner class CheckForNoteToSelfObserver : Observer<RoomsOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(roomsOverall: RoomsOverall) {
|
||||
val rooms = roomsOverall.ocs?.data
|
||||
rooms?.let {
|
||||
try {
|
||||
val noteToSelf = rooms.first {
|
||||
val model = ConversationModel.mapToConversationModel(it)
|
||||
ConversationUtils.isNoteToSelfConversation(model)
|
||||
}
|
||||
_getNoteToSelfAvaliability.value = NoteToSelfAvaliableState(noteToSelf.token!!)
|
||||
} catch (e: NoSuchElementException) {
|
||||
_getNoteToSelfAvaliability.value = NoteToSelfNotAvaliableState
|
||||
Log.e(TAG, "Note to self not found $e")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.d(TAG, "Error when getting rooms for Note to Self Observer $e")
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = ChatViewModel::class.simpleName
|
||||
const val JOIN_ROOM_RETRY_COUNT: Long = 3
|
||||
|
@ -152,7 +152,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
|
||||
remotePath
|
||||
)
|
||||
} else {
|
||||
Log.d(TAG, "starting normal upload (not chunked)")
|
||||
Log.d(TAG, "starting normal upload (not chunked) of $fileName")
|
||||
|
||||
uploadSuccess = FileUploader(
|
||||
context,
|
||||
|
@ -38,6 +38,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.DialogMessageActionsBinding
|
||||
import com.nextcloud.talk.models.domain.ConversationModel
|
||||
@ -48,6 +49,8 @@ import com.nextcloud.talk.models.domain.ReactionDeletedModel
|
||||
import com.nextcloud.talk.models.json.chat.ChatMessage
|
||||
import com.nextcloud.talk.repositories.reactions.ReactionsRepository
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.ConversationUtils
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import com.vanniktech.emoji.EmojiPopup
|
||||
import com.vanniktech.emoji.EmojiTextView
|
||||
@ -96,6 +99,31 @@ class MessageActionsDialog(
|
||||
viewThemeUtils.platform.themeDialog(dialogMessageActionsBinding.root)
|
||||
initEmojiBar(hasChatPermission)
|
||||
initMenuItemCopy(!message.isDeleted)
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1))
|
||||
chatActivity.chatViewModel.checkForNoteToSelf(
|
||||
ApiUtils.getCredentials(user!!.username, user.token),
|
||||
ApiUtils.getUrlForRooms(
|
||||
apiVersion,
|
||||
user.baseUrl
|
||||
),
|
||||
false
|
||||
)
|
||||
chatActivity.chatViewModel.getNoteToSelfAvaliability.observe(this) { state ->
|
||||
when (state) {
|
||||
is ChatViewModel.NoteToSelfAvaliableState -> {
|
||||
initMenuAddToNote(
|
||||
!message.isDeleted && !ConversationUtils.isNoteToSelfConversation(currentConversation),
|
||||
state.roomToken
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
initMenuAddToNote(
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initMenuItemTranslate(
|
||||
!message.isDeleted &&
|
||||
ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() &&
|
||||
@ -374,6 +402,16 @@ class MessageActionsDialog(
|
||||
dialogMessageActionsBinding.menuSaveMessage.visibility = getVisibility(visible)
|
||||
}
|
||||
|
||||
private fun initMenuAddToNote(visible: Boolean, roomToken: String = "") {
|
||||
if (visible) {
|
||||
dialogMessageActionsBinding.menuShareToNote.setOnClickListener {
|
||||
chatActivity.shareToNotes(message, roomToken)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
dialogMessageActionsBinding.menuShareToNote.visibility = getVisibility(visible)
|
||||
}
|
||||
|
||||
private fun getVisibility(visible: Boolean): Int {
|
||||
return if (visible) {
|
||||
View.VISIBLE
|
||||
|
21
app/src/main/res/drawable/ic_edit_note_24.xml
Normal file
21
app/src/main/res/drawable/ic_edit_note_24.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<!--
|
||||
@author Google LLC
|
||||
Copyright (C) 2021 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M3,10h11v2H3V10zM3,8h11V6H3V8zM3,16h7v-2H3V16zM18.01,12.87l0.71,-0.71c0.39,-0.39 1.02,-0.39 1.41,0l0.71,0.71c0.39,0.39 0.39,1.02 0,1.41l-0.71,0.71L18.01,12.87zM17.3,13.58l-5.3,5.3V21h2.12l5.3,-5.3L17.3,13.58z"/>
|
||||
|
||||
</vector>
|
@ -358,6 +358,39 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_share_to_note"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/menu_icon_share_to_note"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingEnd="@dimen/zero"
|
||||
android:src="@drawable/ic_edit_note_24"
|
||||
app:tint="@color/high_emphasis_menu_icon" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/menu_text_share_to_note"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:paddingStart="@dimen/standard_double_padding"
|
||||
android:paddingEnd="@dimen/standard_padding"
|
||||
android:text="@string/add_to_notes"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text"
|
||||
android:textSize="@dimen/bottom_sheet_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/menu_share"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -786,4 +786,5 @@ How to translate with transifex:
|
||||
<string name="nc_caption">Caption</string>
|
||||
<string name="languages_error_title">Retrieval failed</string>
|
||||
<string name="languages_error_message">Languages could not be retrieved</string>
|
||||
<string name="add_to_notes">Add to Notes</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user