Merge pull request #2851 from Naveen3Singh/feature_resize_images
Add option to bulk resize images
This commit is contained in:
commit
a5fb4e5e7a
6 changed files with 335 additions and 63 deletions
|
@ -16,7 +16,6 @@ import android.graphics.Bitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
|
@ -47,7 +46,6 @@ import com.simplemobiletools.gallery.pro.R
|
||||||
import com.simplemobiletools.gallery.pro.adapters.MyPagerAdapter
|
import com.simplemobiletools.gallery.pro.adapters.MyPagerAdapter
|
||||||
import com.simplemobiletools.gallery.pro.asynctasks.GetMediaAsynctask
|
import com.simplemobiletools.gallery.pro.asynctasks.GetMediaAsynctask
|
||||||
import com.simplemobiletools.gallery.pro.dialogs.DeleteWithRememberDialog
|
import com.simplemobiletools.gallery.pro.dialogs.DeleteWithRememberDialog
|
||||||
import com.simplemobiletools.gallery.pro.dialogs.ResizeWithPathDialog
|
|
||||||
import com.simplemobiletools.gallery.pro.dialogs.SaveAsDialog
|
import com.simplemobiletools.gallery.pro.dialogs.SaveAsDialog
|
||||||
import com.simplemobiletools.gallery.pro.dialogs.SlideshowDialog
|
import com.simplemobiletools.gallery.pro.dialogs.SlideshowDialog
|
||||||
import com.simplemobiletools.gallery.pro.extensions.*
|
import com.simplemobiletools.gallery.pro.extensions.*
|
||||||
|
@ -1047,62 +1045,9 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.N)
|
|
||||||
private fun resizeImage() {
|
private fun resizeImage() {
|
||||||
val oldPath = getCurrentPath()
|
val oldPath = getCurrentPath()
|
||||||
val originalSize = oldPath.getImageResolution(this) ?: return
|
launchResizeImageDialog(oldPath)
|
||||||
ResizeWithPathDialog(this, originalSize, oldPath) { newSize, newPath ->
|
|
||||||
ensureBackgroundThread {
|
|
||||||
try {
|
|
||||||
var oldExif: ExifInterface? = null
|
|
||||||
if (isNougatPlus()) {
|
|
||||||
val inputStream = contentResolver.openInputStream(Uri.fromFile(File(oldPath)))
|
|
||||||
oldExif = ExifInterface(inputStream!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
val newBitmap = Glide.with(applicationContext).asBitmap().load(oldPath).submit(newSize.x, newSize.y).get()
|
|
||||||
|
|
||||||
val newFile = File(newPath)
|
|
||||||
val newFileDirItem = FileDirItem(newPath, newPath.getFilenameFromPath())
|
|
||||||
getFileOutputStream(newFileDirItem, true) {
|
|
||||||
if (it != null) {
|
|
||||||
saveBitmap(newFile, newBitmap, it, oldExif, File(oldPath).lastModified())
|
|
||||||
} else {
|
|
||||||
toast(R.string.image_editing_failed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: OutOfMemoryError) {
|
|
||||||
toast(R.string.out_of_memory_error)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
showErrorToast(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.N)
|
|
||||||
private fun saveBitmap(file: File, bitmap: Bitmap, out: OutputStream, oldExif: ExifInterface?, lastModified: Long) {
|
|
||||||
try {
|
|
||||||
bitmap.compress(file.absolutePath.getCompressionFormat(), 90, out)
|
|
||||||
|
|
||||||
if (isNougatPlus()) {
|
|
||||||
val newExif = ExifInterface(file.absolutePath)
|
|
||||||
oldExif?.copyNonDimensionAttributesTo(newExif)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
}
|
|
||||||
|
|
||||||
toast(R.string.file_saved)
|
|
||||||
val paths = arrayListOf(file.absolutePath)
|
|
||||||
rescanPaths(paths) {
|
|
||||||
fixDateTaken(paths, false)
|
|
||||||
|
|
||||||
if (config.keepLastModified && lastModified != 0L) {
|
|
||||||
File(file.absolutePath).setLastModified(lastModified)
|
|
||||||
updateLastModified(file.absolutePath, lastModified)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkDeleteConfirmation() {
|
private fun checkDeleteConfirmation() {
|
||||||
|
|
|
@ -30,14 +30,9 @@ import com.simplemobiletools.gallery.pro.interfaces.MediaOperationsListener
|
||||||
import com.simplemobiletools.gallery.pro.models.Medium
|
import com.simplemobiletools.gallery.pro.models.Medium
|
||||||
import com.simplemobiletools.gallery.pro.models.ThumbnailItem
|
import com.simplemobiletools.gallery.pro.models.ThumbnailItem
|
||||||
import com.simplemobiletools.gallery.pro.models.ThumbnailSection
|
import com.simplemobiletools.gallery.pro.models.ThumbnailSection
|
||||||
import kotlinx.android.synthetic.main.photo_item_grid.view.*
|
import kotlinx.android.synthetic.main.photo_item_grid.view.file_type
|
||||||
import kotlinx.android.synthetic.main.thumbnail_section.view.*
|
import kotlinx.android.synthetic.main.thumbnail_section.view.thumbnail_section
|
||||||
import kotlinx.android.synthetic.main.video_item_grid.view.*
|
import kotlinx.android.synthetic.main.video_item_grid.view.*
|
||||||
import kotlinx.android.synthetic.main.video_item_grid.view.favorite
|
|
||||||
import kotlinx.android.synthetic.main.video_item_grid.view.media_item_holder
|
|
||||||
import kotlinx.android.synthetic.main.video_item_grid.view.medium_check
|
|
||||||
import kotlinx.android.synthetic.main.video_item_grid.view.medium_name
|
|
||||||
import kotlinx.android.synthetic.main.video_item_grid.view.medium_thumbnail
|
|
||||||
|
|
||||||
class MediaAdapter(
|
class MediaAdapter(
|
||||||
activity: BaseSimpleActivity, var media: ArrayList<ThumbnailItem>, val listener: MediaOperationsListener?, val isAGetIntent: Boolean,
|
activity: BaseSimpleActivity, var media: ArrayList<ThumbnailItem>, val listener: MediaOperationsListener?, val isAGetIntent: Boolean,
|
||||||
|
@ -144,6 +139,7 @@ class MediaAdapter(
|
||||||
findItem(R.id.cab_open_with).isVisible = isOneItemSelected
|
findItem(R.id.cab_open_with).isVisible = isOneItemSelected
|
||||||
findItem(R.id.cab_edit).isVisible = isOneItemSelected
|
findItem(R.id.cab_edit).isVisible = isOneItemSelected
|
||||||
findItem(R.id.cab_set_as).isVisible = isOneItemSelected
|
findItem(R.id.cab_set_as).isVisible = isOneItemSelected
|
||||||
|
findItem(R.id.cab_resize).isVisible = canResize(selectedItems)
|
||||||
findItem(R.id.cab_confirm_selection).isVisible = isAGetIntent && allowMultiplePicks && selectedKeys.isNotEmpty()
|
findItem(R.id.cab_confirm_selection).isVisible = isAGetIntent && allowMultiplePicks && selectedKeys.isNotEmpty()
|
||||||
findItem(R.id.cab_restore_recycle_bin_files).isVisible = selectedPaths.all { it.startsWith(activity.recycleBinPath) }
|
findItem(R.id.cab_restore_recycle_bin_files).isVisible = selectedPaths.all { it.startsWith(activity.recycleBinPath) }
|
||||||
findItem(R.id.cab_create_shortcut).isVisible = isOreoPlus() && isOneItemSelected
|
findItem(R.id.cab_create_shortcut).isVisible = isOreoPlus() && isOneItemSelected
|
||||||
|
@ -179,6 +175,7 @@ class MediaAdapter(
|
||||||
R.id.cab_open_with -> openPath()
|
R.id.cab_open_with -> openPath()
|
||||||
R.id.cab_fix_date_taken -> fixDateTaken()
|
R.id.cab_fix_date_taken -> fixDateTaken()
|
||||||
R.id.cab_set_as -> setAs()
|
R.id.cab_set_as -> setAs()
|
||||||
|
R.id.cab_resize -> resize()
|
||||||
R.id.cab_delete -> checkDeleteConfirmation()
|
R.id.cab_delete -> checkDeleteConfirmation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,6 +283,34 @@ class MediaAdapter(
|
||||||
activity.setAs(path)
|
activity.setAs(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resize() {
|
||||||
|
val paths = getSelectedItems().filter { it.isImage() }.map { it.path }
|
||||||
|
if (isOneItemSelected()) {
|
||||||
|
val path = paths.first()
|
||||||
|
activity.launchResizeImageDialog(path) {
|
||||||
|
finishActMode()
|
||||||
|
listener?.refreshItems()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
activity.launchResizeMultipleImagesDialog(paths) {
|
||||||
|
finishActMode()
|
||||||
|
listener?.refreshItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canResize(selectedItems: ArrayList<Medium>): Boolean {
|
||||||
|
val selectionContainsImages = selectedItems.any { it.isImage() }
|
||||||
|
if (!selectionContainsImages) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val parentPath = selectedItems.first { it.isImage() }.parentPath
|
||||||
|
val isCommonParent = selectedItems.all { parentPath == it.parentPath }
|
||||||
|
val isRestrictedDir = activity.isRestrictedWithSAFSdk30(parentPath)
|
||||||
|
return isExternalStorageManager() || (isCommonParent && !isRestrictedDir)
|
||||||
|
}
|
||||||
|
|
||||||
private fun toggleFileVisibility(hide: Boolean) {
|
private fun toggleFileVisibility(hide: Boolean) {
|
||||||
ensureBackgroundThread {
|
ensureBackgroundThread {
|
||||||
getSelectedItems().forEach {
|
getSelectedItems().forEach {
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
package com.simplemobiletools.gallery.pro.dialogs
|
||||||
|
|
||||||
|
import android.graphics.Point
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||||
|
import com.simplemobiletools.commons.extensions.*
|
||||||
|
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||||
|
import com.simplemobiletools.gallery.pro.R
|
||||||
|
import com.simplemobiletools.gallery.pro.extensions.ensureWriteAccess
|
||||||
|
import com.simplemobiletools.gallery.pro.extensions.rescanPathsAndUpdateLastModified
|
||||||
|
import com.simplemobiletools.gallery.pro.extensions.resizeImage
|
||||||
|
import kotlinx.android.synthetic.main.dialog_resize_multiple_images.view.resize_factor_edit_text
|
||||||
|
import kotlinx.android.synthetic.main.dialog_resize_multiple_images.view.resize_factor_input_layout
|
||||||
|
import kotlinx.android.synthetic.main.dialog_resize_multiple_images.view.resize_progress
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
private const val DEFAULT_RESIZE_FACTOR = "75"
|
||||||
|
|
||||||
|
class ResizeMultipleImagesDialog(
|
||||||
|
private val activity: BaseSimpleActivity,
|
||||||
|
private val imagePaths: List<String>,
|
||||||
|
private val imageSizes: List<Point>,
|
||||||
|
private val callback: () -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
private var dialog: AlertDialog? = null
|
||||||
|
private val view = activity.layoutInflater.inflate(R.layout.dialog_resize_multiple_images, null)
|
||||||
|
private val progressView = view.resize_progress
|
||||||
|
private val resizeFactorEditText = view.resize_factor_edit_text
|
||||||
|
|
||||||
|
init {
|
||||||
|
resizeFactorEditText.setText(DEFAULT_RESIZE_FACTOR)
|
||||||
|
progressView.apply {
|
||||||
|
max = imagePaths.size
|
||||||
|
setIndicatorColor(activity.getProperPrimaryColor())
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.getAlertDialogBuilder()
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.apply {
|
||||||
|
activity.setupDialogStuff(view, this, R.string.resize_multiple_images) { alertDialog ->
|
||||||
|
dialog = alertDialog
|
||||||
|
alertDialog.showKeyboard(resizeFactorEditText)
|
||||||
|
|
||||||
|
val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
|
||||||
|
val negativeButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE)
|
||||||
|
positiveButton.setOnClickListener {
|
||||||
|
val resizeFactorText = resizeFactorEditText.text?.toString()
|
||||||
|
if (resizeFactorText.isNullOrEmpty() || resizeFactorText.toInt() !in 10..90) {
|
||||||
|
activity.toast(R.string.resize_factor_error)
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
val resizeFactor = resizeFactorText.toFloat().div(100)
|
||||||
|
|
||||||
|
alertDialog.setCanceledOnTouchOutside(false)
|
||||||
|
arrayOf(view.resize_factor_input_layout, positiveButton, negativeButton).forEach {
|
||||||
|
it.isEnabled = false
|
||||||
|
it.alpha = 0.6f
|
||||||
|
}
|
||||||
|
resizeImages(resizeFactor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resizeImages(factor: Float) {
|
||||||
|
progressView.show()
|
||||||
|
with(activity) {
|
||||||
|
val newSizes = imageSizes.map {
|
||||||
|
val width = (it.x * factor).roundToInt()
|
||||||
|
val height = (it.y * factor).roundToInt()
|
||||||
|
Point(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
val parentPath = imagePaths.first().getParentPath()
|
||||||
|
val pathsToRescan = arrayListOf<String>()
|
||||||
|
val pathLastModifiedMap = mutableMapOf<String, Long>()
|
||||||
|
|
||||||
|
ensureWriteAccess(parentPath) {
|
||||||
|
ensureBackgroundThread {
|
||||||
|
for (i in imagePaths.indices) {
|
||||||
|
val path = imagePaths[i]
|
||||||
|
val size = newSizes[i]
|
||||||
|
val lastModified = File(path).lastModified()
|
||||||
|
|
||||||
|
try {
|
||||||
|
resizeImage(path, size) {
|
||||||
|
if (it) {
|
||||||
|
pathsToRescan.add(path)
|
||||||
|
pathLastModifiedMap[path] = lastModified
|
||||||
|
runOnUiThread {
|
||||||
|
progressView.progress = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: OutOfMemoryError) {
|
||||||
|
toast(R.string.out_of_memory_error)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showErrorToast(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val failureCount = imagePaths.size - pathsToRescan.size
|
||||||
|
if (failureCount > 0) {
|
||||||
|
toast(resources.getQuantityString(R.plurals.failed_to_resize_images, failureCount, failureCount))
|
||||||
|
} else {
|
||||||
|
toast(R.string.images_resized_successfully)
|
||||||
|
}
|
||||||
|
|
||||||
|
rescanPathsAndUpdateLastModified(pathsToRescan, pathLastModifiedMap) {
|
||||||
|
activity.runOnUiThread {
|
||||||
|
dialog?.dismiss()
|
||||||
|
callback.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Matrix
|
import android.graphics.Matrix
|
||||||
|
import android.graphics.Point
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.LayerDrawable
|
import android.graphics.drawable.LayerDrawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
@ -39,6 +40,8 @@ import com.simplemobiletools.gallery.pro.activities.SettingsActivity
|
||||||
import com.simplemobiletools.gallery.pro.activities.SimpleActivity
|
import com.simplemobiletools.gallery.pro.activities.SimpleActivity
|
||||||
import com.simplemobiletools.gallery.pro.dialogs.AllFilesPermissionDialog
|
import com.simplemobiletools.gallery.pro.dialogs.AllFilesPermissionDialog
|
||||||
import com.simplemobiletools.gallery.pro.dialogs.PickDirectoryDialog
|
import com.simplemobiletools.gallery.pro.dialogs.PickDirectoryDialog
|
||||||
|
import com.simplemobiletools.gallery.pro.dialogs.ResizeMultipleImagesDialog
|
||||||
|
import com.simplemobiletools.gallery.pro.dialogs.ResizeWithPathDialog
|
||||||
import com.simplemobiletools.gallery.pro.helpers.DIRECTORY
|
import com.simplemobiletools.gallery.pro.helpers.DIRECTORY
|
||||||
import com.simplemobiletools.gallery.pro.helpers.RECYCLE_BIN
|
import com.simplemobiletools.gallery.pro.helpers.RECYCLE_BIN
|
||||||
import com.simplemobiletools.gallery.pro.models.DateTaken
|
import com.simplemobiletools.gallery.pro.models.DateTaken
|
||||||
|
@ -734,6 +737,136 @@ fun BaseSimpleActivity.copyFile(source: String, destination: String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun BaseSimpleActivity.ensureWriteAccess(path: String, callback: () -> Unit) {
|
||||||
|
when {
|
||||||
|
isRestrictedSAFOnlyRoot(path) -> {
|
||||||
|
handleAndroidSAFDialog(path) {
|
||||||
|
if (!it) {
|
||||||
|
return@handleAndroidSAFDialog
|
||||||
|
}
|
||||||
|
callback.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
needsStupidWritePermissions(path) -> {
|
||||||
|
handleSAFDialog(path) {
|
||||||
|
if (!it) {
|
||||||
|
return@handleSAFDialog
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isAccessibleWithSAFSdk30(path) -> {
|
||||||
|
handleSAFDialogSdk30(path) {
|
||||||
|
if (!it) {
|
||||||
|
return@handleSAFDialogSdk30
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BaseSimpleActivity.launchResizeMultipleImagesDialog(paths: List<String>, callback: (() -> Unit)? = null) {
|
||||||
|
ensureBackgroundThread {
|
||||||
|
val imagePaths = mutableListOf<String>()
|
||||||
|
val imageSizes = mutableListOf<Point>()
|
||||||
|
for (path in paths) {
|
||||||
|
val size = path.getImageResolution(this)
|
||||||
|
if (size != null) {
|
||||||
|
imagePaths.add(path)
|
||||||
|
imageSizes.add(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runOnUiThread {
|
||||||
|
ResizeMultipleImagesDialog(this, imagePaths, imageSizes) {
|
||||||
|
callback?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BaseSimpleActivity.launchResizeImageDialog(path: String, callback: (() -> Unit)? = null) {
|
||||||
|
val originalSize = path.getImageResolution(this) ?: return
|
||||||
|
ResizeWithPathDialog(this, originalSize, path) { newSize, newPath ->
|
||||||
|
ensureBackgroundThread {
|
||||||
|
val file = File(newPath)
|
||||||
|
val pathLastModifiedMap = mapOf(file.absolutePath to file.lastModified())
|
||||||
|
try {
|
||||||
|
resizeImage(newPath, newSize) { success ->
|
||||||
|
if (success) {
|
||||||
|
toast(R.string.file_saved)
|
||||||
|
|
||||||
|
val paths = arrayListOf(file.absolutePath)
|
||||||
|
rescanPathsAndUpdateLastModified(paths, pathLastModifiedMap) {
|
||||||
|
runOnUiThread {
|
||||||
|
callback?.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast(R.string.image_editing_failed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: OutOfMemoryError) {
|
||||||
|
toast(R.string.out_of_memory_error)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showErrorToast(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BaseSimpleActivity.resizeImage(path: String, size: Point, callback: (success: Boolean) -> Unit) {
|
||||||
|
var oldExif: ExifInterface? = null
|
||||||
|
if (isNougatPlus()) {
|
||||||
|
val inputStream = contentResolver.openInputStream(Uri.fromFile(File(path)))
|
||||||
|
oldExif = ExifInterface(inputStream!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newBitmap = Glide.with(applicationContext).asBitmap().load(path).submit(size.x, size.y).get()
|
||||||
|
|
||||||
|
val newFile = File(path)
|
||||||
|
val newFileDirItem = FileDirItem(path, path.getFilenameFromPath())
|
||||||
|
getFileOutputStream(newFileDirItem, true) { out ->
|
||||||
|
if (out != null) {
|
||||||
|
out.use {
|
||||||
|
try {
|
||||||
|
newBitmap.compress(newFile.absolutePath.getCompressionFormat(), 90, out)
|
||||||
|
|
||||||
|
if (isNougatPlus()) {
|
||||||
|
val newExif = ExifInterface(newFile.absolutePath)
|
||||||
|
oldExif?.copyNonDimensionAttributesTo(newExif)
|
||||||
|
}
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun BaseSimpleActivity.rescanPathsAndUpdateLastModified(paths: ArrayList<String>, pathLastModifiedMap: Map<String, Long>, callback: () -> Unit) {
|
||||||
|
fixDateTaken(paths, false)
|
||||||
|
for (path in paths) {
|
||||||
|
val file = File(path)
|
||||||
|
val lastModified = pathLastModifiedMap[path]
|
||||||
|
if (config.keepLastModified && lastModified != null && lastModified != 0L) {
|
||||||
|
File(file.absolutePath).setLastModified(lastModified)
|
||||||
|
updateLastModified(file.absolutePath, lastModified)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rescanPaths(paths, callback)
|
||||||
|
}
|
||||||
|
|
||||||
fun saveFile(path: String, bitmap: Bitmap, out: FileOutputStream, degrees: Int) {
|
fun saveFile(path: String, bitmap: Bitmap, out: FileOutputStream, degrees: Int) {
|
||||||
val matrix = Matrix()
|
val matrix = Matrix()
|
||||||
matrix.postRotate(degrees.toFloat())
|
matrix.postRotate(degrees.toFloat())
|
||||||
|
|
41
app/src/main/res/layout/dialog_resize_multiple_images.xml
Normal file
41
app/src/main/res/layout/dialog_resize_multiple_images.xml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/resize_progress"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginVertical="@dimen/normal_margin"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.simplemobiletools.commons.views.MyTextInputLayout
|
||||||
|
android:id="@+id/resize_factor_input_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/big_margin"
|
||||||
|
android:layout_marginTop="@dimen/normal_margin"
|
||||||
|
android:hint="@string/resize_factor"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
app:helperText="@string/resize_factor_info"
|
||||||
|
app:helperTextEnabled="true"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/resize_progress"
|
||||||
|
app:suffixText="%">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/resize_factor_edit_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textCursorDrawable="@null"
|
||||||
|
android:textSize="@dimen/bigger_text_size" />
|
||||||
|
|
||||||
|
</com.simplemobiletools.commons.views.MyTextInputLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -83,6 +83,11 @@
|
||||||
android:showAsAction="never"
|
android:showAsAction="never"
|
||||||
android:title="@string/set_as"
|
android:title="@string/set_as"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/cab_resize"
|
||||||
|
android:showAsAction="never"
|
||||||
|
android:title="@string/resize"
|
||||||
|
app:showAsAction="never" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/cab_edit"
|
android:id="@+id/cab_edit"
|
||||||
android:icon="@drawable/ic_edit_vector"
|
android:icon="@drawable/ic_edit_vector"
|
||||||
|
|
Loading…
Reference in a new issue