Attachments: use a lib which handles for us all the intent stuff.
This commit is contained in:
parent
3073470c38
commit
0a9ebb6bf6
|
@ -12,7 +12,7 @@ buildscript {
|
|||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||
classpath 'com.google.gms:google-services:4.3.2'
|
||||
classpath "com.airbnb.okreplay:gradle-plugin:1.5.0"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
|
|
@ -320,6 +320,7 @@ dependencies {
|
|||
|
||||
// File picker
|
||||
implementation 'com.github.jaiselrahman:FilePicker:1.2.2'
|
||||
implementation 'com.kbeanie:multipicker:1.6@aar'
|
||||
|
||||
// DI
|
||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.attachments
|
||||
|
||||
import im.vector.riotx.core.resources.MIME_TYPE_ALL_CONTENT
|
||||
|
||||
data class Attachment(val path: String,
|
||||
val mimeType: String,
|
||||
val name: String? = "",
|
||||
val width: Long? = 0,
|
||||
val height: Long? = 0,
|
||||
val size: Long = 0,
|
||||
val duration: Long? = 0,
|
||||
val date: Long = 0) {
|
||||
|
||||
val type: Int
|
||||
get() {
|
||||
if (mimeType == null) {
|
||||
return TYPE_FILE
|
||||
}
|
||||
return when {
|
||||
mimeType.startsWith("image/") -> TYPE_IMAGE
|
||||
mimeType.startsWith("video/") -> TYPE_VIDEO
|
||||
mimeType.startsWith("audio/")
|
||||
-> TYPE_AUDIO
|
||||
else -> TYPE_FILE
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TYPE_FILE = 0
|
||||
val TYPE_IMAGE = 1
|
||||
val TYPE_AUDIO = 2
|
||||
val TYPE_VIDEO = 3
|
||||
}
|
||||
}
|
|
@ -15,138 +15,174 @@
|
|||
*/
|
||||
package im.vector.riotx.features.attachments
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import android.provider.OpenableColumns
|
||||
import androidx.core.content.FileProvider
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.riotx.BuildConfig
|
||||
import im.vector.riotx.core.resources.MIME_TYPE_ALL_CONTENT
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import com.kbeanie.multipicker.api.CameraImagePicker
|
||||
import com.kbeanie.multipicker.api.FilePicker
|
||||
import com.kbeanie.multipicker.api.ImagePicker
|
||||
import com.kbeanie.multipicker.api.Picker.*
|
||||
import com.kbeanie.multipicker.api.callbacks.FilePickerCallback
|
||||
import com.kbeanie.multipicker.api.callbacks.ImagePickerCallback
|
||||
import com.kbeanie.multipicker.api.entity.ChosenFile
|
||||
import com.kbeanie.multipicker.api.entity.ChosenImage
|
||||
import com.kbeanie.multipicker.api.entity.ChosenVideo
|
||||
import com.kbeanie.multipicker.core.PickerManager
|
||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||
import im.vector.riotx.core.platform.Restorable
|
||||
|
||||
private const val CAPTURE_PATH_KEY = "CAPTURE_PATH_KEY"
|
||||
|
||||
class AttachmentsHelper(private val context: Context) {
|
||||
class AttachmentsHelper(private val fragment: Fragment, private val callback: Callback) : Restorable {
|
||||
|
||||
private var capturePath: String? = null
|
||||
|
||||
fun selectFile(fragment: Fragment, requestCode: Int) {
|
||||
selectMediaType(fragment, "*/*", null, requestCode)
|
||||
interface Callback {
|
||||
fun onAttachmentsReady(attachments: List<ContentAttachmentData>)
|
||||
fun onAttachmentsProcessFailed()
|
||||
}
|
||||
|
||||
fun selectGallery(fragment: Fragment, requestCode: Int) {
|
||||
selectMediaType(fragment, "image/*", arrayOf("image/*", "video/*"), requestCode)
|
||||
private val attachmentsPickerCallback = AttachmentsPickerCallback(callback)
|
||||
|
||||
private val imagePicker by lazy {
|
||||
ImagePicker(fragment).also {
|
||||
it.setImagePickerCallback(attachmentsPickerCallback)
|
||||
it.allowMultiple()
|
||||
}
|
||||
}
|
||||
|
||||
fun openCamera(fragment: Fragment, requestCode: Int) {
|
||||
dispatchTakePictureIntent(fragment, requestCode)
|
||||
private val cameraImagePicker by lazy {
|
||||
CameraImagePicker(fragment).also {
|
||||
it.setImagePickerCallback(attachmentsPickerCallback)
|
||||
}
|
||||
}
|
||||
|
||||
private val filePicker by lazy {
|
||||
FilePicker(fragment).also {
|
||||
it.allowMultiple()
|
||||
it.setFilePickerCallback(attachmentsPickerCallback)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleOpenCameraResult(): List<Attachment> {
|
||||
val attachment = getAttachmentFromContentResolver(Uri.parse(capturePath))
|
||||
return if (attachment == null) {
|
||||
emptyList()
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
capturePath?.also {
|
||||
outState.putString(CAPTURE_PATH_KEY, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
|
||||
capturePath = savedInstanceState?.getString(CAPTURE_PATH_KEY)
|
||||
}
|
||||
|
||||
var capturePath: String? = null
|
||||
private set
|
||||
|
||||
fun selectFile() {
|
||||
filePicker.pickFile()
|
||||
}
|
||||
|
||||
fun selectGallery() {
|
||||
imagePicker.pickImage()
|
||||
}
|
||||
|
||||
fun openCamera() {
|
||||
capturePath = cameraImagePicker.pickImage()
|
||||
}
|
||||
|
||||
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
val pickerManager = getPickerManager(requestCode)
|
||||
if (pickerManager != null) {
|
||||
pickerManager.submit(data)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getPickerManager(requestCode: Int): PickerManager? {
|
||||
return when (requestCode) {
|
||||
PICK_IMAGE_DEVICE -> imagePicker
|
||||
PICK_IMAGE_CAMERA -> cameraImagePicker
|
||||
PICK_FILE -> filePicker
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private inner class AttachmentsPickerCallback(private val callback: Callback) : ImagePickerCallback, FilePickerCallback {
|
||||
|
||||
override fun onFilesChosen(files: MutableList<ChosenFile>?) {
|
||||
if (files.isNullOrEmpty()) {
|
||||
callback.onAttachmentsProcessFailed()
|
||||
} else {
|
||||
listOf(attachment)
|
||||
val attachments = files.map {
|
||||
it.toContentAttachmentData()
|
||||
}
|
||||
callback.onAttachmentsReady(attachments)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSelectResult(data: Intent?): List<Attachment> {
|
||||
val clipData = data?.clipData
|
||||
if (clipData != null) {
|
||||
return (0 until clipData.itemCount).map {
|
||||
clipData.getItemAt(it)
|
||||
}.mapNotNull {
|
||||
getAttachmentFromContentResolver(it.uri)
|
||||
}
|
||||
override fun onImagesChosen(images: MutableList<ChosenImage>?) {
|
||||
if (images.isNullOrEmpty()) {
|
||||
callback.onAttachmentsProcessFailed()
|
||||
} else {
|
||||
val uri = data?.data ?: return emptyList()
|
||||
val attachment = getAttachmentFromContentResolver(uri)
|
||||
return if (attachment == null) {
|
||||
emptyList()
|
||||
} else {
|
||||
listOf(attachment)
|
||||
val attachments = images.map {
|
||||
it.toContentAttachmentData()
|
||||
}
|
||||
callback.onAttachmentsReady(attachments)
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectMediaType(fragment: Fragment, type: String, extraMimeType: Array<String>?, requestCode: Int) {
|
||||
val intent = Intent()
|
||||
intent.type = type
|
||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
if (extraMimeType != null) {
|
||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeType)
|
||||
}
|
||||
intent.action = Intent.ACTION_OPEN_DOCUMENT
|
||||
try {
|
||||
fragment.startActivityForResult(intent, requestCode)
|
||||
return
|
||||
} catch (exception: ActivityNotFoundException) {
|
||||
Timber.e(exception)
|
||||
}
|
||||
intent.action = Intent.ACTION_GET_CONTENT
|
||||
try {
|
||||
fragment.startActivityForResult(intent, requestCode)
|
||||
} catch (exception: ActivityNotFoundException) {
|
||||
Timber.e(exception)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAttachmentFromContentResolver(uri: Uri): Attachment? {
|
||||
return context.contentResolver.query(uri, null, null, null, null)?.use {
|
||||
if (it.moveToFirst()) {
|
||||
val fileName = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
|
||||
val fileSize = it.getLong(it.getColumnIndex(OpenableColumns.SIZE))
|
||||
val mimeType = context.contentResolver.getType(uri) ?: MIME_TYPE_ALL_CONTENT
|
||||
Attachment(uri.toString(), mimeType, fileName, fileSize)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
override fun onError(error: String?) {
|
||||
callback.onAttachmentsProcessFailed()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun createImageFile(context: Context): File {
|
||||
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
|
||||
val imageFileName = "JPEG_" + timeStamp + "_"
|
||||
val storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
|
||||
val image = File.createTempFile(
|
||||
imageFileName, /* prefix */
|
||||
".jpg", /* suffix */
|
||||
storageDir /* directory */
|
||||
private fun ChosenFile.toContentAttachmentData(): ContentAttachmentData {
|
||||
return ContentAttachmentData(
|
||||
path = originalPath,
|
||||
mimeType = mimeType,
|
||||
type = mapType(),
|
||||
size = size,
|
||||
date = createdAt.time,
|
||||
name = displayName
|
||||
)
|
||||
// Save a file: path for use with ACTION_VIEW intents
|
||||
capturePath = image.absolutePath
|
||||
return image
|
||||
}
|
||||
|
||||
private fun dispatchTakePictureIntent(fragment: Fragment, requestCode: Int) {
|
||||
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||
// Ensure that there's a camera activity to handle the intent
|
||||
if (takePictureIntent.resolveActivity(fragment.requireActivity().packageManager) != null) {
|
||||
// Create the File where the photo should go
|
||||
var photoFile: File? = null
|
||||
try {
|
||||
photoFile = createImageFile(fragment.requireContext())
|
||||
} catch (ex: IOException) {
|
||||
Timber.e(ex, "Couldn't create image file")
|
||||
}
|
||||
// Continue only if the File was successfully created
|
||||
if (photoFile != null) {
|
||||
val photoURI = FileProvider.getUriForFile(fragment.requireContext(), BuildConfig.APPLICATION_ID + ".fileProvider", photoFile)
|
||||
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
|
||||
fragment.startActivityForResult(takePictureIntent, requestCode)
|
||||
}
|
||||
private fun ChosenFile.mapType(): ContentAttachmentData.Type {
|
||||
return when {
|
||||
mimeType.startsWith("image/") -> ContentAttachmentData.Type.IMAGE
|
||||
mimeType.startsWith("video/") -> ContentAttachmentData.Type.VIDEO
|
||||
mimeType.startsWith("audio/") -> ContentAttachmentData.Type.AUDIO
|
||||
else -> ContentAttachmentData.Type.FILE
|
||||
}
|
||||
}
|
||||
|
||||
private fun ChosenImage.toContentAttachmentData(): ContentAttachmentData {
|
||||
return ContentAttachmentData(
|
||||
path = originalPath,
|
||||
mimeType = mimeType,
|
||||
type = mapType(),
|
||||
name = displayName,
|
||||
size = size,
|
||||
height = height.toLong(),
|
||||
width = width.toLong(),
|
||||
date = createdAt.time
|
||||
)
|
||||
}
|
||||
|
||||
private fun ChosenVideo.toContentAttachmentData(): ContentAttachmentData {
|
||||
return ContentAttachmentData(
|
||||
path = originalPath,
|
||||
mimeType = mimeType,
|
||||
type = ContentAttachmentData.Type.VIDEO,
|
||||
size = size,
|
||||
date = createdAt.time,
|
||||
height = height.toLong(),
|
||||
width = width.toLong(),
|
||||
duration = duration,
|
||||
name = displayName
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -16,17 +16,17 @@
|
|||
|
||||
package im.vector.riotx.features.home.room.detail
|
||||
|
||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotx.features.attachments.Attachment
|
||||
|
||||
sealed class RoomDetailActions {
|
||||
|
||||
data class SaveDraft(val draft: String) : RoomDetailActions()
|
||||
data class SendMessage(val text: String, val autoMarkdown: Boolean) : RoomDetailActions()
|
||||
data class SendMedia(val attachments: List<Attachment>) : RoomDetailActions()
|
||||
data class SendMedia(val attachments: List<ContentAttachmentData>) : RoomDetailActions()
|
||||
data class TimelineEventTurnsVisible(val event: TimelineEvent) : RoomDetailActions()
|
||||
data class TimelineEventTurnsInvisible(val event: TimelineEvent) : RoomDetailActions()
|
||||
data class LoadMoreTimelineEvents(val direction: Timeline.Direction) : RoomDetailActions()
|
||||
|
|
|
@ -49,13 +49,12 @@ import com.airbnb.mvrx.*
|
|||
import com.github.piasy.biv.BigImageViewer
|
||||
import com.github.piasy.biv.loader.ImageLoader
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.jaiselrahman.filepicker.activity.FilePickerActivity
|
||||
import com.jaiselrahman.filepicker.model.MediaFile
|
||||
import com.otaliastudios.autocomplete.Autocomplete
|
||||
import com.otaliastudios.autocomplete.AutocompleteCallback
|
||||
import com.otaliastudios.autocomplete.CharPolicy
|
||||
import im.vector.matrix.android.api.permalinks.PermalinkFactory
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
|
@ -125,10 +124,7 @@ data class RoomDetailArgs(
|
|||
) : Parcelable
|
||||
|
||||
|
||||
private const val REQUEST_CODE_SELECT_FILE = 1
|
||||
private const val REQUEST_CODE_SELECT_GALLERY = 2
|
||||
private const val REQUEST_CODE_OPEN_CAMERA = 3
|
||||
private const val REACTION_SELECT_REQUEST_CODE = 4
|
||||
private const val REACTION_SELECT_REQUEST_CODE = 0
|
||||
|
||||
class RoomDetailFragment :
|
||||
VectorBaseFragment(),
|
||||
|
@ -136,7 +132,8 @@ class RoomDetailFragment :
|
|||
AutocompleteUserPresenter.Callback,
|
||||
VectorInviteView.Callback,
|
||||
JumpToReadMarkerView.Callback,
|
||||
AttachmentTypeSelectorView.Callback {
|
||||
AttachmentTypeSelectorView.Callback,
|
||||
AttachmentsHelper.Callback {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -198,7 +195,15 @@ class RoomDetailFragment :
|
|||
|
||||
private lateinit var actionViewModel: ActionsHandler
|
||||
private lateinit var layoutManager: LinearLayoutManager
|
||||
private lateinit var attachmentsHelper: AttachmentsHelper
|
||||
|
||||
private lateinit var _attachmentsHelper: AttachmentsHelper
|
||||
private val attachmentsHelper: AttachmentsHelper
|
||||
get() {
|
||||
if (::_attachmentsHelper.isInitialized.not()) {
|
||||
_attachmentsHelper = AttachmentsHelper(this, this).register()
|
||||
}
|
||||
return _attachmentsHelper
|
||||
}
|
||||
|
||||
@BindView(R.id.composerLayout)
|
||||
lateinit var composerLayout: TextComposerView
|
||||
|
@ -213,7 +218,6 @@ class RoomDetailFragment :
|
|||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
actionViewModel = ViewModelProviders.of(requireActivity()).get(ActionsHandler::class.java)
|
||||
attachmentsHelper = AttachmentsHelper((requireActivity()))
|
||||
setupToolbar(roomToolbar)
|
||||
setupRecyclerView()
|
||||
setupComposer()
|
||||
|
@ -430,17 +434,9 @@ class RoomDetailFragment :
|
|||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (requestCode == REQUEST_CODE_OPEN_CAMERA) {
|
||||
val attachments = attachmentsHelper.handleOpenCameraResult()
|
||||
roomDetailViewModel.process(RoomDetailActions.SendMedia(attachments))
|
||||
} else if (data != null) {
|
||||
val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data)
|
||||
if (!hasBeenHandled && resultCode == RESULT_OK && data != null) {
|
||||
when (requestCode) {
|
||||
REQUEST_CODE_SELECT_FILE,
|
||||
REQUEST_CODE_SELECT_GALLERY -> {
|
||||
val attachments = attachmentsHelper.handleSelectResult(data)
|
||||
roomDetailViewModel.process(RoomDetailActions.SendMedia(attachments))
|
||||
}
|
||||
REACTION_SELECT_REQUEST_CODE -> {
|
||||
val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID)
|
||||
?: return
|
||||
|
@ -452,7 +448,6 @@ class RoomDetailFragment :
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE METHODS *****************************************************************************
|
||||
|
||||
|
@ -1108,14 +1103,25 @@ class RoomDetailFragment :
|
|||
roomDetailViewModel.process(RoomDetailActions.MarkAllAsRead)
|
||||
}
|
||||
|
||||
// AttachmentTypeSelectorView.Callback *********************************************************
|
||||
// AttachmentTypeSelectorView.Callback
|
||||
|
||||
override fun onTypeSelected(type: Int) {
|
||||
when (type) {
|
||||
AttachmentTypeSelectorView.TYPE_CAMERA -> attachmentsHelper.openCamera(this, REQUEST_CODE_OPEN_CAMERA)
|
||||
AttachmentTypeSelectorView.TYPE_FILE -> attachmentsHelper.selectFile(this, REQUEST_CODE_SELECT_FILE)
|
||||
AttachmentTypeSelectorView.TYPE_GALLERY -> attachmentsHelper.selectGallery(this, REQUEST_CODE_SELECT_GALLERY)
|
||||
AttachmentTypeSelectorView.TYPE_CAMERA -> attachmentsHelper.openCamera()
|
||||
AttachmentTypeSelectorView.TYPE_FILE -> attachmentsHelper.selectFile()
|
||||
AttachmentTypeSelectorView.TYPE_GALLERY -> attachmentsHelper.selectGallery()
|
||||
AttachmentTypeSelectorView.TYPE_STICKER -> vectorBaseActivity.notImplemented("Adding stickers")
|
||||
}
|
||||
}
|
||||
|
||||
// AttachmentsHelper.Callback
|
||||
|
||||
override fun onAttachmentsReady(attachments: List<ContentAttachmentData>) {
|
||||
Timber.v("onAttachmentsReady")
|
||||
roomDetailViewModel.process(RoomDetailActions.SendMedia(attachments))
|
||||
}
|
||||
|
||||
override fun onAttachmentsProcessFailed() {
|
||||
Timber.v("onAttachmentsProcessFailed")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -466,24 +466,8 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||
}
|
||||
|
||||
private fun handleSendMedia(action: RoomDetailActions.SendMedia) {
|
||||
val attachments = action.attachments.map {
|
||||
val nameWithExtension = getFilenameFromUri(null, Uri.parse(it.path))
|
||||
|
||||
ContentAttachmentData(
|
||||
size = it.size,
|
||||
duration = it.duration,
|
||||
date = it.date,
|
||||
height = it.height,
|
||||
width = it.width,
|
||||
name = nameWithExtension ?: it.name,
|
||||
path = it.path,
|
||||
mimeType = it.mimeType,
|
||||
type = ContentAttachmentData.Type.values()[it.type]
|
||||
)
|
||||
}
|
||||
|
||||
val attachments = action.attachments
|
||||
val homeServerCapabilities = session.getHomeServerCapabilities()
|
||||
|
||||
val maxUploadFileSize = homeServerCapabilities.maxUploadFileSize
|
||||
|
||||
if (maxUploadFileSize == HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN) {
|
||||
|
|
Loading…
Reference in New Issue