Fixed images viewing in system default image viewer not working
This commit is contained in:
parent
45ad7f64ac
commit
1bfa65b740
43
src/main/kotlin/com/jetpackduba/gitnuro/AppFilesManager.kt
Normal file
43
src/main/kotlin/com/jetpackduba/gitnuro/AppFilesManager.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package com.jetpackduba.gitnuro
|
||||||
|
|
||||||
|
import com.jetpackduba.gitnuro.extensions.OS
|
||||||
|
import com.jetpackduba.gitnuro.extensions.getCurrentOs
|
||||||
|
import com.jetpackduba.gitnuro.logging.printLog
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val TAG = "AppFilesManager"
|
||||||
|
|
||||||
|
class AppFilesManager @Inject constructor() {
|
||||||
|
fun getAppFolder(): File {
|
||||||
|
val baseFolderPath = when (getCurrentOs()) {
|
||||||
|
OS.LINUX -> {
|
||||||
|
var configFolder: String? = System.getenv("XDG_CONFIG_HOME")
|
||||||
|
|
||||||
|
if (configFolder.isNullOrEmpty())
|
||||||
|
configFolder = "${System.getenv("HOME")}/.config"
|
||||||
|
|
||||||
|
|
||||||
|
configFolder
|
||||||
|
}
|
||||||
|
|
||||||
|
OS.WINDOWS -> System.getenv("APPDATA").orEmpty()
|
||||||
|
OS.MAC -> System.getProperty("user.home") + "/Library/Application"
|
||||||
|
else -> {
|
||||||
|
printLog(TAG, "Unknown OS")
|
||||||
|
throw Exception("Invalid OS")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val baseFolder = File(baseFolderPath)
|
||||||
|
|
||||||
|
baseFolder.mkdirs()
|
||||||
|
|
||||||
|
val appFolder = File(baseFolder, "gitnuro")
|
||||||
|
// TODO test if mkdir fails for some reason
|
||||||
|
if(!appFolder.exists() || !appFolder.isDirectory)
|
||||||
|
appFolder.mkdir()
|
||||||
|
|
||||||
|
return appFolder
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,23 @@
|
|||||||
package com.jetpackduba.gitnuro
|
package com.jetpackduba.gitnuro
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.di.TabScope
|
import com.jetpackduba.gitnuro.di.TabScope
|
||||||
|
import com.jetpackduba.gitnuro.extensions.openDirectory
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.io.path.createTempDirectory
|
|
||||||
import kotlin.io.path.deleteIfExists
|
|
||||||
|
|
||||||
@TabScope
|
@TabScope
|
||||||
class TempFilesManager @Inject constructor() {
|
class TempFilesManager @Inject constructor(
|
||||||
val tempDir by lazy {
|
private val appFilesManager: AppFilesManager,
|
||||||
val tempDirPath = createTempDirectory("gitnuro_")
|
) {
|
||||||
tempDirPath.toFile().deleteOnExit()
|
fun tempDir(): File {
|
||||||
|
val appDataDir = appFilesManager.getAppFolder()
|
||||||
|
val tempDir = appDataDir.openDirectory("tmp")
|
||||||
|
|
||||||
tempDirPath
|
if(!tempDir.exists() || !tempDir.isDirectory) {
|
||||||
}
|
tempDir.mkdir()
|
||||||
|
}
|
||||||
|
|
||||||
fun removeTempDir() {
|
return tempDir
|
||||||
tempDir.deleteIfExists()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.jetpackduba.gitnuro.extensions
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun File.openDirectory(dirName: String): File {
|
||||||
|
val newDir = File(this, dirName)
|
||||||
|
|
||||||
|
if (!newDir.exists() || !newDir.isDirectory) {
|
||||||
|
newDir.mkdir()
|
||||||
|
}
|
||||||
|
|
||||||
|
return newDir
|
||||||
|
}
|
@ -28,7 +28,7 @@ fun runCommand(command: String): String? {
|
|||||||
fun runCommandWithoutResult(command: String, args: String, file: String): Boolean {
|
fun runCommandWithoutResult(command: String, args: String, file: String): Boolean {
|
||||||
val parts: Array<String> = prepareCommand(command, args, file)
|
val parts: Array<String> = prepareCommand(command, args, file)
|
||||||
|
|
||||||
printLog(TAG, "Running command $parts")
|
printLog(TAG, "Running command ${parts.joinToString( )}")
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val p = Runtime.getRuntime().exec(parts) ?: return false
|
val p = Runtime.getRuntime().exec(parts) ?: return false
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.jetpackduba.gitnuro.git
|
package com.jetpackduba.gitnuro.git
|
||||||
|
|
||||||
import com.jetpackduba.gitnuro.TempFilesManager
|
import com.jetpackduba.gitnuro.TempFilesManager
|
||||||
import com.jetpackduba.gitnuro.extensions.systemSeparator
|
import com.jetpackduba.gitnuro.extensions.fileName
|
||||||
import org.eclipse.jgit.diff.ContentSource
|
import org.eclipse.jgit.diff.ContentSource
|
||||||
import org.eclipse.jgit.diff.DiffEntry
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
import org.eclipse.jgit.diff.RawText
|
import org.eclipse.jgit.diff.RawText
|
||||||
@ -11,16 +11,21 @@ import org.eclipse.jgit.storage.pack.PackConfig
|
|||||||
import org.eclipse.jgit.treewalk.AbstractTreeIterator
|
import org.eclipse.jgit.treewalk.AbstractTreeIterator
|
||||||
import org.eclipse.jgit.treewalk.WorkingTreeIterator
|
import org.eclipse.jgit.treewalk.WorkingTreeIterator
|
||||||
import org.eclipse.jgit.util.LfsFactory
|
import org.eclipse.jgit.util.LfsFactory
|
||||||
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.io.path.createTempFile
|
|
||||||
|
|
||||||
|
|
||||||
private const val DEFAULT_BINARY_FILE_THRESHOLD = PackConfig.DEFAULT_BIG_FILE_THRESHOLD
|
private const val DEFAULT_BINARY_FILE_THRESHOLD = PackConfig.DEFAULT_BIG_FILE_THRESHOLD
|
||||||
private const val IMAGE_CONTENT_TYPE = "image/"
|
private const val IMAGE_CONTENT_TYPE = "image/"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max of chars that a file name can have. Used to avoid issues with OS chars limit in file names.
|
||||||
|
*/
|
||||||
|
private const val FILE_NAME_MAX_LENGTH = 250
|
||||||
|
|
||||||
val animatedImages = arrayOf(
|
val animatedImages = arrayOf(
|
||||||
"image/gif",
|
"image/gif",
|
||||||
"image/webp"
|
"image/webp"
|
||||||
@ -76,17 +81,21 @@ class RawFileManager @Inject constructor(
|
|||||||
entry: DiffEntry,
|
entry: DiffEntry,
|
||||||
side: DiffEntry.Side
|
side: DiffEntry.Side
|
||||||
): EntryContent.ImageBinary {
|
): EntryContent.ImageBinary {
|
||||||
val tempDir = tempFilesManager.tempDir
|
val tempDir = tempFilesManager.tempDir()
|
||||||
|
|
||||||
val tempFile = createTempFile(tempDir, prefix = "${entry.newPath.replace(systemSeparator, "_")}_${side.name}")
|
val prefix = "${entry.getId(side).name().take(20)}_${side.name}_"
|
||||||
tempFile.toFile().deleteOnExit()
|
val tempFileName = prefix + entry.fileName.take(FILE_NAME_MAX_LENGTH - prefix.length)
|
||||||
|
|
||||||
val out = FileOutputStream(tempFile.toFile())
|
val tempFile = File(tempDir, tempFileName)
|
||||||
|
|
||||||
|
tempFile.deleteOnExit()
|
||||||
|
|
||||||
|
val out = FileOutputStream(tempFile)
|
||||||
out.use {
|
out.use {
|
||||||
ldr.copyTo(out)
|
ldr.copyTo(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
return EntryContent.ImageBinary(tempFile, Files.probeContentType(Path.of(entry.newPath)).orEmpty())
|
return EntryContent.ImageBinary(tempFile.absolutePath, Files.probeContentType(Path.of(entry.newPath)).orEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isImage(entry: DiffEntry): Boolean {
|
private fun isImage(entry: DiffEntry): Boolean {
|
||||||
@ -102,7 +111,7 @@ sealed class EntryContent {
|
|||||||
object InvalidObjectBlob : EntryContent()
|
object InvalidObjectBlob : EntryContent()
|
||||||
data class Text(val rawText: RawText) : EntryContent()
|
data class Text(val rawText: RawText) : EntryContent()
|
||||||
sealed class BinaryContent : EntryContent()
|
sealed class BinaryContent : EntryContent()
|
||||||
data class ImageBinary(val tempFilePath: Path, val contentType: String) : BinaryContent()
|
data class ImageBinary(val imagePath: String, val contentType: String) : BinaryContent()
|
||||||
object Binary : BinaryContent()
|
object Binary : BinaryContent()
|
||||||
object TooLargeEntry : EntryContent()
|
object TooLargeEntry : EntryContent()
|
||||||
}
|
}
|
@ -27,7 +27,6 @@ import androidx.compose.ui.input.pointer.PointerEventType
|
|||||||
import androidx.compose.ui.input.pointer.onPointerEvent
|
import androidx.compose.ui.input.pointer.onPointerEvent
|
||||||
import androidx.compose.ui.res.loadImageBitmap
|
import androidx.compose.ui.res.loadImageBitmap
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@ -58,8 +57,6 @@ import org.jetbrains.compose.animatedimage.animate
|
|||||||
import org.jetbrains.compose.animatedimage.loadAnimatedImage
|
import org.jetbrains.compose.animatedimage.loadAnimatedImage
|
||||||
import org.jetbrains.compose.resources.loadOrNull
|
import org.jetbrains.compose.resources.loadOrNull
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.io.path.absolutePathString
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
private const val MAX_MOVES_COUNT = 5
|
private const val MAX_MOVES_COUNT = 5
|
||||||
@ -253,7 +250,7 @@ fun SideTitle(text: String) {
|
|||||||
fun SideDiff(entryContent: EntryContent) {
|
fun SideDiff(entryContent: EntryContent) {
|
||||||
when (entryContent) {
|
when (entryContent) {
|
||||||
EntryContent.Binary -> BinaryDiff()
|
EntryContent.Binary -> BinaryDiff()
|
||||||
is EntryContent.ImageBinary -> ImageDiff(entryContent.tempFilePath, entryContent.contentType)
|
is EntryContent.ImageBinary -> ImageDiff(entryContent.imagePath, entryContent.contentType)
|
||||||
else -> {
|
else -> {
|
||||||
}
|
}
|
||||||
// is EntryContent.Text -> //TODO maybe have a text view if the file was a binary before?
|
// is EntryContent.Text -> //TODO maybe have a text view if the file was a binary before?
|
||||||
@ -262,9 +259,7 @@ fun SideDiff(entryContent: EntryContent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ImageDiff(tempImagePath: Path, contentType: String) {
|
private fun ImageDiff(imagePath: String, contentType: String) {
|
||||||
val imagePath = tempImagePath.absolutePathString()
|
|
||||||
|
|
||||||
if (animatedImages.contains(contentType)) {
|
if (animatedImages.contains(contentType)) {
|
||||||
AnimatedImage(imagePath)
|
AnimatedImage(imagePath)
|
||||||
} else {
|
} else {
|
||||||
@ -305,13 +300,13 @@ private fun StaticImage(tempImagePath: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AnimatedImage(tempImagePath: String) {
|
private fun AnimatedImage(iamgePath: String) {
|
||||||
Image(
|
Image(
|
||||||
bitmap = loadOrNull(tempImagePath) { loadAnimatedImage(tempImagePath) }?.animate() ?: ImageBitmap.Blank,
|
bitmap = loadOrNull(iamgePath) { loadAnimatedImage(iamgePath) }?.animate() ?: ImageBitmap.Blank,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
.handMouseClickable {
|
.handMouseClickable {
|
||||||
openFileWithExternalApp(tempImagePath)
|
openFileWithExternalApp(iamgePath)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user