Чат, вложения

This commit is contained in:
Халимов Рустам
2026-04-14 21:53:44 +03:00
parent 118f8b8971
commit 58fdf1aca1
22 changed files with 799 additions and 125 deletions
+90
View File
@@ -0,0 +1,90 @@
package core.utils
import android.app.DownloadManager
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.widget.Toast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.URL
object DownloadUtils {
suspend fun downloadMedia(context: Context, url: String, fileName: String?) {
withContext(Dispatchers.IO) {
try {
val name = fileName ?: url.substringAfterLast("/")
val extension = name.substringAfterLast(".", "jpg")
val isImage = extension in listOf("jpg", "jpeg", "png", "webp", "gif")
val isVideo = extension in listOf("mp4", "webm", "mkv", "mov")
if (isImage || isVideo) {
saveToGallery(context, url, name, isImage)
} else {
useDownloadManager(context, url, name)
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(context, "Ошибка скачивания: ${e.localizedMessage}", Toast.LENGTH_SHORT).show()
}
}
}
}
private fun useDownloadManager(context: Context, url: String, fileName: String) {
val request = DownloadManager.Request(Uri.parse(url))
.setTitle(fileName)
.setDescription("Downloading...")
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
.setAllowedOverMetered(true)
.setAllowedOverRoaming(true)
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
// Show feedback
(context as? android.app.Activity)?.runOnUiThread {
Toast.makeText(context, "Загрузка началась: $fileName", Toast.LENGTH_SHORT).show()
}
}
private suspend fun saveToGallery(context: Context, url: String, fileName: String, isImage: Boolean) {
val resolver = context.contentResolver
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.MIME_TYPE, if (isImage) "image/jpeg" else "video/mp4")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.MediaColumns.RELATIVE_PATH, if (isImage) Environment.DIRECTORY_PICTURES else Environment.DIRECTORY_MOVIES)
put(MediaStore.MediaColumns.IS_PENDING, 1)
}
}
val collection = if (isImage) {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
} else {
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
}
val uri = resolver.insert(collection, contentValues)
uri?.let {
URL(url).openStream().use { input ->
resolver.openOutputStream(it).use { output ->
input.copyTo(output!!)
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.clear()
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
resolver.update(it, contentValues, null, null)
}
withContext(Dispatchers.Main) {
Toast.makeText(context, "Сохранено в галерею", Toast.LENGTH_SHORT).show()
}
}
}
}
+9 -1
View File
@@ -8,8 +8,16 @@ import java.util.UUID
fun copyUriToFile(context: Context, uri: Uri): File? {
return try {
val cursor = context.contentResolver.query(uri, null, null, null, null)
val originalName = cursor?.use {
if (it.moveToFirst()) {
val index = it.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
if (index != -1) it.getString(index) else null
} else null
} ?: "${UUID.randomUUID()}.tmp"
val inputStream = context.contentResolver.openInputStream(uri)
val file = File(context.cacheDir, "${UUID.randomUUID()}.tmp")
val file = File(context.cacheDir, originalName)
val outputStream = FileOutputStream(file)
inputStream?.use { input ->
outputStream.use { output ->
+59
View File
@@ -0,0 +1,59 @@
package core.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.net.Uri
import java.io.File
import java.io.FileOutputStream
import java.util.UUID
object ImageUtils {
fun compressImage(context: Context, uri: Uri, maxWidth: Int = 1280, maxHeight: Int = 1280, quality: Int = 80): File? {
return try {
val inputStream = context.contentResolver.openInputStream(uri)
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeStream(inputStream, null, options)
inputStream?.close()
var inSampleSize = 1
if (options.outHeight > maxHeight || options.outWidth > maxWidth) {
val halfHeight = options.outHeight / 2
val halfWidth = options.outWidth / 2
while (halfHeight / inSampleSize >= maxHeight && halfWidth / inSampleSize >= maxWidth) {
inSampleSize *= 2
}
}
val decodeOptions = BitmapFactory.Options().apply {
inSampleSize = inSampleSize
}
val finalInputStream = context.contentResolver.openInputStream(uri)
var bitmap = BitmapFactory.decodeStream(finalInputStream, null, decodeOptions)
finalInputStream?.close()
if (bitmap == null) return null
// Resize if still too large
if (bitmap.width > maxWidth || bitmap.height > maxHeight) {
val scale = Math.min(maxWidth.toFloat() / bitmap.width, maxHeight.toFloat() / bitmap.height)
val matrix = Matrix().apply { postScale(scale, scale) }
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
val file = File(context.cacheDir, "compressed_${UUID.randomUUID()}.jpg")
val out = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out)
out.flush()
out.close()
file
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}