Skip to content

Commit a2f9e3d

Browse files
Sirpixelalotclaude
andcommitted
Fix multiple UI and functionality bugs, update to version 1.7.1
Bug Fixes: - Wire up imageMethod speed setting in ImageCompressor (Fast/Average/Slow compression) - Wire up createRpaAfter setting to automatically create RPA archives after compression - Add validation to prevent compression when all media types are disabled - Move progress polling to Dispatchers.IO to prevent UI freezing and ANRs - Fix compression stats to track real totals and handle failures correctly - Make file filtering case-insensitive for all extensions (.RPA, .RPYC, .RPY, etc.) Technical Improvements: - ImageCompressor now respects imageMethod setting for lossless compression effort - CompressionManager tracks actual file counts (X/Y files) instead of misleading 100% - Failed file compressions count original size (accurate reduction percentage) - Empty folders/disabled types treated as graceful success instead of failure - Progress updates show real progress with failed file counts - File picker now accepts uppercase extensions from Windows distributions - Python decompiler and RPA tool now case-insensitive for file extensions - All I/O operations moved off main thread (countRpycFiles, file reads, etc.) Version: - Updated versionCode to 10 - Updated versionName to "1.7.1" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 435c29d commit a2f9e3d

File tree

10 files changed

+202
-48
lines changed

10 files changed

+202
-48
lines changed

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ android {
2020
applicationId "com.renpytool"
2121
minSdk 33
2222
targetSdk 34
23-
versionCode 9
24-
versionName "1.7"
23+
versionCode 10
24+
versionName "1.7.1"
2525

2626
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
2727

app/src/main/java/com/renpytool/CompressionManager.kt

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ class CompressionManager(private val context: Context) {
8282
audioFiles.sumOf { it.length() } +
8383
videoFiles.sumOf { it.length() }
8484

85-
Log.i(TAG, "Total original size: ${totalOriginalSize / (1024 * 1024)} MB")
85+
val totalFileCount = imageFiles.size + audioFiles.size + videoFiles.size
86+
87+
Log.i(TAG, "Total files: $totalFileCount, Total original size: ${totalOriginalSize / (1024 * 1024)} MB")
8688

8789
var totalCompressedSize = 0L
8890
var totalProcessed = 0
@@ -102,14 +104,15 @@ class CompressionManager(private val context: Context) {
102104

103105
Log.d(TAG, "Image compression complete: success=${result.success}")
104106

105-
if (result.success) {
106-
totalProcessed += result.filesProcessed
107-
totalFailed += result.filesFailed
108-
totalCompressedSize += result.compressedSizeBytes
107+
// Always aggregate results, even if some/all files failed
108+
totalProcessed += result.filesProcessed
109+
totalFailed += result.filesFailed
110+
totalCompressedSize += result.compressedSizeBytes
109111

112+
if (result.success) {
110113
Log.i(TAG, "Image compression complete: ${result.filesProcessed} succeeded, ${result.filesFailed} failed")
111114
} else {
112-
Log.e(TAG, "Image compression failed: ${result.error}")
115+
Log.w(TAG, "Image compression had issues: ${result.filesProcessed} succeeded, ${result.filesFailed} failed, error: ${result.error}")
113116
}
114117
}
115118

@@ -152,6 +155,8 @@ class CompressionManager(private val context: Context) {
152155
totalCompressedSize += result.compressedSize
153156
} else {
154157
totalFailed++
158+
// For failed files, count original size (no reduction)
159+
totalCompressedSize += result.originalSize
155160
Log.w(TAG, "Failed to compress audio: ${audioFile.name}")
156161
}
157162
}
@@ -198,6 +203,8 @@ class CompressionManager(private val context: Context) {
198203
totalCompressedSize += result.compressedSize
199204
} else {
200205
totalFailed++
206+
// For failed files, count original size (no reduction)
207+
totalCompressedSize += result.originalSize
201208
Log.w(TAG, "Failed to compress video: ${videoFile.name}")
202209
}
203210
}
@@ -213,22 +220,27 @@ class CompressionManager(private val context: Context) {
213220
}
214221

215222
// Final progress update
216-
Log.i(TAG, "All phases complete. Total: $totalProcessed processed, $totalFailed failed")
223+
Log.i(TAG, "All phases complete. Total: $totalProcessed processed, $totalFailed failed out of $totalFileCount")
217224
updateProgress(
218225
progressTracker,
219226
"compress",
220-
totalProcessed,
221-
totalProcessed,
227+
totalProcessed + totalFailed,
228+
totalFileCount,
222229
"Complete",
223230
startTime,
224231
totalOriginalSize,
225232
totalCompressedSize,
226233
status = "completed"
227234
)
228235

236+
// Success if:
237+
// - At least one file was processed successfully, OR
238+
// - Nothing to do (zero files found) is graceful success
239+
val isSuccess = totalProcessed > 0 || totalFileCount == 0
240+
229241
Log.d(TAG, "Returning compression result...")
230242
CompressionResult(
231-
success = totalProcessed > 0,
243+
success = isSuccess,
232244
filesProcessed = totalProcessed,
233245
filesFailed = totalFailed,
234246
originalSizeBytes = totalOriginalSize,

app/src/main/java/com/renpytool/FilePickerActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class FilePickerActivity : ComponentActivity() {
127127
// Check if we should open the editor instead of returning
128128
val openEditor = intent.getBooleanExtra("OPEN_EDITOR", false)
129129

130-
if (openEditor && file.name.endsWith(".rpy")) {
130+
if (openEditor && file.name.lowercase().endsWith(".rpy")) {
131131
// Open .rpy file in editor
132132
val editorIntent = Intent(this, RpyEditorActivityNew::class.java)
133133
editorIntent.putExtra(RpyEditorActivityNew.EXTRA_FILE_PATH, file.absolutePath)

app/src/main/java/com/renpytool/ImageCompressor.kt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ class ImageCompressor(private val context: Context) {
101101
// Update progress after each image (synchronized)
102102
synchronized(lock) {
103103
processedCount++
104-
totalCompressed += result.compressedSize
104+
// For failed files, count original size (no reduction)
105+
totalCompressed += if (result.success) {
106+
result.compressedSize
107+
} else {
108+
result.originalSize
109+
}
105110

106111
updateProgress(
107112
progressTracker,
@@ -144,7 +149,7 @@ class ImageCompressor(private val context: Context) {
144149
)
145150

146151
CompressionManager.CompressionResult(
147-
success = successful > 0,
152+
success = successful > 0 || totalImages == 0, // Success if we processed files or nothing to do
148153
filesProcessed = successful,
149154
filesFailed = failed,
150155
originalSizeBytes = totalOriginalSize,
@@ -220,7 +225,13 @@ class ImageCompressor(private val context: Context) {
220225
// Compress to WebP
221226
FileOutputStream(tempFile).use { out ->
222227
val quality = if (settings.imageLossless) {
223-
100 // For lossless, 100 = best compression
228+
// For lossless, quality controls compression effort (speed vs file size)
229+
// Map imageMethod (0=fast, 4=average, 6=slow) to quality
230+
when {
231+
settings.imageMethod <= 2 -> 80 // Fast: less compression, faster
232+
settings.imageMethod in 3..5 -> 90 // Average: balanced
233+
else -> 100 // Slow: best compression, slower
234+
}
224235
} else {
225236
settings.imageQuality // For lossy, quality 1-100
226237
}

app/src/main/java/com/renpytool/MainViewModel.kt

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.renpytool
33
import android.app.Application
44
import android.content.Context
55
import android.content.SharedPreferences
6+
import android.util.Log
67
import androidx.lifecycle.AndroidViewModel
78
import androidx.lifecycle.viewModelScope
89
import com.chaquo.python.PyObject
@@ -581,7 +582,100 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
581582

582583
if (result.success) {
583584
val ratio = String.format("%.1f", result.reductionPercent)
584-
_compressStatus.value = "Compressed ${result.filesProcessed} files ($ratio% reduction)"
585+
586+
// Handle "nothing to do" case
587+
if (result.filesProcessed == 0 && result.filesFailed == 0) {
588+
_compressStatus.value = "No compressible files found"
589+
} else {
590+
val statusMsg = buildString {
591+
append("Compressed ${result.filesProcessed} files ($ratio% reduction)")
592+
if (result.filesFailed > 0) {
593+
append(", ${result.filesFailed} failed")
594+
}
595+
}
596+
_compressStatus.value = statusMsg
597+
}
598+
599+
// If createRpaAfter is enabled and we have files, create RPA archive from compressed output
600+
if (settings.createRpaAfter && result.filesProcessed > 0) {
601+
try {
602+
Log.i("MainViewModel", "Creating RPA archive from compressed output...")
603+
604+
// Update progress for RPA creation phase
605+
val rpaProgress = ProgressData().apply {
606+
operation = "create"
607+
status = "in_progress"
608+
startTime = System.currentTimeMillis()
609+
lastUpdateTime = System.currentTimeMillis()
610+
totalFiles = 0
611+
processedFiles = 0
612+
currentFile = "Creating RPA archive..."
613+
}
614+
tracker.writeProgress(rpaProgress)
615+
616+
// Generate output RPA file path
617+
val outputDir = File(outputDirPath)
618+
val rpaFileName = "${outputDir.name}.rpa"
619+
val rpaOutputPath = File(outputDir.parentFile, rpaFileName).absolutePath
620+
621+
// Call Python creation
622+
val rpaResult = rpaModule.callAttr(
623+
"create_rpa",
624+
outputDirPath,
625+
rpaOutputPath,
626+
3,
627+
0xDEADBEEF.toInt(),
628+
tracker.progressFilePath
629+
)
630+
631+
if (rpaResult != null) {
632+
val successObj = rpaResult.callAttr("__getitem__", "success")
633+
val filesObj = rpaResult.callAttr("__getitem__", "files")
634+
635+
val rpaSuccess = successObj.toJava(Boolean::class.java)
636+
val fileCount = filesObj.asList().size
637+
638+
if (rpaSuccess) {
639+
val rpaStatusMsg = buildString {
640+
append("Compressed ${result.filesProcessed} files ($ratio% reduction)")
641+
if (result.filesFailed > 0) {
642+
append(", ${result.filesFailed} failed")
643+
}
644+
append(" + created RPA with $fileCount files")
645+
}
646+
_compressStatus.value = rpaStatusMsg
647+
Log.i("MainViewModel", "RPA archive created successfully: $rpaOutputPath")
648+
} else {
649+
_compressStatus.value = buildString {
650+
append("Compressed ${result.filesProcessed} files ($ratio% reduction)")
651+
if (result.filesFailed > 0) {
652+
append(", ${result.filesFailed} failed")
653+
}
654+
append(" but RPA creation failed")
655+
}
656+
Log.w("MainViewModel", "RPA creation failed")
657+
}
658+
} else {
659+
_compressStatus.value = buildString {
660+
append("Compressed ${result.filesProcessed} files ($ratio% reduction)")
661+
if (result.filesFailed > 0) {
662+
append(", ${result.filesFailed} failed")
663+
}
664+
append(" but RPA creation returned null")
665+
}
666+
}
667+
668+
} catch (e: Exception) {
669+
Log.e("MainViewModel", "Error creating RPA after compression", e)
670+
_compressStatus.value = buildString {
671+
append("Compressed ${result.filesProcessed} files ($ratio% reduction)")
672+
if (result.filesFailed > 0) {
673+
append(", ${result.filesFailed} failed")
674+
}
675+
append(" but RPA creation error: ${e.message}")
676+
}
677+
}
678+
}
585679
} else {
586680
_compressStatus.value = "Compression failed: ${result.error ?: "Unknown error"}"
587681
}

app/src/main/java/com/renpytool/ui/CompressionSettingsDialog.kt

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ fun CompressionSettingsDialog(
3939
var threads by remember { mutableIntStateOf(initialSettings.threads) }
4040
var createRpaAfter by remember { mutableStateOf(initialSettings.createRpaAfter) }
4141

42+
var errorMessage by remember { mutableStateOf<String?>(null) }
4243
val scrollState = rememberScrollState()
4344

4445
AlertDialog(
@@ -65,7 +66,10 @@ fun CompressionSettingsDialog(
6566
) {
6667
Checkbox(
6768
checked = skipImages,
68-
onCheckedChange = { skipImages = it }
69+
onCheckedChange = {
70+
skipImages = it
71+
errorMessage = null
72+
}
6973
)
7074
Text("Skip Images")
7175
}
@@ -129,7 +133,10 @@ fun CompressionSettingsDialog(
129133
) {
130134
Checkbox(
131135
checked = skipAudio,
132-
onCheckedChange = { skipAudio = it }
136+
onCheckedChange = {
137+
skipAudio = it
138+
errorMessage = null
139+
}
133140
)
134141
Text("Skip Audio")
135142
}
@@ -165,7 +172,10 @@ fun CompressionSettingsDialog(
165172
) {
166173
Checkbox(
167174
checked = skipVideo,
168-
onCheckedChange = { skipVideo = it }
175+
onCheckedChange = {
176+
skipVideo = it
177+
errorMessage = null
178+
}
169179
)
170180
Text("Skip Video")
171181
}
@@ -214,6 +224,17 @@ fun CompressionSettingsDialog(
214224
)
215225
Text("Create RPA after compression")
216226
}
227+
228+
// Error message display
229+
errorMessage?.let { message ->
230+
Spacer(modifier = Modifier.height(16.dp))
231+
Text(
232+
text = message,
233+
color = MaterialTheme.colorScheme.error,
234+
style = MaterialTheme.typography.bodyMedium,
235+
modifier = Modifier.fillMaxWidth()
236+
)
237+
}
217238
}
218239

219240
// Custom scrollbar indicator
@@ -240,7 +261,14 @@ fun CompressionSettingsDialog(
240261
threads = threads,
241262
createRpaAfter = createRpaAfter
242263
)
243-
onConfirm(settings)
264+
265+
// Validate that at least one media type is enabled
266+
if (!settings.hasEnabledTypes()) {
267+
errorMessage = "Please enable at least one media type (Images, Audio, or Video)"
268+
} else {
269+
errorMessage = null
270+
onConfirm(settings)
271+
}
244272
}
245273
) {
246274
Text("Start")

app/src/main/java/com/renpytool/viewmodel/FilePickerViewModel.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,12 @@ class FilePickerViewModel : ViewModel() {
8383
continue
8484
}
8585

86-
// Apply file filter if in FILE mode
86+
// Apply file filter if in FILE mode (case-insensitive)
8787
val state = _uiState.value
8888
if (state.mode == FilePickerUiState.MODE_FILE &&
8989
!file.isDirectory &&
9090
state.fileFilter != null) {
91-
if (!file.name.endsWith(state.fileFilter)) {
91+
if (!file.name.lowercase().endsWith(state.fileFilter.lowercase())) {
9292
continue
9393
}
9494
}

0 commit comments

Comments
 (0)