android/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt

196 lines
6.6 KiB
Kotlin

/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2023 ZetaTom
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.client.editimage
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import com.canhub.cropper.CropImageView
import com.nextcloud.client.di.Injectable
import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.client.jobs.upload.FileUploadWorker
import com.nextcloud.utils.extensions.getParcelableArgument
import com.owncloud.android.R
import com.owncloud.android.databinding.ActivityEditImageBinding
import com.owncloud.android.datamodel.OCFile
import com.owncloud.android.files.services.NameCollisionPolicy
import com.owncloud.android.lib.common.operations.OnRemoteOperationListener
import com.owncloud.android.operations.UploadFileOperation
import com.owncloud.android.ui.activity.FileActivity
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.MimeType
import java.io.File
class EditImageActivity :
FileActivity(),
OnRemoteOperationListener,
CropImageView.OnSetImageUriCompleteListener,
CropImageView.OnCropImageCompleteListener,
Injectable {
private lateinit var binding: ActivityEditImageBinding
private lateinit var file: OCFile
private lateinit var format: Bitmap.CompressFormat
companion object {
const val EXTRA_FILE = "FILE"
const val OPEN_IMAGE_EDITOR = "OPEN_IMAGE_EDITOR"
private val supportedMimeTypes = arrayOf(
MimeType.PNG,
MimeType.JPEG,
MimeType.WEBP,
MimeType.TIFF,
MimeType.BMP,
MimeType.HEIC
)
fun canBePreviewed(file: OCFile): Boolean {
return file.mimeType in supportedMimeTypes
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityEditImageBinding.inflate(layoutInflater)
setContentView(binding.root)
file = intent.extras?.getParcelableArgument(EXTRA_FILE, OCFile::class.java)
?: throw IllegalArgumentException("Missing file argument")
setSupportActionBar(binding.toolbar)
supportActionBar?.apply {
title = file.fileName
setDisplayHomeAsUpEnabled(true)
}
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
windowInsetsController.hide(WindowInsetsCompat.Type.statusBars())
window.statusBarColor = ContextCompat.getColor(this, R.color.black)
window.navigationBarColor = getColor(R.color.black)
setupCropper()
}
override fun onCropImageComplete(view: CropImageView, result: CropImageView.CropResult) {
if (!result.isSuccessful) {
DisplayUtils.showSnackMessage(this, getString(R.string.image_editor_unable_to_edit_image))
return
}
val resultUri = result.getUriFilePath(this, false)
val newFileName = file.fileName.substring(0, file.fileName.lastIndexOf('.')) +
" " + getString(R.string.image_editor_file_edited_suffix) +
resultUri?.substring(resultUri.lastIndexOf('.'))
resultUri?.let {
FileUploadHelper().uploadNewFiles(
user = storageManager.user,
localPaths = arrayOf(it),
remotePaths = arrayOf(file.parentRemotePath + File.separator + newFileName),
createRemoteFolder = false,
createdBy = UploadFileOperation.CREATED_BY_USER,
requiresWifi = false,
requiresCharging = false,
nameCollisionPolicy = NameCollisionPolicy.RENAME,
localBehavior = FileUploadWorker.LOCAL_BEHAVIOUR_DELETE
)
}
}
override fun onSetImageUriComplete(view: CropImageView, uri: Uri, error: Exception?) {
if (error != null) {
DisplayUtils.showSnackMessage(this, getString(R.string.image_editor_unable_to_edit_image))
return
}
view.visibility = View.VISIBLE
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
// add save button to action bar
menuInflater.inflate(R.menu.custom_menu_placeholder, menu)
val saveIcon = AppCompatResources.getDrawable(this, R.drawable.ic_check)?.also {
DrawableCompat.setTint(it, resources.getColor(R.color.white, theme))
}
menu?.findItem(R.id.custom_menu_placeholder_item)?.apply {
icon = saveIcon
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
contentDescription = getString(R.string.common_save)
}
}
return true
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.custom_menu_placeholder_item -> {
binding.cropImageView.croppedImageAsync(format)
finish()
true
}
else -> {
finish()
true
}
}
/**
* Set up image cropper and image editor control strip.
*/
private fun setupCropper() {
val cropper = binding.cropImageView
@Suppress("MagicNumber")
binding.rotateLeft.setOnClickListener {
cropper.rotateImage(-90)
}
@Suppress("MagicNumber")
binding.rotateRight.setOnClickListener {
cropper.rotateImage(90)
}
binding.flipVertical.setOnClickListener {
cropper.flipImageVertically()
}
binding.flipHorizontal.setOnClickListener {
cropper.flipImageHorizontally()
}
cropper.setOnSetImageUriCompleteListener(this)
cropper.setOnCropImageCompleteListener(this)
cropper.setImageUriAsync(file.storageUri)
// determine output file format
format = when (file.mimeType) {
MimeType.PNG -> Bitmap.CompressFormat.PNG
MimeType.WEBP -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Bitmap.CompressFormat.WEBP_LOSSY
} else {
@Suppress("DEPRECATION")
Bitmap.CompressFormat.WEBP
}
}
else -> Bitmap.CompressFormat.JPEG
}
}
}