diff --git a/.idea/other.xml b/.idea/other.xml
deleted file mode 100644
index b45a6e0..0000000
--- a/.idea/other.xml
+++ /dev/null
@@ -1,340 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..16660f1
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 3cdd796..e37d87b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -12,8 +12,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
- versionCode 20
- versionName "v2.9.4-jamtart"
+ versionCode 21
+ versionName "v2.9.5-3dfx"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
diff --git a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java
index d7dd746..613c4f8 100644
--- a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java
+++ b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java
@@ -162,6 +162,8 @@ public class MainVNCActivity extends VncCanvasActivity {
ImageButton rightGameBtn = findViewById(R.id.rightGameBtn);
ImageButton enterGameBtn = findViewById(R.id.enterGameBtn);
qmpBtn = findViewById(R.id.btnQmp);
+ ImageButton appsBtn = findViewById(R.id.btnPrograms);
+ appsBtn.setVisibility(View.GONE);
upGameBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
diff --git a/app/src/main/java/com/vectras/vm/AboutActivity.java b/app/src/main/java/com/vectras/vm/AboutActivity.java
index fde0b0a..3e364e8 100644
--- a/app/src/main/java/com/vectras/vm/AboutActivity.java
+++ b/app/src/main/java/com/vectras/vm/AboutActivity.java
@@ -38,6 +38,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.vectras.vm.R;
+import com.vectras.vterm.Terminal;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@@ -46,7 +47,7 @@ import java.net.URL;
public class AboutActivity extends AppCompatActivity implements View.OnClickListener{
- Button btn_osl, btn_clog, btn_youtube, btn_github, btn_telegram, btn_instagram, btn_facebook;
+ Button btn_osl, btn_clog, btn_discord, btn_youtube, btn_github, btn_telegram, btn_instagram, btn_facebook;
String appInfo;
public String TAG = "AboutActivity";
@@ -66,6 +67,7 @@ public class AboutActivity extends AppCompatActivity implements View.OnClickList
btn_github = (Button) findViewById(R.id.btn_github);
btn_instagram = (Button) findViewById(R.id.btn_instagram);
btn_facebook = (Button) findViewById(R.id.btn_facebook);
+ btn_discord = (Button) findViewById(R.id.btn_discord);
btn_osl = (Button) findViewById(R.id.btn_osl);
btn_clog = (Button) findViewById(R.id.btn_changelog);
//onclicklistener
@@ -74,6 +76,7 @@ public class AboutActivity extends AppCompatActivity implements View.OnClickList
btn_youtube.setOnClickListener(this);
btn_instagram.setOnClickListener(this);
btn_facebook.setOnClickListener(this);
+ btn_discord.setOnClickListener(this);
btn_osl.setOnClickListener(this);
btn_clog.setOnClickListener(this);
@@ -138,6 +141,22 @@ public class AboutActivity extends AppCompatActivity implements View.OnClickList
GithubUserAdapter adapter = new GithubUserAdapter(this, usernames);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
+
+
+ TextView qemuVersion = findViewById(R.id.qemuVersion);
+
+ String command = "qemu-system-x86_64 --version";
+ new Terminal(this).extractQemuVersion(command, false, this, (output, errors) -> {
+ if (errors.isEmpty()) {
+ String versionStr = "Unknown";
+ if (output.equals("8.2.1"))
+ versionStr = output + " - 3dfx";
+ Log.d(TAG, "QEMU Version: " + versionStr);
+ qemuVersion.setText(versionStr);
+ } else {
+ Log.e(TAG, "Errors: " + errors);
+ }
+ });
}
@Override
@@ -152,6 +171,7 @@ public class AboutActivity extends AppCompatActivity implements View.OnClickList
public static final int GT = R.id.btn_github;
public static final int IG = R.id.btn_instagram;
public static final int FB = R.id.btn_facebook;
+ public static final int DD = R.id.btn_discord;
public static final int CL = R.id.btn_changelog;
public static final int OSL = R.id.btn_osl;
@Override
@@ -177,6 +197,11 @@ public class AboutActivity extends AppCompatActivity implements View.OnClickList
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(ig));
startActivity(i);
+ } else if (id == DD) {
+ String dd = "https://discord.gg/t8TACrKSk7";
+ Intent f = new Intent(Intent.ACTION_VIEW);
+ f.setData(Uri.parse(dd));
+ startActivity(f);
} else if (id == FB) {
String fb = AppConfig.vectrasWebsite + "community.html";
Intent f = new Intent(Intent.ACTION_VIEW);
diff --git a/app/src/main/java/com/vectras/vm/AppConfig.java b/app/src/main/java/com/vectras/vm/AppConfig.java
index 7037afd..fa266e4 100644
--- a/app/src/main/java/com/vectras/vm/AppConfig.java
+++ b/app/src/main/java/com/vectras/vm/AppConfig.java
@@ -74,4 +74,9 @@ public class AppConfig {
public static String vmFolder = maindirpath + "roms/";
public static String pendingCommand = "";
+ public static String neededPkgs = "tar dwm xfce4-terminal 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 sdl2 gtk+3.0 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";
+
}
diff --git a/app/src/main/java/com/vectras/vm/MainActivity.java b/app/src/main/java/com/vectras/vm/MainActivity.java
index b651b8d..e065046 100644
--- a/app/src/main/java/com/vectras/vm/MainActivity.java
+++ b/app/src/main/java/com/vectras/vm/MainActivity.java
@@ -7,6 +7,7 @@ import com.termux.app.TermuxService;
import static com.vectras.vm.VectrasApp.getApp;
import static com.vectras.vm.VectrasApp.getAppInfo;
+import static com.vectras.vm.utils.LibraryChecker.isPackageInstalled2;
import static com.vectras.vm.utils.UIUtils.UIAlert;
import android.app.ActivityManager;
@@ -82,6 +83,7 @@ import com.vectras.vm.adapter.LogsAdapter;
import com.vectras.vm.logger.VectrasStatus;
import com.vectras.vm.utils.AppUpdater;
import com.vectras.vm.utils.FileUtils;
+import com.vectras.vm.utils.LibraryChecker;
import com.vectras.vm.utils.UIUtils;
import com.vectras.vterm.Terminal;
@@ -99,8 +101,10 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import com.google.gson.Gson;
@@ -152,6 +156,8 @@ public class MainActivity extends AppCompatActivity {
if (MainSettingsManager.getPromptUpdateVersion(activity))
updateApp(false);
+ new LibraryChecker(activity).checkMissingLibraries(activity);
+
romsLayout = findViewById(R.id.romsLayout);
SwipeRefreshLayout refreshRoms = findViewById(R.id.refreshRoms);
@@ -174,7 +180,7 @@ public class MainActivity extends AppCompatActivity {
MainService.stopService();
Terminal vterm = new Terminal(activity);
- vterm.executeShellCommand("killall qemu-system-*", false, activity);
+ vterm.executeShellCommand2("killall qemu-system-*", false, activity);
extVncLayout.setVisibility(View.GONE);
appbar.setExpanded(false);
@@ -249,12 +255,12 @@ public class MainActivity extends AppCompatActivity {
}
if (id == R.id.navigation_item_help) {
String tw = AppConfig.vectrasHelp;
- Intent w = new Intent(Intent.ACTION_VIEW);
+ Intent w = new Intent(ACTION_VIEW);
w.setData(Uri.parse(tw));
startActivity(w);
} else if (id == R.id.navigation_item_website) {
String tw = AppConfig.vectrasWebsite;
- Intent w = new Intent(Intent.ACTION_VIEW);
+ Intent w = new Intent(ACTION_VIEW);
w.setData(Uri.parse(tw));
startActivity(w);
} else if (id == R.id.navigation_item_import_iso) {
@@ -434,6 +440,52 @@ public class MainActivity extends AppCompatActivity {
startActivityForResult(intent, 1006);
}
+ } else if (id == R.id.navigation_item_desktop) {
+ if (SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle("X11 Feature Not Supported")
+ .setMessage("The X11 feature is currently not supported on Android 14 and above. Please use a device with Android 13 or below for X11 functionality.")
+ .setPositiveButton("OK", (dialog, which) -> dialog.dismiss())
+ .create()
+ .show();
+ } else {
+ // XFCE4 meta-package
+ String xfce4Package = "xfce4";
+
+ // Check if XFCE4 is installed
+ isPackageInstalled2(activity, xfce4Package, (output, errors) -> {
+ boolean isInstalled = false;
+
+ // Check if the package exists in the installed packages output
+ if (output != null) {
+ Set installedPackages = new HashSet<>();
+ for (String installedPackage : output.split("\n")) {
+ installedPackages.add(installedPackage.trim());
+ }
+
+ isInstalled = installedPackages.contains(xfce4Package.trim());
+ }
+
+ // If not installed, show a dialog to install it
+ if (!isInstalled) {
+ new AlertDialog.Builder(activity, R.style.MainDialogTheme)
+ .setTitle("Install XFCE4")
+ .setMessage("XFCE4 is not installed. Would you like to install it?")
+ .setCancelable(false)
+ .setPositiveButton("Install", (dialog, which) -> {
+ String installCommand = "apk add " + xfce4Package;
+ new Terminal(activity).executeShellCommand(installCommand, true, activity);
+ })
+ .setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss())
+ .show();
+ } else {
+ new Terminal(activity).executeShellCommand2("killall xfce4-session", false, activity);
+ startActivity(new Intent(activity, X11Activity.class));
+ new Terminal(activity).executeShellCommand2("xfce4-session", false, MainActivity.activity);
+ }
+ });
+
+ }
} else if (id == R.id.navigation_item_terminal) {
/*com.vectras.vterm.TerminalBottomSheetDialog VTERM = new com.vectras.vterm.TerminalBottomSheetDialog(activity);
VTERM.showVterm();*/
@@ -448,8 +500,8 @@ public class MainActivity extends AppCompatActivity {
Timer _timer = new Timer();
TimerTask t;
- LinearLayoutManager layoutManager = new LinearLayoutManager(VectrasApp.getApp());
- LogsAdapter mLogAdapter = new LogsAdapter(layoutManager, VectrasApp.getApp());
+ LinearLayoutManager layoutManager = new LinearLayoutManager(getApp());
+ LogsAdapter mLogAdapter = new LogsAdapter(layoutManager, getApp());
RecyclerView logList = (RecyclerView) view.findViewById(R.id.recyclerLog);
logList.setAdapter(mLogAdapter);
logList.setLayoutManager(layoutManager);
@@ -495,7 +547,7 @@ public class MainActivity extends AppCompatActivity {
startActivity(new Intent(activity, DataExplorerActivity.class));
} else if (id == R.id.navigation_item_donate) {
String tw = "https://www.patreon.com/VectrasTeam";
- Intent w = new Intent(Intent.ACTION_VIEW);
+ Intent w = new Intent(ACTION_VIEW);
w.setData(Uri.parse(tw));
startActivity(w);
} else if (id== R.id.setup_sound) {
@@ -550,7 +602,7 @@ public class MainActivity extends AppCompatActivity {
VectrasApp.killallqemuprocesses(getApplicationContext());
VectrasApp.deleteDirectory(AppConfig.vmFolder);
VectrasApp.deleteDirectory(AppConfig.recyclebin);
- File vDir = new File(com.vectras.vm.AppConfig.maindirpath);
+ File vDir = new File(AppConfig.maindirpath);
vDir.mkdirs();
errorjsondialog();
}
@@ -618,7 +670,7 @@ public class MainActivity extends AppCompatActivity {
}
});
if (Config.debug)
- UIUtils.UIAlert(activity, getString(R.string.debug_testing_build_5), getString(R.string.welcome_to_debug_build_of_vectras_vm_br) +
+ UIAlert(activity, getString(R.string.debug_testing_build_5), getString(R.string.welcome_to_debug_build_of_vectras_vm_br) +
getString(R.string.this_version_unstable_and_has_alot_of_bugs_br) +
getString(R.string.don_t_forget_to_tell_us_on_github_issues_or_telegram_bot_br) +
getString(R.string.a_href_https_t_me_vectras_protect_bot_telegram_report_bot_a_br) +
@@ -632,7 +684,7 @@ public class MainActivity extends AppCompatActivity {
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.join), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
String tg = "https://t.me/vectras_os";
- Intent f = new Intent(Intent.ACTION_VIEW);
+ Intent f = new Intent(ACTION_VIEW);
f.setData(Uri.parse(tg));
startActivity(f);
return;
@@ -739,6 +791,21 @@ public class MainActivity extends AppCompatActivity {
_timer.scheduleAtFixedRate(t, (int) (0), (int) (1000));
ShellExecutor shellExec = new ShellExecutor();
shellExec.exec(TermuxService.PREFIX_PATH + "/bin/termux-x11 :0");
+
+ TextView qemuVersion = findViewById(R.id.qemuVersion);
+
+ String command = "qemu-system-x86_64 --version";
+ new Terminal(activity).extractQemuVersion(command, false, activity, (output, errors) -> {
+ if (errors.isEmpty()) {
+ String versionStr = "Unknown";
+ if (output.equals("8.2.1"))
+ versionStr = output + " - 3dfx";
+ Log.d(TAG, "QEMU Version: " + versionStr);
+ qemuVersion.setText(versionStr);
+ } else {
+ Log.e(TAG, "Errors: " + errors);
+ }
+ });
}
@Override
@@ -778,7 +845,7 @@ public class MainActivity extends AppCompatActivity {
.setNegativeButton("Update", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
- startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(obj.getString("url"))));
+ startActivity(new Intent(ACTION_VIEW, Uri.parse(obj.getString("url"))));
} catch (JSONException e) {
}
@@ -1378,7 +1445,7 @@ public class MainActivity extends AppCompatActivity {
}
public String getPath(Uri uri) {
- return com.vectras.vm.utils.FileUtils.getPath(this, uri);
+ return FileUtils.getPath(this, uri);
}
private void errorjsondialog() {
diff --git a/app/src/main/java/com/vectras/vm/MainService.java b/app/src/main/java/com/vectras/vm/MainService.java
index dda3d1b..6d9a343 100644
--- a/app/src/main/java/com/vectras/vm/MainService.java
+++ b/app/src/main/java/com/vectras/vm/MainService.java
@@ -13,6 +13,7 @@ import android.util.Log;
import androidx.core.app.NotificationCompat;
import com.vectras.qemu.MainVNCActivity;
+import com.vectras.vm.core.PulseAudio;
import com.vectras.vterm.Terminal;
import java.io.File;
@@ -48,10 +49,8 @@ public class MainService extends Service {
if (service != null) {
String filesDir = MainActivity.activity.getFilesDir().getAbsolutePath();
Terminal vterm = new Terminal(this);
- //vterm.executeShellCommand("chmod 770 /run/pulse -R");
- //vterm.executeShellCommand("pulseaudio --system --disallow-exit --disallow-module-loading --daemonize --log-level=debug --log-time=1");
- vterm.executeShellCommand("dwm", false, MainActivity.activity);
- vterm.executeShellCommand(env, true, MainActivity.activity);
+ vterm.executeShellCommand2("dwm", false, MainActivity.activity);
+ vterm.executeShellCommand2(env, false, MainActivity.activity);
}
} else
Log.e(TAG, "env is null");
diff --git a/app/src/main/java/com/vectras/vm/SetupQemuActivity.java b/app/src/main/java/com/vectras/vm/SetupQemuActivity.java
index a0e45a3..cc01bdb 100644
--- a/app/src/main/java/com/vectras/vm/SetupQemuActivity.java
+++ b/app/src/main/java/com/vectras/vm/SetupQemuActivity.java
@@ -609,9 +609,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
" echo \"Starting setup...\";" +
" apk update;" +
" echo \"Installing packages...\";" +
- " apk add tar dwm 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 sdl2 gtk+3.0 fuse libpulse libseccomp jack pipewire liburing mesa-dri-gallium;" +
+ " apk add " + AppConfig.neededPkgs + ";" +
" echo \"Installing Qemu...\";" +
" tar -xzvf " + tarPath + " -C /;" +
" rm " + tarPath + ";" +
@@ -633,9 +631,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
" echo \"Starting setup...\";" +
" apk update;" +
" echo \"Installing packages...\";" +
- " apk add tar dwm 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 sdl2 gtk+3.0 fuse libpulse libseccomp jack pipewire liburing;" +
+ " apk add " + AppConfig.neededPkgs + ";" +
//" tar -xzvf " + tarPath + " -C /;" +
" echo \"Installing Qemu...\";" +
" apk add qemu-system-x86_64 qemu-system-ppc qemu-system-i386 qemu-system-aarch64 qemu-pr-helper qemu-img qemu-audio-sdl pulseaudio mesa-dri-gallium;" +
@@ -657,9 +653,7 @@ public class SetupQemuActivity extends AppCompatActivity implements View.OnClick
" echo \"Starting setup...\";" +
" apk update;" +
" echo \"Installing packages...\";" +
- " apk add tar dwm 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 sdl2 gtk+3.0 fuse libpulse libseccomp jack pipewire liburing mesa-dri-gallium;" +
+ " apk add " + AppConfig.neededPkgs + ";" +
" echo \"Downloading Qemu...\";" +
" curl -o setup.tar.gz -L " + bootstrapfilelink + ";" +
" echo \"Installing Qemu...\";" +
diff --git a/app/src/main/java/com/vectras/vm/VectrasApp.java b/app/src/main/java/com/vectras/vm/VectrasApp.java
index 8c2f550..b068937 100644
--- a/app/src/main/java/com/vectras/vm/VectrasApp.java
+++ b/app/src/main/java/com/vectras/vm/VectrasApp.java
@@ -620,10 +620,10 @@ public class VectrasApp extends Application {
public static void killallqemuprocesses(Context context) {
Terminal vterm = new Terminal(context);
- vterm.executeShellCommand("killall -9 qemu-system-i386", false, MainActivity.activity);
- vterm.executeShellCommand("killall -9 qemu-system-x86_64", false, MainActivity.activity);
- vterm.executeShellCommand("killall -9 qemu-system-aarch64", false, MainActivity.activity);
- vterm.executeShellCommand("killall -9 qemu-system-ppc", false, MainActivity.activity);
+ vterm.executeShellCommand2("killall -9 qemu-system-i386", false, MainActivity.activity);
+ vterm.executeShellCommand2("killall -9 qemu-system-x86_64", false, MainActivity.activity);
+ vterm.executeShellCommand2("killall -9 qemu-system-aarch64", false, MainActivity.activity);
+ vterm.executeShellCommand2("killall -9 qemu-system-ppc", false, MainActivity.activity);
}
public static void killcurrentqemuprocess(Context context) {
@@ -643,7 +643,7 @@ public class VectrasApp extends Application {
env += "qemu-system-x86_64";
break;
}
- vterm.executeShellCommand(env, false, MainActivity.activity);
+ vterm.executeShellCommand2(env, false, MainActivity.activity);
}
public static boolean isAppInstalled(String packagename, Context context) {
@@ -679,7 +679,7 @@ public class VectrasApp extends Application {
public static boolean isQemuRunning() {
Terminal vterm = new Terminal(MainActivity.activity);
- vterm.executeShellCommand("ps -e", false, MainActivity.activity);
+ vterm.executeShellCommand2("ps -e", false, MainActivity.activity);
if (TerminalOutput.contains("qemu-system")) {
Log.d("VectrasApp.isQemuRunning", "Yes");
return true;
@@ -691,7 +691,7 @@ public class VectrasApp extends Application {
public static boolean isThisVMRunning(String intemExtra, String itemPath) {
Terminal vterm = new Terminal(MainActivity.activity);
- vterm.executeShellCommand("ps -e", false, MainActivity.activity);
+ vterm.executeShellCommand2("ps -e", false, MainActivity.activity);
if (TerminalOutput.contains(intemExtra) && TerminalOutput.contains(itemPath)) {
Log.d("VectrasApp.isThisVMRunning", "Yes");
return true;
diff --git a/app/src/main/java/com/vectras/vm/x11/X11Activity.java b/app/src/main/java/com/vectras/vm/x11/X11Activity.java
index dcba615..08b9048 100644
--- a/app/src/main/java/com/vectras/vm/x11/X11Activity.java
+++ b/app/src/main/java/com/vectras/vm/x11/X11Activity.java
@@ -11,6 +11,8 @@ import android.os.Looper;
import static android.view.InputDevice.KEYBOARD_TYPE_ALPHABETIC;
import static android.view.KeyEvent.*;
import static android.view.WindowManager.LayoutParams.*;
+
+import android.view.WindowManager;
import android.widget.TextView;
import androidx.fragment.app.FragmentTransaction;
import com.vectras.qemu.MainSettingsManager;
@@ -80,6 +82,7 @@ import com.vectras.vm.x11.utils.KeyInterceptor;
import com.vectras.vm.x11.utils.TermuxX11ExtraKeys;
import com.vectras.vm.x11.utils.X11ToolbarViewPager;
import com.vectras.vm.R;
+import com.vectras.vterm.Terminal;
import java.util.Map;
import java.util.Objects;
@@ -394,6 +397,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
ImageButton leftGameBtn = findViewById(R.id.leftGameBtn);
ImageButton rightGameBtn = findViewById(R.id.rightGameBtn);
ImageButton enterGameBtn = findViewById(R.id.enterGameBtn);
+ ImageButton appsBtn = findViewById(R.id.btnPrograms);
qmpBtn = findViewById(R.id.btnQmp);
@@ -415,6 +419,38 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
});
+ appsBtn.setOnClickListener(v -> {
+ Dialog dialog = new Dialog(activity);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ dialog.setContentView(R.layout.dialog_programs);
+ dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
+
+ WindowManager.LayoutParams layoutParams = dialog.getWindow().getAttributes();
+ layoutParams.alpha = 1f;
+ dialog.getWindow().setAttributes(layoutParams);
+
+ ImageButton termBtn = dialog.findViewById(R.id.btnTerminal);
+ ImageButton vkCubeBtn = dialog.findViewById(R.id.btnVkCube);
+ ImageButton glxGearsBtn = dialog.findViewById(R.id.btnGlxGears);
+
+ termBtn.setOnClickListener(v1 -> {
+ new Terminal(activity).executeShellCommand2("xfce4-terminal", false, activity);
+ dialog.dismiss();
+ });
+
+ glxGearsBtn.setOnClickListener(v1 -> {
+ new Terminal(activity).executeShellCommand2("glxgears", false, activity);
+ dialog.dismiss();
+ });
+
+ vkCubeBtn.setOnClickListener(v1 -> {
+ new Terminal(activity).executeShellCommand2("vkcube", false, activity);
+ dialog.dismiss();
+ });
+
+ dialog.show();
+ });
+
upGameBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
@@ -1424,7 +1460,13 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
@Override
- public void onBackPressed() {}
+ public void onBackPressed() {
+ if (findViewById(R.id.mainControl).getVisibility() == View.GONE) {
+ findViewById(R.id.mainControl).setVisibility(View.VISIBLE);
+ } else if (findViewById(R.id.mainControl).getVisibility() == View.VISIBLE) {
+ findViewById(R.id.mainControl).setVisibility(View.GONE);
+ }
+ }
public static boolean hasPipPermission(@NonNull Context context) {
AppOpsManager appOpsManager =
diff --git a/app/src/main/java/com/vectras/vterm/Terminal.java b/app/src/main/java/com/vectras/vterm/Terminal.java
index feb951a..6fa4089 100644
--- a/app/src/main/java/com/vectras/vterm/Terminal.java
+++ b/app/src/main/java/com/vectras/vterm/Terminal.java
@@ -1,6 +1,7 @@
package com.vectras.vterm;
import android.app.Activity;
+import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -12,6 +13,7 @@ import android.util.Log;
import androidx.appcompat.app.AlertDialog;
import com.termux.app.TermuxService;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -23,6 +25,8 @@ import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.qemu.MainVNCActivity;
@@ -70,7 +74,7 @@ public class Terminal {
AlertDialog dialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
dialog.setTitle("Execution Result");
dialog.setMessage(message);
- //.setPositiveButton("OK", (dialogInterface, i) -> dialogInterface.dismiss())
+ //.setPositiveButton("OK", (dialogInterface, i) -> dialogInterface.dismiss())
dialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//killQemuProcess();
@@ -89,6 +93,100 @@ public class Terminal {
StringBuilder errors = new StringBuilder();
Log.d(TAG, userCommand);
com.vectras.vm.logger.VectrasStatus.logError("VTERM: >" + userCommand + "");
+
+ // Show ProgressDialog
+ ProgressDialog progressDialog = new ProgressDialog(dialogActivity);
+ progressDialog.setMessage("Executing command, please wait...");
+ progressDialog.setCancelable(false);
+ progressDialog.show();
+
+ new Thread(() -> {
+ try {
+ ProcessBuilder processBuilder = new ProcessBuilder();
+
+ String filesDir = Objects.requireNonNull(context.getFilesDir().getAbsolutePath());
+ File tmpDir = new File(Objects.requireNonNull(context.getFilesDir()), "usr/tmp");
+
+ processBuilder.environment().put("PROOT_TMP_DIR", tmpDir.getAbsolutePath());
+ processBuilder.environment().put("HOME", "/root");
+ processBuilder.environment().put("USER", user);
+ processBuilder.environment().put("TERM", "xterm-256color");
+ processBuilder.environment().put("TMPDIR", tmpDir.getAbsolutePath());
+ processBuilder.environment().put("SHELL", "/bin/sh");
+ processBuilder.environment().put("DISPLAY", DISPLAY);
+ processBuilder.environment().put("PULSE_SERVER", "127.0.0.1");
+
+ String[] prootCommand = {
+ TermuxService.PREFIX_PATH + "/bin/proot",
+ "--kill-on-exit",
+ "--link2symlink",
+ "-0",
+ "-r", filesDir + "/distro",
+ "-b", "/dev",
+ "-b", "/proc",
+ "-b", "/sys",
+ "-b", "/data/data/com.vectras.vm/files/distro/root:/dev/shm",
+ "-b", "/sdcard",
+ "-b", "/storage",
+ "-b", "/data",
+ "-b", "/data/data/com.vectras.vm/files/usr/tmp:/tmp",
+ "-w", "/root",
+ "/bin/sh",
+ "--login"
+ };
+
+ 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("VTERM: >" + line + "");
+ output.append(line).append("\n");
+ }
+
+ while ((line = errorReader.readLine()) != null) {
+ Log.w(TAG, line);
+ com.vectras.vm.logger.VectrasStatus.logError("VTERM ERROR: >" + line + "");
+ 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) {
+ progressDialog.dismiss(); // Dismiss ProgressDialog
+ output.append(e.getMessage());
+ errors.append(Log.getStackTraceString(e));
+ } finally {
+ new Handler(Looper.getMainLooper()).post(() -> {
+ progressDialog.dismiss(); // Dismiss ProgressDialog
+ VectrasApp.TerminalOutput = output.toString();
+ if (showResultDialog) {
+ String finalOutput = output.toString();
+ String finalErrors = errors.toString();
+ showDialog(finalOutput.isEmpty() ? finalErrors : finalOutput.replace("read interrupted", "Done!"), dialogActivity, userCommand);
+ }
+ });
+ }
+ }).start();
+ }
+
+ public void executeShellCommand2(String userCommand, boolean showResultDialog, Activity dialogActivity) {
+ StringBuilder output = new StringBuilder();
+ StringBuilder errors = new StringBuilder();
+ Log.d(TAG, userCommand);
+ com.vectras.vm.logger.VectrasStatus.logError("VTERM: >" + userCommand + "");
new Thread(() -> {
try {
// Setup the qemuProcess builder to start PRoot with environmental variables and commands
@@ -195,6 +293,212 @@ public class Terminal {
}).start();
}
+ public void extractQemuVersion(String userCommand, boolean showResultDialog, Activity dialogActivity, CommandCallback callback) {
+ StringBuilder output = new StringBuilder();
+ StringBuilder errors = new StringBuilder();
+ Log.d(TAG, userCommand);
+ com.vectras.vm.logger.VectrasStatus.logError("VTERM: >" + userCommand + "");
+
+ new Thread(() -> {
+ try {
+ // Process setup (same as your original code)
+ ProcessBuilder processBuilder = new ProcessBuilder();
+
+ String filesDir = Objects.requireNonNull(context.getFilesDir().getAbsolutePath());
+ File tmpDir = new File(Objects.requireNonNull(context.getFilesDir()), "usr/tmp");
+
+ processBuilder.environment().put("PROOT_TMP_DIR", tmpDir.getAbsolutePath());
+ processBuilder.environment().put("HOME", "/root");
+ processBuilder.environment().put("USER", user);
+ processBuilder.environment().put("TERM", "xterm-256color");
+ processBuilder.environment().put("TMPDIR", tmpDir.getAbsolutePath());
+ processBuilder.environment().put("SHELL", "/bin/sh");
+ processBuilder.environment().put("DISPLAY", DISPLAY);
+ processBuilder.environment().put("PULSE_SERVER", "127.0.0.1");
+ processBuilder.environment().put("XDG_RUNTIME_DIR", "${TMPDIR}");
+ processBuilder.environment().put("SDL_VIDEODRIVER", "x11");
+
+ String[] prootCommand = {
+ TermuxService.PREFIX_PATH + "/bin/proot",
+ "--kill-on-exit",
+ "--link2symlink",
+ "-0",
+ "-r", filesDir + "/distro",
+ "-b", "/dev",
+ "-b", "/proc",
+ "-b", "/sys",
+ "-b", "/data/data/com.vectras.vm/files/distro/root:/dev/shm",
+ "-b", "/sdcard",
+ "-b", "/storage",
+ "-b", "/data",
+ "-b", "/data/data/com.vectras.vm/files/usr/tmp:/tmp",
+ "-w", "/root",
+ "/bin/sh",
+ "--login"
+ };
+
+ 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) {
+ output.append(line).append("\n");
+ }
+
+ while ((line = errorReader.readLine()) != null) {
+ errors.append(line).append("\n");
+ }
+
+ reader.close();
+ errorReader.close();
+
+ qemuProcess.waitFor();
+
+ } catch (IOException | InterruptedException e) {
+ output.append(e.getMessage());
+ errors.append(Log.getStackTraceString(e));
+ } finally {
+ String finalOutput = output.toString();
+ String finalErrors = errors.toString();
+
+ // Extract version using regex
+ String version = extractVersion(finalOutput);
+
+ // Run callback on main thread
+ new Handler(Looper.getMainLooper()).post(() -> {
+ if (callback != null) {
+ callback.onCommandCompleted(version != null ? version : finalOutput, finalErrors);
+ }
+ if (showResultDialog) {
+ showDialog(finalOutput.isEmpty() ? finalErrors : finalOutput, dialogActivity, userCommand);
+ }
+ });
+ }
+ }).start();
+ }
+
+ private String extractVersion(String output) {
+ String regex = "QEMU emulator version ([\\d.]+)";
+ Pattern pattern = Pattern.compile(regex);
+ Matcher matcher = pattern.matcher(output);
+ if (matcher.find()) {
+ return matcher.group(1); // Return the version number
+ }
+ return null;
+ }
+
+ public interface CommandCallback {
+ void onCommandCompleted(String output, String errors);
+ }
+
+ public String executeShellCommand(String userCommand, Activity dialogActivity, CommandCallback callback) {
+ StringBuilder output = new StringBuilder();
+ StringBuilder errors = new StringBuilder();
+ Log.d(TAG, userCommand);
+ com.vectras.vm.logger.VectrasStatus.logError("VTERM: >" + userCommand + "");
+
+ // Show ProgressDialog on the main thread
+ ProgressDialog progressDialog = new ProgressDialog(dialogActivity);
+ progressDialog.setMessage("Executing command, please wait...");
+ progressDialog.setCancelable(false);
+
+ // Make sure to show the dialog on the main thread
+ new Handler(Looper.getMainLooper()).post(() -> progressDialog.show());
+
+ new Thread(() -> {
+ try {
+ ProcessBuilder processBuilder = new ProcessBuilder();
+
+ String filesDir = Objects.requireNonNull(context.getFilesDir().getAbsolutePath());
+ File tmpDir = new File(Objects.requireNonNull(context.getFilesDir()), "usr/tmp");
+
+ processBuilder.environment().put("PROOT_TMP_DIR", tmpDir.getAbsolutePath());
+ processBuilder.environment().put("HOME", "/root");
+ processBuilder.environment().put("USER", user);
+ processBuilder.environment().put("TERM", "xterm-256color");
+ processBuilder.environment().put("TMPDIR", tmpDir.getAbsolutePath());
+ processBuilder.environment().put("SHELL", "/bin/sh");
+ processBuilder.environment().put("DISPLAY", DISPLAY);
+ processBuilder.environment().put("PULSE_SERVER", "127.0.0.1");
+
+ String[] prootCommand = {
+ TermuxService.PREFIX_PATH + "/bin/proot",
+ "--kill-on-exit",
+ "--link2symlink",
+ "-0",
+ "-r", filesDir + "/distro",
+ "-b", "/dev",
+ "-b", "/proc",
+ "-b", "/sys",
+ "-b", "/data/data/com.vectras.vm/files/distro/root:/dev/shm",
+ "-b", "/sdcard",
+ "-b", "/storage",
+ "-b", "/data",
+ "-b", "/data/data/com.vectras.vm/files/usr/tmp:/tmp",
+ "-w", "/root",
+ "/bin/sh",
+ "--login"
+ };
+
+ 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("VTERM: >" + line + "");
+ output.append(line).append("\n");
+ }
+
+ while ((line = errorReader.readLine()) != null) {
+ Log.w(TAG, line);
+ com.vectras.vm.logger.VectrasStatus.logError("VTERM ERROR: >" + line + "");
+ 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());
+ 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));
+ }
+ }).start();
+
+ 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");
@@ -215,4 +519,27 @@ public class Terminal {
Log.d(TAG, "QEMU process was null.");
}
}
+
+ /**
+ * Checks if a package is installed using `apk info`.
+ *
+ * @param packageName The name of the package to check.
+ * @return True if the package is installed, otherwise false.
+ */
+ private static boolean isPackageInstalled(String packageName) {
+ try {
+ Process process = new ProcessBuilder("apk", "info", packageName).start();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.equals(packageName)) {
+ return true;
+ }
+ }
+ process.waitFor();
+ } catch (Exception e) {
+ Log.e(TAG, "Error checking package: " + packageName, e);
+ }
+ return false;
+ }
}
diff --git a/app/src/main/res/layout/content_about.xml b/app/src/main/res/layout/content_about.xml
index bd4ff8a..12eb2fe 100644
--- a/app/src/main/res/layout/content_about.xml
+++ b/app/src/main/res/layout/content_about.xml
@@ -23,8 +23,8 @@
+ android:gravity="center"
+ android:orientation="vertical">
@@ -91,6 +91,29 @@
+
+
+
+
+
+
+
@@ -107,80 +130,87 @@
-
+
+
+
-
-
-
-
+ android:orientation="horizontal">
-
+
-
+
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/controls_fragment.xml b/app/src/main/res/layout/controls_fragment.xml
index 9be3ec1..84b4414 100644
--- a/app/src/main/res/layout/controls_fragment.xml
+++ b/app/src/main/res/layout/controls_fragment.xml
@@ -3,7 +3,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:alpha="0.5">
+ android:alpha="0.5"
+ android:layoutDirection="ltr">
+ android:translationY="-6dp"
+ android:layoutDirection="ltr">
+
+
+ app:layout_constraintLeft_toLeftOf="parent"
+ android:layoutDirection="ltr">
+ android:orientation="vertical"
+ android:layoutDirection="ltr">
diff --git a/app/src/main/res/layout/game_controls.xml b/app/src/main/res/layout/game_controls.xml
index 9dab1df..9de4f32 100644
--- a/app/src/main/res/layout/game_controls.xml
+++ b/app/src/main/res/layout/game_controls.xml
@@ -16,7 +16,8 @@
+ android:orientation="vertical"
+ android:layoutDirection="ltr">
+ app:layout_constraintRight_toRightOf="parent"
+ android:layoutDirection="ltr">
+
-
+
- English
- العربية
- 中文
+ - русский
- en
- ar
- zh
+ - ru
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b4da3b4..9e6e19b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -3,7 +3,7 @@
Vectras VM
- v2.9.4 (Jam tart)
+ v2.9.5 (3dfx)
Stable
Home
Logger
@@ -567,4 +567,6 @@
vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim.
RomInfo
+ Alpine Xfce4
+ Discord
diff --git a/web/data/UpdateConfig.json b/web/data/UpdateConfig.json
index f274825..06f7935 100644
--- a/web/data/UpdateConfig.json
+++ b/web/data/UpdateConfig.json
@@ -1,8 +1,8 @@
{
- "versionCode":"20",
- "versionName":"v2.9.4,v2.9.4-jellybean,v2.9.4-lemonmeringuepie,v2.9.4-newyorkcheesecake,v2.9.4-oatmealcookie,v2.9.4-apricottart,v2.9.4-biscuit,v2.9.4-croissant,v2.9.4-danishpastry,v2.9.4-ecclescake,v2.9.4-frenchtoast,v2.9.4-genoise,v2.9.4-honeycake,v2.9.4-italianmeringue,v2.9.4-jamtart",
+ "versionCode":"21",
+ "versionName":"v2.9.5",
"size": "60 MB",
- "url": "https://github.com/xoureldeen/Vectras-VM-Android/releases/v2.9.4",
- "Message": "
v2.9.4
- Now you can change locale in app settings.
- Now you can access x11 settings.
- New button to set full screen in x11 display.
- Enhanced UI.
- Chinese language by @adk23333
New updates are live!",
+ "url": "https://github.com/xoureldeen/Vectras-VM-Android/releases/v2.9.5",
+ "Message": "v2.9.5-3dfx
- Bring back 3dfx support.
- Enhance app execution.
- Added some linux programs in x11 display.
- Russian language by @OFGING
New updates are live!",
"cancellable": true
}