From 0b35971ee55b2de27781040cc1750373997b8edf Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 8 Jan 2021 09:55:21 +0100 Subject: [PATCH] add ability to upload one or multiple files from local storage #254 Signed-off-by: Marcel Hibbe --- app/build.gradle | 2 - .../java/com/nextcloud/talk/api/NcApi.java | 12 +- .../talk/controllers/ChatController.kt | 114 ++++++++--- .../talk/jobs/UploadAndShareFilesWorker.kt | 178 ++++++++++++++++++ .../nextcloud/talk/models/database/User.java | 31 ++- .../talk/ui/dialog/AttachmentDialog.kt | 66 +++++++ .../com/nextcloud/talk/utils/ApiUtils.java | 11 +- .../com/nextcloud/talk/utils/LoggingUtils.kt | 2 +- app/src/main/res/layout/dialog_attachment.xml | 45 +++++ app/src/main/res/values/strings.xml | 6 + 10 files changed, 430 insertions(+), 37 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt create mode 100644 app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt create mode 100644 app/src/main/res/layout/dialog_attachment.xml diff --git a/app/build.gradle b/app/build.gradle index 963e96dd5..763ccbd3f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,8 +60,6 @@ android { lintOptions { disable 'InvalidPackage' disable 'MissingTranslation' - disable "ValidController" - disable "ValidControllerChangeHandler" } javaCompileOptions { 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 b11da3786..66a56acd4 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -20,6 +20,8 @@ */ package com.nextcloud.talk.api; +import androidx.annotation.Nullable; + import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; import com.nextcloud.talk.models.json.chat.ChatOverall; import com.nextcloud.talk.models.json.conversations.RoomOverall; @@ -39,7 +41,6 @@ import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; import java.util.List; import java.util.Map; -import androidx.annotation.Nullable; import io.reactivex.Observable; import okhttp3.RequestBody; import okhttp3.ResponseBody; @@ -327,7 +328,6 @@ public interface NcApi { @PUT Observable setReadOnlyState(@Header("Authorization") String authorization, @Url String url, @Field("state") int state); - @FormUrlEncoded @POST Observable createRemoteShare(@Nullable @Header("Authorization") String authorization, @Url String url, @@ -338,15 +338,21 @@ public interface NcApi { @FormUrlEncoded @PUT Observable setLobbyForConversation(@Header("Authorization") String authorization, - @Url String url, @Field("state") Integer state, + @Url String url, @Field("state") Integer state, @Field("timer") Long timer); @POST Observable setReadStatusPrivacy(@Header("Authorization") String authorization, @Url String url, @Body RequestBody body); + @POST Observable searchContactsByPhoneNumber(@Header("Authorization") String authorization, @Url String url, @Body RequestBody search); + + @PUT + Observable> uploadFile(@Header("Authorization") String authorization, + @Url String url, + @Body RequestBody body); } 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 6331ccc17..6f2a929d7 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -20,6 +20,7 @@ package com.nextcloud.talk.controllers +import android.app.Activity.RESULT_OK import android.content.ClipData import android.content.Context import android.content.Intent @@ -44,6 +45,9 @@ import androidx.emoji.widget.EmojiEditText import androidx.emoji.widget.EmojiTextView import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import androidx.work.Data +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager import autodagger.AutoInjector import butterknife.BindView import butterknife.OnClick @@ -70,6 +74,7 @@ import com.nextcloud.talk.components.filebrowser.controllers.BrowserController import com.nextcloud.talk.controllers.base.BaseController import com.nextcloud.talk.events.UserMentionClickEvent import com.nextcloud.talk.events.WebSocketCommunicationEvent +import com.nextcloud.talk.jobs.UploadAndShareFilesWorker import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatOverall @@ -80,11 +85,9 @@ import com.nextcloud.talk.models.json.conversations.RoomsOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.mention.Mention import com.nextcloud.talk.presenters.MentionAutocompletePresenter +import com.nextcloud.talk.ui.dialog.AttachmentDialog import com.nextcloud.talk.utils.* import com.nextcloud.talk.utils.bundle.BundleKeys -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 import com.nextcloud.talk.utils.database.user.UserUtils import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder @@ -123,46 +126,60 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter @Inject @JvmField var ncApi: NcApi? = null + @Inject @JvmField var userUtils: UserUtils? = null + @Inject @JvmField var appPreferences: AppPreferences? = null + @Inject @JvmField var context: Context? = null + @Inject @JvmField var eventBus: EventBus? = null + @BindView(R.id.messagesListView) @JvmField var messagesListView: MessagesList? = null + @BindView(R.id.messageInputView) @JvmField var messageInputView: MessageInput? = null + @BindView(R.id.messageInput) @JvmField var messageInput: EmojiEditText? = null + @BindView(R.id.popupBubbleView) @JvmField var popupBubble: PopupBubble? = null + @BindView(R.id.progressBar) @JvmField var loadingProgressBar: ProgressBar? = null + @BindView(R.id.smileyButton) @JvmField var smileyButton: ImageButton? = null + @BindView(R.id.lobby_view) @JvmField var lobbyView: RelativeLayout? = null + @BindView(R.id.lobby_text_view) @JvmField var conversationLobbyText: TextView? = null val disposableList = ArrayList() + @JvmField @BindView(R.id.quotedChatMessageView) var quotedChatMessageView: RelativeLayout? = null + @BindView(R.id.callControlToggleChat) @JvmField var toggleChat: SimpleDraweeView? = null @@ -378,7 +395,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter messagesListView?.setAdapter(adapter) adapter?.setLoadMoreListener(this) adapter?.setDateHeadersFormatter { format(it) } - adapter?.setOnMessageViewLongClickListener { view, message -> onMessageViewLongClick(view, message)} + adapter?.setOnMessageViewLongClickListener { view, message -> onMessageViewLongClick(view, message) } layoutManager = messagesListView?.layoutManager as LinearLayoutManager? @@ -400,8 +417,8 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter toggleChat?.visibility = View.VISIBLE wasDetached = true } - - toggleChat?.setOnClickListener{ + + toggleChat?.setOnClickListener { (activity as MagicCallActivity).showCall() } @@ -467,8 +484,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter }) messageInputView?.setAttachmentsListener { - showBrowserScreen(BrowserController - .BrowserType.DAV_BROWSER) + activity?.let { AttachmentDialog(it, this).show() }; } messageInputView?.button?.setOnClickListener { v -> submitMessage() } @@ -495,7 +511,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter private fun checkReadOnlyState() { if (currentConversation != null) { - if (currentConversation?.shouldShowLobby(conversationUser)?: false || currentConversation?.conversationReadOnlyState != null && currentConversation?.conversationReadOnlyState == Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY) { + if (currentConversation?.shouldShowLobby(conversationUser) ?: false || currentConversation?.conversationReadOnlyState != null && currentConversation?.conversationReadOnlyState == Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY) { conversationVoiceCallMenuItem?.icon?.alpha = 99 conversationVideoMenuItem?.icon?.alpha = 99 @@ -559,7 +575,58 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter } } - private fun showBrowserScreen(browserType: BrowserController.BrowserType) { + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == REQUEST_CODE_CHOOSE_FILE) { + if (resultCode == RESULT_OK) { + uploadFile(data) + } + } + } + + private fun uploadFile(intentData: Intent?) { + try { + checkNotNull(intentData) + val files: MutableList = ArrayList() + intentData.clipData?.let { + for (index in 0 until it.itemCount) { + files.add(it.getItemAt(index).uri.toString()) + } + } ?: run { + checkNotNull(intentData.data) + intentData.data.let { + files.add(intentData.data.toString()) + } + } + require(files.isNotEmpty()) + val data: Data = Data.Builder() + .putStringArray(UploadAndShareFilesWorker.DEVICE_SOURCEFILES, files.toTypedArray()) + .putString(UploadAndShareFilesWorker.NC_TARGETPATH, conversationUser?.getAttachmentFolder()) + .putString(UploadAndShareFilesWorker.ROOM_TOKEN, roomToken) + .build() + val uploadWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(UploadAndShareFilesWorker::class.java) + .setInputData(data) + .build() + WorkManager.getInstance().enqueue(uploadWorker) + } catch (e: IllegalStateException) { + Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show() + Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) + } catch (e: IllegalArgumentException) { + Toast.makeText(context, context?.resources?.getString(R.string.nc_upload_failed), Toast.LENGTH_LONG).show() + Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) + } + } + + fun sendSelectLocalFileIntent() { + val action = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + type = "*/*" + addCategory(Intent.CATEGORY_OPENABLE) + putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) + } + startActivityForResult(Intent.createChooser(action, context?.resources?.getString( + R.string.nc_upload_choose_local_files)), REQUEST_CODE_CHOOSE_FILE); + } + + fun showBrowserScreen(browserType: BrowserController.BrowserType) { val bundle = Bundle() bundle.putParcelable(BundleKeys.KEY_BROWSER_TYPE, Parcels.wrap(browserType)) bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, Parcels.wrap(conversationUser)) @@ -618,13 +685,13 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter emojiPopup = messageInput?.let { EmojiPopup.Builder.fromRootView(view).setOnEmojiPopupShownListener { - if (resources != null) { - smileyButton?.setColorFilter(resources!!.getColor(R.color.colorPrimary), PorterDuff.Mode.SRC_IN) - } - }.setOnEmojiPopupDismissListener { - smileyButton?.setColorFilter(resources!!.getColor(R.color.emoji_icons), - PorterDuff.Mode.SRC_IN) - }.setOnEmojiClickListener { emoji, imageView -> messageInput?.editableText?.append(" ") }.build(it) + if (resources != null) { + smileyButton?.setColorFilter(resources!!.getColor(R.color.colorPrimary), PorterDuff.Mode.SRC_IN) + } + }.setOnEmojiPopupDismissListener { + smileyButton?.setColorFilter(resources!!.getColor(R.color.emoji_icons), + PorterDuff.Mode.SRC_IN) + }.setOnEmojiClickListener { emoji, imageView -> messageInput?.editableText?.append(" ") }.build(it) } if (activity != null) { @@ -656,7 +723,7 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter override fun onDetach(view: View) { super.onDetach(view) - + if (!isLeavingForConversation) { // current room is still "active", we need the info ApplicationWideCurrentRoomHolder.getInstance().clear() @@ -878,11 +945,11 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter if (conversationUser != null) { ncApi!!.sendChatMessage( - credentials, ApiUtils.getUrlForChat( - conversationUser.baseUrl, - roomToken - ), - message, conversationUser.displayName, replyTo + credentials, + ApiUtils.getUrlForChat(conversationUser.baseUrl, roomToken), + message, + conversationUser.displayName, + replyTo ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) @@ -1457,5 +1524,6 @@ class ChatController(args: Bundle) : BaseController(args), MessagesListAdapter private val TAG = "ChatController" private val CONTENT_TYPE_SYSTEM_MESSAGE: Byte = 1 private val CONTENT_TYPE_UNREAD_NOTICE_MESSAGE: Byte = 2 + val REQUEST_CODE_CHOOSE_FILE: Int = 555 } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt new file mode 100644 index 000000000..c4a1db980 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt @@ -0,0 +1,178 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2021 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.jobs + +import android.content.Context +import android.database.Cursor +import android.net.Uri +import android.provider.OpenableColumns +import android.util.Log +import androidx.work.* +import autodagger.AutoInjector +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_PATHS +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN +import com.nextcloud.talk.utils.database.user.UserUtils +import com.nextcloud.talk.utils.preferences.AppPreferences +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import okhttp3.MediaType +import okhttp3.RequestBody +import retrofit2.Response +import java.io.InputStream +import java.util.* +import javax.inject.Inject + + +@AutoInjector(NextcloudTalkApplication::class) +class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerParameters) : + Worker(context, workerParameters) { + + @Inject + lateinit var ncApi: NcApi + + @Inject + lateinit var userUtils: UserUtils + + @Inject + lateinit var appPreferences: AppPreferences + + override fun doWork(): Result { + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + + try { + val currentUser = userUtils.currentUser + val sourcefiles = inputData.getStringArray(DEVICE_SOURCEFILES) + val ncTargetpath = inputData.getString(NC_TARGETPATH) + val roomToken = inputData.getString(ROOM_TOKEN) + + checkNotNull(currentUser) + checkNotNull(sourcefiles) + require(sourcefiles.isNotEmpty()) + checkNotNull(ncTargetpath) + checkNotNull(roomToken) + + for (index in sourcefiles.indices) { + val sourcefileUri = Uri.parse(sourcefiles[index]) + var filename = getFileName(sourcefileUri) + val requestBody = createRequestBody(sourcefileUri) + uploadFile(currentUser, ncTargetpath, filename, roomToken, requestBody) + } + } catch (e: IllegalStateException) { + Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) + return Result.failure() + } catch (e: IllegalArgumentException) { + Log.e(javaClass.simpleName, "Something went wrong when trying to upload file", e) + return Result.failure() + } + return Result.success() + } + + private fun createRequestBody(sourcefileUri: Uri): RequestBody? { + var requestBody: RequestBody? = null + try { + val input: InputStream = context.contentResolver.openInputStream(sourcefileUri)!! + val buf = ByteArray(input.available()) + while (input.read(buf) != -1); + requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), buf) + } catch (e: Exception) { + Log.e(javaClass.simpleName, "failed to create RequestBody for $sourcefileUri", e) + } + return requestBody + } + + private fun uploadFile(currentUser: UserEntity, ncTargetpath: String?, filename: String?, roomToken: String?, requestBody: RequestBody?) { + ncApi.uploadFile( + ApiUtils.getCredentials(currentUser.username, currentUser.token), + ApiUtils.getUrlForFileUpload(currentUser.baseUrl, currentUser.userId, ncTargetpath, filename), + requestBody + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer> { + override fun onSubscribe(d: Disposable) { + } + + override fun onNext(t: Response) { + } + + override fun onError(e: Throwable) { + Log.e(TAG, "failed to upload file $filename") + } + + override fun onComplete() { + shareFile(roomToken, currentUser, ncTargetpath, filename) + } + }) + } + + private fun shareFile(roomToken: String?, currentUser: UserEntity, ncTargetpath: String?, filename: String?) { + val paths: MutableList = ArrayList() + paths.add("$ncTargetpath/$filename") + + val data = Data.Builder() + .putLong(KEY_INTERNAL_USER_ID, currentUser.id) + .putString(KEY_ROOM_TOKEN, roomToken) + .putStringArray(KEY_FILE_PATHS, paths.toTypedArray()) + .build() + val shareWorker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java) + .setInputData(data) + .build() + WorkManager.getInstance().enqueue(shareWorker) + } + + private fun getFileName(uri: Uri): String? { + var filename: String? = null + if (uri.scheme == "content") { + val cursor: Cursor? = context.contentResolver.query(uri, null, null, null, null) + try { + if (cursor != null && cursor.moveToFirst()) { + filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)) + } + } finally { + cursor?.close() + } + } + if (filename == null) { + Log.e(TAG, "failed to get DISPLAY_NAME from uri. using fallback.") + filename = uri.path + val lastIndexOfSlash = filename!!.lastIndexOf('/') + if (lastIndexOfSlash != -1) { + filename = filename.substring(lastIndexOfSlash + 1) + } + } + return filename + } + + companion object { + const val TAG = "UploadFileWorker" + const val DEVICE_SOURCEFILES = "DEVICE_SOURCEFILES" + const val NC_TARGETPATH = "NC_TARGETPATH" + const val ROOM_TOKEN = "ROOM_TOKEN" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/models/database/User.java b/app/src/main/java/com/nextcloud/talk/models/database/User.java index f28cc1f5a..28b0f4b5c 100644 --- a/app/src/main/java/com/nextcloud/talk/models/database/User.java +++ b/app/src/main/java/com/nextcloud/talk/models/database/User.java @@ -138,7 +138,7 @@ public interface User extends Parcelable, Persistable, Serializable { capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); return capabilities != null && capabilities.getSpreedCapability() != null && - capabilities.getSpreedCapability().getFeatures() != null && + capabilities.getSpreedCapability().getFeatures() != null && capabilities.getSpreedCapability().getFeatures().contains("phonebook-search"); } catch (IOException e) { e.printStackTrace(); @@ -165,15 +165,15 @@ public interface User extends Parcelable, Persistable, Serializable { } return false; } - + default boolean isReadStatusPrivate() { if (getCapabilities() != null) { Capabilities capabilities; try { capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); - if (capabilities != null && - capabilities.getSpreedCapability() != null && - capabilities.getSpreedCapability().getConfig() != null && + if (capabilities != null && + capabilities.getSpreedCapability() != null && + capabilities.getSpreedCapability().getConfig() != null && capabilities.getSpreedCapability().getConfig().containsKey("chat")) { HashMap map = capabilities.getSpreedCapability().getConfig().get("chat"); if (map != null && map.containsKey("read-privacy")) { @@ -186,4 +186,25 @@ public interface User extends Parcelable, Persistable, Serializable { } return false; } + + default String getAttachmentFolder() { + if (getCapabilities() != null) { + Capabilities capabilities; + try { + capabilities = LoganSquare.parse(getCapabilities(), Capabilities.class); + if (capabilities != null && + capabilities.getSpreedCapability() != null && + capabilities.getSpreedCapability().getConfig() != null && + capabilities.getSpreedCapability().getConfig().containsKey("attachments")) { + HashMap map = capabilities.getSpreedCapability().getConfig().get("attachments"); + if (map != null && map.containsKey("folder")) { + return map.get("folder"); + } + } + } catch (IOException e) { + Log.e("User.java", "Failed to get attachment folder", e); + } + } + return "/Talk"; + } } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt new file mode 100644 index 000000000..300209207 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt @@ -0,0 +1,66 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2021 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.ui.dialog + +import android.app.Activity +import android.os.Bundle +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatTextView +import butterknife.BindView +import butterknife.ButterKnife +import butterknife.Unbinder +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.nextcloud.talk.R +import com.nextcloud.talk.components.filebrowser.controllers.BrowserController +import com.nextcloud.talk.controllers.ChatController + + +class AttachmentDialog(val activity: Activity, var chatController :ChatController) : BottomSheetDialog(activity) { + + @BindView(R.id.txt_attach_file_from_local) + @JvmField + var attachFromLocal: AppCompatTextView? = null + + @BindView(R.id.txt_attach_file_from_cloud) + @JvmField + var attachFromCloud: AppCompatTextView? = null + + private var unbinder: Unbinder? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState); + val view = layoutInflater.inflate(R.layout.dialog_attachment, null); + setContentView(view); + + window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + unbinder = ButterKnife.bind(this, view); + + attachFromLocal?.setOnClickListener { + chatController.sendSelectLocalFileIntent(); + dismiss() + } + attachFromCloud?.setOnClickListener { + chatController.showBrowserScreen(BrowserController.BrowserType.DAV_BROWSER) + dismiss() + } + } +} 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 3d8bb1d3f..ba80f8f90 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -22,6 +22,8 @@ package com.nextcloud.talk.utils; import android.net.Uri; import android.text.TextUtils; +import androidx.annotation.DimenRes; + import com.nextcloud.talk.BuildConfig; import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; @@ -32,7 +34,6 @@ import java.util.Map; import javax.annotation.Nullable; -import androidx.annotation.DimenRes; import okhttp3.Credentials; public class ApiUtils { @@ -245,7 +246,7 @@ public class ApiUtils { } public static String getUrlForAvatarWithNameForGuests(String baseUrl, String name, - @DimenRes int avatarSize) { + @DimenRes int avatarSize) { avatarSize = Math.round(NextcloudTalkApplication .Companion.getSharedApplication().getResources().getDimension(avatarSize)); @@ -283,8 +284,12 @@ public class ApiUtils { public static String getUrlForReadOnlyState(String baseUrl, String roomToken) { return baseUrl + ocsApiVersion + spreedApiVersion + "/room/" + roomToken + "/read-only"; } - + public static String getUrlForSearchByNumber(String baseUrl) { return baseUrl + ocsApiVersion + "/cloud/users/search/by-phone"; } + + public static String getUrlForFileUpload(String baseUrl, String user, String attachmentFolder, String filename) { + return baseUrl + "/remote.php/dav/files/" + user + attachmentFolder + "/" + filename; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt index 6718758fb..774a906b1 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/LoggingUtils.kt @@ -54,7 +54,7 @@ object LoggingUtils { fun sendMailWithAttachment(context: Context) { val logFile = context.getFileStreamPath("nc_log.txt") val emailIntent = Intent(Intent.ACTION_SEND) - val mailto = "android@nextcloud.com" + val mailto = "mario@nextcloud.com" emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(mailto)) emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Talk logs") emailIntent.type = "text/plain" diff --git a/app/src/main/res/layout/dialog_attachment.xml b/app/src/main/res/layout/dialog_attachment.xml new file mode 100644 index 000000000..a013ecb0a --- /dev/null +++ b/app/src/main/res/layout/dialog_attachment.xml @@ -0,0 +1,45 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8091ebe9f..af63da3d7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -321,6 +321,12 @@ Copy Reply + + Upload local file + Upload from Nextcloud + Failed to upload file + Choose files +