Merge pull request #4759 from nextcloud/attach_files

Attach files to messages for fresh users
This commit is contained in:
Marcel Hibbe 2025-03-06 12:39:59 +00:00 committed by GitHub
commit 70bca0582e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 152 additions and 36 deletions

View File

@ -151,7 +151,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
} else {
Log.d(TAG, "starting normal upload (not chunked) of $fileName")
FileUploader(context, currentUser, roomToken, ncApi)
FileUploader(okHttpClient, context, currentUser, roomToken, ncApi, file!!)
.upload(sourceFileUri, fileName, remotePath, metaData)
.blockingFirst()
}

View File

@ -10,7 +10,10 @@ package com.nextcloud.talk.upload.normal
import android.content.Context
import android.net.Uri
import android.util.Log
import at.bitfire.dav4jvm.DavResource
import at.bitfire.dav4jvm.exception.HttpException
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.dagger.modules.RestModule
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.jobs.ShareOperationWorker
import com.nextcloud.talk.utils.ApiUtils
@ -18,24 +21,48 @@ import com.nextcloud.talk.utils.FileUtils
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Protocol
import okhttp3.RequestBody
import okhttp3.Response
import java.io.File
import java.io.IOException
import java.io.InputStream
class FileUploader(
okHttpClient: OkHttpClient,
val context: Context,
val currentUser: User,
val roomToken: String,
val ncApi: NcApi
val ncApi: NcApi,
val file: File
) {
private var okHttpClientNoRedirects: OkHttpClient? = null
private var okhttpClient: OkHttpClient = okHttpClient
init {
initHttpClient(okHttpClient, currentUser)
}
fun upload(sourceFileUri: Uri, fileName: String, remotePath: String, metaData: String?): Observable<Boolean> {
return ncApi.uploadFile(
ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlForFileUpload(currentUser.baseUrl!!, currentUser.userId!!, remotePath),
ApiUtils.getCredentials(
currentUser.username,
currentUser.token
),
ApiUtils.getUrlForFileUpload(
currentUser.baseUrl!!,
currentUser.userId!!,
remotePath
),
createRequestBody(sourceFileUri)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).map { response ->
.observeOn(AndroidSchedulers.mainThread())
.flatMap { response ->
if (response.isSuccessful) {
ShareOperationWorker.shareFile(
roomToken,
@ -44,13 +71,52 @@ class FileUploader(
metaData
)
FileUtils.copyFileToCache(context, sourceFileUri, fileName)
true
Observable.just(true)
} else {
false
if (response.code() == HTTP_CODE_NOT_FOUND ||
response.code() == HTTP_CODE_CONFLICT
) {
createDavResource(sourceFileUri, fileName, remotePath, metaData)
} else {
Observable.just(false)
}
}
}
}
private fun createDavResource(
sourceFileUri: Uri,
fileName: String,
remotePath: String,
metaData: String?
): Observable<Boolean> {
return Observable.fromCallable {
val userFileUploadPath = ApiUtils.userFileUploadPath(
currentUser.baseUrl!!,
currentUser.userId!!
)
val userTalkAttachmentsUploadPath = ApiUtils.userTalkAttachmentsUploadPath(
currentUser.baseUrl!!,
currentUser.userId!!
)
var davResource = DavResource(
okHttpClientNoRedirects!!,
userFileUploadPath.toHttpUrlOrNull()!!
)
createFolder(davResource)
initHttpClient(okHttpClient = okhttpClient, currentUser)
davResource = DavResource(
okHttpClientNoRedirects!!,
userTalkAttachmentsUploadPath.toHttpUrlOrNull()!!
)
createFolder(davResource)
true
}
.subscribeOn(Schedulers.io())
.flatMap { upload(sourceFileUri, fileName, remotePath, metaData) }
}
@Suppress("Detekt.TooGenericExceptionCaught")
private fun createRequestBody(sourceFileUri: Uri): RequestBody? {
var requestBody: RequestBody? = null
@ -68,7 +134,49 @@ class FileUploader(
return requestBody
}
private fun initHttpClient(okHttpClient: OkHttpClient, currentUser: User) {
val okHttpClientBuilder: OkHttpClient.Builder = okHttpClient.newBuilder()
okHttpClientBuilder.followRedirects(false)
okHttpClientBuilder.followSslRedirects(false)
okHttpClientBuilder.protocols(listOf(Protocol.HTTP_1_1))
okHttpClientBuilder.authenticator(
RestModule.HttpAuthenticator(
ApiUtils.getCredentials(
currentUser.username,
currentUser.token
)!!,
"Authorization"
)
)
this.okHttpClientNoRedirects = okHttpClientBuilder.build()
}
@Suppress("Detekt.ThrowsCount")
private fun createFolder(davResource: DavResource) {
try {
davResource.mkCol(
xmlBody = null
) { response: Response ->
if (!response.isSuccessful) {
throw IOException("failed to create folder. response code: " + response.code)
}
}
} catch (e: IOException) {
throw IOException("failed to create folder", e)
} catch (e: HttpException) {
if (e.code == METHOD_NOT_ALLOWED_CODE) {
Log.d(TAG, "Folder most probably already exists, that's okay, just continue..")
} else {
throw IOException("failed to create folder", e)
}
}
}
companion object {
private val TAG = FileUploader::class.simpleName
private const val METHOD_NOT_ALLOWED_CODE: Int = 405
private const val HTTP_CODE_NOT_FOUND: Int = 404
private const val HTTP_CODE_CONFLICT: Int = 409
}
}

View File

@ -459,6 +459,14 @@ object ApiUtils {
return "$baseUrl/remote.php/dav/files/$user/$remotePath"
}
fun userFileUploadPath(baseUrl: String, user: String): String {
return "$baseUrl/remote.php/dav/files/$user"
}
fun userTalkAttachmentsUploadPath(baseUrl: String, user: String): String {
return "$baseUrl/remote.php/dav/files/$user/Talk"
}
fun getUrlForTempAvatar(baseUrl: String): String {
return "$baseUrl$OCS_API_VERSION/apps/spreed/temp-user-avatar"
}

View File

@ -90,6 +90,35 @@
</RelativeLayout>
<LinearLayout
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:id="@+id/notification_settings_view"
layout="@layout/item_notification_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/standard_quarter_margin" />
<include
android:id="@+id/webinar_info_view"
layout="@layout/item_webinar_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/standard_quarter_margin" />
<include
android:id="@+id/guest_access_view"
layout="@layout/item_guest_access_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/standard_quarter_margin" />
</LinearLayout>
<LinearLayout
android:id="@+id/conversation_description"
android:layout_width="match_parent"
@ -139,35 +168,6 @@
</LinearLayout>
<LinearLayout
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:id="@+id/notification_settings_view"
layout="@layout/item_notification_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/standard_quarter_margin" />
<include
android:id="@+id/webinar_info_view"
layout="@layout/item_webinar_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/standard_quarter_margin" />
<include
android:id="@+id/guest_access_view"
layout="@layout/item_guest_access_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/standard_quarter_margin" />
</LinearLayout>
<LinearLayout
android:id="@+id/shared_items"
android:layout_width="match_parent"