Skip to content

Commit 1274be7

Browse files
Sirpixelalotclaude
andcommitted
Update to version 1.2 with iOS 17 icon refresh and improvements
- Update version to 1.2 (versionCode 2) - Add iOS 17 outlined icons for better visual consistency: * PNG file icon (.png files) * ZIP file icon (.zip files) * Script icon (.rpy, .rpyb files and decompile card) * Save icon (create RPA card) - Apply colorPrimary tint to all main card icons for consistency - Add unrpyc decompiler Python library for RPYC decompilation - Improve file type detection in file browser 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent a7e04a2 commit 1274be7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+5600
-12
lines changed

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ android {
1818
applicationId "com.renpytool"
1919
minSdk 33
2020
targetSdk 34
21-
versionCode 1
22-
versionName "1.1"
21+
versionCode 2
22+
versionName "1.2"
2323

2424
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
2525

app/src/main/java/com/renpytool/FilePickerAdapter.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,21 @@ public void bind(FileItem item, OnItemClickListener listener, OnItemLongClickLis
128128
ivIcon.setImageResource(R.drawable.ic_folder);
129129
tvDetails.setText("Folder");
130130
} else {
131-
// File
132-
ivIcon.setImageResource(android.R.drawable.ic_menu_upload);
131+
// File - check file type for custom icons
132+
String fileName = item.getName().toLowerCase();
133+
if (fileName.endsWith(".rpy") || fileName.endsWith(".rpyb")) {
134+
ivIcon.setImageResource(R.drawable.ic_script_file);
135+
} else if (fileName.endsWith(".rpa") || fileName.endsWith(".rpyc") || fileName.endsWith(".rpymc")) {
136+
ivIcon.setImageResource(R.drawable.ic_rpa_file);
137+
} else if (fileName.endsWith(".apk")) {
138+
ivIcon.setImageResource(R.drawable.ic_apk_file);
139+
} else if (fileName.endsWith(".png")) {
140+
ivIcon.setImageResource(R.drawable.ic_png_file);
141+
} else if (fileName.endsWith(".zip")) {
142+
ivIcon.setImageResource(R.drawable.ic_zip_file);
143+
} else {
144+
ivIcon.setImageResource(android.R.drawable.ic_menu_upload);
145+
}
133146
tvDetails.setText(formatFileSize(item.getSize()) + " • " + formatDate(item.getLastModified()));
134147
}
135148

app/src/main/java/com/renpytool/MainActivity.java

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@ public class MainActivity extends AppCompatActivity {
3636

3737
private static final int PERMISSION_REQUEST_CODE = 100;
3838

39-
private MaterialCardView cardExtract, cardCreate;
40-
private TextView tvStatus, tvExtractStatus, tvCreateStatus;
39+
private MaterialCardView cardExtract, cardCreate, cardDecompile;
40+
private TextView tvStatus, tvExtractStatus, tvCreateStatus, tvDecompileStatus;
4141
private ProgressBar progressBar;
4242

4343
private Python python;
4444
private PyObject rpaModule;
45+
private PyObject decompileModule;
4546

4647
private ExecutorService executorService;
4748

@@ -50,6 +51,7 @@ public class MainActivity extends AppCompatActivity {
5051
private ActivityResultLauncher<Intent> extractDirPickerLauncher;
5152
private ActivityResultLauncher<Intent> createSourcePickerLauncher;
5253
private ActivityResultLauncher<Intent> createOutputPickerLauncher;
54+
private ActivityResultLauncher<Intent> decompileDirPickerLauncher;
5355

5456
// Temporary storage for multi-step file picking
5557
private String selectedRpaPath;
@@ -68,6 +70,7 @@ protected void onCreate(Bundle savedInstanceState) {
6870
}
6971
python = Python.getInstance();
7072
rpaModule = python.getModule("rpa_wrapper");
73+
decompileModule = python.getModule("decompile_wrapper");
7174

7275
// Initialize executor service
7376
executorService = Executors.newSingleThreadExecutor();
@@ -88,14 +91,17 @@ protected void onCreate(Bundle savedInstanceState) {
8891
private void initViews() {
8992
cardExtract = findViewById(R.id.card_extract);
9093
cardCreate = findViewById(R.id.card_create);
94+
cardDecompile = findViewById(R.id.card_decompile);
9195
tvStatus = findViewById(R.id.tv_status);
9296
tvExtractStatus = findViewById(R.id.tv_extract_status);
9397
tvCreateStatus = findViewById(R.id.tv_create_status);
98+
tvDecompileStatus = findViewById(R.id.tv_decompile_status);
9499
progressBar = findViewById(R.id.progress_bar);
95100

96101
// Set up click listeners
97102
cardExtract.setOnClickListener(v -> startExtractFlow());
98103
cardCreate.setOnClickListener(v -> startCreateFlow());
104+
cardDecompile.setOnClickListener(v -> startDecompileFlow());
99105
}
100106

101107

@@ -212,6 +218,31 @@ private void initFilePickerLaunchers() {
212218
showOutputFileNameDialog();
213219
}
214220
});
221+
222+
// Decompile: Pick source directory
223+
decompileDirPickerLauncher = registerForActivityResult(
224+
new ActivityResultContracts.StartActivityForResult(),
225+
result -> {
226+
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
227+
String sourcePath = null;
228+
229+
// Check for multi-select first (user may have accidentally entered multi-select mode)
230+
ArrayList<String> selectedPaths = result.getData().getStringArrayListExtra(FilePickerActivity.EXTRA_SELECTED_PATHS);
231+
if (selectedPaths != null && !selectedPaths.isEmpty()) {
232+
// If multi-select happened, just use the first path
233+
sourcePath = selectedPaths.get(0);
234+
} else {
235+
// Normal single selection
236+
sourcePath = result.getData().getStringExtra(FilePickerActivity.EXTRA_SELECTED_PATH);
237+
}
238+
239+
if (sourcePath != null && !sourcePath.isEmpty()) {
240+
performDecompile(sourcePath);
241+
} else {
242+
Toast.makeText(this, "No directory selected", Toast.LENGTH_SHORT).show();
243+
}
244+
}
245+
});
215246
}
216247

217248
private void startExtractFlow() {
@@ -705,18 +736,114 @@ private void performCreation(String sourceDirPath, String outputFilePath) {
705736
});
706737
}
707738

739+
private void startDecompileFlow() {
740+
// Launch file picker for directory containing .rpyc files
741+
Intent intent = new Intent(this, FilePickerActivity.class);
742+
intent.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIRECTORY);
743+
intent.putExtra(FilePickerActivity.EXTRA_TITLE, "Select Folder with RPYC Files");
744+
decompileDirPickerLauncher.launch(intent);
745+
}
746+
747+
private void performDecompile(String sourceDirPath) {
748+
// Clear any old progress data before starting
749+
ProgressTracker tracker = new ProgressTracker(MainActivity.this);
750+
tracker.clearProgress();
751+
752+
// Launch progress activity
753+
Intent intent = new Intent(this, ProgressActivity.class);
754+
startActivity(intent);
755+
756+
executorService.execute(() -> {
757+
try {
758+
// Initialize progress with start time
759+
ProgressData initialData = new ProgressData();
760+
initialData.operation = "decompile";
761+
initialData.status = "in_progress";
762+
initialData.startTime = System.currentTimeMillis();
763+
initialData.lastUpdateTime = System.currentTimeMillis();
764+
initialData.totalFiles = 0;
765+
initialData.processedFiles = 0;
766+
initialData.currentFile = "Starting decompilation...";
767+
tracker.writeProgress(initialData);
768+
769+
// Get progress file path
770+
String progressFilePath = tracker.getProgressFilePath();
771+
772+
// Call Python decompilation
773+
PyObject result = decompileModule.callAttr("decompile_directory",
774+
sourceDirPath,
775+
progressFilePath);
776+
777+
// Check if result is valid
778+
if (result == null) {
779+
throw new Exception("Python function returned null");
780+
}
781+
782+
// Access dictionary items using __getitem__
783+
PyObject successObj = result.callAttr("__getitem__", "success");
784+
PyObject messageObj = result.callAttr("__getitem__", "message");
785+
PyObject statsObj = result.callAttr("__getitem__", "stats");
786+
787+
final boolean success = successObj.toJava(Boolean.class);
788+
final String message = messageObj.toJava(String.class);
789+
790+
// Extract stats
791+
PyObject totalObj = statsObj.callAttr("__getitem__", "total");
792+
PyObject successCountObj = statsObj.callAttr("__getitem__", "success");
793+
PyObject skippedObj = statsObj.callAttr("__getitem__", "skipped");
794+
PyObject failedObj = statsObj.callAttr("__getitem__", "failed");
795+
796+
final int total = totalObj.toJava(Integer.class);
797+
final int successCount = successCountObj.toJava(Integer.class);
798+
final int skipped = skippedObj.toJava(Integer.class);
799+
final int failed = failedObj.toJava(Integer.class);
800+
801+
runOnUiThread(() -> {
802+
if (success) {
803+
tvDecompileStatus.setText(String.format(
804+
"Decompiled %d files (%d success, %d skipped, %d failed)",
805+
total, successCount, skipped, failed
806+
));
807+
} else {
808+
tvDecompileStatus.setText("Decompilation failed: " + message);
809+
}
810+
});
811+
812+
} catch (Exception e) {
813+
e.printStackTrace();
814+
815+
// Update progress file with error
816+
try {
817+
ProgressData errorData = new ProgressData();
818+
errorData.operation = "decompile";
819+
errorData.status = "failed";
820+
errorData.errorMessage = "Error: " + e.getMessage();
821+
tracker.writeProgress(errorData);
822+
} catch (Exception ex) {
823+
ex.printStackTrace();
824+
}
825+
826+
runOnUiThread(() -> {
827+
tvDecompileStatus.setText("Decompilation error: " + e.getMessage());
828+
});
829+
}
830+
});
831+
}
832+
708833
private void showProgress(String message) {
709834
progressBar.setVisibility(View.VISIBLE);
710835
tvStatus.setText(message);
711836
cardExtract.setEnabled(false);
712837
cardCreate.setEnabled(false);
838+
cardDecompile.setEnabled(false);
713839
}
714840

715841
private void hideProgress() {
716842
progressBar.setVisibility(View.GONE);
717843
tvStatus.setText("Ready");
718844
cardExtract.setEnabled(true);
719845
cardCreate.setEnabled(true);
846+
cardDecompile.setEnabled(true);
720847
}
721848

722849
private void showSuccess(String message) {

app/src/main/java/com/renpytool/ProgressActivity.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,13 @@ private void updateUI(ProgressData data) {
9595
operationType = data.operation.equals("extract") ?
9696
"Copying files to destination..." : "Copying archive to destination...";
9797
} else {
98-
operationType = data.operation.equals("extract") ?
99-
"Extracting RPA..." : "Creating RPA Archive...";
98+
if (data.operation.equals("extract")) {
99+
operationType = "Extracting RPA...";
100+
} else if (data.operation.equals("decompile")) {
101+
operationType = "Decompiling RPYC...";
102+
} else {
103+
operationType = "Creating RPA Archive...";
104+
}
100105
}
101106

102107
// Add batch information if in batch mode

0 commit comments

Comments
 (0)