- Added dialog about Alpine Xfce not supporting 32-bit.
- Tiny Vterm will be used instead of Terminal if device is 32-bit.
- You can now open Terminal in Advanced setup.
- VNC is default if device is 32-bit.
- Fixed setup failure on 32-bit devices.
- Improved Tiny Vterm.
This commit is contained in:
An Bui 2025-10-16 14:33:27 +07:00
parent f7093df107
commit d3046c50f7
14 changed files with 217 additions and 140 deletions

View file

@ -4,14 +4,11 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-10-09T19:55:30.113124600Z">
<DropdownSelection timestamp="2025-10-16T07:18:10.628540500Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="FirebaseDirectAccess" identifier="reservation=projects/nguyenbaoanbui-9cba5/deviceSessions/session-2nrdccjx040yd" />
<DeviceId pluginId="PhysicalDevice" identifier="serial=8c847fb6" />
</handle>
<template>
<DeviceId pluginId="FirebaseDirectAccess" type="TEMPLATE" identifier="model_id=b0q/33" />
</template>
</Target>
</DropdownSelection>
<DialogSelection />

View file

@ -10,8 +10,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
versionCode 24
versionName "3.2.1"
versionCode 25
versionName "3.2.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
@ -89,7 +89,7 @@ dependencies {
// implementation 'com.google.android.gms:play-services-ads:24.6.0'
implementation 'com.google.guava:guava:33.5.0-jre'
implementation 'com.google.code.gson:gson:2.13.2'
implementation 'com.squareup.okhttp3:okhttp:5.2.0'
implementation 'com.squareup.okhttp3:okhttp:5.2.1'
implementation "androidx.window:window:1.5.0"
implementation "commons-io:commons-io:2.20.0"
implementation 'org.zeroturnaround:zt-zip:1.17'

View file

@ -82,7 +82,7 @@ public class AppConfig {
" libusb libaio ncurses-libs curl libnfs gtk+3.0 gtk+3.0-dev fuse libpulse libseccomp jack pipewire liburing" +
" mesa-dri-gallium mesa-vulkan-swrast vulkan-loader mesa-utils mesa-egl mesa-gbm mesa-vulkan-ati mesa-vulkan-broadcom mesa-vulkan-freedreno mesa-vulkan-panfrost";
public static String neededPkgs32bit = "aria2 tar dwm xfce4-terminal libslirp libslirp-dev pulseaudio-dev glib-dev pixman-dev zlib-dev spice-dev" +
public static String neededPkgs32bit = "aria2 tar dwm xterm libslirp libslirp-dev pulseaudio-dev glib-dev pixman-dev zlib-dev spice-dev" +
" libusbredirparser usbredir-dev libiscsi-dev sdl2 sdl2-dev libepoxy-dev virglrenderer-dev rdma-core" +
" libusb ncurses-libs curl libnfs gtk+3.0 fuse libpulse libseccomp jack pipewire liburing";

View file

@ -4,6 +4,7 @@ import static android.content.Intent.ACTION_OPEN_DOCUMENT;
import static android.content.Intent.ACTION_VIEW;
import static android.view.View.GONE;
import com.termux.app.TermuxActivity;
import com.termux.app.TermuxService;
import static com.vectras.vm.utils.UIUtils.UIAlert;
@ -227,7 +228,6 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
} else {
extractSystemFiles("alpine21", "distro");
}
}
}
@ -886,6 +886,14 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
});
binding.advancedsetup.ivClose.setOnClickListener(v -> linearsimplesetupui.setVisibility(View.VISIBLE));
binding.advancedsetup.ivOpenterminal.setOnClickListener(v -> {
if (DeviceUtils.is64bit()) {
startActivity(new Intent(this, TermuxActivity.class));
} else {
com.vectras.vterm.TerminalBottomSheetDialog VTERM = new com.vectras.vterm.TerminalBottomSheetDialog(this);
VTERM.showVterm();
}
});
binding.advancedsetup.ivCopycommandsetup.setOnClickListener(v -> copyToClipboard(binding.advancedsetup.tvCommandsetup.getText().toString()));
}

View file

@ -14,6 +14,7 @@ import androidx.preference.PreferenceManager;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.home.HomeActivity;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.UIUtils;
@ -141,7 +142,7 @@ public class SplashActivity extends AppCompatActivity implements Runnable {
} else {
startActivity(new Intent(this, SetupQemuActivity.class));
//For Android 14+
if (Build.VERSION.SDK_INT >= 34) {
if (!DeviceUtils.is64bit() || Build.VERSION.SDK_INT >= 34) {
MainSettingsManager.setVmUi(this, "VNC");
}
}

View file

@ -65,6 +65,7 @@ import com.vectras.vm.home.romstore.RomStoreFragment;
import com.vectras.vm.home.vms.VmsFragment;
import com.vectras.vm.logger.VectrasStatus;
import com.vectras.vm.settings.UpdaterActivity;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.LibraryChecker;
@ -478,9 +479,12 @@ public class HomeActivity extends AppCompatActivity implements RomStoreFragment.
} else if (id == R.id.navigation_item_desktop) {
DisplaySystem.launchX11(this, true);
} else if (id == R.id.navigation_item_terminal) {
/*com.vectras.vterm.TerminalBottomSheetDialog VTERM = new com.vectras.vterm.TerminalBottomSheetDialog(activity);
VTERM.showVterm();*/
startActivity(new Intent(this, TermuxActivity.class));
if (DeviceUtils.is64bit()) {
startActivity(new Intent(this, TermuxActivity.class));
} else {
com.vectras.vterm.TerminalBottomSheetDialog VTERM = new com.vectras.vterm.TerminalBottomSheetDialog(this);
VTERM.showVterm();
}
} else if (id == R.id.navigation_item_view_logs) {
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(this);
View view = getLayoutInflater().inflate(R.layout.bottomsheetdialog_logger, null);

View file

@ -13,6 +13,7 @@ import com.vectras.qemu.MainSettingsManager;
import com.vectras.qemu.MainVNCActivity;
import com.vectras.vm.R;
import com.vectras.vm.core.ShellExecutor;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.x11.X11Activity;
@ -50,6 +51,17 @@ public class DisplaySystem {
null,
null
);
} else 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),
true, R.drawable.error_96px,
true,
null,
null
);
} else {
// XFCE4 meta-package
String xfce4Package = "xfce4";

View file

@ -7,7 +7,9 @@ import com.vectras.vterm.Terminal;
public class CommandUtils {
public static String createForSelectedMirror(boolean _https, String _url, String _beforemain) {
String command = "echo \"\" > /etc/apk/repositories && sed -i -e \"1ihttps://xssFjnj58Id/yttGkok69Je/edge/testing\" /etc/apk/repositories && sed -i -e \"1ihttps://xssFjnj58Id/yttGkok69Je/v3.22/community\" /etc/apk/repositories && sed -i -e \"1ihttps://xssFjnj58Id/yttGkok69Je/v3.22/main\" /etc/apk/repositories";
String command = "echo \"\" > /etc/apk/repositories && sed -i -e \"1ihttps://xssFjnj58Id/yttGkok69Je/edge/testing\" /etc/apk/repositories && sed -i -e \"1ihttps://xssFjnj58Id/yttGkok69Je/"
+ (DeviceUtils.is64bit() ? "v3.22" : "v3.21") + "/community\" /etc/apk/repositories && sed -i -e \"1ihttps://xssFjnj58Id/yttGkok69Je/v3.22/main\" /etc/apk/repositories";
command = command.replaceAll("/yttGkok69Je", _beforemain);
if (!_https)
command = command.replaceAll("https://", "http://");

View file

@ -1,20 +1,21 @@
package com.vectras.vterm;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.termux.app.TermuxService;
import com.vectras.vm.AppConfig;
import com.vectras.vm.R;
import com.vectras.vterm.view.ZoomableTextView;
@ -28,13 +29,16 @@ import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Objects;
public class TerminalBottomSheetDialog {
private ZoomableTextView terminalOutput;
private EditText commandInput;
private View view;
private Activity activity;
private BottomSheetDialog bottomSheetDialog;
private final ZoomableTextView terminalOutput;
private final EditText commandInput;
private final View view;
private final Activity activity;
private final BottomSheetDialog bottomSheetDialog;
LinearLayout inputContainer;
boolean isAllowAddToResultCommand = true;
public TerminalBottomSheetDialog(Activity activity) {
this.activity = activity;
@ -45,27 +49,20 @@ public class TerminalBottomSheetDialog {
terminalOutput = view.findViewById(R.id.tvTerminalOutput);
commandInput = view.findViewById(R.id.etCommandInput);
inputContainer = view.findViewById(R.id.ln_input);
TextView tvPrompt = view.findViewById(R.id.tvPrompt);
updateUserPrompt(tvPrompt);
// Show the keyboard
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(commandInput, InputMethodManager.SHOW_IMPLICIT);
commandInput.requestFocus();
forcusCommandInput();
// Whenever you modify the text of the EditText, do the following to ensure the cursor is at the end:
commandInput.setSelection(commandInput.getText().length());
// when user click terminal view will open keyboard
terminalOutput.setOnClickListener(view -> {
// Request focus for the EditText
commandInput.requestFocus();
// Show the keyboard
InputMethodManager imm1 = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm1.showSoftInput(commandInput, InputMethodManager.SHOW_IMPLICIT);
forcusCommandInput();
});
// Configure the editor to handle the "Done" action on the soft keyboard
commandInput.setOnEditorActionListener((v, actionId, event) -> {
@ -78,21 +75,18 @@ public class TerminalBottomSheetDialog {
return false;
});
commandInput.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER)) {
activity.runOnUiThread(() -> appendTextAndScroll(commandInput.getText().toString() + "\n"));
executeShellCommand(commandInput.getText().toString());
commandInput.setText("");
activity.runOnUiThread(() -> {
commandInput.requestFocus(); // Request focus again
});
return true;
}
return false;
commandInput.setOnKeyListener((v, keyCode, event) -> {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
(keyCode == KeyEvent.KEYCODE_ENTER)) {
activity.runOnUiThread(() -> appendTextAndScroll(commandInput.getText().toString() + "\n"));
executeShellCommand(commandInput.getText().toString());
commandInput.setText("");
// Request focus again
activity.runOnUiThread(commandInput::requestFocus);
return true;
}
return false;
});
}
@ -106,9 +100,7 @@ public class TerminalBottomSheetDialog {
String username = null;
// Update the prompt on the UI thread
String finalUsername = username != null ? username : "root";
activity.runOnUiThread(() -> {
promptView.setText(finalUsername + "@localhost:~$ ");
});
activity.runOnUiThread(() -> promptView.setText(finalUsername + "@localhost:~$ "));
}).start();
}
@ -117,15 +109,19 @@ public class TerminalBottomSheetDialog {
ScrollView scrollView = view.findViewById(R.id.scrollView);
// Update the text
terminalOutput.append(textToAdd);
if (textToAdd.contains("@localhost:~$ clear")) {
isAllowAddToResultCommand = false;
terminalOutput.setText("");
} else {
if (isAllowAddToResultCommand) {
terminalOutput.append(textToAdd);
} else {
isAllowAddToResultCommand = true;
}
}
// Scroll to the bottom
scrollView.post(new Runnable() {
@Override
public void run() {
scrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
scrollView.post(() -> scrollView.fullScroll(ScrollView.FOCUS_DOWN));
}
private String getLocalIpAddress() {
@ -134,8 +130,8 @@ public class TerminalBottomSheetDialog {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && inetAddress.getHostAddress().toString().contains(".")) {
return inetAddress.getHostAddress().toString();
if (!inetAddress.isLoopbackAddress() && Objects.requireNonNull(inetAddress.getHostAddress()).contains(".")) {
return inetAddress.getHostAddress();
}
}
}
@ -150,47 +146,51 @@ public class TerminalBottomSheetDialog {
if (checkInstallation())
new Thread(() -> {
try {
// Setup the process builder to start PRoot with environmental variables and commands
activity.runOnUiThread(() -> {
if (terminalOutput.getVisibility() == View.GONE) terminalOutput.setVisibility(View.VISIBLE);
appendTextAndScroll("root@localhost:~$ " + userCommand + "\n");
inputContainer.setVisibility(View.GONE);
});
// Setup the qemuProcess builder to start PRoot with environmental variables and commands
ProcessBuilder processBuilder = new ProcessBuilder();
// Adjust these environment variables as necessary for your app
String filesDir = activity.getFilesDir().getAbsolutePath();
String nativeLibDir = activity.getApplicationInfo().nativeLibraryDir;
String filesDir = AppConfig.internalDataDirPath;
File tmpDir = new File(activity.getFilesDir(), "tmp");
File tmpDir = new File(filesDir, "usr/tmp");
// Setup environment for the PRoot process
// Setup environment for the PRoot qemuProcess
processBuilder.environment().put("PROOT_TMP_DIR", tmpDir.getAbsolutePath());
processBuilder.environment().put("PROOT_LOADER", nativeLibDir + "/libproot-loader.so");
processBuilder.environment().put("PROOT_LOADER_32", nativeLibDir + "/libproot-loader32.so");
processBuilder.environment().put("HOME", "/root");
processBuilder.environment().put("USER", "root");
processBuilder.environment().put("PATH", "/bin:/usr/bin:/sbin:/usr/sbin");
//processBuilder.environment().put("PATH", "/bin:/usr/bin:/sbin:/usr/sbin");
//processBuilder.environment().put("LD_LIBRARY_PATH", TermuxService.PREFIX_PATH + "/lib");
processBuilder.environment().put("TERM", "xterm-256color");
processBuilder.environment().put("TMPDIR", tmpDir.getAbsolutePath());
processBuilder.environment().put("SHELL", "/bin/sh");
processBuilder.environment().put("DISPLAY", getLocalIpAddress()+":1");
processBuilder.environment().put("PULSE_SERVER", "/run/pulse/native");
processBuilder.environment().put("XDG_RUNTIME_DIR", "/run");
processBuilder.environment().put("QEMU_AUDIO_DRV", "sdl");
processBuilder.environment().put("DISPLAY", ":0");
processBuilder.environment().put("PULSE_SERVER", "127.0.0.1");
processBuilder.environment().put("XDG_RUNTIME_DIR", "${TMPDIR}");
processBuilder.environment().put("SDL_VIDEODRIVER", "x11");
String[] prootCommand = {
nativeLibDir + "/libproot.so", // PRoot binary path
"--kill-on-exit",
"--link2symlink",
"-0",
"-r", filesDir + "/distro", // Path to the rootfs
"-b", "/dev",
"-b", "/proc",
"-b", "/sys",
"-b", "/sdcard",
"-b", "/storage",
"-b", "/data",
"-w", "/root",
"/bin/sh",
"--login"// The shell to execute inside PRoot
String[] prootCommand = {
TermuxService.PREFIX_PATH + "/bin/proot", // PRoot binary path
"--kill-on-exit",
"--link2symlink",
"-0",
"-r", filesDir + "/distro", // Path to the rootfs
"-b", "/dev",
"-b", "/proc",
"-b", "/sys",
"-b", AppConfig.internalDataDirPath + "distro/root:/dev/shm",
"-b", "/sdcard",
"-b", "/storage",
"-b", "/data",
"-b", AppConfig.internalDataDirPath + "usr/tmp:/tmp",
"-w", "/root",
"/bin/sh",
"--login"// The shell to execute inside PRoot
};
processBuilder.command(prootCommand);
@ -210,13 +210,21 @@ public class TerminalBottomSheetDialog {
String line;
while ((line = reader.readLine()) != null) {
final String outputLine = line;
activity.runOnUiThread(() -> appendTextAndScroll(outputLine + "\n"));
activity.runOnUiThread(() -> {
appendTextAndScroll(outputLine + "\n");
inputContainer.setVisibility(View.VISIBLE);
forcusCommandInput();
});
}
// Read any errors from the error stream
while ((line = errorReader.readLine()) != null) {
final String errorLine = line;
activity.runOnUiThread(() -> appendTextAndScroll(errorLine + "\n"));
activity.runOnUiThread(() -> {
appendTextAndScroll(errorLine + "\n");
inputContainer.setVisibility(View.VISIBLE);
forcusCommandInput();
});
}
// Clean up
@ -229,7 +237,11 @@ public class TerminalBottomSheetDialog {
} catch (IOException | InterruptedException e) {
// Handle exceptions by printing the stack trace in the terminal output
final String errorMessage = e.getMessage();
activity.runOnUiThread(() -> appendTextAndScroll("Error: " + errorMessage + "n"));
activity.runOnUiThread(() -> {
appendTextAndScroll("Error: " + errorMessage + "n");
inputContainer.setVisibility(View.VISIBLE);
forcusCommandInput();
});
}
}).start(); // Execute the command in a separate thread to prevent blocking the UI thread
else
@ -245,4 +257,12 @@ public class TerminalBottomSheetDialog {
File distro = new File(filesDir, "distro");
return distro.exists();
}
private void forcusCommandInput() {
commandInput.post(() -> {
commandInput.requestFocus();
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(commandInput, InputMethodManager.SHOW_IMPLICIT);
});
}
}

View file

@ -5,6 +5,6 @@
android:right="2dp"
android:bottom="2dp" />
<corners android:radius="2dp" />
<solid android:color="@color/cursor" />
<solid android:color="#fff" />
<size android:height="20dp" android:width="10dp" />
</shape>

View file

@ -53,6 +53,14 @@
android:textSize="16sp"
android:singleLine="true"/>
<ImageView
android:id="@+id/iv_openterminal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:src="@drawable/terminal_24px"
android:background="?attr/selectableItemBackgroundBorderless"/>
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"

View file

@ -3,72 +3,96 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:padding="18dp">
android:paddingHorizontal="8dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tiny_vterm"
style="@style/MaterialAlertDialog.Material3.Title.Text"/>
<!-- <TextView-->
<!-- android:id="@+id/title"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="@string/tiny_vterm"-->
<!-- style="@style/MaterialAlertDialog.Material3.Title.Text"/>-->
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:id="@+id/drag_handle"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<RelativeLayout
android:id="@+id/mainTerminal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/scrollView"
<com.google.android.material.card.MaterialCardView
style="@style/Widget.Material3.CardView.Filled"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:nestedScrollingEnabled="true"
android:padding="5dp">
<LinearLayout
app:cardBackgroundColor="#000">
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.vectras.vterm.view.ZoomableTextView
android:id="@+id/tvTerminalOutput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textSize="12sp" />
android:layout_height="match_parent"
android:fillViewport="true"
android:nestedScrollingEnabled="true"
android:padding="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:padding="8dp"
android:background="#000"
android:orientation="vertical">
<TextView
android:id="@+id/tvPrompt"
android:layout_width="wrap_content"
<com.vectras.vterm.view.ZoomableTextView
android:id="@+id/tvTerminalOutput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center_vertical"
android:text="user@localhost:~$ "
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textSize="12sp" />
android:visibility="gone"
android:textIsSelectable="true"
android:textSize="12sp"
android:textColor="#fff"
app:fontFamily="@font/jetbrainsmonoregular"
tools:visibility="visible"/>
<EditText
android:id="@+id/etCommandInput"
android:layout_width="0dp"
<LinearLayout
android:id="@+id/ln_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="true"
android:background="@android:color/transparent"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:imeOptions="actionDone"
android:inputType="text|textNoSuggestions"
android:textCursorDrawable="@drawable/cursor"
android:textSize="12sp" />
android:orientation="horizontal">
<TextView
android:id="@+id/tvPrompt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center_vertical"
android:text="user@localhost:~$ "
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textSize="12sp"
android:textColor="#fff"
app:fontFamily="@font/jetbrainsmonoregular" />
<EditText
android:id="@+id/etCommandInput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="true"
android:background="@android:color/transparent"
android:focusableInTouchMode="true"
android:gravity="center_vertical"
android:imeOptions="actionDone"
android:inputType="text|textNoSuggestions"
android:textCursorDrawable="@drawable/cursor"
android:textSize="12sp"
android:textColor="#fff"
app:fontFamily="@font/jetbrainsmonoregular" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
</ScrollView>
</com.google.android.material.card.MaterialCardView>
</RelativeLayout>
</LinearLayout>

View file

@ -3,7 +3,7 @@
<string name="app_name">Vectras VM</string>
<!--======================VECTRAS STRINGS====================-->
<string name="app_version" translatable="false">3.2.1</string>
<string name="app_version" translatable="false">3.2.2</string>
<string name="qemu_version" translatable="false">Stable</string>
<string name="str_home">Home</string>
<string name="str_logger">Logger</string>
@ -574,6 +574,7 @@
<string name="could_not_create_dir_to_save_cvbi_content">Something went wrong. Could not create directory to save cvbi file.</string>
<string name="quick_start">Quick start</string>
<string name="quick_start_description">To start up faster the next time you open Vectras VM if you don\'t remove it from multitasking.</string>
<string name="cpu_not_support_64_xfce">Your device\'s Android OS or CPU doesn\'t support 64-bit, which means you can\'t use this feature.</string>
<!--======================TERMUX STRINGS====================-->

View file

@ -5,11 +5,11 @@
"url": "https://github.com/xoureldeen/Vectras-VM-Android/releases/v2.9.5",
"Message": "<h2>v2.9.5-3dfx</h2><ul><li>Bring back 3dfx support.</li><li>Enhance app execution.</li><li>Added some linux programs in x11 display.</li><li>Added alpine linux (x11).</li><li>Russian language by <a href=\"https://github.com/OFGING\" target=\"_blank\">@OFGING</a></li></ul><br><br>New updates are live!",
"cancellable": true,
"versionCodeBeta":"24",
"versionNameBeta":"3.2.1",
"versionNameBetas":"v2.9.5.1-3dfx,v2.9.5.2-3dfx,v2.9.5.3-3dfx,v2.9.5.4-3dfx,v2.9.5.5-3dfx,v2.9.5.6-3dfx,v2.9.5.7-3dfx,v2.9.5.8-3dfx,v2.9.5.9-3dfx,v2.9.5.10-3dfx,v2.9.5.11-3dfx,v2.9.5.12-3dfx,v2.9.5.13-3dfx,3.0.0,3.1.0,3.2.1",
"versionCodeBeta":"25",
"versionNameBeta":"3.2.2",
"versionNameBetas":"v2.9.5.1-3dfx,v2.9.5.2-3dfx,v2.9.5.3-3dfx,v2.9.5.4-3dfx,v2.9.5.5-3dfx,v2.9.5.6-3dfx,v2.9.5.7-3dfx,v2.9.5.8-3dfx,v2.9.5.9-3dfx,v2.9.5.10-3dfx,v2.9.5.11-3dfx,v2.9.5.12-3dfx,v2.9.5.13-3dfx,3.0.0,3.1.0,3.2.1,3.2.2",
"sizeBeta": "50 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
"MessageBeta": "<h2>3.2.1</h2>Bugs fixed.",
"MessageBeta": "<h2>3.2.2</h2>Bugs fixed.",
"cancellableBeta": true
}