- Fixed issues when using custom memory.
- Fixed issues when switching between desktop and game layouts.
- Fixed issues in System Monitor.
- Fixed issues in VM creator.
This commit is contained in:
An Bui 2026-01-08 22:08:58 +07:00
parent 51d77c16fb
commit 4e7ad5f3cd
14 changed files with 188 additions and 219 deletions

View file

@ -9,6 +9,8 @@ import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.text.InputFilter;
import android.text.InputType;
import android.util.Log;
import androidx.preference.PreferenceManager;
@ -424,6 +426,13 @@ public class MainSettingsManager extends AppCompatActivity
EditTextPreference memory = findPreference("memory");
assert memory != null;
memory.setOnBindEditTextListener(editText -> {
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
editText.setFilters(new InputFilter[]{
new InputFilter.LengthFilter(4)
});
});
if (!customMemory.isChecked()) {
memory.setEnabled(false);
}

View file

@ -8,6 +8,7 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.utils.TextUtils;
public class RamInfo {
public static Activity activity;
@ -29,7 +30,7 @@ public class RamInfo {
int freeRamInt = safeLongToInt(freeMem);
int totalRamInt = safeLongToInt(totalMem);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
if (prefs.getBoolean("customMemory", false)) {
if (prefs.getBoolean("customMemory", false) && TextUtils.isNumberOnly(prefs.getString("memory", String.valueOf(256)))) {
return Integer.parseInt(prefs.getString("memory", String.valueOf(256)));
} else {
return freeRamInt - 100;

View file

@ -27,8 +27,8 @@ public class ControlersOptionsFragment extends DialogFragment {
public void onClick(View v) {
MainSettingsManager.setControlMode(getActivity(), "G");
if (MainSettingsManager.getVmUi(getActivity()).equals("X11")) {
X11Activity.desktop.setVisibility(View.GONE);
X11Activity.gamepad.setVisibility(View.VISIBLE);
if (X11Activity.desktop != null ) X11Activity.desktop.setVisibility(View.GONE);
if (X11Activity.gamepad != null ) X11Activity.gamepad.setVisibility(View.VISIBLE);
} else if (MainSettingsManager.getVmUi(getActivity()).equals("VNC")) {
MainVNCActivity.desktop.setVisibility(View.GONE);
MainVNCActivity.gamepad.setVisibility(View.VISIBLE);

View file

@ -3,7 +3,6 @@ package com.vectras.vm;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.text.Editable;
@ -35,7 +34,6 @@ import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.ImageUtils;
import com.vectras.vm.utils.JSONUtils;
import com.vectras.vm.utils.PackageUtils;
import com.vectras.vm.utils.UIUtils;
@ -219,19 +217,13 @@ public class VMCreatorActivity extends AppCompatActivity {
created = true;
binding.addRomBtn.setText(R.string.save_changes);
if (binding != null && current.itemName != null) {
binding.title.setText(current.itemName);
if (binding != null && current != null) {
if (current.itemName != null) binding.title.setText(current.itemName);
if (current.itemPath != null) binding.drive.setText(current.itemPath);
if (current.imgCdrom != null) binding.cdrom.setText(current.imgCdrom);
if (current.itemIcon != null) thumbnailPath = current.itemIcon;
}
if (binding != null && current.itemPath != null) {
binding.drive.setText(current.itemPath);
}
if (binding != null && current.imgCdrom != null) {
binding.cdrom.setText(current.imgCdrom);
}
thumbnailPath = current.itemIcon;
vmID = getIntent().getStringExtra("VMID");
if (vmID == null || vmID.isEmpty()) {
@ -669,13 +661,17 @@ public class VMCreatorActivity extends AppCompatActivity {
private void selectedDiskFile(Uri _content_describer, boolean _addtodrive) {
if (FileUtils.isValidFilePath(this, FileUtils.getPath(this, _content_describer), false)) {
File selectedFilePath = new File(getPath(_content_describer));
if (VMManager.isADiskFile(selectedFilePath.getPath())) {
startProcessingHardDriveFile(_content_describer, _addtodrive);
} else {
DialogUtils.twoDialog(this, getString(R.string.problem_has_been_detected), getString(R.string.file_format_is_not_supported), getResources().getString(R.string.continuetext), getResources().getString(R.string.cancel), true, R.drawable.hard_drive_24px, true,
() -> startProcessingHardDriveFile(_content_describer, _addtodrive), null, null);
}
new Thread(() -> {
File selectedFilePath = new File(getPath(_content_describer));
runOnUiThread(() -> {
if (VMManager.isADiskFile(selectedFilePath.getPath())) {
startProcessingHardDriveFile(_content_describer, _addtodrive);
} else {
DialogUtils.twoDialog(this, getString(R.string.problem_has_been_detected), getString(R.string.file_format_is_not_supported), getResources().getString(R.string.continuetext), getResources().getString(R.string.cancel), true, R.drawable.hard_drive_24px, true,
() -> startProcessingHardDriveFile(_content_describer, _addtodrive), null, null);
}
});
}).start();
} else {
startProcessingHardDriveFile(_content_describer, _addtodrive);
}

View file

@ -79,6 +79,8 @@ public class VMManager {
ArrayList<HashMap<String, Object>> vmList = new Gson().fromJson(vmJsonListContent, new TypeToken<ArrayList<HashMap<String, Object>>>() {
}.getType());
if (vmList == null) return false;
for (int _repeat = 0; _repeat < vmList.size(); _repeat++) {
if (vmList.get(_repeat).containsKey("vmID")
&& Objects.requireNonNull(vmList.get(_repeat).get("vmID")).toString().equals(vmId)) {
@ -97,6 +99,9 @@ public class VMManager {
ArrayList<HashMap<String, Object>> vmList = new Gson().fromJson(vmListJson, new TypeToken<ArrayList<HashMap<String, Object>>>() {
}.getType());
if (vmList == null) return false;
HashMap<String, Object> vmConfigMap = new Gson().fromJson(vmConfigJson, new TypeToken<HashMap<String, Object>>() {
}.getType());
@ -117,6 +122,8 @@ public class VMManager {
ArrayList<HashMap<String, Object>> vmList = new Gson().fromJson(vmListJson, new TypeToken<ArrayList<HashMap<String, Object>>>() {
}.getType());
if (vmList == null) return false;
if (!vmID.isEmpty()) {
generatedVMId = vmID;
vmConfigMap.put("vmID", generatedVMId);
@ -134,6 +141,9 @@ public class VMManager {
int finalPosition = postion;
ArrayList<HashMap<String, Object>> vmList = new Gson().fromJson(vmListJson, new TypeToken<ArrayList<HashMap<String, Object>>>() {
}.getType());
if (vmList == null) return false;
HashMap<String, Object> vmConfigMap = new Gson().fromJson(vmConfigJson, new TypeToken<HashMap<String, Object>>() {
}.getType());
@ -165,6 +175,8 @@ public class VMManager {
ArrayList<HashMap<String, Object>> vmList = new Gson().fromJson(vmListJson, new TypeToken<ArrayList<HashMap<String, Object>>>() {
}.getType());
if (vmList == null) return false;
if (postion == -1) {
for (int _repeat = 0; _repeat < vmList.size(); _repeat++) {
if (vmList.get(_repeat).containsKey("vmID")

View file

@ -9,19 +9,29 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.vectras.vm.R;
import android.content.Context;
import java.util.Locale;
import java.util.UnknownFormatConversionException;
import java.util.FormatFlagsConversionMismatchException;
import android.annotation.SuppressLint;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import android.content.pm.PackageManager;
import java.io.ByteArrayInputStream;
import android.content.pm.Signature;
import java.security.MessageDigest;
import java.util.Arrays;
import android.content.pm.PackageInfo;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
@ -29,33 +39,33 @@ import java.security.cert.CertificateException;
* Created by arne on 24.04.16.
*/
public class LogItem implements Parcelable {
private Object[] mArgs = null;
private Object[] mArgs = null;
private String mMessage = null;
private int mResourceId;
private int mResourceId;
// Default log priority
VectrasStatus.LogLevel mLevel = VectrasStatus.LogLevel.INFO;
private long logtime = System.currentTimeMillis();
private int mVerbosityLevel = -1;
public LogItem(int resId, Object... args) {
public LogItem(int resId, Object... args) {
mResourceId = resId;
mArgs = args;
mArgs = args;
}
public LogItem(VectrasStatus.LogLevel loglevel, int verblevel, String msg) {
public LogItem(VectrasStatus.LogLevel loglevel, int verblevel, String msg) {
mLevel = loglevel;
mMessage = msg;
mVerbosityLevel = verblevel;
mVerbosityLevel = verblevel;
}
public LogItem(VectrasStatus.LogLevel level, int resId, Object... args) {
mLevel = level;
mResourceId = resId;
mArgs = args;
mResourceId = resId;
mArgs = args;
}
public LogItem(VectrasStatus.LogLevel loglevel, String msg) {
public LogItem(VectrasStatus.LogLevel loglevel, String msg) {
mLevel = loglevel;
mMessage = msg;
}
@ -65,8 +75,8 @@ public class LogItem implements Parcelable {
mResourceId = ressourceId;
mLevel = loglevel;
}
@Override
@Override
public String toString() {
return getString(null);
}
@ -80,16 +90,16 @@ public class LogItem implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeArray(mArgs);
dest.writeString(mMessage);
dest.writeInt(mResourceId);
dest.writeInt(mResourceId);
dest.writeInt(mLevel.getInt());
dest.writeLong(logtime);
}
public LogItem(Parcel in) {
mArgs = in.readArray(Object.class.getClassLoader());
mMessage = in.readString();
mResourceId = in.readInt();
mResourceId = in.readInt();
mLevel = VectrasStatus.LogLevel.getEnumByValue(in.readInt());
logtime = in.readLong();
}
@ -112,18 +122,18 @@ public class LogItem implements Parcelable {
public long getLogtime() {
return logtime;
}
public String getMessage() {
return mMessage;
}
public String getString(Context c) {
public String getMessage() {
return mMessage;
}
public String getString(Context c) {
try {
if (mMessage != null) {
return mMessage;
} else {
if (c != null) {
if (mResourceId == R.string.app_name)
if (mResourceId == R.string.app_name)
return getAppInfoString(c);
else if (mArgs == null)
return c.getString(mResourceId);
@ -149,10 +159,10 @@ public class LogItem implements Parcelable {
throw e;
}
}
//private String listb = "";
// The lint is wrong here
//private String listb = "";
// The lint is wrong here
@SuppressLint("StringFormatMatches")
private String getAppInfoString(Context c) {
c.getPackageManager();
@ -161,7 +171,7 @@ public class LogItem implements Parcelable {
String version = "error getting version";
try {
@SuppressLint("PackageManagerGetSignatures")
Signature raw = c.getPackageManager().getPackageInfo(c.getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
Signature raw = c.getPackageManager().getPackageInfo(c.getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray()));
MessageDigest md = MessageDigest.getInstance("SHA-1");
@ -174,7 +184,7 @@ public class LogItem implements Parcelable {
version = String.format("%s Projeto %d", packageinfo.versionName, packageinfo.versionCode);
} catch (PackageManager.NameNotFoundException | CertificateException |
NoSuchAlgorithmException ignored) {
NoSuchAlgorithmException ignored) {
}
/* Object[] argsext = Arrays.copyOf(mArgs, mArgs.length);
@ -184,8 +194,8 @@ public class LogItem implements Parcelable {
return c.getString(R.string.app_name, version, apksign);
}
// TextUtils.join will cause not macked exeception in tests ....
// TextUtils.join will cause not macked exeception in tests ....
public static String join(CharSequence delimiter, Object[] tokens) {
StringBuilder sb = new StringBuilder();
boolean firstTime = true;
@ -199,8 +209,8 @@ public class LogItem implements Parcelable {
}
return sb.toString();
}
public int getVerbosityLevel() {
public int getVerbosityLevel() {
if (mVerbosityLevel == -1) {
// Hack:
// For message not from OpenVPN, report the status level as log level

View file

@ -209,7 +209,7 @@ public class SystemMonitorFragment extends Fragment {
if (!isAdded()) return;
String result = Terminal.executeShellCommandWithResult("ps -e command && echo \"psendhere\" && cat /proc/cpuinfo", requireActivity());
requireActivity().runOnUiThread(() -> {
binding.tvProcesses.setText(result.isEmpty() ? getString(R.string.nothing_here) : result.substring(0, result.indexOf("\npsendhere")));
binding.tvProcesses.setText(result.isEmpty() && !result.contains("\npsendhere") ? getString(R.string.nothing_here) : result.substring(0, result.indexOf("\npsendhere")));
binding.tvQemuversion.setText(getString(R.string.version) + " " + (qemuVersionName.isEmpty() ? getString(R.string.unknow) : qemuVersionName) + ".");
if (!result.isEmpty()) {

View file

@ -23,6 +23,7 @@ import android.widget.TextView;
import androidx.preference.PreferenceManager;
import com.google.android.material.color.MaterialColors;
import com.vectras.vm.AppConfig;
import com.vectras.vm.R;
public class DialogUtils {
@ -254,10 +255,10 @@ public class DialogUtils {
_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));
_context.startActivity(f);
Intent intent = new Intent(ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_BROWSABLE);
intent.setData(Uri.parse(AppConfig.telegramLink));
_context.startActivity(intent);
}, null,
() -> {
SharedPreferences.Editor edit = prefs.edit();

View file

@ -5,8 +5,13 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Objects;
public class NetworkUtils {
public static boolean isPortOpen(String host, int port, int timeout) {
@ -46,4 +51,21 @@ public class NetworkUtils {
}
return false;
}
public static String getLocalIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && Objects.requireNonNull(inetAddress.getHostAddress()).contains(".")) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException ex) {
ex.printStackTrace();
}
return null;
}
}

View file

@ -3,6 +3,10 @@ package com.vectras.vm.utils;
import java.util.Random;
public class TextUtils {
public static boolean isNumberOnly(String content) {
return content.matches("\\d+");
}
public static String randomALetter() {
String addAdb;
Random random = new Random();

View file

@ -1,5 +1,6 @@
package com.vectras.vm.view;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@ -79,7 +80,10 @@ public class GithubUserView extends LinearLayout {
userName.setText(!user.getLogin().isEmpty() ? user.getLogin() : getContext().getString(R.string.unknow));
userDescription.setText(!user.getBio().isEmpty() ? user.getBio() : getContext().getString(R.string.unknow));
if (!user.getAvatarUrl().isEmpty()) {
Glide.with(getContext()).load(user.getAvatarUrl()).placeholder(R.drawable.account_circle_24px).error(R.drawable.account_circle_24px).into(profileImage);
if (getContext() instanceof Activity activity) {
if (!activity.isFinishing() && !activity.isDestroyed())
Glide.with(getContext()).load(user.getAvatarUrl()).placeholder(R.drawable.account_circle_24px).error(R.drawable.account_circle_24px).into(profileImage);
}
} else {
profileImage.setImageResource(R.drawable.account_circle_24px);
}

View file

@ -24,6 +24,7 @@ import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import com.vectras.vm.R;
import com.vectras.vm.VMManager;
@ -35,8 +36,8 @@ import com.vectras.vm.utils.NotificationUtils;
public class Terminal {
private static final String TAG = "Vterm";
private Context context;
private static String user = "root";
private final Context context;
private static final String user = "root";
public static Process qemuProcess;
public static String DISPLAY = ":0";
@ -45,24 +46,6 @@ public class Terminal {
this.context = context;
}
private String getLocalIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && Objects.requireNonNull(inetAddress.getHostAddress()).contains(".")) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException ex) {
ex.printStackTrace();
}
return null;
}
private void showDialog(String message, Context context, String usercommand) {
if (VMManager.isExecutedCommandError(usercommand, message, context))
return;
@ -77,7 +60,7 @@ public class Terminal {
}
public void executeShellCommand(String userCommand, boolean showResultDialog, boolean showProgressDialog, String progressDialogMessage, Context dialogActivity) {
StringBuilder output = new StringBuilder();
AtomicReference<StringBuilder> output = new AtomicReference<>(new StringBuilder());
StringBuilder errors = new StringBuilder();
Log.d(TAG, userCommand);
com.vectras.vm.logger.VectrasStatus.logError("<font color='#4db6ac'>VTERM: >" + userCommand + "</font>");
@ -131,35 +114,10 @@ public class Terminal {
processBuilder.command(prootCommand);
qemuProcess = processBuilder.start();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(qemuProcess.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(qemuProcess.getInputStream()));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(qemuProcess.getErrorStream()));
writer.write(userCommand);
writer.newLine();
writer.flush();
writer.close();
String line;
while ((line = reader.readLine()) != null) {
// Log.d(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("<font color='#4db6ac'>VTERM: >" + line + "</font>");
output.append(line).append("\n");
}
while ((line = errorReader.readLine()) != null) {
Log.w(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("<font color='red'>VTERM ERROR: >" + line + "</font>");
output.append(line).append("\n");
}
int exitCode = qemuProcess.waitFor();
if (exitCode != 0) {
output.append("Execution finished with exit code: ").append(exitCode).append("\n");
}
} catch (IOException | InterruptedException e) {
output.set(streamLog(userCommand, qemuProcess, false));
} catch (IOException e) {
progressDialog.dismiss(); // Dismiss ProgressDialog
output.append(e.getMessage());
output.get().append(e.getMessage());
errors.append(Log.getStackTraceString(e));
} finally {
new Handler(Looper.getMainLooper()).post(() -> {
@ -176,7 +134,7 @@ public class Terminal {
}
public void executeShellCommand2(String userCommand, boolean showResultDialog, Context dialogActivity) {
StringBuilder output = new StringBuilder();
AtomicReference<StringBuilder> output = new AtomicReference<>(new StringBuilder());
StringBuilder errors = new StringBuilder();
Log.d(TAG, userCommand);
com.vectras.vm.logger.VectrasStatus.logError("<font color='#4db6ac'>VTERM: >" + userCommand + "</font>");
@ -226,46 +184,10 @@ public class Terminal {
processBuilder.command(prootCommand);
qemuProcess = processBuilder.start();
// Get the input and output streams of the qemuProcess
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(qemuProcess.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(qemuProcess.getInputStream()));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(qemuProcess.getErrorStream()));
// Send user command to PRoot
writer.write(userCommand);
writer.newLine();
writer.flush();
writer.close();
// Read the input stream for the output of the command
String line;
while (qemuProcess.isAlive() && (line = reader.readLine()) != null) {
// Log.d(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("<font color='#4db6ac'>VTERM: >" + line + "</font>");
output.append(line).append("\n");
}
// Read any errors from the error stream
while (qemuProcess.isAlive() && (line = errorReader.readLine()) != null) {
Log.w(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("<font color='red'>VTERM ERROR: >" + line + "</font>");
output.append(line).append("\n");
}
// Clean up
reader.close();
errorReader.close();
int exitCode = qemuProcess.waitFor(); // Wait for the process to finish
if (exitCode == 0) {
output.append("Execution finished successfully.\n");
} else {
output.append("Execution finished with exit code: ").append(exitCode).append("\n");
}
output.append(reader.readLine()).append("\n");
Log.i(TAG, reader.readLine());
} catch (IOException | InterruptedException e) {
output.append(e.getMessage());
output.set(streamLog(userCommand, qemuProcess, false));
} catch (IOException e) {
output.get().append(e.getMessage());
errors.append(Log.getStackTraceString(e));
NotificationUtils.clearAll(VectrasApp.getContext());
} finally {
@ -327,33 +249,8 @@ public class Terminal {
processBuilder.command(prootCommand);
qemuProcess = processBuilder.start();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(qemuProcess.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(qemuProcess.getInputStream()));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(qemuProcess.getErrorStream()));
writer.write(userCommand);
writer.newLine();
writer.flush();
writer.close();
String line;
while ((line = reader.readLine()) != null) {
// Log.d(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("<font color='#4db6ac'>VTERM: >" + line + "</font>");
output.append(line).append("\n");
}
while ((line = errorReader.readLine()) != null) {
Log.w(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("<font color='red'>VTERM ERROR: >" + line + "</font>");
output.append(line).append("\n");
}
int exitCode = qemuProcess.waitFor();
if (exitCode != 0) {
output.append("Execution finished with exit code: ").append(exitCode).append("\n");
}
} catch (IOException | InterruptedException e) {
output = streamLog(userCommand, qemuProcess, false);
} catch (IOException e) {
output.append(e.getMessage());
errors.append(Log.getStackTraceString(e));
}
@ -365,7 +262,7 @@ public class Terminal {
}
public String executeShellCommand(String userCommand, Context dialogActivity, boolean isShowProgressDialog, CommandCallback callback) {
StringBuilder output = new StringBuilder();
AtomicReference<StringBuilder> output = new AtomicReference<>(new StringBuilder());
StringBuilder errors = new StringBuilder();
Log.d(TAG, userCommand);
com.vectras.vm.logger.VectrasStatus.logError("<font color='#4db6ac'>VTERM: >" + userCommand + "</font>");
@ -420,46 +317,17 @@ public class Terminal {
processBuilder.command(prootCommand);
qemuProcess = processBuilder.start();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(qemuProcess.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(qemuProcess.getInputStream()));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(qemuProcess.getErrorStream()));
output.set(streamLog(userCommand, qemuProcess, true));
writer.write(userCommand);
writer.newLine();
writer.flush();
writer.close();
String line;
while ((line = reader.readLine()) != null) {
// Log.d(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("<font color='#4db6ac'>VTERM: >" + line + "</font>");
output.append(line).append("\n");
}
while ((line = errorReader.readLine()) != null) {
Log.w(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("<font color='red'>VTERM ERROR: >" + line + "</font>");
errors.append(line).append("\n");
}
int exitCode = qemuProcess.waitFor();
if (exitCode != 0) {
output.append("Execution finished with exit code: ").append(exitCode).append("\n");
}
} catch (IOException | InterruptedException e) {
output.append(e.getMessage());
} catch (IOException e) {
output.get().append(e.getMessage());
errors.append(Log.getStackTraceString(e));
} finally {
// Dismiss ProgressDialog on the main thread
new Handler(Looper.getMainLooper()).post(progressDialog::dismiss);
// Return the output and errors via callback on the main thread
String finalOutput = output.toString();
String finalErrors = errors.toString();
// Use callback to return both output and errors
new Handler(Looper.getMainLooper()).post(() -> callback.onCommandCompleted(finalOutput, finalErrors));
new Handler(Looper.getMainLooper()).post(() -> callback.onCommandCompleted(output.toString(), errors.toString()));
}
}).start();
@ -489,6 +357,48 @@ public class Terminal {
return false;
}
public static StringBuilder streamLog(String command, Process process, boolean isShortProcess) {
StringBuilder output = new StringBuilder();
try {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
writer.write(command);
writer.newLine();
writer.flush();
writer.close();
String line;
while ((line = reader.readLine()) != null) {
com.vectras.vm.logger.VectrasStatus.logError("<font color='#4db6ac'>VTERM: >" + line + "</font>");
output.append(line).append("\n");
}
while ((line = errorReader.readLine()) != null) {
Log.w(TAG, line);
com.vectras.vm.logger.VectrasStatus.logError("<font color='red'>VTERM ERROR: >" + line + "</font>");
output.append(line).append("\n");
}
if (isShortProcess) {
int exitCode = process.waitFor();
if (exitCode == 0) {
output.append("Execution finished successfully.\n");
} else {
output.append("Execution finished with exit code: ").append(exitCode).append("\n");
}
}
reader.close();
errorReader.close();
} catch (Exception e) {
output.append(e.getMessage());
Log.e(TAG, "streamLog: ", e);
}
return output;
}
private Context getContext() {
if (context == null) {
return VectrasApp.getContext();