forked from upstream-mirrors/quik
rebased to latest master
This commit is contained in:
@@ -18,19 +18,24 @@
|
||||
*/
|
||||
package dev.octoshrimpy.quik.extensions
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toFile
|
||||
|
||||
fun Uri.resourceExists(context: Context): Boolean {
|
||||
// check if resource at uri still exists
|
||||
var retVal: Boolean? = null
|
||||
try {
|
||||
return context.contentResolver.query(
|
||||
this, null, null, null, null
|
||||
)?.use {
|
||||
it.moveToFirst()
|
||||
} ?: false
|
||||
when (this.scheme) {
|
||||
ContentResolver.SCHEME_CONTENT -> {
|
||||
retVal = context.contentResolver.query(
|
||||
this, null, null, null, null
|
||||
)?.use { it.moveToFirst() }
|
||||
}
|
||||
ContentResolver.SCHEME_FILE -> retVal = this.toFile().exists()
|
||||
}
|
||||
} catch (e: Exception) { /* nothing */
|
||||
}
|
||||
catch (e: Exception) { /* nothing */ }
|
||||
|
||||
return false
|
||||
return retVal ?: false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2025
|
||||
*
|
||||
* This file is part of QUIK.
|
||||
*
|
||||
* QUIK is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* QUIK is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with QUIK. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.moez.QKSMS.manager
|
||||
|
||||
import android.content.Context
|
||||
import android.media.MediaRecorder
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
|
||||
object MediaRecorderManager : MediaRecorder() {
|
||||
|
||||
enum class RecordingState {
|
||||
Initial,
|
||||
DataSourceConfigured,
|
||||
Prepared,
|
||||
Recording,
|
||||
Error
|
||||
}
|
||||
|
||||
const val AUDIO_FILE_PREFIX = "recorded-"
|
||||
const val AUDIO_FILE_SUFFIX = ".3ga"
|
||||
|
||||
var recordingState: RecordingState = RecordingState.Initial
|
||||
private set
|
||||
|
||||
var uri: Uri = Uri.EMPTY
|
||||
private set
|
||||
|
||||
fun stopRecording(): Uri {
|
||||
return try {
|
||||
if (recordingState == RecordingState.Recording)
|
||||
stop()
|
||||
|
||||
reset()
|
||||
recordingState = RecordingState.Initial
|
||||
|
||||
uri
|
||||
}
|
||||
catch (e: Exception) {
|
||||
Uri.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
fun startRecording(context: Context): Uri {
|
||||
return try {
|
||||
val file = File(
|
||||
context.cacheDir,
|
||||
AUDIO_FILE_PREFIX + UUID.randomUUID() + AUDIO_FILE_SUFFIX
|
||||
)
|
||||
|
||||
// ensure stopped before using again
|
||||
stopRecording()
|
||||
|
||||
// configure
|
||||
setAudioSource(AudioSource.MIC)
|
||||
setOutputFormat(OutputFormat.THREE_GPP)
|
||||
setAudioEncoder(AudioEncoder.AMR_WB)
|
||||
recordingState = RecordingState.DataSourceConfigured
|
||||
|
||||
setOutputFile(file.path)
|
||||
|
||||
prepare()
|
||||
recordingState = RecordingState.Prepared
|
||||
|
||||
start()
|
||||
recordingState = RecordingState.Recording
|
||||
|
||||
uri = file.toUri()
|
||||
|
||||
uri
|
||||
}
|
||||
catch (e: Exception) {
|
||||
Uri.EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -72,6 +72,10 @@ class PermissionManagerImpl @Inject constructor(private val context: Context) :
|
||||
return hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
|
||||
override fun hasRecordAudio(): Boolean {
|
||||
return hasPermission(Manifest.permission.RECORD_AUDIO)
|
||||
}
|
||||
|
||||
override fun hasExactAlarms(): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
return true
|
||||
|
||||
@@ -47,6 +47,7 @@ import dev.octoshrimpy.quik.compat.TelephonyCompat
|
||||
import dev.octoshrimpy.quik.extensions.anyOf
|
||||
import dev.octoshrimpy.quik.extensions.isImage
|
||||
import dev.octoshrimpy.quik.extensions.isVideo
|
||||
import dev.octoshrimpy.quik.extensions.resourceExists
|
||||
import dev.octoshrimpy.quik.manager.ActiveConversationManager
|
||||
import dev.octoshrimpy.quik.manager.KeyManager
|
||||
import dev.octoshrimpy.quik.model.Attachment
|
||||
@@ -368,28 +369,31 @@ class MessageRepositoryImpl @Inject constructor(
|
||||
parts += MMSPart("text", ContentType.TEXT_PLAIN, bytes)
|
||||
}
|
||||
|
||||
// Attach those that can't be compressed (ie. everything but not images)
|
||||
// Attach those that can't be compressed (ie. everything but images)
|
||||
parts += attachments
|
||||
// filter out images
|
||||
// filter in non-images only
|
||||
.filter { !it.isImage(context) }
|
||||
// filter out items that have no data available (ie user may have deleted
|
||||
// the file from storage)
|
||||
.filter { it.getResourceBytes(context).isNotEmpty() }
|
||||
// filter in only items that exist (user may have deleted the file)
|
||||
.filter { it.uri.resourceExists(context) }
|
||||
.map {
|
||||
remainingBytes -= it.getResourceBytes(context).size
|
||||
MMSPart(
|
||||
val mmsPart = MMSPart(
|
||||
it.getName(context),
|
||||
it.getType(context),
|
||||
it.getResourceBytes(context)
|
||||
)
|
||||
|
||||
// release the attachment hold on the image bytes so the GC can reclaim
|
||||
it.releaseResourceBytes()
|
||||
|
||||
mmsPart
|
||||
}
|
||||
|
||||
val imageBytesByAttachment = attachments
|
||||
// filter out non-images
|
||||
// filter in images only
|
||||
.filter { it.isImage(context) }
|
||||
// filter out items that have no data available (ie user may have deleted
|
||||
// the file from storage)
|
||||
.filter { it.getResourceBytes(context).isNotEmpty() }
|
||||
// filter in only items that exist (user may have deleted the file)
|
||||
.filter { it.uri.resourceExists(context) }
|
||||
.associateWith {
|
||||
when (it.getType(context) == "image/gif") {
|
||||
true -> ImageUtils.getScaledGif(context, it.uri, maxWidth, maxHeight)
|
||||
@@ -434,6 +438,9 @@ class MessageRepositoryImpl @Inject constructor(
|
||||
}
|
||||
|
||||
Timber.d("Compression attempt $attempts: ${scaledBytes.size / 1024}/${maxBytes.toInt() / 1024}Kb ($width*$height -> $newWidth*$newHeight)")
|
||||
|
||||
// release the attachment hold on the image bytes so the GC can reclaim
|
||||
attachment.releaseResourceBytes()
|
||||
}
|
||||
|
||||
Timber.v("Compressed ${originalBytes.size / 1024}Kb to ${scaledBytes.size / 1024}Kb with a target size of ${maxBytes.toInt() / 1024}Kb in $attempts attempts")
|
||||
|
||||
Reference in New Issue
Block a user