From 6130045432605005e330ba4fa41f6459459cb781 Mon Sep 17 00:00:00 2001
From: An Bui <91354810+AnBui2004@users.noreply.github.com>
Date: Tue, 9 Dec 2025 00:24:45 +0700
Subject: [PATCH] 3.4.2
- Added settings for X11 display.
- Added option to run Qemu with Xterm.
- Fixed missing fluxbox.
- Improved X11 on Android 14+.
- Improved System monitor stability.
- Improved interface for Quick Edit.
- Added option to check rom file before extracting.
- Improved interface for Theme settings.
---
.gitignore | 1 +
app/build.gradle | 5 +-
app/src/main/AndroidManifest.xml | 4 +
.../com/vectras/qemu/MainSettingsManager.java | 224 ++++++++++--------
.../main/java/com/vectras/vm/AppConfig.java | 4 +-
.../main/java/com/vectras/vm/MainService.java | 17 +-
.../vectras/vm/QemuParamsEditorActivity.java | 6 +-
.../main/java/com/vectras/vm/VMManager.java | 45 ++--
.../com/vectras/vm/home/HomeActivity.java | 4 +-
.../vectras/vm/home/core/DisplaySystem.java | 68 +++---
.../com/vectras/vm/home/core/HomeStartVM.java | 164 ++++++++-----
.../home/monitor/SystemMonitorFragment.java | 17 +-
.../ImportExportSettingsActivity.java | 9 +-
.../settings/X11DisplaySettingsActivity.java | 54 +++++
.../com/vectras/vm/utils/DialogUtils.java | 64 ++---
.../java/com/vectras/vm/utils/FileUtils.java | 24 +-
.../com/vectras/vm/utils/LibraryChecker.java | 6 +-
.../java/com/vectras/vm/utils/ZipUtils.java | 33 +++
.../main/java/com/vectras/vterm/Terminal.java | 55 ++---
.../activity_import_export_settings.xml | 42 ++++
.../layout/activity_qemu_params_editor.xml | 94 +++++---
app/src/main/res/layout/activity_theme.xml | 97 ++++----
.../main/res/layout/activity_vnc_settings.xml | 3 +-
.../layout/activity_x11_display_settings.xml | 132 +++++++++++
app/src/main/res/values/strings.xml | 15 +-
app/src/main/res/xml/headers_preference.xml | 9 +
gradle/wrapper/gradle-wrapper.properties | 4 +-
web/data/UpdateConfig.json | 8 +-
28 files changed, 802 insertions(+), 406 deletions(-)
create mode 100644 app/src/main/java/com/vectras/vm/settings/X11DisplaySettingsActivity.java
create mode 100644 app/src/main/res/layout/activity_x11_display_settings.xml
diff --git a/.gitignore b/.gitignore
index d23639a..be1363e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,7 @@ captures/
#.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
#.idea/navEditor.xml
+.kotlin/
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
diff --git a/app/build.gradle b/app/build.gradle
index 25cb1d1..59c838b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -13,8 +13,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
- versionCode 45
- versionName "3.4.1"
+ versionCode 46
+ versionName "3.4.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
@@ -110,6 +110,7 @@ dependencies {
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation "androidx.documentfile:documentfile:1.1.0"
implementation 'androidx.core:core-ktx:1.17.0'
+ implementation 'androidx.activity:activity:1.12.1'
compileOnly project(':shell-loader:stub')
implementation project(":terminal-view")
implementation project(":library")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a3748f6..c625705 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -48,6 +48,10 @@
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
+
binding.appbar.setExpanded(false, false));
+ setSupportActionBar(binding.toolbar);
+ binding.toolbar.setNavigationOnClickListener(v -> finish());
+
if (getIntent().hasExtra("content")) {
result = getIntent().getStringExtra("content");
binding.edittext1.setText(result);
}
- binding.materialbutton1.setOnClickListener(v -> {
+ binding.done.setOnClickListener(v -> {
result = binding.edittext1.getText().toString();
finish();
});
diff --git a/app/src/main/java/com/vectras/vm/VMManager.java b/app/src/main/java/com/vectras/vm/VMManager.java
index afbb4d3..effa144 100644
--- a/app/src/main/java/com/vectras/vm/VMManager.java
+++ b/app/src/main/java/com/vectras/vm/VMManager.java
@@ -35,6 +35,7 @@ import com.vectras.qemu.MainVNCActivity;
import com.vectras.qemu.VNCConfig;
import com.vectras.qemu.utils.QmpClient;
import com.vectras.vm.home.HomeActivity;
+import com.vectras.vm.home.core.HomeStartVM;
import com.vectras.vm.settings.VNCSettingsActivity;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
@@ -49,11 +50,13 @@ import org.json.JSONArray;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
+import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
import java.util.Random;
+import java.util.Vector;
public class VMManager {
@@ -70,6 +73,7 @@ public class VMManager {
public static int restoredVMs = 0;
public static boolean isKeptSomeFiles = false;
public static boolean isQemuStopedWithError = false;
+ public static boolean isTryAgain = false;
public static String latestUnsafeCommandReason = "";
public static String lastQemuCommand = "";
@@ -583,7 +587,7 @@ public class VMManager {
}
}
- public static boolean isExecutedCommandError(@NonNull String _command, String _result, Activity _activity) {
+ public static boolean isExecutedCommandError(@NonNull String _command, String _result, Context _activity) {
if (!_command.contains("qemu-system")) {
isQemuStopedWithError = false;
return false;
@@ -604,41 +608,51 @@ public class VMManager {
Intent intent = new Intent();
intent.setClass(_activity, SplashActivity.class);
_activity.startActivity(intent);
- _activity.finish();
},
null, null);
isQemuStopedWithError = true;
return true;
} else if (_result.contains(") exists") && _result.contains("drive with bus")) {
//Error code: DRIVE_INDEX_0_EXISTS
- DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_DRIVE_INDEX_0_EXISTS) + "\n\n" + _result, _activity.getString(R.string.ok),true, R.drawable.hard_drive_24px, true,null, null);
+ DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_DRIVE_INDEX_0_EXISTS) + "\n\n" + _result, R.drawable.hard_drive_24px);
isQemuStopedWithError = true;
return true;
} else if (_result.contains("gtk initialization failed") || _result.contains("x11 not available")) {
//Error code: X11_NOT_AVAILABLE
- DialogUtils.twoDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_X11_NOT_AVAILABLE), _activity.getString(R.string.continuetext), _activity.getString(R.string.cancel), true, R.drawable.cast_24px, true,
+ DialogUtils.twoDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_X11_NOT_AVAILABLE), _activity.getString(R.string.switch_to_vnc), _activity.getString(R.string.cancel), true, R.drawable.cast_24px, true,
() -> {
MainSettingsManager.setVmUi(_activity, "VNC");
- DialogUtils.oneDialog(_activity, _activity.getString(R.string.done), _activity.getString(R.string.switched_to_VNC), _activity.getString(R.string.ok),true, R.drawable.check_24px, true,null, null);
+ DialogUtils.oneDialog(_activity, _activity.getString(R.string.done), _activity.getString(R.string.switched_to_VNC), R.drawable.check_24px);
},
null, null);
isQemuStopedWithError = true;
return true;
+ } else if (_result.contains("Couldn't connect to XServer")) {
+ if (isTryAgain) {
+ DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.x11_display_cannot_be_used_at_this_time_content) + "\n\n" + _result, R.drawable.cast_warning_24px);
+ _activity.stopService(new Intent(_activity, MainService.class));
+ isQemuStopedWithError = true;
+ isTryAgain = false;
+ } else {
+ HomeStartVM.startTryAgain(_activity);
+ isTryAgain = true;
+ }
+ return true;
} else if (_result.contains("No such file or directory")) {
//Error code: NO_SUCH_FILE_OR_DIRECTORY
- DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_NO_SUCH_FILE_OR_DIRECTORY) + "\n\n" + _result, _activity.getString(R.string.ok),true, R.drawable.file_copy_24px, true,null, null);
+ DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_NO_SUCH_FILE_OR_DIRECTORY) + "\n\n" + _result, R.drawable.file_copy_24px);
_activity.stopService(new Intent(_activity, MainService.class));
isQemuStopedWithError = true;
return true;
} else if (_result.contains("another process using")) {
//Error code: ANOTHER_PROCESS_USING_IMAGE
- DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_ANOTHER_PROCESS_USING_IMAGE) + "\n\n" + _result, _activity.getString(R.string.ok),true, R.drawable.file_copy_24px, true,null, null);
+ DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_ANOTHER_PROCESS_USING_IMAGE) + "\n\n" + _result, R.drawable.file_copy_24px);
_activity.stopService(new Intent(_activity, MainService.class));
isQemuStopedWithError = true;
return true;
} else if (_command.contains("qemu-system") && _result.contains("qemu-system") && !_result.contains("warning:")) {
//Error code: UNKNOW_ERROR
- DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.vm_could_not_be_run_content) + "\n\n" + _result, _activity.getString(R.string.ok),true, R.drawable.error_96px, true,null, null);
+ DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.vm_could_not_be_run_content) + "\n\n" + _result, R.drawable.error_96px);
_activity.stopService(new Intent(_activity, MainService.class));
isQemuStopedWithError = true;
return true;
@@ -672,11 +686,12 @@ public class VMManager {
}
public static void fixRomsDataJsonResult(Activity _context) {
- if (restoredVMs == 0) {
- DialogUtils.oneDialog(_context, _context.getString(R.string.done), _context.getString(R.string.roms_data_json_fixed_unsuccessfully), _context.getString(R.string.ok),true, R.drawable.error_96px, true,null, null);
- } else {
- DialogUtils.oneDialog(_context, _context.getString(R.string.done), _context.getString(R.string.roms_data_json_fixed_successfully), _context.getString(R.string.ok),true, R.drawable.check_24px, true,null, null);
- }
+ DialogUtils.oneDialog(
+ _context,
+ _context.getString(R.string.done),
+ restoredVMs == 0 ? _context.getString(R.string.roms_data_json_fixed_unsuccessfully) : _context.getString(R.string.roms_data_json_fixed_successfully),
+ R.drawable.error_96px
+ );
HomeActivity.refeshVMListNow();
movetoRecycleBin();
}
@@ -743,8 +758,8 @@ public class VMManager {
return true;
}
- public static boolean isVMRunning(Activity activity, String vmID) {
- String result = Terminal.executeShellCommandWithResult("ps -e", activity);
+ public static boolean isVMRunning(Context context, String vmID) {
+ String result = Terminal.executeShellCommandWithResult("ps -e", context);
if (result.contains(Config.getCacheDir() + "/" + vmID + "/qmpsocket")) {
Log.d("VMManager.isThisVMRunning", "Yes");
return true;
diff --git a/app/src/main/java/com/vectras/vm/home/HomeActivity.java b/app/src/main/java/com/vectras/vm/home/HomeActivity.java
index 194b4ba..6814f63 100644
--- a/app/src/main/java/com/vectras/vm/home/HomeActivity.java
+++ b/app/src/main/java/com/vectras/vm/home/HomeActivity.java
@@ -272,12 +272,10 @@ public class HomeActivity extends AppCompatActivity implements RomStoreFragment.
checkMissingLibraries(this);
setupDrawer();
- DisplaySystem.startTermuxX11();
DialogUtils.joinTelegram(this);
NotificationUtils.clearAll(this);
if (MainSettingsManager.getPromptUpdateVersion(this))
-
updateApp();
}
@@ -342,6 +340,8 @@ public class HomeActivity extends AppCompatActivity implements RomStoreFragment.
if (binding.searchview.isShowing()) binding.searchview.hide();
bindingContent.bottomNavigation.setSelectedItemId(R.id.item_home);
}
+
+ new Handler(Looper.getMainLooper()).post(() -> DisplaySystem.startTermuxX11(this));
}
private final ActivityResultLauncher isoPicker =
diff --git a/app/src/main/java/com/vectras/vm/home/core/DisplaySystem.java b/app/src/main/java/com/vectras/vm/home/core/DisplaySystem.java
index 6df6647..c7b2074 100644
--- a/app/src/main/java/com/vectras/vm/home/core/DisplaySystem.java
+++ b/app/src/main/java/com/vectras/vm/home/core/DisplaySystem.java
@@ -4,6 +4,7 @@ import static android.os.Build.VERSION.SDK_INT;
import static com.vectras.vm.utils.LibraryChecker.isPackageInstalled2;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
@@ -27,12 +28,13 @@ import java.util.Set;
public class DisplaySystem {
private static final String TAG = "DisplaySystem";
+ private static boolean isTermuxClassLoaded = false;
- public static void launch(Activity activity) {
- if (MainSettingsManager.getVmUi(activity).equals("VNC")) {
- activity.startActivity(new Intent(activity, MainVNCActivity.class));
- } else if (MainSettingsManager.getVmUi(activity).equals("X11")) {
- DisplaySystem.launchX11(activity, false);
+ public static void launch(Context context) {
+ if (MainSettingsManager.getVmUi(context).equals("VNC")) {
+ context.startActivity(new Intent(context, MainVNCActivity.class));
+ } else if (MainSettingsManager.getVmUi(context).equals("X11")) {
+ DisplaySystem.launchX11(context, false);
}
}
@@ -44,21 +46,21 @@ public class DisplaySystem {
activity.startActivity(new Intent(activity, MainVNCActivity.class));
}
- public static void launchX11(Activity activity, boolean isKill) {
+ public static void launchX11(Context context, boolean isKill) {
if (!DeviceUtils.is64bit()) {
DialogUtils.oneDialog(
- activity,
- activity.getString(R.string.x11_feature_not_supported),
- activity.getString(R.string.cpu_not_support_64_xfce),
- activity.getString(R.string.ok),
+ context,
+ context.getString(R.string.x11_feature_not_supported),
+ context.getString(R.string.cpu_not_support_64_xfce),
+ context.getString(R.string.ok),
true, R.drawable.error_96px,
true,
null,
null
);
} else {
- if (SDK_INT >= 34 && !PackageUtils.isInstalled("com.termux.x11", activity)) {
- DialogUtils.needInstallTermuxX11(activity);
+ if (SDK_INT >= 34 && !PackageUtils.isInstalled("com.termux.x11", context)) {
+ DialogUtils.needInstallTermuxX11(context);
return;
}
@@ -66,7 +68,7 @@ public class DisplaySystem {
String necessaryPackage = "fluxbox";
// Check if XFCE4 is installed
- isPackageInstalled2(activity, necessaryPackage, (output, errors) -> {
+ isPackageInstalled2(context, necessaryPackage, (output, errors) -> {
boolean isInstalled = false;
// Check if the package exists in the installed packages output
@@ -82,50 +84,62 @@ public class DisplaySystem {
// If not installed, show a dialog to install it
if (!isInstalled) {
DialogUtils.twoDialog(
- activity,
- activity.getString(R.string.action_needed),
- activity.getString(R.string.the_required_package_is_not_installed_content),
- activity.getString(R.string.install),
- activity.getString(R.string.cancel),
+ context,
+ context.getString(R.string.action_needed),
+ context.getString(R.string.the_required_package_is_not_installed_content),
+ context.getString(R.string.install),
+ context.getString(R.string.cancel),
true,
R.drawable.desktop_24px,
true,
() -> {
String installCommand = "apk add " + necessaryPackage;
- new Terminal(activity).executeShellCommand(installCommand, true, true, activity.getString(R.string.just_a_moment), activity);
+ new Terminal(context).executeShellCommand(installCommand, true, true, context.getString(R.string.just_a_moment), context);
},
null,
null
);
} else {
if (SDK_INT >= 34) {
-// activity.startActivity(new Intent(activity, XServerActivity.class));
+ Log.d(TAG, "launchX11: Opened: com.termux.x11.MainActivity.");
+ // activity.startActivity(new Intent(activity, XServerActivity.class));
Intent intent = new Intent();
intent.setClassName("com.termux.x11", "com.termux.x11.MainActivity");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- activity.startActivity(intent);
+ context.startActivity(intent);
try {
TermuxX11.main(new String[]{":0"});
} catch (Exception e) {
Log.e(TAG, "TermuxX11.main: ", e);
}
} else {
- activity.startActivity(new Intent(activity, X11Activity.class));
+ context.startActivity(new Intent(context, X11Activity.class));
+ }
+ if (isKill) {
+ new Terminal(context).executeShellCommand2("killall fluxbox && " + (SDK_INT >= 34 ? "export DISPLAY=:0 && sleep 5 && " : "") + "fluxbox > /dev/null", false, context);
}
-
- if (isKill)
- new Terminal(activity).executeShellCommand2("killall fluxbox", false, activity);
- new Terminal(activity).executeShellCommand2(SDK_INT >= 34 ? "export DISPLAY=:0 && sleep 5 && fluxbox" : "fluxbox", false, activity);
}
});
}
}
- public static void startTermuxX11() {
+ public static void startTermuxX11(Context context) {
+ if (isTermuxClassLoaded || !MainSettingsManager.getVmUi(context).equals("X11")) return;
+ isTermuxClassLoaded = true;
+
+ Log.d(TAG, "startTermuxX11...");
if (Build.VERSION.SDK_INT < 34) {
ShellExecutor shellExec = new ShellExecutor();
shellExec.exec(TermuxService.PREFIX_PATH + "/bin/termux-x11 :0");
+ } else {
+ if (PackageUtils.isInstalled("com.termux.x11", context)){
+ try {
+ TermuxX11.main(new String[]{":0"});
+ } catch (Exception e) {
+ Log.e(TAG, "TermuxX11.main: ", e);
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/vectras/vm/home/core/HomeStartVM.java b/app/src/main/java/com/vectras/vm/home/core/HomeStartVM.java
index 4dfb4e0..f19e566 100644
--- a/app/src/main/java/com/vectras/vm/home/core/HomeStartVM.java
+++ b/app/src/main/java/com/vectras/vm/home/core/HomeStartVM.java
@@ -2,7 +2,7 @@ package com.vectras.vm.home.core;
import static android.os.Build.VERSION.SDK_INT;
-import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
@@ -42,6 +42,11 @@ public class HomeStartVM {
public static boolean isStopNow = false;
public static final Handler handlerForLaunch = new Handler(Looper.getMainLooper());
public static Runnable tickForLaunch = null;
+
+ public static String lastVMName = "";
+ public static String lastEnv = "";
+ public static String lastVMID = "";
+ public static String lastThumbnailFile = "";
public static String pendingVMName = "";
public static String pendingEnv = "";
public static String pendingVMID = "";
@@ -50,7 +55,7 @@ public class HomeStartVM {
public static String runCommandFormat = "export TMPDIR=/tmp && mkdir -p $TMPDIR/pulse && export XDG_RUNTIME_DIR=/tmp && chmod -R 775 $TMPDIR/pulse && pulseaudio --start --exit-idle-time=-1 > /dev/null 2>&1 && %s";
public static void startNow(
- Activity activity,
+ Context context,
String vmName,
String env,
String vmID,
@@ -62,20 +67,25 @@ public class HomeStartVM {
if (pendingVMID.isEmpty()) return;
pendingVMID = "";
} else {
- if (MainSettingsManager.getVmUi(activity).equals("X11")) {
- runCommandFormat = String.format(runCommandFormat, "xterm -e bash -c '%s'");
+ lastVMName = vmName;
+ lastEnv = env;
+ lastVMID = vmID;
+ lastThumbnailFile = thumbnailFile;
+
+ if (MainSettingsManager.getVmUi(context).equals("X11")) {
+ if (MainSettingsManager.getRunQemuWithXterm(context)) {
+ runCommandFormat = String.format(runCommandFormat, "xterm -e bash -c '%s'");
+ } else {
+ runCommandFormat = String.format(runCommandFormat, "bash -c '%s'");
+ }
+
if (SDK_INT < 34) {
pendingVMName = vmName;
pendingEnv = env;
pendingVMID = vmID;
pendingThumbnailFile = thumbnailFile;
- DisplaySystem.launch(activity);
+ DisplaySystem.launch(context);
return;
- } else {
- if (!PackageUtils.isInstalled("com.termux.x11", activity)) {
- DialogUtils.needInstallTermuxX11(activity);
- return;
- }
}
}
}
@@ -94,29 +104,39 @@ public class HomeStartVM {
File romDir = new File(Config.getCacheDir() + "/" + finalvmID);
if (!romDir.exists()) {
if (!romDir.mkdirs()) {
- DialogUtils.oneDialog(activity, activity.getString(R.string.problem_has_been_detected), activity.getString(R.string.vm_cache_dir_failed_to_create_content), activity.getString(R.string.ok), true, R.drawable.warning_48px, true, null, null);
+ DialogUtils.oneDialog(
+ context,
+ context.getString(R.string.problem_has_been_detected),
+ context.getString(R.string.vm_cache_dir_failed_to_create_content),
+ R.drawable.warning_48px
+ );
return;
}
}
- if (!VMManager.isthiscommandsafe(env, activity.getApplicationContext())) {
- DialogUtils.oneDialog(activity, activity.getString(R.string.problem_has_been_detected), activity.getString(R.string.harmful_command_was_detected) + " " + activity.getResources().getString(R.string.reason) + ": " + VMManager.latestUnsafeCommandReason, activity.getString(R.string.ok), true, R.drawable.verified_user_24px, true, null, null);
+ if (!VMManager.isthiscommandsafe(env, context.getApplicationContext())) {
+ DialogUtils.oneDialog(
+ context,
+ context.getString(R.string.problem_has_been_detected),
+ context.getString(R.string.harmful_command_was_detected) + " " + context.getResources().getString(R.string.reason) + ": " + VMManager.latestUnsafeCommandReason,
+ R.drawable.verified_user_24px
+ );
return;
}
- if (MainSettingsManager.getSharedFolder(activity)
- && !MainSettingsManager.getArch(activity).equals("I386")
- && FileUtils.getFolderSize(FileUtils.getExternalFilesDirectory(activity).getPath() + "/SharedFolder") * Math.pow(10, -6) > 516) {
+ if (MainSettingsManager.getSharedFolder(context)
+ && !MainSettingsManager.getArch(context).equals("I386")
+ && FileUtils.getFolderSize(FileUtils.getExternalFilesDirectory(context).getPath() + "/SharedFolder") * Math.pow(10, -6) > 516) {
DialogUtils.twoDialog(
- activity,
- activity.getString(R.string.problem_has_been_detected),
- activity.getString(R.string.shared_folder_is_too_large_content),
- activity.getString(R.string.open_shared_folder),
- activity.getString(R.string.close),
+ context,
+ context.getString(R.string.problem_has_been_detected),
+ context.getString(R.string.shared_folder_is_too_large_content),
+ context.getString(R.string.open_shared_folder),
+ context.getString(R.string.close),
true,
R.drawable.warning_48px,
true,
- () -> FileUtils.openFolder(activity, FileUtils.getExternalFilesDirectory(activity).getPath() + "/SharedFolder"),
+ () -> FileUtils.openFolder(context, FileUtils.getExternalFilesDirectory(context).getPath() + "/SharedFolder"),
null,
null
);
@@ -125,76 +145,83 @@ public class HomeStartVM {
VMManager.lastQemuCommand = env;
- if (VMManager.isVMRunning(activity, finalvmID)) {
- Toast.makeText(activity, "This VM is already running.", Toast.LENGTH_LONG).show();
- if (MainSettingsManager.getVmUi(activity).equals("VNC"))
- activity.startActivity(new Intent(activity, MainVNCActivity.class));
- else if (MainSettingsManager.getVmUi(activity).equals("X11"))
- DisplaySystem.launchX11(activity, false);
+ if (VMManager.isVMRunning(context, finalvmID)) {
+ Toast.makeText(context, "This VM is already running.", Toast.LENGTH_LONG).show();
+ if (MainSettingsManager.getVmUi(context).equals("VNC"))
+ context.startActivity(new Intent(context, MainVNCActivity.class));
+ else if (MainSettingsManager.getVmUi(context).equals("X11"))
+ DisplaySystem.launchX11(context, false);
return;
}
if (AppConfig.getSetupFiles().contains("arm") && !AppConfig.getSetupFiles().contains("arm64")) {
if (env.contains("tcg,thread=multi")) {
- DialogUtils.twoDialog(activity, activity.getResources().getString(R.string.problem_has_been_detected), activity.getResources().getString(R.string.can_not_use_mttcg), activity.getString(R.string.ok), activity.getString(R.string.cancel), true, R.drawable.warning_48px, true,
- () -> startNow(activity, vmName, env.replace("tcg,thread=multi", "tcg,thread=single"), finalvmID, thumbnailFile), null, null);
+ DialogUtils.twoDialog(context, context.getResources().getString(R.string.problem_has_been_detected), context.getResources().getString(R.string.can_not_use_mttcg), context.getString(R.string.ok), context.getString(R.string.cancel), true, R.drawable.warning_48px, true,
+ () -> startNow(context, vmName, env.replace("tcg,thread=multi", "tcg,thread=single"), finalvmID, thumbnailFile), null, null);
return;
}
}
- if (MainSettingsManager.getArch(activity).equals("ARM64") && MainSettingsManager.getIfType(activity).equals("ide") && skipIDEwithARM64DialogInStartVM) {
- DialogUtils.twoDialog(activity, activity.getString(R.string.problem_has_been_detected), activity.getString(R.string.you_cannot_use_IDE_hard_drive_type_with_ARM64), activity.getString(R.string.continuetext), activity.getString(R.string.cancel), true, R.drawable.warning_48px, true,
+ if (MainSettingsManager.getArch(context).equals("ARM64") && MainSettingsManager.getIfType(context).equals("ide") && skipIDEwithARM64DialogInStartVM) {
+ DialogUtils.twoDialog(context, context.getString(R.string.problem_has_been_detected), context.getString(R.string.you_cannot_use_IDE_hard_drive_type_with_ARM64), context.getString(R.string.continuetext), context.getString(R.string.cancel), true, R.drawable.warning_48px, true,
() -> {
skipIDEwithARM64DialogInStartVM = true;
- startNow(activity, vmName, env, finalvmID, thumbnailFile);
+ startNow(context, vmName, env, finalvmID, thumbnailFile);
}, null, null);
return;
} else if (skipIDEwithARM64DialogInStartVM) {
skipIDEwithARM64DialogInStartVM = false;
}
- if (MainSettingsManager.getSharedFolder(activity) && MainSettingsManager.getArch(activity).equals("I386")) {
- Toast.makeText(activity, R.string.shared_folder_is_not_used_because_i386_does_not_support_it, Toast.LENGTH_LONG).show();
+ if (MainSettingsManager.getSharedFolder(context) && MainSettingsManager.getArch(context).equals("I386")) {
+ Toast.makeText(context, R.string.shared_folder_is_not_used_because_i386_does_not_support_it, Toast.LENGTH_LONG).show();
}
- if (MainSettingsManager.getVncExternal(activity) &&
+ if (MainSettingsManager.getVncExternal(context) &&
NetworkUtils.isPortOpen("localhost", Config.defaultVNCPort + Config.defaultVNCPort, 500)) {
- DialogUtils.twoDialog(activity, activity.getString(R.string.problem_has_been_detected),
- activity.getString(R.string.the_vnc_server_port_you_set_is_currently_in_use_by_other),
- activity.getString(R.string.go_to_settings),
- activity.getString(R.string.close),
+ DialogUtils.twoDialog(context, context.getString(R.string.problem_has_been_detected),
+ context.getString(R.string.the_vnc_server_port_you_set_is_currently_in_use_by_other),
+ context.getString(R.string.go_to_settings),
+ context.getString(R.string.close),
true, R.drawable.warning_48px, true,
- () -> activity.startActivity(new Intent(activity, ExternalVNCSettingsActivity.class)),
+ () -> context.startActivity(new Intent(context, ExternalVNCSettingsActivity.class)),
null,
null);
return;
}
- showProgressDialog(activity, vmName, thumbnailFile);
+ showProgressDialog(context, vmName, thumbnailFile);
VMManager.isQemuStopedWithError = false;
String finalCommand = VMManager.addAudioDevSdl(String.format(runCommandFormat, env));
- if (MainSettingsManager.getVmUi(activity).equals("X11") && SDK_INT >= 34) {
- finalCommand = "export DISPLAY=:0 && sleep 5\nfluxbox &\n" + finalCommand;
+ if (MainSettingsManager.getVmUi(context).equals("X11") && SDK_INT >= 34) {
+ finalCommand = "export DISPLAY=:0 && sleep 5\nfluxbox > /dev/null &\n" + finalCommand;
}
Log.i(TAG, finalCommand);
- if (ServiceUtils.isServiceRunning(activity, MainService.class)) {
- MainService.startCommand(finalCommand, activity);
+ if (ServiceUtils.isServiceRunning(context, MainService.class)) {
+ MainService.startCommand(finalCommand, context);
} else {
- Intent serviceIntent = new Intent(activity, MainService.class);
+ Intent serviceIntent = new Intent(context, MainService.class);
MainService.env = finalCommand;
MainService.CHANNEL_ID = vmName;
- MainService.activity = activity;
if (SDK_INT >= Build.VERSION_CODES.O) {
- activity.startForegroundService(serviceIntent);
+ context.startForegroundService(serviceIntent);
} else {
- activity.startService(serviceIntent);
+ context.startService(serviceIntent);
}
}
+ if (MainSettingsManager.getVmUi(context).equals("X11") && SDK_INT >= 34) {
+ if (!PackageUtils.isInstalled("com.termux.x11", context)) {
+ DialogUtils.needInstallTermuxX11(context);
+ return;
+ }
+ DisplaySystem.launchX11(context, false);
+ }
+
tickForLaunch = new Runnable() {
@Override
public void run() {
@@ -204,21 +231,29 @@ public class HomeStartVM {
progressDialog.dismiss();
if (!isStopNow && !VMManager.isQemuStopedWithError) {
- if (MainSettingsManager.getVmUi(activity).equals("VNC")) {
- if (MainSettingsManager.getVncExternal(activity)) {
+ if (MainSettingsManager.getVmUi(context).equals("VNC")) {
+ if (MainSettingsManager.getVncExternal(context)) {
Config.currentVNCServervmID = finalvmID;
- DialogUtils.oneDialog(activity, activity.getString(R.string.vnc_server), activity.getString(R.string.running_vm_with_vnc_server_content) + " " + (Integer.parseInt(MainSettingsManager.getVncExternalDisplay(activity)) + 5900) + ".", activity.getString(R.string.ok), true, R.drawable.cast_24px, true, null, null);
+ DialogUtils.oneDialog(context,
+ context.getString(R.string.vnc_server),
+ context.getString(R.string.running_vm_with_vnc_server_content) + " " + (Integer.parseInt(MainSettingsManager.getVncExternalDisplay(context)) + 5900) + ".",
+ R.drawable.cast_24px
+ );
} else {
MainVNCActivity.started = true;
- activity.startActivity(new Intent(activity, MainVNCActivity.class));
+ context.startActivity(new Intent(context, MainVNCActivity.class));
}
// } else if (MainSettingsManager.getVmUi(activity).equals("SPICE")) {
// //This feature is not available yet.
- } else if (MainSettingsManager.getVmUi(activity).equals("X11") && SDK_INT >= 34) {
- DisplaySystem.launchX11(activity, false);
}
Log.i(TAG, "Virtual machine running.");
+ } if (MainSettingsManager.getVmUi(context).equals("X11") && SDK_INT >= 34) {
+ Intent intent = new Intent();
+ intent.setClassName("com.termux.x11", "com.termux.x11.MainActivity");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ context.startActivity(intent);
}
skipIDEwithARM64DialogInStartVM = false;
@@ -242,14 +277,19 @@ public class HomeStartVM {
setDefault();
}
- public static void startPending(Activity activity) {
+ public static void startTryAgain(Context context) {
+ startNow(context, lastVMName, lastEnv, lastVMID, lastThumbnailFile);
+ VMManager.isTryAgain = false;
+ }
+
+ public static void startPending(Context context) {
isLaunchFromPending = true;
- startNow(activity, pendingVMName, pendingEnv, pendingVMID, pendingThumbnailFile);
+ startNow(context, pendingVMName, pendingEnv, pendingVMID, pendingThumbnailFile);
setDefault();
}
- public static void showProgressDialog(Activity activity, String _content, String thumbnailFile) {
- View progressView = LayoutInflater.from(activity).inflate(R.layout.dialog_start_vm, null);
+ public static void showProgressDialog(Context context, String _content, String thumbnailFile) {
+ View progressView = LayoutInflater.from(context).inflate(R.layout.dialog_start_vm, null);
TextView tvVMName = progressView.findViewById(R.id.vm_name);
tvVMName.setText(_content);
@@ -260,7 +300,7 @@ public class HomeStartVM {
VMManager.setIconWithName(ivThumbnail, _content);
} else {
if (FileUtils.isFileExists(thumbnailFile)) {
- Glide.with(activity.getApplicationContext())
+ Glide.with(context.getApplicationContext())
.load(new File(thumbnailFile))
.placeholder(R.drawable.ic_computer_180dp_with_padding)
.error(R.drawable.ic_computer_180dp_with_padding)
@@ -277,7 +317,7 @@ public class HomeStartVM {
VMManager.shutdownCurrentVM();
});
- progressDialog = new MaterialAlertDialogBuilder(activity, R.style.CenteredDialogTheme)
+ progressDialog = new MaterialAlertDialogBuilder(context, R.style.CenteredDialogTheme)
.setView(progressView)
.setCancelable(false)
.create();
diff --git a/app/src/main/java/com/vectras/vm/home/monitor/SystemMonitorFragment.java b/app/src/main/java/com/vectras/vm/home/monitor/SystemMonitorFragment.java
index 135ced1..426a2ec 100644
--- a/app/src/main/java/com/vectras/vm/home/monitor/SystemMonitorFragment.java
+++ b/app/src/main/java/com/vectras/vm/home/monitor/SystemMonitorFragment.java
@@ -41,6 +41,7 @@ public class SystemMonitorFragment extends Fragment {
final String TAG = "SystemMonitorFragment";
FragmentHomeSystemMonitorBinding binding;
ExecutorService executor = Executors.newSingleThreadExecutor();
+ ScheduledExecutorService executorUpdate;
boolean isStopUpdateMonitor = false;
@Override
@@ -83,10 +84,18 @@ public class SystemMonitorFragment extends Fragment {
stopMonitor();
}
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (executorUpdate != null && !executorUpdate.isShutdown()) {
+ executorUpdate.shutdownNow();
+ }
+ }
+
private void initialize() {
binding.btStopqemu.setOnClickListener(v -> VMManager.requestKillAllQemuProcess(requireActivity(), () -> {
- ScheduledExecutorService executorUpdate = Executors.newSingleThreadScheduledExecutor();
+ executorUpdate = Executors.newSingleThreadScheduledExecutor();
executorUpdate.schedule(() -> {
if (getContext() != null) {
requireActivity().runOnUiThread(this::getQemuInfo);
@@ -129,7 +138,7 @@ public class SystemMonitorFragment extends Fragment {
private final Runnable monitorTask = new Runnable() {
@Override
public void run() {
- if (isStopUpdateMonitor) return;
+ if (isStopUpdateMonitor || !isAdded()) return;
updateSystemMonitor();
handler.postDelayed(this, 1000);
}
@@ -147,6 +156,8 @@ public class SystemMonitorFragment extends Fragment {
@SuppressLint("SetTextI18n")
private void updateSystemMonitor() {
+ if (!isAdded()) return;
+
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
ActivityManager activityManager = (ActivityManager) requireActivity().getSystemService(ACTIVITY_SERVICE);
activityManager.getMemoryInfo(mi);
@@ -187,6 +198,8 @@ public class SystemMonitorFragment extends Fragment {
@SuppressLint("SetTextI18n")
private void getQemuInfo() {
+ if (!isAdded()) return;
+
String currentArch = MainSettingsManager.getArch(requireActivity());
binding.tvQemuarch.setText(getString(R.string.arch) + " " + currentArch + ".");
diff --git a/app/src/main/java/com/vectras/vm/settings/ImportExportSettingsActivity.java b/app/src/main/java/com/vectras/vm/settings/ImportExportSettingsActivity.java
index c6f9133..cd90d3a 100644
--- a/app/src/main/java/com/vectras/vm/settings/ImportExportSettingsActivity.java
+++ b/app/src/main/java/com/vectras/vm/settings/ImportExportSettingsActivity.java
@@ -4,13 +4,8 @@ import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.core.graphics.Insets;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.WindowInsetsCompat;
import com.vectras.qemu.MainSettingsManager;
-import com.vectras.vm.R;
-import com.vectras.vm.databinding.ActivityExternalVncSettingsBinding;
import com.vectras.vm.databinding.ActivityImportExportSettingsBinding;
import java.util.Objects;
@@ -33,11 +28,15 @@ public class ImportExportSettingsActivity extends AppCompatActivity {
private void initialize() {
binding.swSmartSizeCalculation.setChecked(MainSettingsManager.getSmartSizeCalculation(this));
+ binding.swCheckBeforeExtract.setChecked(MainSettingsManager.getCheckBeforeExtract(this));
binding.swCyclicRedundancyCheck.setChecked(MainSettingsManager.getCyclicRedundancyCheck(this));
binding.swSmartSizeCalculation.setOnCheckedChangeListener((buttonView, isChecked) -> MainSettingsManager.setSmartSizeCalculation(this, isChecked));
binding.lnSmartSizeCalculation.setOnClickListener(v -> binding.swSmartSizeCalculation.toggle());
+ binding.swCheckBeforeExtract.setOnCheckedChangeListener((buttonView, isChecked) -> MainSettingsManager.setCheckBeforeExtract(this, isChecked));
+ binding.lnCheckBeforeExtract.setOnClickListener(v -> binding.swCheckBeforeExtract.toggle());
+
binding.swCyclicRedundancyCheck.setOnCheckedChangeListener((buttonView, isChecked) -> MainSettingsManager.setCyclicRedundancyCheck(this, isChecked));
binding.lnCyclicRedundancyCheck.setOnClickListener(v -> binding.swCyclicRedundancyCheck.toggle());
}
diff --git a/app/src/main/java/com/vectras/vm/settings/X11DisplaySettingsActivity.java b/app/src/main/java/com/vectras/vm/settings/X11DisplaySettingsActivity.java
new file mode 100644
index 0000000..724862b
--- /dev/null
+++ b/app/src/main/java/com/vectras/vm/settings/X11DisplaySettingsActivity.java
@@ -0,0 +1,54 @@
+package com.vectras.vm.settings;
+
+import android.os.Bundle;
+
+import androidx.activity.EdgeToEdge;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.vectras.qemu.MainSettingsManager;
+import com.vectras.vm.R;
+import com.vectras.vm.databinding.ActivityX11DisplaySettingsBinding;
+
+import java.util.Objects;
+
+public class X11DisplaySettingsActivity extends AppCompatActivity {
+
+ ActivityX11DisplaySettingsBinding binding;
+ boolean isInitialized = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ EdgeToEdge.enable(this);
+ setContentView(R.layout.activity_external_vnc_settings);
+ binding = ActivityX11DisplaySettingsBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+ setSupportActionBar(binding.toolbar);
+ Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
+ binding.toolbar.setNavigationOnClickListener(view -> finish());
+ initialize();
+ }
+
+ private void initialize() {
+ binding.swEnabled.setChecked(MainSettingsManager.getVmUi(this).equals("X11"));
+ binding.swRunQemuWithXterm.setChecked(MainSettingsManager.getRunQemuWithXterm(this));
+
+ binding.swEnabled.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ MainSettingsManager.setVmUi(this, isChecked ? "X11" : "VNC");
+ uiController(isChecked);
+ });
+ binding.lnEnabled.setOnClickListener(v -> binding.swEnabled.toggle());
+
+ binding.swRunQemuWithXterm.setOnCheckedChangeListener((buttonView, isChecked) -> MainSettingsManager.setRunQemuWithXterm(this, isChecked));
+ binding.lnRunQemuWithXtermh.setOnClickListener(v -> binding.swRunQemuWithXterm.toggle());
+
+ isInitialized = true;
+
+ uiController(binding.swEnabled.isChecked());
+ }
+
+ private void uiController(boolean isEnabled) {
+ binding.lnAllOptions.setAlpha(isEnabled ? 1f : 0.5f);
+ binding.lnRunQemuWithXtermh.setEnabled(isEnabled);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/vectras/vm/utils/DialogUtils.java b/app/src/main/java/com/vectras/vm/utils/DialogUtils.java
index 1896850..3d9e6ce 100644
--- a/app/src/main/java/com/vectras/vm/utils/DialogUtils.java
+++ b/app/src/main/java/com/vectras/vm/utils/DialogUtils.java
@@ -2,8 +2,8 @@ package com.vectras.vm.utils;
import static android.content.Intent.ACTION_VIEW;
-import android.app.Activity;
import android.app.AlertDialog;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
@@ -26,10 +26,14 @@ import com.vectras.vm.R;
public class DialogUtils {
- public static void oneDialog(Activity _context, String _title, String _message, String _textPositiveButton, boolean _isicon, int _iconid, boolean _cancel, Runnable _onPositive, Runnable _onDismiss) {
- View buttonsView = LayoutInflater.from(_context).inflate(R.layout.dialog_layout, null);
+ public static void oneDialog(Context context, String title, String message, int iconid) {
+ oneDialog(context, title, message, context.getString(R.string.ok), iconid != -1, iconid, true, null, null);
+ }
- AlertDialog dialog = new AlertDialog.Builder(_context).create();
+ public static void oneDialog(Context context, String _title, String _message, String _textPositiveButton, boolean _isicon, int _iconid, boolean _cancel, Runnable _onPositive, Runnable _onDismiss) {
+ View buttonsView = LayoutInflater.from(context).inflate(R.layout.dialog_layout, null);
+
+ AlertDialog dialog = new AlertDialog.Builder(context).create();
dialog.setCancelable(_cancel);
dialog.setView(buttonsView);
@@ -84,10 +88,10 @@ public class DialogUtils {
dialog.show();
}
- public static void twoDialog(Activity _context, String _title, String _message, String _textPositiveButton, String _textNegativeButton, boolean _isicon, int _iconid, boolean _cancel, Runnable _onPositive, Runnable _onNegative, Runnable _onDismiss) {
- View buttonsView = LayoutInflater.from(_context).inflate(R.layout.dialog_layout, null);
+ public static void twoDialog(Context context, String _title, String _message, String _textPositiveButton, String _textNegativeButton, boolean _isicon, int _iconid, boolean _cancel, Runnable _onPositive, Runnable _onNegative, Runnable _onDismiss) {
+ View buttonsView = LayoutInflater.from(context).inflate(R.layout.dialog_layout, null);
- AlertDialog dialog = new AlertDialog.Builder(_context).create();
+ AlertDialog dialog = new AlertDialog.Builder(context).create();
dialog.setCancelable(_cancel);
dialog.setView(buttonsView);
@@ -151,10 +155,10 @@ public class DialogUtils {
dialog.show();
}
- public static void threeDialog(Activity _context, String _title, String _message, String _textPositiveButton, String _textNegativeButton, String _textNeutralButton, boolean _isicon, int _iconid, boolean _cancel, Runnable _onPositive, Runnable _onNegative, Runnable _onNeutral, Runnable _onDismiss) {
- View buttonsView = LayoutInflater.from(_context).inflate(R.layout.dialog_layout, null);
+ public static void threeDialog(Context context, String _title, String _message, String _textPositiveButton, String _textNegativeButton, String _textNeutralButton, boolean _isicon, int _iconid, boolean _cancel, Runnable _onPositive, Runnable _onNegative, Runnable _onNeutral, Runnable _onDismiss) {
+ View buttonsView = LayoutInflater.from(context).inflate(R.layout.dialog_layout, null);
- AlertDialog dialog = new AlertDialog.Builder(_context).create();
+ AlertDialog dialog = new AlertDialog.Builder(context).create();
dialog.setCancelable(_cancel);
dialog.setView(buttonsView);
@@ -228,18 +232,18 @@ public class DialogUtils {
dialog.show();
}
- public static void joinTelegram(Activity _activity) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(_activity);
+ public static void joinTelegram(Context _context) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(_context);
if (!prefs.getBoolean("tgDialog", false)) {
- threeDialog(_activity, _activity.getResources().getString(R.string.join_us_on_telegram),
- _activity.getResources().getString(R.string.join_us_on_telegram_where_we_publish_all_the_news_and_updates_and_receive_your_opinions_and_bugs),
- _activity.getResources().getString(R.string.join), _activity.getResources().getString(R.string.cancel), _activity.getResources().getString(R.string.dont_show_again),
+ threeDialog(_context, _context.getResources().getString(R.string.join_us_on_telegram),
+ _context.getResources().getString(R.string.join_us_on_telegram_where_we_publish_all_the_news_and_updates_and_receive_your_opinions_and_bugs),
+ _context.getResources().getString(R.string.join), _context.getResources().getString(R.string.cancel), _context.getResources().getString(R.string.dont_show_again),
true, R.drawable.send_24px, true,
() -> {
String tg = "https://t.me/vectras_os";
Intent f = new Intent(ACTION_VIEW);
f.setData(Uri.parse(tg));
- _activity.startActivity(f);
+ _context.startActivity(f);
}, null,
() -> {
SharedPreferences.Editor edit = prefs.edit();
@@ -249,26 +253,26 @@ public class DialogUtils {
}
}
- public static void needInstallTermuxX11(Activity _activity) {
- twoDialog(_activity, _activity.getResources().getString(R.string.action_needed),
- _activity.getResources().getString(R.string.need_install_termux_x11_content),
- _activity.getResources().getString(R.string.install), _activity.getResources().getString(R.string.cancel),
+ public static void needInstallTermuxX11(Context _context) {
+ twoDialog(_context, _context.getResources().getString(R.string.action_needed),
+ _context.getResources().getString(R.string.need_install_termux_x11_content),
+ _context.getResources().getString(R.string.install), _context.getResources().getString(R.string.cancel),
true, R.drawable.warning_24px, true,
() -> {
String tg = "https://github.com/termux/termux-x11/releases";
Intent f = new Intent(ACTION_VIEW);
f.setData(Uri.parse(tg));
- _activity.startActivity(f);
+ _context.startActivity(f);
}, null, null);
}
- public static void fileDeletionResult(Activity activity, boolean isCompleted) {
+ public static void fileDeletionResult(Context _context, boolean isCompleted) {
if (isCompleted) {
DialogUtils.oneDialog(
- activity,
- activity.getString(R.string.done),
- activity.getString(R.string.file_deleted),
- activity.getString(R.string.ok),
+ _context,
+ _context.getString(R.string.done),
+ _context.getString(R.string.file_deleted),
+ _context.getString(R.string.ok),
true,
R.drawable.check_24px,
true,
@@ -277,10 +281,10 @@ public class DialogUtils {
);
} else {
DialogUtils.oneDialog(
- activity,
- activity.getString(R.string.oops),
- activity.getString(R.string.delete_file_failed_content),
- activity.getString(R.string.ok),
+ _context,
+ _context.getString(R.string.oops),
+ _context.getString(R.string.delete_file_failed_content),
+ _context.getString(R.string.ok),
true,
R.drawable.error_96px,
true,
diff --git a/app/src/main/java/com/vectras/vm/utils/FileUtils.java b/app/src/main/java/com/vectras/vm/utils/FileUtils.java
index 558b29f..7b93873 100644
--- a/app/src/main/java/com/vectras/vm/utils/FileUtils.java
+++ b/app/src/main/java/com/vectras/vm/utils/FileUtils.java
@@ -868,15 +868,15 @@ public class FileUtils {
return true;
}
- public static void openFolder(Activity activity, String folderPath) {
+ public static void openFolder(Context context, String folderPath) {
File folder = new File(folderPath);
if (!folder.exists() || !folder.isDirectory()) {
DialogUtils.oneDialog(
- activity,
- activity.getString(R.string.oops),
- activity.getString(R.string.directory_does_not_exist),
- activity.getString(R.string.ok),
+ context,
+ context.getString(R.string.oops),
+ context.getString(R.string.directory_does_not_exist),
+ context.getString(R.string.ok),
true,
R.drawable.error_96px,
true,
@@ -888,8 +888,8 @@ public class FileUtils {
}
Uri uri = FileProvider.getUriForFile(
- activity,
- activity.getPackageName() + ".provider",
+ context,
+ context.getPackageName() + ".provider",
folder
);
@@ -900,13 +900,13 @@ public class FileUtils {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- activity.startActivity(intent);
+ context.startActivity(intent);
} catch (Exception e) {
DialogUtils.oneDialog(
- activity,
- activity.getString(R.string.oops),
- activity.getString(R.string.there_is_no_app_to_perform_this_action),
- activity.getString(R.string.ok),
+ context,
+ context.getString(R.string.oops),
+ context.getString(R.string.there_is_no_app_to_perform_this_action),
+ context.getString(R.string.ok),
true,
R.drawable.error_96px,
true,
diff --git a/app/src/main/java/com/vectras/vm/utils/LibraryChecker.java b/app/src/main/java/com/vectras/vm/utils/LibraryChecker.java
index 2692003..3313a56 100644
--- a/app/src/main/java/com/vectras/vm/utils/LibraryChecker.java
+++ b/app/src/main/java/com/vectras/vm/utils/LibraryChecker.java
@@ -89,11 +89,11 @@ public class LibraryChecker {
}
// Method to check if the package is installed
- public static void isPackageInstalled2(Activity activity, String packageName, Terminal.CommandCallback callback) {
+ public static void isPackageInstalled2(Context context, String packageName, Terminal.CommandCallback callback) {
String command = "apk info";
- Terminal terminal = new Terminal(activity);
- terminal.executeShellCommand(command, activity, false, (output, errors) -> {
+ Terminal terminal = new Terminal(context);
+ terminal.executeShellCommand(command, context, false, (output, errors) -> {
if (callback != null) {
callback.onCommandCompleted(output, errors);
}
diff --git a/app/src/main/java/com/vectras/vm/utils/ZipUtils.java b/app/src/main/java/com/vectras/vm/utils/ZipUtils.java
index dab5993..47b85d9 100644
--- a/app/src/main/java/com/vectras/vm/utils/ZipUtils.java
+++ b/app/src/main/java/com/vectras/vm/utils/ZipUtils.java
@@ -43,6 +43,11 @@ public class ZipUtils {
TextView statusTextView,
ProgressBar progressBar
) {
+ if (MainSettingsManager.getCheckBeforeExtract(context)) {
+ updateStatus(statusTextView, progressBar, context.getString(R.string.checking), 0);
+ if (isZipFileCorrupted(context, fileZip)) return false;
+ }
+
try {
File outdir = new File(destDir);
if (!outdir.exists()) outdir.mkdirs();
@@ -213,6 +218,34 @@ public class ZipUtils {
}
}
+ public static boolean isZipFileCorrupted(Context context, String path) {
+ try (ZipFile zip = new ZipFile(path)) {
+
+ Enumeration extends ZipEntry> entries = zip.entries();
+ byte[] buffer;
+ if (DeviceUtils.totalMemoryCapacity(context) < 4L * 1024 * 1024 * 1024)
+ buffer = new byte[64 * 1024];
+ else if (DeviceUtils.totalMemoryCapacity(context) < 11L * 1024 * 1024 * 1024)
+ buffer = new byte[128 * 1024];
+ else
+ buffer = new byte[256 * 1024];
+
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+
+ try (InputStream is = zip.getInputStream(entry)) {
+ while (is.read(buffer) != -1) {
+
+ }
+ }
+ }
+
+ return false;
+ } catch (Exception e) {
+ return true;
+ }
+ }
+
public static boolean compress(
Context context,
String[] filePaths,
diff --git a/app/src/main/java/com/vectras/vterm/Terminal.java b/app/src/main/java/com/vectras/vterm/Terminal.java
index 364a063..d42bacb 100644
--- a/app/src/main/java/com/vectras/vterm/Terminal.java
+++ b/app/src/main/java/com/vectras/vterm/Terminal.java
@@ -68,20 +68,20 @@ public class Terminal {
return null;
}
- private void showDialog(String message, Activity activity, String usercommand) {
- if (VMManager.isExecutedCommandError(usercommand, message, activity))
+ private void showDialog(String message, Context context, String usercommand) {
+ if (VMManager.isExecutedCommandError(usercommand, message, context))
return;
- DialogUtils.twoDialog(activity, "Execution Result", message, activity.getString(R.string.copy), activity.getString(R.string.close), true, R.drawable.round_terminal_24, true,
- () -> ClipboardUltils.copyToClipboard(activity, message), null, null);
+ DialogUtils.twoDialog(context, "Execution Result", message, context.getString(R.string.copy), context.getString(R.string.close), true, R.drawable.round_terminal_24, true,
+ () -> ClipboardUltils.copyToClipboard(context, message), null, null);
}
// Method to execute the shell command
- public void executeShellCommand(String userCommand, boolean showResultDialog, boolean showProgressDialog, Activity dialogActivity) {
+ public void executeShellCommand(String userCommand, boolean showResultDialog, boolean showProgressDialog, Context dialogActivity) {
executeShellCommand(userCommand, showResultDialog, showProgressDialog, dialogActivity.getString(R.string.executing_command_please_wait), dialogActivity);
}
- public void executeShellCommand(String userCommand, boolean showResultDialog, boolean showProgressDialog, String progressDialogMessage, Activity dialogActivity) {
+ public void executeShellCommand(String userCommand, boolean showResultDialog, boolean showProgressDialog, String progressDialogMessage, Context dialogActivity) {
StringBuilder output = new StringBuilder();
StringBuilder errors = new StringBuilder();
Log.d(TAG, userCommand);
@@ -147,7 +147,7 @@ public class Terminal {
String line;
while ((line = reader.readLine()) != null) {
- Log.d(TAG, line);
+// Log.d(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("VTERM: >" + line + "");
output.append(line).append("\n");
}
@@ -180,7 +180,7 @@ public class Terminal {
}).start();
}
- public void executeShellCommand2(String userCommand, boolean showResultDialog, Activity dialogActivity) {
+ public void executeShellCommand2(String userCommand, boolean showResultDialog, Context dialogActivity) {
StringBuilder output = new StringBuilder();
StringBuilder errors = new StringBuilder();
Log.d(TAG, userCommand);
@@ -191,7 +191,7 @@ public class Terminal {
ProcessBuilder processBuilder = new ProcessBuilder();
// Adjust these environment variables as necessary for your app
- String filesDir = context.getFilesDir().getAbsolutePath();
+ String filesDir = getContext().getFilesDir().getAbsolutePath();
File tmpDir = new File(context.getFilesDir(), "usr/tmp");
@@ -245,7 +245,7 @@ public class Terminal {
// Read the input stream for the output of the command
String line;
while ((line = reader.readLine()) != null) {
- Log.d(TAG, line);
+// Log.d(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("VTERM: >" + line + "");
output.append(line).append("\n");
}
@@ -345,7 +345,7 @@ public class Terminal {
String line;
while ((line = reader.readLine()) != null) {
- Log.d(TAG, line);
+// Log.d(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("VTERM: >" + line + "");
output.append(line).append("\n");
}
@@ -474,7 +474,7 @@ public class Terminal {
void onCommandCompleted(String output, String errors);
}
- public String executeShellCommand(String userCommand, Activity dialogActivity, boolean isShowProgressDialog, CommandCallback callback) {
+ public String executeShellCommand(String userCommand, Context dialogActivity, boolean isShowProgressDialog, CommandCallback callback) {
StringBuilder output = new StringBuilder();
StringBuilder errors = new StringBuilder();
Log.d(TAG, userCommand);
@@ -541,7 +541,7 @@ public class Terminal {
String line;
while ((line = reader.readLine()) != null) {
- Log.d(TAG, line);
+// Log.d(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("VTERM: >" + line + "");
output.append(line).append("\n");
}
@@ -576,28 +576,6 @@ public class Terminal {
return "Execution is in progress..."; // Returning a message indicating the command execution is ongoing
}
-
- private boolean checkInstallation() {
- String filesDir = context.getFilesDir().getAbsolutePath();
- File distro = new File(filesDir, "distro");
- return distro.exists();
- }
-
- public static void killQemuProcess() {
- if (qemuProcess != null) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
- qemuProcess.destroyForcibly();
- else
- qemuProcess.destroy();
- //MainVNCActivity.activity.finish();
- MainVNCActivity.started = false;
- qemuProcess = null; // Set it to null after destroying it
- Log.d(TAG, "QEMU process destroyed.");
- } else {
- Log.d(TAG, "QEMU process was null.");
- }
- }
-
/**
* Checks if a package is installed using `apk info`.
*
@@ -620,4 +598,11 @@ public class Terminal {
}
return false;
}
+
+ private Context getContext() {
+ if (context == null) {
+ return VectrasApp.getContext();
+ }
+ return context;
+ }
}
diff --git a/app/src/main/res/layout/activity_import_export_settings.xml b/app/src/main/res/layout/activity_import_export_settings.xml
index f62f331..11da3da 100644
--- a/app/src/main/res/layout/activity_import_export_settings.xml
+++ b/app/src/main/res/layout/activity_import_export_settings.xml
@@ -82,6 +82,48 @@
android:clickable="false"/>
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
\ No newline at end of file
+ android:layout_height="match_parent"
+ android:padding="16dp">
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_theme.xml b/app/src/main/res/layout/activity_theme.xml
index 6a20906..c712f1c 100644
--- a/app/src/main/res/layout/activity_theme.xml
+++ b/app/src/main/res/layout/activity_theme.xml
@@ -16,8 +16,7 @@
+ app:title="@string/theme">
-
-
+
+
+ android:layout_height="wrap_content"
+ android:text="@string/themes" />
-
+
+
+ android:layout_weight="1"
+ android:text="@string/system" />
-
+ android:layout_weight="1"
+ android:text="@string/light" />
-
+
-
-
-
-
-
-
-
+
+
+ app:title="@string/vnc">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 973bb4e..7280667 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -3,7 +3,7 @@
Vectras VM
- 3.4.1
+ 3.4.2
Stable
VM STARTED!
@@ -214,7 +214,7 @@
You cannot use IDE hard drive type with ARM64. If you continue, the default hard drive type will be used.
The system is not working, necessary files are missing. This problem may be caused by incorrect setup. Do you want to try the setup again? Error code: PROOT_IS_MISSING_0.
An error occurred and the virtual machine cannot start. There are two or more drives that cannot be plugged into the same slot. Please check your virtual machine settings and make sure all drives are not plugged into the same slot. Error code: DRIVE_INDEX_0_EXISTS.
- An error occurred and the virtual machine cannot start. X11 is not working. We recommend you switch to VNC. Do you want to switch to VNC? Error code: X11_NOT_AVAILABLE.
+ An error occurred and the virtual machine is not running. The X11 display is temporarily unavailable, please exit the Vectras VM and come back to try again. You can try switching to VNC which is more stable. Error code: X11_NOT_AVAILABLE.
Switched to VNC. You can try running the virtual machine again now.
Restore
Deleted virtual machines will be restored if you previously chose to keep all files.
@@ -459,6 +459,17 @@
The required package is not installed, you need to install it before using this feature.
Termux:X11 app is not installed, you need to install it to continue.
X11 display
+ Settings for X11 display.
+ Use X11 display
+ Check before extract
+ Quickly detect errors to avoid wasting time decompressing and errors occurring after decompression.
+ Checking…
+ X11 display cannot be used at this time, try closing and reopening the Vectras VM.
+ Run Qemu with Xterm
+ To run commands with Qemu monitor easier without switching.
+ Quick edit
+ Write something…
+ Switch to VNC
diff --git a/app/src/main/res/xml/headers_preference.xml b/app/src/main/res/xml/headers_preference.xml
index cd6e419..4d78740 100644
--- a/app/src/main/res/xml/headers_preference.xml
+++ b/app/src/main/res/xml/headers_preference.xml
@@ -29,6 +29,15 @@
android:targetPackage="com.vectras.vm"
android:targetClass="com.vectras.vm.settings.VNCSettingsActivity" />
+
+
+
3.4.0\n- Fixed the issue of exporting rom with virtual machine ID.\n- New dialog for copying files in creating new virtual machine.\n- New dialog showing information after successful rom import.\n- Fixed no sound playing with Termux.\n- Fixed invalid file path error when using Manual setup.\n- Fixed dialog showing content without line breaks.\n- Fixed rom export error.\n- Added Processes to System monitor.\n- Improved interface.\n- Thinner fonts.\n- Fixed an issue with interaction in Rom store.\n- Fixed the External VNC Server switch being incorrect in certain cases.\n- Removed unnecessary resources.\n- Updated Chinese (Simplified) language (contributed by @WeiguangTWK).\n- Fixed issue with installing system files after not completing the first time.\n- New setup wizard.\n- Added auto return to Home after importing rom and creating virtual machine in Rom store.\n- Improved image viewer.\n- Fixed Unknow display error in architecture in rom info if it is PowerPC architecture.\n- New setup wizard interface that automatically changes according to screen size.\n- New ID generator for virtual machine.\n- Added dialog when deleting virtual machine.",
"cancellable": true,
- "versionCodeBeta":"45",
- "versionNameBeta":"3.4.1",
- "versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8,3.3.9,3.4.1",
+ "versionCodeBeta":"46",
+ "versionNameBeta":"3.4.2",
+ "versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8,3.3.9,3.4.1,3.4.2",
"sizeBeta": "55 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
- "MessageBeta": "3.4.1
Bugs fixed.",
+ "MessageBeta": "3.4.2
Bugs fixed.",
"cancellableBeta": true
}