Merge branch 'master' into add-simple-characters
This commit is contained in:
commit
2aa8e3953b
36 changed files with 440 additions and 29 deletions
|
|
@ -29,4 +29,8 @@ class Config(context: Context) : BaseConfig(context) {
|
|||
var lockScreenVisibilitySetting: Int
|
||||
get() = prefs.getInt(LOCK_SCREEN_VISIBILITY, LOCK_SCREEN_SENDER_MESSAGE)
|
||||
set(lockScreenVisibilitySetting) = prefs.edit().putInt(LOCK_SCREEN_VISIBILITY, lockScreenVisibilitySetting).apply()
|
||||
|
||||
var mmsFileSizeLimit: Long
|
||||
get() = prefs.getLong(MMS_FILE_SIZE_LIMIT, FILE_SIZE_1_MB)
|
||||
set(mmsFileSizeLimit) = prefs.edit().putLong(MMS_FILE_SIZE_LIMIT, mmsFileSizeLimit).apply()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const val SHOW_CHARACTER_COUNTER = "show_character_counter"
|
|||
const val USE_SIMPLE_CHARACTERS = "use_simple_characters"
|
||||
const val LOCK_SCREEN_VISIBILITY = "lock_screen_visibility"
|
||||
const val ENABLE_DELIVERY_REPORTS = "enable_delivery_reports"
|
||||
const val MMS_FILE_SIZE_LIMIT = "mms_file_size_limit"
|
||||
|
||||
private const val PATH = "com.simplemobiletools.smsmessenger.action."
|
||||
const val MARK_AS_READ = PATH + "mark_as_read"
|
||||
|
|
@ -34,6 +35,14 @@ const val LOCK_SCREEN_SENDER_MESSAGE = 1
|
|||
const val LOCK_SCREEN_SENDER = 2
|
||||
const val LOCK_SCREEN_NOTHING = 3
|
||||
|
||||
const val FILE_SIZE_NONE = -1L
|
||||
const val FILE_SIZE_100_KB = 102_400L
|
||||
const val FILE_SIZE_200_KB = 204_800L
|
||||
const val FILE_SIZE_300_KB = 307_200L
|
||||
const val FILE_SIZE_600_KB = 614_400L
|
||||
const val FILE_SIZE_1_MB = 1_048_576L
|
||||
const val FILE_SIZE_2_MB = 2_097_152L
|
||||
|
||||
fun refreshMessages() {
|
||||
EventBus.getDefault().post(Events.RefreshMessages())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
package com.simplemobiletools.smsmessenger.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Matrix
|
||||
import android.media.ExifInterface
|
||||
import android.net.Uri
|
||||
import com.simplemobiletools.commons.extensions.getCompressionFormat
|
||||
import com.simplemobiletools.commons.extensions.getMyFileUri
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.smsmessenger.extensions.extension
|
||||
import com.simplemobiletools.smsmessenger.extensions.getExtensionFromMimeType
|
||||
import com.simplemobiletools.smsmessenger.extensions.getFileSizeFromUri
|
||||
import com.simplemobiletools.smsmessenger.extensions.isImageMimeType
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
/**
|
||||
* Compress image to a given size based on
|
||||
* [Compressor](https://github.com/zetbaitsu/Compressor/)
|
||||
* */
|
||||
class ImageCompressor(private val context: Context) {
|
||||
private val contentResolver = context.contentResolver
|
||||
private val outputDirectory = File(context.cacheDir, "compressed").apply {
|
||||
if (!exists()) {
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
fun compressImage(uri: Uri, compressSize: Long, callback: (compressedFileUri: Uri?) -> Unit) {
|
||||
ensureBackgroundThread {
|
||||
try {
|
||||
val fileSize = context.getFileSizeFromUri(uri)
|
||||
if (fileSize > compressSize) {
|
||||
val mimeType = contentResolver.getType(uri)!!
|
||||
if (mimeType.isImageMimeType()) {
|
||||
val byteArray = contentResolver.openInputStream(uri)?.readBytes()!!
|
||||
var destinationFile = File(outputDirectory, System.currentTimeMillis().toString().plus(mimeType.getExtensionFromMimeType()))
|
||||
destinationFile.writeBytes(byteArray)
|
||||
val constraint = SizeConstraint(compressSize)
|
||||
while (constraint.isSatisfied(destinationFile).not()) {
|
||||
destinationFile = constraint.satisfy(destinationFile)
|
||||
}
|
||||
callback.invoke(context.getMyFileUri(destinationFile))
|
||||
} else {
|
||||
callback.invoke(null)
|
||||
}
|
||||
} else {
|
||||
//no need to compress since the file is less than the compress size
|
||||
callback.invoke(uri)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
callback.invoke(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun overWrite(imageFile: File, bitmap: Bitmap, format: Bitmap.CompressFormat = imageFile.path.getCompressionFormat(), quality: Int = 100): File {
|
||||
val result = if (format == imageFile.path.getCompressionFormat()) {
|
||||
imageFile
|
||||
} else {
|
||||
File("${imageFile.absolutePath.substringBeforeLast(".")}.${format.extension()}")
|
||||
}
|
||||
imageFile.delete()
|
||||
saveBitmap(bitmap, result, format, quality)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun saveBitmap(bitmap: Bitmap, destination: File, format: Bitmap.CompressFormat = destination.path.getCompressionFormat(), quality: Int = 100) {
|
||||
destination.parentFile?.mkdirs()
|
||||
var fileOutputStream: FileOutputStream? = null
|
||||
try {
|
||||
fileOutputStream = FileOutputStream(destination.absolutePath)
|
||||
bitmap.compress(format, quality, fileOutputStream)
|
||||
} finally {
|
||||
fileOutputStream?.run {
|
||||
flush()
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadBitmap(imageFile: File) = BitmapFactory.decodeFile(imageFile.absolutePath).run {
|
||||
determineImageRotation(imageFile, this)
|
||||
}
|
||||
|
||||
private fun determineImageRotation(imageFile: File, bitmap: Bitmap): Bitmap {
|
||||
val exif = ExifInterface(imageFile.absolutePath)
|
||||
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0)
|
||||
val matrix = Matrix()
|
||||
when (orientation) {
|
||||
6 -> matrix.postRotate(90f)
|
||||
3 -> matrix.postRotate(180f)
|
||||
8 -> matrix.postRotate(270f)
|
||||
}
|
||||
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
|
||||
}
|
||||
|
||||
private inner class SizeConstraint(
|
||||
private val maxFileSize: Long,
|
||||
private val stepSize: Int = 10,
|
||||
private val maxIteration: Int = 10,
|
||||
private val minQuality: Int = 10
|
||||
) {
|
||||
private var iteration: Int = 0
|
||||
|
||||
fun isSatisfied(imageFile: File): Boolean {
|
||||
return imageFile.length() <= maxFileSize || iteration >= maxIteration
|
||||
}
|
||||
|
||||
fun satisfy(imageFile: File): File {
|
||||
iteration++
|
||||
val quality = (100 - iteration * stepSize).takeIf { it >= minQuality } ?: minQuality
|
||||
return overWrite(imageFile, loadBitmap(imageFile), quality = quality)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue