Files
bitchat-android/app/src/main/java/com/bitchat/android/model/FileSharingManager.kt
T
callebtcandGitHub 633a506753 Media transfers (#440)
* tor voice wip

* worky BLE a bit

* can send sound

* remove tor

* ui cleanup

* recording time

* progress bar color

* nicknames for audio

* onboarding permissions no microphone

* may work

* fix destionation

* extend

* refactor voice input component

* fix keyboard collapse issue

* send images

* wip image open

* image sending works

* wip waveforms

* better

* better animation

* fix cursor for sending audio

* image sending animation

* image sending animation

* full screen image viewer

* gossip sync for fragments too

* reduce delays

* fix keyboard focus

* use v2 for file transfers

* do not sync fragments

* scrollable image viewer

* ui

* ui adjustments

* nicer animation

* seek through audio

* add spec

* add more details to documentation

* File sharing E2E:
- Add TLV BitchatFilePacket, FileSharingManager
- Implement sendFileNote in ChatViewModel
- File receive path: save to files/incoming and render [file] messages with FileMessageItem or FileSendingAnimation during transfer
- SAF FilePickerButton and dispatcher wiring; image/file choice to follow in MediaPickerOptions
- Add FileViewerDialog with system open/save, FileProvider and file_paths
- Hook transfer progress to file sending UI
- Manifest: READ_MEDIA_* and FileProvider
- Fix MessageHandler saving and prefix for non-image payloads
- Add helper utils (FileUtils)

* kinda wip

* fix buttons

* files half working

* wip file transfer

* file packet has 2-byte TLV and chunks. it wokrs but it sucks

* clean

* remove gossip sync for fragments

* fix audio and image rendering

* adjust FILE_SIZE TLV size too

* cleanup

* haptic

* private messages media

* read receipts for media

* use enum for message type not string

* delivery ack checks dont push content

* check

* animation fix

* refactor

* ui adjustments

* comments

* refactor

* fix crash on send and receive of the same file

* refactor notifications

* tests
2025-09-19 22:46:14 +02:00

93 lines
2.7 KiB
Kotlin

package com.bitchat.android.model
import android.content.Context
import android.net.Uri
import android.util.Log
import com.bitchat.android.features.file.FileUtils
import java.io.File
/**
* Business logic for file sharing operations
*/
object FileSharingManager {
private const val TAG = "FileSharingManager"
/**
* Create a file packet from URI for sending
*/
fun createFilePacketFromUri(
context: Context,
uri: Uri,
originalName: String? = null
): BitchatFilePacket? {
return try {
// Get file name from URI or use original name
val fileName = originalName ?: getFileNameFromUri(context, uri) ?: "unknown_file"
// Copy file to our temp storage for sending
val localPath = FileUtils.copyFileForSending(context, uri) ?: return null
// Determine MIME type
val mimeType = FileUtils.getMimeTypeFromExtension(fileName)
// Read file content
val file = File(localPath)
val content = file.readBytes()
val fileSize = file.length()
// Clean up temp file
file.delete()
val packet = BitchatFilePacket(
fileName = fileName,
fileSize = fileSize,
mimeType = mimeType,
content = content
)
Log.d(TAG, "Created file packet: name=$fileName, size=${FileUtils.formatFileSize(fileSize)}, mime=$mimeType")
packet
} catch (e: Exception) {
Log.e(TAG, "Failed to create file packet from URI", e)
null
}
}
/**
* Extract filename from URI
*/
private fun getFileNameFromUri(context: Context, uri: Uri): String? {
return try {
context.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
val nameIndex = cursor.getColumnIndex(android.provider.MediaStore.MediaColumns.DISPLAY_NAME)
cursor.moveToFirst()
cursor.getString(nameIndex)
} ?: uri.lastPathSegment
} catch (e: Exception) {
Log.w(TAG, "Failed to get filename from URI", e)
uri.lastPathSegment
}
}
/**
* Process a received file packet and return file info
*/
data class ReceivedFileInfo(
val fileName: String,
val fileSize: Long,
val mimeType: String,
val content: ByteArray
)
fun processReceivedFile(packet: BitchatFilePacket): ReceivedFileInfo {
return ReceivedFileInfo(
fileName = packet.fileName,
fileSize = packet.fileSize,
mimeType = packet.mimeType,
content = packet.content
)
}
}