attachments: proper metadata plumbing
This commit is contained in:
@@ -2,55 +2,23 @@ package net.buzzert.kordophonedroid.ui.messagelist
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.core.net.toFile
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import coil.Coil
|
||||
import coil.ComponentRegistry
|
||||
import coil.ImageLoader
|
||||
import coil.ImageLoaderFactory
|
||||
import coil.decode.DataSource
|
||||
import coil.decode.ImageSource
|
||||
import coil.disk.DiskCache
|
||||
import coil.fetch.FetchResult
|
||||
import coil.fetch.Fetcher
|
||||
import coil.fetch.SourceResult
|
||||
import coil.memory.MemoryCache
|
||||
import coil.request.CachePolicy
|
||||
import coil.request.Options
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.channels.consumeEach
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import net.buzzert.kordophone.backend.model.Conversation
|
||||
import net.buzzert.kordophone.backend.model.GUID
|
||||
import net.buzzert.kordophone.backend.model.Message
|
||||
import net.buzzert.kordophone.backend.model.OutgoingMessage
|
||||
import net.buzzert.kordophone.backend.model.UploadingAttachmentMetadata
|
||||
import net.buzzert.kordophone.backend.server.ChatRepository
|
||||
import net.buzzert.kordophonedroid.ui.attachments.AttachmentFetchData
|
||||
import net.buzzert.kordophonedroid.ui.attachments.AttachmentViewModel
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.InputStream
|
||||
import java.nio.file.FileSystem
|
||||
import java.util.Date
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.xml.transform.Source
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
const val MVM_LOG: String = "MessageListViewModel"
|
||||
|
||||
@@ -106,7 +74,22 @@ class MessageListViewModel @Inject constructor(
|
||||
conversation = conversation!!,
|
||||
attachmentUris = attachmentUris,
|
||||
attachmentDataSource = { uri ->
|
||||
context.contentResolver.openInputStream(uri)
|
||||
val resolver = context.contentResolver
|
||||
val inputStream = resolver.openInputStream(uri)
|
||||
val mimeType = resolver.getType(uri)
|
||||
|
||||
val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) ?: "jpg"
|
||||
val filename = uri.lastPathSegment + ".$extension"
|
||||
|
||||
if (inputStream != null && mimeType != null) {
|
||||
UploadingAttachmentMetadata(
|
||||
inputStream = inputStream,
|
||||
mimeType = mimeType,
|
||||
filename = filename
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -71,11 +71,17 @@ data class Message(
|
||||
}
|
||||
}
|
||||
|
||||
data class UploadingAttachmentMetadata(
|
||||
val inputStream: InputStream,
|
||||
val mimeType: String,
|
||||
val filename: String,
|
||||
)
|
||||
|
||||
data class OutgoingMessage(
|
||||
val body: String,
|
||||
val conversation: Conversation,
|
||||
val attachmentUris: Set<Uri>,
|
||||
val attachmentDataSource: (Uri) -> InputStream?
|
||||
val attachmentDataSource: (Uri) -> UploadingAttachmentMetadata?
|
||||
) {
|
||||
val guid: String = UUID.randomUUID().toString()
|
||||
|
||||
|
||||
@@ -1,45 +1,37 @@
|
||||
package net.buzzert.kordophone.backend.server
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.net.toFile
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.consumeEach
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.buzzert.kordophone.backend.db.CachedChatDatabase
|
||||
import net.buzzert.kordophone.backend.events.MessageDeliveredEvent
|
||||
import net.buzzert.kordophone.backend.model.Conversation
|
||||
import net.buzzert.kordophone.backend.model.GUID
|
||||
import net.buzzert.kordophone.backend.model.Message
|
||||
import net.buzzert.kordophone.backend.model.OutgoingMessage
|
||||
import net.buzzert.kordophone.backend.model.UpdateItem
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.RequestBody
|
||||
import okio.BufferedSource
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.lang.Error
|
||||
import java.net.URL
|
||||
import java.util.Date
|
||||
import java.util.Queue
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ArrayBlockingQueue
|
||||
import java.util.concurrent.CancellationException
|
||||
import kotlin.Boolean
|
||||
import kotlin.Int
|
||||
import kotlin.String
|
||||
import kotlin.let
|
||||
|
||||
const val REPO_LOG: String = "ChatRepository"
|
||||
const val CONVERSATION_MESSAGE_SYNC_COUNT = 10
|
||||
@@ -210,7 +202,10 @@ class ChatRepository(
|
||||
private suspend fun uploadAttachment(filename: String, mediaType: String, source: InputStream): String {
|
||||
val attachmentData = source.readBytes()
|
||||
val requestBody = RequestBody.create(MediaType.get(mediaType), attachmentData)
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
source.close()
|
||||
}
|
||||
|
||||
val response = apiInterface.uploadAttachment(filename, requestBody)
|
||||
return response.bodyOnSuccessOrThrow().transferGUID
|
||||
@@ -237,7 +232,7 @@ class ChatRepository(
|
||||
.onEach { it.conversation = conversation }
|
||||
}
|
||||
|
||||
private suspend fun handleConversationChangedUpdate(conversation: Conversation) {
|
||||
private fun handleConversationChangedUpdate(conversation: Conversation) {
|
||||
Log.d(REPO_LOG, "Handling conversation changed update")
|
||||
database.writeConversations(listOf(conversation))
|
||||
}
|
||||
@@ -276,13 +271,10 @@ class ChatRepository(
|
||||
val attachmentGUIDs = mutableListOf<String>()
|
||||
try {
|
||||
for (uri: Uri in it.attachmentUris) {
|
||||
val inputStream = it.attachmentDataSource(uri)
|
||||
?: throw java.lang.Exception("No input stream")
|
||||
|
||||
val filename = uri.lastPathSegment ?: "attachment.png"
|
||||
val mediaType = "image/png" // TODO: Actually get this: it needs to be plumbed through ContentResolver
|
||||
val guid = uploadAttachment(filename, mediaType, inputStream)
|
||||
val uploadData = it.attachmentDataSource(uri)
|
||||
?: throw java.lang.Exception("No upload data.")
|
||||
|
||||
val guid = uploadAttachment(uploadData.filename, uploadData.mimeType, uploadData.inputStream)
|
||||
attachmentGUIDs.add(guid)
|
||||
}
|
||||
} catch (e: java.lang.Exception) {
|
||||
|
||||
Reference in New Issue
Block a user