From 67d8f2ce1237cbd355dc40a63dd210aa60ff4bf9 Mon Sep 17 00:00:00 2001 From: An Bui <91354810+AnBui2004@users.noreply.github.com> Date: Fri, 22 May 2026 23:42:23 +0700 Subject: [PATCH] 4.2.0 - Added high-quality image option for VNC. - Added option to completely hide control buttons on the VNC screen. - Added battery emulation. - Added secondary optical drive. - Improved the virtual machine bootloader. - Fixed the issue where the socket file was not cleaned up after the virtual machine shut down. - Fixed other bugs. --- app/build.gradle | 4 +- app/src/main/assets/roms/battery.asl | 78 ++++++++ .../java/android/androidVNC/VncCanvas.java | 5 + .../main/java/com/vectras/qemu/Config.java | 4 + .../com/vectras/qemu/MainSettingsManager.java | 12 ++ .../com/vectras/qemu/MainVNCActivity.java | 68 ++++--- .../main/java/com/vectras/vm/AppConfig.java | 10 +- .../com/vectras/vm/ExportRomActivity.java | 8 + .../Fragment/ControlersOptionsFragment.java | 74 +++---- .../java/com/vectras/vm/SplashActivity.java | 2 + app/src/main/java/com/vectras/vm/StartVM.java | 72 +++---- .../vectras/vm/creator/VMCreatorActivity.java | 65 +++++-- .../com/vectras/vm/main/core/MainStartVM.java | 183 ++++++++++-------- .../vectras/vm/main/core/PendingCommand.java | 5 +- .../vectras/vm/main/core/StartVmDialog.java | 146 ++++++++++++++ .../com/vectras/vm/main/vms/DataMainRoms.java | 4 + .../vectras/vm/main/vms/VmsHomeAdapter.java | 7 +- .../vm/manager/BatteryEmulatorManager.java | 65 +++++++ .../com/vectras/vm/manager/QemuManager.java | 6 + .../com/vectras/vm/manager/VmFileManager.java | 12 ++ .../vm/settings/VNCSettingsActivity.java | 5 + .../vm/setupwizard/SetupFeatureCore.java | 14 ++ .../com/vectras/vm/utils/DeviceUtils.java | 25 +++ .../main/res/drawable/hide_source_24px.xml | 10 + .../main/res/layout/activity_vm_creator.xml | 38 +++- .../main/res/layout/activity_vnc_settings.xml | 42 ++++ app/src/main/res/layout/controls_fragment.xml | 1 + app/src/main/res/layout/dialog_start_vm.xml | 1 + .../layout/fragment_controlers_options.xml | 86 +++++--- app/src/main/res/values-vi/strings.xml | 6 + app/src/main/res/values/strings.xml | 6 + web/data/UpdateConfig.json | 12 +- 32 files changed, 825 insertions(+), 251 deletions(-) create mode 100644 app/src/main/assets/roms/battery.asl create mode 100644 app/src/main/java/com/vectras/vm/main/core/StartVmDialog.java create mode 100644 app/src/main/java/com/vectras/vm/manager/BatteryEmulatorManager.java create mode 100644 app/src/main/res/drawable/hide_source_24px.xml diff --git a/app/build.gradle b/app/build.gradle index 33e5131..6f19370 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { applicationId "com.vectras.vm" minSdk minApi targetSdk targetApi - versionCode 123 - versionName "4.1.9" + versionCode 124 + versionName "4.2.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true diff --git a/app/src/main/assets/roms/battery.asl b/app/src/main/assets/roms/battery.asl new file mode 100644 index 0000000..4f546a4 --- /dev/null +++ b/app/src/main/assets/roms/battery.asl @@ -0,0 +1,78 @@ +DefinitionBlock ("battery.aml", "SSDT", 2, "QEMU", "BATTOS", 0x00000001) +{ + Scope (\_SB) + { + + Device (EC0) + { + Name (_HID, EisaId ("PNP0C09")) + + Method (_STA, 0, NotSerialized) + { + Return (0x0F) + } + } + + Device (AC) + { + Name (_HID, "ACPI0003") + + Method (_STA, 0, NotSerialized) + { + Return (0x0F) + } + + Method (_PSR, 0, NotSerialized) + { + Return (One) // Charging AC + } + } + + Device (BAT0) + { + Name (_HID, EisaId ("PNP0C0A")) + Name (_UID, One) + + Name (_PCL, Package () + { + \_SB + }) + + Method (_STA, 0, NotSerialized) + { + Return (0x1F) + } + + Method (_BIF, 0, NotSerialized) + { + Return (Package () + { + One, // Power Unit = mAh + 9000, // Design Capacity + ${last-full-charge-capacity}, // Last Full Charge Capacity + One, // Rechargeable + 11111, // Design Voltage (11.111V) + 400, // Warning Capacity + 200, // Low Capacity + 100, + 100, + "99Wh Laptop Battery", + "202605", + "LiIon", + "VECTRAS_VM_BATTERY_EMULATOR" + }) + } + + Method (_BST, 0, NotSerialized) + { + Return (Package () + { + ${charging-status}, // Charging + 500, // Charge rate + ${remaining-capacity}, // Remaining capacity + 12600 // Current voltage + }) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/android/androidVNC/VncCanvas.java b/app/src/main/java/android/androidVNC/VncCanvas.java index 2cd16be..fed9308 100644 --- a/app/src/main/java/android/androidVNC/VncCanvas.java +++ b/app/src/main/java/android/androidVNC/VncCanvas.java @@ -545,6 +545,11 @@ public class VncCanvas extends AppCompatImageView { public void onDestroy() { Log.v(TAG, "Cleaning up resources"); + + if (rfb != null) { + rfb.close(); + } + if (bitmapData != null) { bitmapData.dispose(); } diff --git a/app/src/main/java/com/vectras/qemu/Config.java b/app/src/main/java/com/vectras/qemu/Config.java index 635f458..229ed2f 100644 --- a/app/src/main/java/com/vectras/qemu/Config.java +++ b/app/src/main/java/com/vectras/qemu/Config.java @@ -165,6 +165,10 @@ public class Config { return Config.getCacheDir()+ "/" + vmID + "/"; } + public static String getCacheVMPath(String vmId) { + return Config.getCacheDir()+ "/" + vmId + "/"; + } + public static enum MouseMode { Trackpad, External } diff --git a/app/src/main/java/com/vectras/qemu/MainSettingsManager.java b/app/src/main/java/com/vectras/qemu/MainSettingsManager.java index 730bcf4..1d10aad 100644 --- a/app/src/main/java/com/vectras/qemu/MainSettingsManager.java +++ b/app/src/main/java/com/vectras/qemu/MainSettingsManager.java @@ -1240,4 +1240,16 @@ public class MainSettingsManager extends AppCompatActivity SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); return prefs.getBoolean("vncPinchToZoom", false); } + + public static void setVncLosslessQuality(Context context, Boolean _boolean) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor edit = prefs.edit(); + edit.putBoolean("vncLosslessQuality", _boolean); + edit.apply(); + } + + public static Boolean getVncLosslessQuality(Context context) { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getBoolean("vncLosslessQuality", true); + } } diff --git a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java index 32444f9..62d574a 100644 --- a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java +++ b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java @@ -28,7 +28,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.os.StrictMode; import android.os.SystemClock; import android.text.Editable; import android.text.TextWatcher; @@ -76,6 +75,11 @@ import com.vectras.vm.utils.UIUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.json.JSONObject; @@ -106,8 +110,6 @@ public class MainVNCActivity extends VncCanvasActivity { public boolean ctrlClicked = false; public boolean altClicked = false; - public static LinearLayout desktop; - public static LinearLayout gamepad; private final ArrayList> listmapForSendKey = new ArrayList<>(); private boolean isConnected = false; @@ -165,18 +167,15 @@ public class MainVNCActivity extends VncCanvasActivity { onFitToScreen(); - desktop = findViewById(R.id.desktop); - gamepad = findViewById(R.id.gamepad); - if (Objects.equals(MainSettingsManager.getControlMode(this), "D")) { - desktop.setVisibility(View.VISIBLE); - gamepad.setVisibility(View.GONE); + bindingControls.desktop.setVisibility(View.VISIBLE); + bindingControls.gamepad.setVisibility(View.GONE); } else if (Objects.equals(MainSettingsManager.getControlMode(this), "G")) { - desktop.setVisibility(View.GONE); - gamepad.setVisibility(View.VISIBLE); + bindingControls.desktop.setVisibility(View.GONE); + bindingControls.gamepad.setVisibility(View.VISIBLE); } else if (Objects.equals(MainSettingsManager.getControlMode(this), "H")) { - desktop.setVisibility(View.GONE); - gamepad.setVisibility(View.GONE); + bindingControls.desktop.setVisibility(View.GONE); + bindingControls.gamepad.setVisibility(View.GONE); } binding.lnNosignal.setOnClickListener(v -> { @@ -791,9 +790,10 @@ public class MainVNCActivity extends VncCanvasActivity { @Override public void onBackPressed() { - super.onBackPressed(); if (bindingSendKey.sendkeylayout.getVisibility() == View.VISIBLE) { bindingSendKey.sendkeylayout.setVisibility(View.GONE); + } else if (bindingControls.mainControl.getVisibility() == View.GONE) { + bindingControls.mainControl.setVisibility(View.VISIBLE); } else { FrameLayout l = findViewById(R.id.mainControl); if (l != null) { @@ -805,7 +805,7 @@ public class MainVNCActivity extends VncCanvasActivity { started = false; if (streamAudio != null && !VmAudioManager.currentVmId.equals(Config.vmID)) streamAudio.setCross(null); - finish(); + super.onBackPressed(); } } @@ -872,13 +872,28 @@ public class MainVNCActivity extends VncCanvasActivity { binding.lnNosignal.setVisibility(View.GONE); binding.lnConnecting.setVisibility(View.VISIBLE); - new Thread(() -> { - StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); - StrictMode.setThreadPolicy(policy); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future future = executor.submit(() -> + FileUtils.isFileExists(Config.getLocalQMPSocketPath()) + ); - boolean isVMRunning = FileUtils.isFileExists(Config.getLocalQMPSocketPath()); + new Thread(() -> { + boolean isVMRunning = false; + try { + isVMRunning = future.get(3, TimeUnit.SECONDS); + } catch (TimeoutException e) { + future.cancel(true); + } catch (Exception e) { + Log.e(TAG, "isQMPPortOpening: ", e); + } finally { + executor.shutdown(); + } + + if (isDestroyed() || isFinishing()) return; + + boolean finalIsVMRunning = isVMRunning; runOnUiThread(() -> { - if (isVMRunning) { + if (finalIsVMRunning) { // Try reconnect. tryReconnect(true); } else if (isFinish) { @@ -1028,12 +1043,7 @@ public class MainVNCActivity extends VncCanvasActivity { return false; }); - bindingControls.btnMode.setOnClickListener(v -> { - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - // Create and show the dialog. - ControlersOptionsFragment newFragment = new ControlersOptionsFragment(); - newFragment.show(ft, "Controllers"); - }); + bindingControls.btnMode.setOnClickListener(v -> showSwitchControlLayoutDialog()); bindingControls.btnSettings.setOnClickListener(v -> { VmControllerDialog vmControllerDialog = new VmControllerDialog(); @@ -1567,4 +1577,12 @@ public class MainVNCActivity extends VncCanvasActivity { public String getPath(Uri uri) { return FileUtils.getPath(this, uri); } + + private void showSwitchControlLayoutDialog() { + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + // Create and show the dialog. + ControlersOptionsFragment newFragment = new ControlersOptionsFragment(); + newFragment.binding = binding.controlsfragment; + newFragment.show(ft, "Controllers"); + } } diff --git a/app/src/main/java/com/vectras/vm/AppConfig.java b/app/src/main/java/com/vectras/vm/AppConfig.java index fba6977..1af1791 100644 --- a/app/src/main/java/com/vectras/vm/AppConfig.java +++ b/app/src/main/java/com/vectras/vm/AppConfig.java @@ -18,7 +18,7 @@ public class AppConfig { // App Config public static String vectrasVersion; public static int vectrasVersionCode; - public static final int standardSetupVersion = 922202605; + public static final int standardSetupVersion = 922202606; public static String vectrasWebsite = "https://vectras.vercel.app/"; public static String vectrasWebsiteRaw = "https://raw.githubusercontent.com/AnBui2004/Vectras-VM-Emu-Android/refs/heads/master/web/"; public static String bootstrapfileslink = vectrasWebsiteRaw + "/data/setupfiles3.json"; @@ -82,13 +82,13 @@ public class AppConfig { " libusbredirparser usbredir-dev sdl2 sdl2-dev sdl2_image-dev libepoxy-dev virglrenderer-dev rdma-core fluxbox" + " libusb libaio ncurses-libs curl libnfs gtk+3.0 gtk+3.0-dev fuse libpulse libseccomp jack pipewire liburing pulseaudio pulseaudio-alsa alsa-plugins-pulse" + " 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" + - " qemu-audio-sdl capstone libcbor snappy lzo ndctl keyutils-libs vde2-libs libdw libbpf sndio-libs linux-pam fuse3-libs libssh vte3"; + " qemu-audio-sdl capstone libcbor snappy lzo ndctl keyutils-libs vde2-libs libdw libbpf sndio-libs linux-pam fuse3-libs libssh vte3 iasl"; } else { return "bash aria2 tar dwm xterm libslirp libslirp-dev pulseaudio-dev glib-dev pixman-dev zlib-dev spice-dev" + " libusbredirparser usbredir-dev sdl2 sdl2-dev sdl2_image-dev libepoxy-dev virglrenderer-dev rdma-core fluxbox" + " libusb libaio ncurses-libs curl libnfs gtk+3.0 gtk+3.0-dev fuse libpulse libseccomp jack pipewire liburing pulseaudio pulseaudio-alsa alsa-plugins-pulse" + " mesa-dri-gallium mesa-vulkan-swrast vulkan-loader mesa-utils mesa-egl" + - " qemu-audio-sdl capstone libcbor snappy lzo ndctl keyutils-libs vde2-libs libdw libbpf sndio-libs linux-pam fuse3-libs libssh vte3"; + " qemu-audio-sdl capstone libcbor snappy lzo ndctl keyutils-libs vde2-libs libdw libbpf sndio-libs linux-pam fuse3-libs libssh vte3 iasl"; } } @@ -98,13 +98,13 @@ public class AppConfig { " libusbredirparser usbredir-dev sdl2 sdl2-dev sdl2_image-dev libepoxy-dev virglrenderer-dev rdma-core fluxbox" + " libusb libaio ncurses-libs curl libnfs gtk+3.0 gtk+3.0-dev fuse libpulse libseccomp jack pipewire liburing pulseaudio pulseaudio-alsa alsa-plugins-pulse" + " mesa-dri-gallium mesa-vulkan-swrast vulkan-loader mesa-utils mesa-egl" + - " qemu-audio-sdl capstone libcbor snappy lzo ndctl keyutils-libs vde2-libs libdw libbpf sndio-libs linux-pam fuse3-libs libssh vte3"; + " qemu-audio-sdl capstone libcbor snappy lzo ndctl keyutils-libs vde2-libs libdw libbpf sndio-libs linux-pam fuse3-libs libssh vte3 iasl"; } else { return "bash aria2 tar dwm xterm libslirp libslirp-dev pulseaudio-dev glib-dev pixman-dev zlib-dev spice-dev" + " libusbredirparser usbredir-dev sdl2 sdl2-dev sdl2_image-dev libepoxy-dev virglrenderer-dev rdma-core fluxbox" + " libusb libaio ncurses-libs curl libnfs gtk+3.0 gtk+3.0-dev fuse libpulse libseccomp jack pipewire liburing pulseaudio pulseaudio-alsa alsa-plugins-pulse" + " mesa-dri-gallium mesa-vulkan-swrast vulkan-loader mesa-utils mesa-egl" + - " qemu-audio-sdl capstone libcbor snappy lzo ndctl keyutils-libs vde2-libs libdw libbpf sndio-libs linux-pam fuse3-libs libssh vte3 libatomic"; + " qemu-audio-sdl capstone libcbor snappy lzo ndctl keyutils-libs vde2-libs libdw libbpf sndio-libs linux-pam fuse3-libs libssh vte3 libatomic iasl"; } } diff --git a/app/src/main/java/com/vectras/vm/ExportRomActivity.java b/app/src/main/java/com/vectras/vm/ExportRomActivity.java index ffbe0ce..a28e4f4 100644 --- a/app/src/main/java/com/vectras/vm/ExportRomActivity.java +++ b/app/src/main/java/com/vectras/vm/ExportRomActivity.java @@ -130,6 +130,8 @@ public class ExportRomActivity extends AppCompatActivity { vmConfigMap.put("cores", current.cores); vmConfigMap.put("threads", current.threads); + vmConfigMap.put("battery", current.battery); + boolean isUsingDiskInQemuExtraParams = VMManager.isHaveADisk(current.itemExtra); if (FileUtils.isFileExists(current.itemPath)) { @@ -148,6 +150,12 @@ public class ExportRomActivity extends AppCompatActivity { vmConfigMap.put("cdrom", isUsingDiskInQemuExtraParams || FileUtils.isFileExists(Objects.requireNonNull(vmConfigMap.get("drive")).toString()) ? "" : VMManager.quickScanISOFileInFolder(getRomPath)); } + if (FileUtils.isFileExists(current.cdrom1)) { + vmConfigMap.put("cdrom1", new File(Objects.requireNonNull(Uri.parse(current.cdrom1).getPath())).getName()); + } else { + vmConfigMap.put("cdrom1", isUsingDiskInQemuExtraParams || FileUtils.isFileExists(Objects.requireNonNull(vmConfigMap.get("drive")).toString()) ? "" : VMManager.quickScanISOFileInFolder(getRomPath)); + } + if (FileUtils.isFileExists(current.fda)) { vmConfigMap.put("fda", new File(Objects.requireNonNull(Uri.parse(current.fda).getPath())).getName()); } else { diff --git a/app/src/main/java/com/vectras/vm/Fragment/ControlersOptionsFragment.java b/app/src/main/java/com/vectras/vm/Fragment/ControlersOptionsFragment.java index c1ef858..c528e01 100644 --- a/app/src/main/java/com/vectras/vm/Fragment/ControlersOptionsFragment.java +++ b/app/src/main/java/com/vectras/vm/Fragment/ControlersOptionsFragment.java @@ -12,9 +12,11 @@ import android.view.Window; import com.vectras.qemu.MainSettingsManager; import com.vectras.qemu.MainVNCActivity; import com.vectras.vm.R; +import com.vectras.vm.databinding.ControlsFragmentBinding; import com.vectras.vm.x11.X11Activity; public class ControlersOptionsFragment extends DialogFragment { + public ControlsFragmentBinding binding; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { @@ -22,48 +24,48 @@ public class ControlersOptionsFragment extends DialogFragment { alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); alertDialog.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT)); alertDialog.setContentView(R.layout.fragment_controlers_options); - alertDialog.findViewById(R.id.gamepadBtn).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - MainSettingsManager.setControlMode(getActivity(), "G"); - if (MainSettingsManager.getVmUi(getActivity()).equals("X11")) { - 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); - } - alertDialog.cancel(); + alertDialog.findViewById(R.id.gamepadBtn).setOnClickListener(v -> { + MainSettingsManager.setControlMode(getActivity(), "G"); + if (MainSettingsManager.getVmUi(getActivity()).equals("X11")) { + 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")) { + binding.desktop.setVisibility(View.GONE); + binding.gamepad.setVisibility(View.VISIBLE); } + alertDialog.cancel(); }); - alertDialog.findViewById(R.id.desktopBtn).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - MainSettingsManager.setControlMode(getActivity(), "D"); - if (MainSettingsManager.getVmUi(getActivity()).equals("X11")) { - if (X11Activity.desktop != null ) X11Activity.desktop.setVisibility(View.VISIBLE); - if (X11Activity.gamepad != null ) X11Activity.gamepad.setVisibility(View.GONE); - } else if (MainSettingsManager.getVmUi(getActivity()).equals("VNC")) { - MainVNCActivity.desktop.setVisibility(View.VISIBLE); - MainVNCActivity.gamepad.setVisibility(View.GONE); - } - alertDialog.cancel(); + alertDialog.findViewById(R.id.desktopBtn).setOnClickListener(v -> { + MainSettingsManager.setControlMode(getActivity(), "D"); + if (MainSettingsManager.getVmUi(getActivity()).equals("X11")) { + if (X11Activity.desktop != null ) X11Activity.desktop.setVisibility(View.VISIBLE); + if (X11Activity.gamepad != null ) X11Activity.gamepad.setVisibility(View.GONE); + } else if (MainSettingsManager.getVmUi(getActivity()).equals("VNC")) { + binding.desktop.setVisibility(View.VISIBLE); + binding.gamepad.setVisibility(View.GONE); } + alertDialog.cancel(); }); - alertDialog.findViewById(R.id.hideBtn).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - MainSettingsManager.setControlMode(getActivity(), "H"); - if (MainSettingsManager.getVmUi(getActivity()).equals("X11")) { - if (X11Activity.desktop != null) X11Activity.desktop.setVisibility(View.GONE); - if (X11Activity.gamepad != null) X11Activity.gamepad.setVisibility(View.GONE); - } else if (MainSettingsManager.getVmUi(getActivity()).equals("VNC")) { - if (MainVNCActivity.desktop != null) MainVNCActivity.desktop.setVisibility(View.GONE); - if (MainVNCActivity.gamepad != null) MainVNCActivity.gamepad.setVisibility(View.GONE); - } - alertDialog.cancel(); + alertDialog.findViewById(R.id.hideBtn).setOnClickListener(v -> { + MainSettingsManager.setControlMode(getActivity(), "H"); + if (MainSettingsManager.getVmUi(getActivity()).equals("X11")) { + if (X11Activity.desktop != null) X11Activity.desktop.setVisibility(View.GONE); + if (X11Activity.gamepad != null) X11Activity.gamepad.setVisibility(View.GONE); + } else if (MainSettingsManager.getVmUi(getActivity()).equals("VNC")) { + binding.desktop.setVisibility(View.GONE); + binding.gamepad.setVisibility(View.GONE); } + alertDialog.cancel(); }); + + if (binding != null) { + alertDialog.findViewById(R.id.hide_all).setVisibility(View.VISIBLE); + + alertDialog.findViewById(R.id.hide_all).setOnClickListener(v -> { + binding.mainControl.setVisibility(View.GONE); + alertDialog.cancel(); + }); + } alertDialog.show(); return alertDialog; } diff --git a/app/src/main/java/com/vectras/vm/SplashActivity.java b/app/src/main/java/com/vectras/vm/SplashActivity.java index c33cbd0..cdcaace 100644 --- a/app/src/main/java/com/vectras/vm/SplashActivity.java +++ b/app/src/main/java/com/vectras/vm/SplashActivity.java @@ -14,6 +14,7 @@ import androidx.appcompat.app.AppCompatDelegate; import androidx.core.os.LocaleListCompat; import androidx.preference.PreferenceManager; +import com.vectras.qemu.Config; import com.vectras.qemu.MainSettingsManager; import com.vectras.qemu.utils.FileInstaller; import com.vectras.vm.crashtracker.LastCrashActivity; @@ -117,6 +118,7 @@ public class SplashActivity extends AppCompatActivity { private void cleanUp() { FileUtils.delete(AppConfig.vmFolder + "QuickRun"); + FileUtils.delete(Config.getCacheDir()); VmFileManager.quickCleanUp(this); } diff --git a/app/src/main/java/com/vectras/vm/StartVM.java b/app/src/main/java/com/vectras/vm/StartVM.java index 9d7195a..a4743bf 100644 --- a/app/src/main/java/com/vectras/vm/StartVM.java +++ b/app/src/main/java/com/vectras/vm/StartVM.java @@ -9,6 +9,7 @@ import com.vectras.qemu.MainSettingsManager; import com.vectras.qemu.utils.RamInfo; import com.vectras.vm.creator.VMCreatorSelector; import com.vectras.vm.main.vms.DataMainRoms; +import com.vectras.vm.manager.BatteryEmulatorManager; import com.vectras.vm.manager.QemuManager; import com.vectras.vm.manager.VmFileManager; import com.vectras.vm.settings.ItemSettingsSelector; @@ -152,55 +153,51 @@ public class StartVM { params.add(hdd0); } - if (vmConfigs.imgCdrom.isEmpty()) { - File cdromFile = new File(filesDir + "/data/Vectras/drive.iso"); - - if (cdromFile.exists()) { - if (MainSettingsManager.getArch(activity).equals("ARM64")) { - cdrom = " -drive"; - cdrom += " if=none,id=cdrom,format=raw,media=cdrom,file='" + cdromFile.getPath() + "'"; - cdrom += "-device"; - cdrom += " usb-storage,drive=cdrom"; - if (!extras.contains("-device nec-usb-xhci")) { - cdrom += " -device"; - cdrom += " qemu-xhci"; - cdrom += " -device"; - cdrom += " nec-usb-xhci"; - } - } else { - if (ifType.isEmpty()) { - cdrom = "-cdrom"; - cdrom += " '" + cdromFile.getPath() + "'"; - } else { - cdrom = "-drive"; - cdrom += " media=cdrom"; - cdrom += ",file='" + cdromFile.getPath() + "'"; - } - } - - params.add(cdrom); - } - } else { + if (!vmConfigs.imgCdrom.isEmpty()) { if (MainSettingsManager.getArch(activity).equals("ARM64")) { cdrom += " -device"; - cdrom += " nec-usb-xhci,id=defaultxhci"; + cdrom += " nec-usb-xhci,id=usbopticaldiscreader0"; cdrom += " -device"; - cdrom += " usb-storage,bus=defaultxhci.0,drive=cdrom"; + cdrom += " usb-storage,bus=usbopticaldiscreader0.0,drive=cdromdrive0"; cdrom += " -drive"; - cdrom += " if=none,id=cdrom,format=raw,media=cdrom,file='" + vmConfigs.imgCdrom + "'"; + cdrom += " if=none,id=cdromdrive0,format=raw,media=cdrom,file='" + vmConfigs.imgCdrom + "'"; } else { - if (ifType.isEmpty()) { + if (!extras.contains("-cdrom ")) { cdrom = "-cdrom"; cdrom += " '" + vmConfigs.imgCdrom + "'"; } else { cdrom = "-drive"; - cdrom += " media=cdrom"; + cdrom += " media=cdromdrive1"; cdrom += ",file='" + vmConfigs.imgCdrom + "'"; } } params.add(cdrom); } + if (!vmConfigs.cdrom1.isEmpty()) { + String cdromParams; + String controllerId = vmConfigs.imgCdrom.isEmpty() ? "usbopticaldiscreader0" : "usbopticaldiscreader1"; + + if (MainSettingsManager.getArch(activity).equals("ARM64")) { + cdromParams = " -device"; + cdromParams += " nec-usb-xhci,id=" + controllerId; + cdromParams += " -device"; + cdromParams += " usb-storage,bus=" + controllerId + ".0,drive=cdromdrive1"; + cdromParams += " -drive"; + cdromParams += " if=none,id=cdromdrive1,format=raw,media=cdrom,file='" + vmConfigs.cdrom1 + "'"; + } else { + if (vmConfigs.imgCdrom.isEmpty() && !extras.contains("-cdrom ")) { + cdromParams = "-cdrom"; + cdromParams += " '" + vmConfigs.cdrom1 + "'"; + } else { + cdromParams = "-drive"; + cdromParams += " media=cdromdrive1"; + cdromParams += ",file='" + vmConfigs.cdrom1 + "'"; + } + } + params.add(cdromParams); + } + File hdd1File = new File(filesDir + "/data/Vectras/hdd1.qcow2"); if (hdd1File.exists()) { @@ -348,6 +345,11 @@ public class StartVM { params.add("defer"); } + if (vmConfigs.battery) { + if (BatteryEmulatorManager.setup(activity, vmConfigs.vmID)) + params.add("-acpitable file=" + VmFileManager.getCompiledBatteryAcpi(activity, vmConfigs.vmID)); + } + return String.join(" ", params); } @@ -383,7 +385,7 @@ public class StartVM { params += vncParams; } else { - params += "unix:" + Config.getLocalVNCSocketPath() + ",lossy=off"; + params += "unix:" + Config.getLocalVNCSocketPath() + (MainSettingsManager.getVncLosslessQuality(context) ? ",lossy=off" : ""); if (QemuManager.isSupportSetRefreshRate(context)) params += ",refresh-rate=" + ((context instanceof Activity activity) ? QemuManager.getAppropriateRefreshRate(activity, mainParams, DeviceUtils.getMaxRefreshRate(activity)) : Integer.parseInt(ItemSettingsSelector.getVncRefreshRateValue(MainSettingsManager.getVncRefreshRate(context)))); diff --git a/app/src/main/java/com/vectras/vm/creator/VMCreatorActivity.java b/app/src/main/java/com/vectras/vm/creator/VMCreatorActivity.java index 00b72b6..e08a0df 100644 --- a/app/src/main/java/com/vectras/vm/creator/VMCreatorActivity.java +++ b/app/src/main/java/com/vectras/vm/creator/VMCreatorActivity.java @@ -83,6 +83,7 @@ public class VMCreatorActivity extends AppCompatActivity { private int cpu = 0; private int cores = 0; private int threads = 0; + private boolean isUseBattery = false; private boolean isShowBootMenu = false; private boolean isUseLocalTime = true; private boolean isUseUefi = false; @@ -206,19 +207,6 @@ public class VMCreatorActivity extends AppCompatActivity { binding.lineardisclaimer.setOnClickListener(v -> DialogUtils.oneDialog(this, getResources().getString(R.string.dont_miss_out), getResources().getString(R.string.disclaimer_when_using_rom), getResources().getString(R.string.i_agree), true, R.drawable.verified_user_24px, true, null, null)); - binding.cbvShowbootmenu.setOnCheckedChangeListener((v, isChecked) -> isShowBootMenu = isChecked); - - binding.cbvUselocaltime.setOnCheckedChangeListener((v, isChecked) -> isUseLocalTime = isChecked); - - binding.cbvUseuefi.setOnCheckedChangeListener((v, isChecked) -> isUseUefi = isChecked); - - binding.cbvUseDefaultBios.setOnCheckedChangeListener((v, isChecked) -> { - isUseDefaultBios = isChecked; - binding.cbvUseuefi.setEnabled(isChecked); - }); - - if (!MainSettingsManager.getArch(this).equals("X86_64")) - binding.cbvUseuefi.setVisibility(View.GONE); binding.sbvCpu.setOnClickListener(v -> VMCreatorSelector.cpu(this, MainSettingsManager.getArch(this), cpu, ((position, name, value) -> { cpu = position; @@ -235,11 +223,32 @@ public class VMCreatorActivity extends AppCompatActivity { binding.sbvThread.setSubtitle(name); }))); + binding.cbvBattery.setOnCheckedChangeListener((v, isChecked) -> isUseBattery = isChecked); + + + binding.sbvBootfrom.setOnClickListener(v -> VMCreatorSelector.bootFrom(this, bootFrom, ((position, name, value) -> { bootFrom = position; binding.sbvBootfrom.setSubtitle(name); }))); + binding.cbvShowbootmenu.setOnCheckedChangeListener((v, isChecked) -> isShowBootMenu = isChecked); + + binding.cbvUselocaltime.setOnCheckedChangeListener((v, isChecked) -> isUseLocalTime = isChecked); + + if (!MainSettingsManager.getArch(this).equals("X86_64")) { + binding.cbvUseuefi.setVisibility(View.GONE); + } else { + binding.cbvUseuefi.setOnCheckedChangeListener((v, isChecked) -> isUseUefi = isChecked); + } + + binding.cbvUseDefaultBios.setOnCheckedChangeListener((v, isChecked) -> { + isUseDefaultBios = isChecked; + binding.cbvUseuefi.setEnabled(isChecked); + }); + + + VmFileManager.removeTemp(this, vmID, peddingTempFolder); modify = getIntent().getBooleanExtra("MODIFY", false); @@ -548,6 +557,10 @@ public class VMCreatorActivity extends AppCompatActivity { setDrive(SELECT_CDROM_0_FILE_MODE, (current.imgCdrom.contains("/") ? "" : VmFileManager.getPath(vmID)).concat(current.imgCdrom)); } + if (current.cdrom1 != null && !current.cdrom1.isEmpty()) { + setDrive(SELECT_CDROM_1_FILE_MODE, (current.cdrom1.contains("/") ? "" : VmFileManager.getPath(vmID)).concat(current.cdrom1)); + } + if (current.itemIcon != null && !current.itemIcon.isEmpty()) { thumbnailPath = (current.itemIcon.contains("/") ? current.itemIcon : VmFileManager.getPath(vmID, current.itemIcon)); updateThumbnailViewer(""); @@ -566,6 +579,9 @@ public class VMCreatorActivity extends AppCompatActivity { threads = current.threads; binding.sbvThread.setSubtitle(String.valueOf(threads + 1)); + isUseBattery = current.battery; + binding.cbvBattery.setChecked(isUseBattery); + bootFrom = current.bootFrom; binding.sbvBootfrom.setSubtitle(Objects.requireNonNull(VMCreatorSelector.getBootFrom(this, current.bootFrom).get("name")).toString()); isShowBootMenu = current.isShowBootMenu; @@ -743,6 +759,7 @@ public class VMCreatorActivity extends AppCompatActivity { current.cpu = cpu; current.cores = cores; current.threads = threads; + current.battery = isUseBattery; current.itemPath = Objects.requireNonNull(binding.drive.getText()).toString(); @@ -752,6 +769,7 @@ public class VMCreatorActivity extends AppCompatActivity { current.fdb = Objects.requireNonNull(binding.tieFdb.getText()).toString(); current.imgCdrom = Objects.requireNonNull(binding.cdrom.getText()).toString(); + current.cdrom1 = Objects.requireNonNull(binding.tieCdrom1.getText()).toString(); current.sharedFolder = sharedFolder; @@ -1224,6 +1242,10 @@ public class VMCreatorActivity extends AppCompatActivity { binding.cdromField.setOnClickListener(v -> pickStorageFile(SELECT_CDROM_0_FILE_MODE)); binding.cdromField.setEndIconOnClickListener(v -> setDrive(SELECT_CDROM_0_FILE_MODE, null)); + binding.tieCdrom1.setOnClickListener(v -> pickStorageFile(SELECT_CDROM_1_FILE_MODE)); + binding.tilCdrom1.setOnClickListener(v -> pickStorageFile(SELECT_CDROM_1_FILE_MODE)); + binding.tilCdrom1.setEndIconOnClickListener(v -> setDrive(SELECT_CDROM_1_FILE_MODE, null)); + binding.svSharedFolder.setSubTitle(AppConfig.sharedFolder); @@ -1233,8 +1255,9 @@ public class VMCreatorActivity extends AppCompatActivity { private final int SELECT_DISK_0_FILE_MODE = 0; private final int SELECT_DISK_1_FILE_MODE = 1; public final int SELECT_CDROM_0_FILE_MODE = 2; - public final int SELECT_FLOPPY_A_FILE_MODE = 3; - public final int SELECT_FLOPPY_B_FILE_MODE = 4; + public final int SELECT_CDROM_1_FILE_MODE = 3; + public final int SELECT_FLOPPY_A_FILE_MODE = 4; + public final int SELECT_FLOPPY_B_FILE_MODE = 5; private int PENDING_SELECT_FILE_MODE = 0; private String PENDING_OLD_FILE_AFTER_SELECTED_NEW_FILE = ""; @@ -1243,7 +1266,7 @@ public class VMCreatorActivity extends AppCompatActivity { PENDING_OLD_FILE_AFTER_SELECTED_NEW_FILE = Objects.requireNonNull(getPeddingStorageEditText().getText()).toString(); try { - if (PENDING_SELECT_FILE_MODE == SELECT_CDROM_0_FILE_MODE) { + if (PENDING_SELECT_FILE_MODE == SELECT_CDROM_0_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_CDROM_1_FILE_MODE) { isoPicker.launch("*/*"); } else if (PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_A_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_B_FILE_MODE) { floppyPicker.launch("*/*"); @@ -1329,13 +1352,13 @@ public class VMCreatorActivity extends AppCompatActivity { peddingTextInputEditText.setText(path != null ? path : ""); if (path == null || path.isEmpty()) { - if (PENDING_SELECT_FILE_MODE == SELECT_CDROM_0_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_A_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_B_FILE_MODE) { + if (PENDING_SELECT_FILE_MODE == SELECT_CDROM_0_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_CDROM_1_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_A_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_B_FILE_MODE) { peddingTextInputLayout.setEndIconMode(TextInputLayout.END_ICON_NONE); } else { peddingTextInputLayout.setEndIconDrawable(R.drawable.add_24px); } } else { - if (PENDING_SELECT_FILE_MODE == SELECT_CDROM_0_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_A_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_B_FILE_MODE) { + if (PENDING_SELECT_FILE_MODE == SELECT_CDROM_0_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_CDROM_1_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_A_FILE_MODE || PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_B_FILE_MODE) { peddingTextInputLayout.setEndIconMode(TextInputLayout.END_ICON_CUSTOM); peddingTextInputLayout.setEndIconDrawable(R.drawable.close_24px); setRemovableDriveEndIconOnClickListener(PENDING_SELECT_FILE_MODE, peddingTextInputLayout); @@ -1355,6 +1378,8 @@ public class VMCreatorActivity extends AppCompatActivity { return binding.tilHd1; } else if (PENDING_SELECT_FILE_MODE == SELECT_CDROM_0_FILE_MODE) { return binding.cdromField; + } else if (PENDING_SELECT_FILE_MODE == SELECT_CDROM_1_FILE_MODE) { + return binding.tilCdrom1; } else if (PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_A_FILE_MODE) { return binding.tilFda; } else if (PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_B_FILE_MODE) { @@ -1369,6 +1394,8 @@ public class VMCreatorActivity extends AppCompatActivity { return binding.tieHd1; } else if (PENDING_SELECT_FILE_MODE == SELECT_CDROM_0_FILE_MODE) { return binding.cdrom; + } else if (PENDING_SELECT_FILE_MODE == SELECT_CDROM_1_FILE_MODE) { + return binding.tieCdrom1; } else if (PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_A_FILE_MODE) { return binding.tieFda; } else if (PENDING_SELECT_FILE_MODE == SELECT_FLOPPY_B_FILE_MODE) { @@ -1382,6 +1409,7 @@ public class VMCreatorActivity extends AppCompatActivity { return (Objects.requireNonNull(binding.drive.getText()).toString().isEmpty()) && (Objects.requireNonNull(binding.tieHd1.getText()).toString().isEmpty()) && (Objects.requireNonNull(binding.cdrom.getText()).toString().isEmpty()) && + (Objects.requireNonNull(binding.tieCdrom1.getText()).toString().isEmpty()) && (Objects.requireNonNull(binding.tieFda.getText()).toString().isEmpty()) && (Objects.requireNonNull(binding.tieFdb.getText()).toString().isEmpty()); } @@ -1392,6 +1420,7 @@ public class VMCreatorActivity extends AppCompatActivity { String paramCollection = Objects.requireNonNull(binding.drive.getText()).toString(); paramCollection += "\n" + Objects.requireNonNull(binding.tieHd1.getText()); paramCollection += "\n" + Objects.requireNonNull(binding.cdrom.getText()); + paramCollection += "\n" + Objects.requireNonNull(binding.tieCdrom1.getText()); paramCollection += "\n" + Objects.requireNonNull(binding.tieFda.getText()); paramCollection += "\n" + Objects.requireNonNull(binding.tieFdb.getText()); paramCollection += "\n" + Objects.requireNonNull(binding.qemu.getText()); diff --git a/app/src/main/java/com/vectras/vm/main/core/MainStartVM.java b/app/src/main/java/com/vectras/vm/main/core/MainStartVM.java index 1ba1124..659811d 100644 --- a/app/src/main/java/com/vectras/vm/main/core/MainStartVM.java +++ b/app/src/main/java/com/vectras/vm/main/core/MainStartVM.java @@ -9,25 +9,18 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; import android.widget.Toast; -import androidx.appcompat.app.AlertDialog; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.vectras.qemu.Config; import com.vectras.qemu.MainSettingsManager; import com.vectras.qemu.MainVNCActivity; import com.vectras.vm.AppConfig; import com.vectras.vm.MainService; import com.vectras.vm.R; +import com.vectras.vm.StartVM; import com.vectras.vm.VMManager; import com.vectras.vm.logger.VectrasStatus; +import com.vectras.vm.main.vms.DataMainRoms; import com.vectras.vm.manager.QmpSender; import com.vectras.vm.manager.VmFileManager; import com.vectras.vm.manager.VmAudioManager; @@ -42,9 +35,8 @@ import java.io.File; public class MainStartVM { public static final String TAG = "HomeStartVM"; - public static AlertDialog progressDialog; public static boolean skipIDEwithARM64DialogInStartVM = false; - public static boolean isStopNow = false; + public static boolean breakNow = false; public static final Handler handlerForLaunch = new Handler(Looper.getMainLooper()); public static Runnable tickForLaunch = null; @@ -60,14 +52,41 @@ public class MainStartVM { public static boolean isLaunchFromPending = false; 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"; + private static StartVmDialog dialog; + + public static void startNow(Activity activity, DataMainRoms vmConfig) { + breakNow = false; + + Config.vmID = vmConfig.vmID; + + if (!(MainSettingsManager.getVmUi(activity).equals("X11") && DisplaySystem.isUseBuiltInX11())) { + showDialog(activity, vmConfig.vmID, vmConfig.itemName, vmConfig.itemIcon, activity.getString(R.string.preparing)); + } + + new Thread(() -> { + VmFileManager.removeTemp(activity, vmConfig.vmID); + + String env = StartVM.env(activity, vmConfig); + activity.runOnUiThread(() -> startNow(activity, vmConfig.itemName, env, vmConfig.vmID, vmConfig.itemIcon, dialog)); + }).start(); + } + public static void startNow( Context context, String vmName, String env, String vmID, - String thumbnailFile + String thumbnailFile, + StartVmDialog dialog ) { + if (breakNow) { + breakNow = false; + return; + } + + setDefault(); + if (isLaunchFromPending) { isLaunchFromPending = false; if (pendingVMID.isEmpty()) return; @@ -104,7 +123,9 @@ public class MainStartVM { } } - isStopNow = false; + breakNow = false; + + if (dialog == null || !dialog.isShowing()) showDialog((Activity) context, vmID, vmName, thumbnailFile, null); String finalvmID; if (vmID == null || vmID.isEmpty()) { @@ -124,6 +145,8 @@ public class MainStartVM { context.getString(R.string.vm_cache_dir_failed_to_create_content), R.drawable.warning_48px ); + + dismissDialog(); return; } } @@ -135,6 +158,8 @@ public class MainStartVM { context.getString(R.string.harmful_command_was_detected) + " " + context.getResources().getString(R.string.reason) + ": " + VMManager.latestUnsafeCommandReason, R.drawable.verified_user_24px ); + + dismissDialog(); return; } @@ -154,6 +179,8 @@ public class MainStartVM { null, null ); + + dismissDialog(); return; } @@ -163,23 +190,31 @@ public class MainStartVM { Toast.makeText(context, "This VM is already running.", Toast.LENGTH_LONG).show(); DisplaySystem.launch(context); if (!MainSettingsManager.getVmUi(context).equals("VNC")) VmAudioManager.stream(vmID); + + dismissDialog(); return; } if (AppConfig.getSetupFiles().contains("arm") && !AppConfig.getSetupFiles().contains("arm64")) { if (env.contains("tcg,thread=multi")) { + StartVmDialog finalDialog1 = dialog; 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); + () -> startNow(context, vmName, env.replace("tcg,thread=multi", "tcg,thread=single"), finalvmID, thumbnailFile, finalDialog1), null, null); + + dismissDialog(); return; } } if (MainSettingsManager.getArch(context).equals("ARM64") && MainSettingsManager.getIfType(context).equals("ide") && skipIDEwithARM64DialogInStartVM) { + StartVmDialog finalDialog = dialog; 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(context, vmName, env, finalvmID, thumbnailFile); + startNow(context, vmName, env, finalvmID, thumbnailFile, finalDialog); }, null, null); + + dismissDialog(); return; } else if (skipIDEwithARM64DialogInStartVM) { skipIDEwithARM64DialogInStartVM = false; @@ -199,16 +234,31 @@ public class MainStartVM { () -> context.startActivity(new Intent(context, ExternalVNCSettingsActivity.class)), null, null); + + dismissDialog(); return; } - VmFileManager.removeTemp(context, vmID); + if (breakNow) { + dismissDialog(); + return; + } - TextView vmBootNote = showProgressDialog(context, vmName, thumbnailFile, vmID); + startVm(context, vmName, env, finalvmID, thumbnailFile); + } + public static void startVm( + Context context, + String vmName, + String env, + String vmID, + String thumbnailFile + ) { VMManager.isQemuStopedWithError = false; - String finalCommand = VMManager.addAudioDevWav(vmID, String.format(runCommandFormat, env)); + String cleanUpCommand = "; rm -r " + Config.getCacheVMPath(vmID); + + String finalCommand = VMManager.addAudioDevWav(vmID, String.format(runCommandFormat, env + cleanUpCommand)); if (MainSettingsManager.getVmUi(context).equals("X11")) { finalCommand = "export DISPLAY=:0 && " + finalCommand; @@ -216,6 +266,11 @@ public class MainStartVM { } Log.i(TAG, finalCommand); + if (breakNow) { + dismissDialog(); + return; + } + if (ServiceUtils.isServiceRunning(context, MainService.class)) { MainService.startCommand(finalCommand, context); } else { @@ -233,19 +288,16 @@ public class MainStartVM { tickForLaunch = new Runnable() { @Override public void run() { - if (isStopNow || VMManager.isQemuStopedWithError || FileUtils.isFileExists(Config.getLocalQMPSocketPath())) { + if (breakNow || VMManager.isQemuStopedWithError || FileUtils.isFileExists(Config.getLocalQMPSocketPath())) { handlerForLaunch.removeCallbacks(this); Activity activity = context instanceof Activity ? (Activity) context : null; - if (!isStopNow && !VMManager.isQemuStopedWithError) { + if (!breakNow && !VMManager.isQemuStopedWithError) { new Thread(() -> { if (!forceDisableMigrate && VMManager.isNeedLoadMigrate()) { if (activity != null) { - activity.runOnUiThread(() -> { - if (vmBootNote != null) - vmBootNote.setText(activity.getString(R.string.resuming)); - }); + activity.runOnUiThread(() -> dialog.setStatus(R.string.resuming)); } String loadMigrateResponse = VMManager.loadMigrate(); Log.d(TAG, "loadMigrateResponse: " + loadMigrateResponse); @@ -268,8 +320,7 @@ public class MainStartVM { QmpSender.quickShutdown(); assert activity != null; activity.runOnUiThread(() -> { - if (DialogUtils.isSafeDismiss(activity, progressDialog)) - progressDialog.dismiss(); + activity.runOnUiThread(MainStartVM::dismissDialog); DialogUtils.threeDialog( activity, @@ -283,11 +334,11 @@ public class MainStartVM { true, () -> { forceDisableMigrate = true; - startNow(context, vmName, env.replace("-incoming defer", ""), vmID, thumbnailFile); + startNow(context, vmName, env.replace("-incoming defer", ""), vmID, thumbnailFile, dialog); }, () -> { VMManager.deleteMigrate(); - startNow(context, vmName, env.replace("-incoming defer", ""), vmID, thumbnailFile); + startNow(context, vmName, env.replace("-incoming defer", ""), vmID, thumbnailFile, dialog); }, null, null @@ -298,13 +349,14 @@ public class MainStartVM { } } else { forceDisableMigrate = false; + if ( activity != null) activity.runOnUiThread(() -> dialog.setStatus(R.string.booting_up)); } QmpSender.resume(); if (MainSettingsManager.getVmUi(context).equals("VNC")) { if (MainSettingsManager.getVncExternal(context)) { - Config.currentVNCServervmID = finalvmID; + Config.currentVNCServervmID = vmID; if (activity != null) { activity.runOnUiThread(() -> DialogUtils.oneDialog(context, context.getString(R.string.vnc_server), @@ -331,16 +383,12 @@ public class MainStartVM { Log.i(TAG, "Virtual machine running."); if (activity != null) { - activity.runOnUiThread(() -> { - if (DialogUtils.isSafeDismiss(activity, progressDialog)) - progressDialog.dismiss(); - }); + activity.runOnUiThread(MainStartVM::dismissDialog); } }).start(); } else { assert activity != null; - if (DialogUtils.isSafeDismiss(activity, progressDialog)) - progressDialog.dismiss(); + activity.runOnUiThread(MainStartVM::dismissDialog); } skipIDEwithARM64DialogInStartVM = false; @@ -360,70 +408,33 @@ public class MainStartVM { VectrasStatus.logInfo(i + ": " + params[i]); Log.d("HomeStartVM", i + ": " + params[i]); } - - setDefault(); } public static void startTryAgain(Context context) { - startNow(context, lastVMName, lastEnv, lastVMID, lastThumbnailFile); + startNow(context, lastVMName, lastEnv, lastVMID, lastThumbnailFile, dialog); VMManager.isTryAgain = false; } public static void startPending(Context context) { isLaunchFromPending = true; - startNow(context, pendingVMName, pendingEnv, pendingVMID, pendingThumbnailFile); - setDefault(); + startNow(context, pendingVMName, pendingEnv, pendingVMID, pendingThumbnailFile, null); } - public static TextView showProgressDialog(Context context, String _content, String thumbnailFile, String vmID) { - View progressView = LayoutInflater.from(context).inflate(R.layout.dialog_start_vm, null); - TextView tvVMName = progressView.findViewById(R.id.vm_name); - TextView vmBootNote = progressView.findViewById(R.id.vm_boot_note); - tvVMName.setText(_content); - - if (thumbnailFile != null) { - ImageView ivThumbnail = progressView.findViewById(R.id.iv_thumbnail); - - if (!thumbnailFile.isEmpty() && FileUtils.isFileExists(thumbnailFile)) { - Glide.with(context.getApplicationContext()) - .load(new File(thumbnailFile)) - .placeholder(R.drawable.ic_computer_180dp_with_padding) - .error(R.drawable.ic_computer_180dp_with_padding) - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .into(ivThumbnail); - } else if (VmFileManager.isScreenshotPngExists(vmID)) { - Glide.with(context.getApplicationContext()) - .load(new File(VmFileManager.getScreenshotPng(vmID))) - .placeholder(R.drawable.ic_computer_180dp_with_padding) - .error(R.drawable.ic_computer_180dp_with_padding) - .skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .into(ivThumbnail); - } else { - VMManager.setIconWithName(ivThumbnail, _content); - } + private static void showDialog(Activity activity, String vmId, String vmName, String thumbnailPath, String status) { + if (dialog != null) { + dialog.dismiss(); } - ImageView ivStop = progressView.findViewById(R.id.ivStop); - ivStop.setOnClickListener(v -> { - isStopNow = true; - ivStop.setVisibility(View.GONE); - vmBootNote.setText(R.string.shutting_down); - new Thread(() -> { - QmpSender.shutdown(); - new Handler(Looper.getMainLooper()).post(() -> progressDialog.dismiss()); - }).start(); - }); + dialog = new StartVmDialog(activity, vmId, vmName, thumbnailPath); + dialog.show(() -> breakNow = true); + if (status != null) dialog.setStatus(status); + } - progressDialog = new MaterialAlertDialogBuilder(context, R.style.CenteredDialogTheme) - .setView(progressView) - .setCancelable(false) - .create(); - - progressDialog.show(); - - return vmBootNote; + private static void dismissDialog() { + if (dialog != null) dialog.dismiss(); + if (breakNow) { + new Thread(QmpSender::shutdown).start(); + } } public static void setDefault() { diff --git a/app/src/main/java/com/vectras/vm/main/core/PendingCommand.java b/app/src/main/java/com/vectras/vm/main/core/PendingCommand.java index 5a6c5e9..c30a936 100644 --- a/app/src/main/java/com/vectras/vm/main/core/PendingCommand.java +++ b/app/src/main/java/com/vectras/vm/main/core/PendingCommand.java @@ -9,6 +9,7 @@ import com.vectras.vm.AppConfig; import com.vectras.vm.R; import com.vectras.vm.StartVM; import com.vectras.vm.VMManager; +import com.vectras.vm.manager.VmFileManager; import com.vectras.vm.utils.DialogUtils; import com.vectras.vm.utils.FileUtils; import com.vectras.vterm.Terminal; @@ -59,10 +60,12 @@ public class PendingCommand { Config.vmID = "quick_run_" + VMManager.idGenerator(); new Thread(() -> { + VmFileManager.removeTemp(activity, Config.vmID); + String env = StartVM.env(activity, command, "", true); FileUtils.createDirectory(AppConfig.vmFolder + Config.vmID); activity.runOnUiThread(() -> { - MainStartVM.startNow(activity, "Quick run", env, Config.vmID, null); + MainStartVM.startNow(activity, "Quick run", env, Config.vmID, null, null); VMManager.lastQemuCommand = command; command = ""; }); diff --git a/app/src/main/java/com/vectras/vm/main/core/StartVmDialog.java b/app/src/main/java/com/vectras/vm/main/core/StartVmDialog.java new file mode 100644 index 0000000..e8de636 --- /dev/null +++ b/app/src/main/java/com/vectras/vm/main/core/StartVmDialog.java @@ -0,0 +1,146 @@ +package com.vectras.vm.main.core; + +import android.app.Activity; +import android.os.Handler; +import android.os.Looper; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.appcompat.app.AlertDialog; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.vectras.qemu.Config; +import com.vectras.vm.R; +import com.vectras.vm.VMManager; +import com.vectras.vm.manager.QmpSender; +import com.vectras.vm.manager.VmFileManager; +import com.vectras.vm.utils.DialogUtils; +import com.vectras.vm.utils.FileUtils; + +import java.io.File; + +public class StartVmDialog { + Activity activity; + + AlertDialog progressDialog; + View progressView; + TextView tvVMName; + TextView vmBootNote; + + String vmName; + String vmId; + String thumbnailFile; + + boolean isShuttingDown; + + public interface StartVmDialogCallBack { + void onStop(); + } + + public StartVmDialog(Activity activity, String vmId, String vmName, String thumbnailFile) { + this.activity = activity; + this.vmName = vmName; + this.vmId = vmId; + this.thumbnailFile = thumbnailFile; + } + + public void show(StartVmDialogCallBack callBack) { + progressView = LayoutInflater.from(activity).inflate(R.layout.dialog_start_vm, null); + tvVMName = progressView.findViewById(R.id.vm_name); + vmBootNote = progressView.findViewById(R.id.vm_boot_note); + tvVMName.setText(vmName); + + if (thumbnailFile != null) { + ImageView ivThumbnail = progressView.findViewById(R.id.iv_thumbnail); + + if (!thumbnailFile.isEmpty() && FileUtils.isFileExists(thumbnailFile)) { + Glide.with(activity.getApplicationContext()) + .load(new File(thumbnailFile)) + .placeholder(R.drawable.ic_computer_180dp_with_padding) + .error(R.drawable.ic_computer_180dp_with_padding) + .skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .into(ivThumbnail); + } else if (VmFileManager.isScreenshotPngExists(vmId)) { + Glide.with(activity.getApplicationContext()) + .load(new File(VmFileManager.getScreenshotPng(vmId))) + .placeholder(R.drawable.ic_computer_180dp_with_padding) + .error(R.drawable.ic_computer_180dp_with_padding) + .skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .into(ivThumbnail); + } else { + VMManager.setIconWithName(ivThumbnail, vmName); + } + } + + ImageView ivStop = progressView.findViewById(R.id.ivStop); + ivStop.setOnClickListener(v -> { + isShuttingDown = true; + ivStop.setVisibility(View.GONE); + vmBootNote.setText(R.string.shutting_down); + new Thread(() -> { + QmpSender.shutdown(); + new Handler(Looper.getMainLooper()).post(() -> { + callBack.onStop(); + }); + + boolean isVmRuning = VMManager.isVMRunning(activity, vmId); + + while (isVmRuning) { + Config.vmID = vmId; + + isVmRuning = VMManager.isVMRunning(activity, vmId); + + QmpSender.shutdown(); + + if (!isVmRuning) { + new Handler(Looper.getMainLooper()).post(() -> { + isShuttingDown = false; + dismiss(); + }); + + break; + } + + try { + Thread.sleep(1000); + } catch (InterruptedException ignored) { + + } + } + }).start(); + }); + + progressDialog = new MaterialAlertDialogBuilder(activity, R.style.CenteredDialogTheme) + .setView(progressView) + .setCancelable(false) + .create(); + + progressDialog.show(); + } + + public void setStatus(String status) { + vmBootNote.setText(status); + } + + public void setStatus(int resourceId) { + vmBootNote.setText(resourceId); + } + + public boolean isShowing() { + return progressDialog != null && progressDialog.isShowing(); + } + + public void dismiss() { + if (isShuttingDown) return; + + DialogUtils.safeDismiss(activity, progressDialog); + progressDialog = null; + progressView = null; + } +} diff --git a/app/src/main/java/com/vectras/vm/main/vms/DataMainRoms.java b/app/src/main/java/com/vectras/vm/main/vms/DataMainRoms.java index 49ca2b8..810b7e9 100644 --- a/app/src/main/java/com/vectras/vm/main/vms/DataMainRoms.java +++ b/app/src/main/java/com/vectras/vm/main/vms/DataMainRoms.java @@ -45,6 +45,8 @@ public class DataMainRoms { ) public String imgCdrom = ""; + public String cdrom1 = ""; + public String fda = ""; public String fdb = ""; @@ -64,4 +66,6 @@ public class DataMainRoms { public boolean isUseUefi = false; public boolean isUseDefaultBios = true; + + public boolean battery = false; } diff --git a/app/src/main/java/com/vectras/vm/main/vms/VmsHomeAdapter.java b/app/src/main/java/com/vectras/vm/main/vms/VmsHomeAdapter.java index 41d746b..2b43969 100644 --- a/app/src/main/java/com/vectras/vm/main/vms/VmsHomeAdapter.java +++ b/app/src/main/java/com/vectras/vm/main/vms/VmsHomeAdapter.java @@ -85,12 +85,7 @@ public class VmsHomeAdapter extends RecyclerView.Adapter { - String env = StartVM.env(activity, current); - activity.runOnUiThread(() -> MainStartVM.startNow(activity, current.itemName, env, current.vmID, current.itemIcon)); - }).start(); + MainStartVM.startNow(activity, current); }); myHolder.cdRoms.setOnLongClickListener(v -> { diff --git a/app/src/main/java/com/vectras/vm/manager/BatteryEmulatorManager.java b/app/src/main/java/com/vectras/vm/manager/BatteryEmulatorManager.java new file mode 100644 index 0000000..170550b --- /dev/null +++ b/app/src/main/java/com/vectras/vm/manager/BatteryEmulatorManager.java @@ -0,0 +1,65 @@ +package com.vectras.vm.manager; + +import android.content.Context; + +import com.vectras.vm.setupwizard.SetupFeatureCore; +import com.vectras.vm.utils.DeviceUtils; +import com.vectras.vm.utils.FileUtils; +import com.vectras.vterm.Terminal; + +import java.io.File; + +public class BatteryEmulatorManager { + private static final String TAG = "BatteryEmulatorManager"; + private static final int DESIGN_CAPACITY = 9000; + private static final double PROPERTY_CAPACITY_DEPLETED_EACH_CYCLE = 0.04; + + private static final String CHARGING_STATUS_CHARGING = "0x02"; + private static final String CHARGING_STATUS_PLUGGED_IN_BUT_NOT_CHARGING = "0x00"; + + private static final String DESIGN_CAPACITY_MARK = "${remaining-capacity}"; + private static final String LAST_FULL_CHARGE_CAPACITY_MARK = "${last-full-charge-capacity}"; + private static final String CHARGING_STATUS_MARK = "${charging-status}"; + + public static boolean setup(Context context, String vmId) { + if (!QemuManager.isSupportAcpiTable(context)) return false; + + int batteryPropertyCapacity = DeviceUtils.getBatteryPropertyCapacity(context); + if (batteryPropertyCapacity < 1) batteryPropertyCapacity = 100; + + int batteryCycleCount = DeviceUtils.getBatteryCycleCount(context); + if (batteryCycleCount < 1) batteryCycleCount = 1; + + File vmTemp = new File(VmFileManager.getTempPath(context, vmId)); + + try { + String batteryAsl = SetupFeatureCore.readTextFromAssets(context, "roms/battery.asl"); + int remainingCapacity; + int lastFullChargeCapacity = (int) (DESIGN_CAPACITY - ((double) (DESIGN_CAPACITY / 100) * (batteryCycleCount * PROPERTY_CAPACITY_DEPLETED_EACH_CYCLE))); + + if (batteryCycleCount < 2) { + remainingCapacity = DESIGN_CAPACITY / 100 * batteryPropertyCapacity; + } else { + remainingCapacity = lastFullChargeCapacity / 100 * batteryPropertyCapacity; + } + + batteryAsl = batteryAsl + .replace(DESIGN_CAPACITY_MARK, String.valueOf(remainingCapacity)) + .replace(LAST_FULL_CHARGE_CAPACITY_MARK, String.valueOf(lastFullChargeCapacity)) + .replace(CHARGING_STATUS_MARK, batteryPropertyCapacity < 100 ? CHARGING_STATUS_CHARGING : CHARGING_STATUS_PLUGGED_IN_BUT_NOT_CHARGING); + + FileUtils.writeToFile(vmTemp.getAbsolutePath(), "battery.asl", batteryAsl); + + if (FileUtils.isFileExists(VmFileManager.getTempPath(context, vmId, "battery.asl"))) { + String respond = Terminal.executeShellCommandWithResult("cd " + vmTemp.getAbsolutePath() + " && iasl battery.asl && echo Compiled", context); + //Log.d(TAG, respond); + return respond.contains("Compiled"); + } else { + return false; + } + } catch (Exception e) { + //Log.d(TAG, "Error: \n", e); + return false; + } + } +} diff --git a/app/src/main/java/com/vectras/vm/manager/QemuManager.java b/app/src/main/java/com/vectras/vm/manager/QemuManager.java index a092141..88b50d8 100644 --- a/app/src/main/java/com/vectras/vm/manager/QemuManager.java +++ b/app/src/main/java/com/vectras/vm/manager/QemuManager.java @@ -14,6 +14,12 @@ public class QemuManager { return Terminal.executeShellCommandWithResult(getQemuExecutableFile(context) + " -vnc help", context).contains("refresh-rate"); } + public static boolean isSupportAcpiTable(Context context) { + String currentArch = MainSettingsManager.getArch(context); + + return currentArch.equals(MainSettingsManager.X86_64_ARCH) || currentArch.equals(MainSettingsManager.I386_ARCH); + } + public static int getAppropriateRefreshRate(Context context, String params, int max) { int result = DEFAULT_REFRESH_RATE; diff --git a/app/src/main/java/com/vectras/vm/manager/VmFileManager.java b/app/src/main/java/com/vectras/vm/manager/VmFileManager.java index b9c4a85..a957340 100644 --- a/app/src/main/java/com/vectras/vm/manager/VmFileManager.java +++ b/app/src/main/java/com/vectras/vm/manager/VmFileManager.java @@ -19,6 +19,7 @@ public class VmFileManager { public static final String SNAPSHOT_SH_FILE_NAME = "snapshot.sh"; public static final String SNAPSHOT_BIN_FILE_NAME = "snapshot.bin"; public static final String CREATE_COMMAND_CONFIG_FILE_NAME = "cqcm.json"; + public static final String COMPILED_BATERRY_ACPI_FILE_NAME = "battery.aml"; public static final String LOG_FILE_NAME = "vm.log"; public static final String TEXT_MARK_VM_PATH = "OhnoIjustrealizeditsmidnightandIstillhavetodothis"; public static final String TEXT_MARK_EXTERNAL_DATA_PATH = "%external_data%"; @@ -234,6 +235,17 @@ public class VmFileManager { return VmFileManager.getAudioRaw(context, vmId); } + public static String getCompiledBatteryAcpi(Context context, String vmId) { + return VmFileManager.getTempPath(context, vmId, COMPILED_BATERRY_ACPI_FILE_NAME); + } + + // Find the location of the aml file if getExternalCacheDir() is null. + public static String findCompiledBatteryAcpi(Context context, String vmId) { + String audioFilePath = VmFileManager.getInternalTempPath(context, vmId, COMPILED_BATERRY_ACPI_FILE_NAME); + if (FileUtils.isFileExists(audioFilePath)) return audioFilePath; + return VmFileManager.getAudioRaw(context, vmId); + } + public static boolean removeAudioRaw(Context context, String vmId) { return VmFileManager.removeTemp(context, vmId, AUDIO_STREAM_FILE_NAME); } diff --git a/app/src/main/java/com/vectras/vm/settings/VNCSettingsActivity.java b/app/src/main/java/com/vectras/vm/settings/VNCSettingsActivity.java index 333ac32..d89db73 100644 --- a/app/src/main/java/com/vectras/vm/settings/VNCSettingsActivity.java +++ b/app/src/main/java/com/vectras/vm/settings/VNCSettingsActivity.java @@ -52,6 +52,9 @@ public class VNCSettingsActivity extends AppCompatActivity { binding.swForcerefesh.setOnCheckedChangeListener((buttonView, isChecked) -> MainSettingsManager.setForceRefeshVNCDisplay(this, isChecked)); binding.lnForcerefesh.setOnClickListener(v -> binding.swForcerefesh.toggle()); + binding.swLossless.setOnCheckedChangeListener((buttonView, isChecked) -> MainSettingsManager.setVncLosslessQuality(this, isChecked)); + binding.lnForceLossless.setOnClickListener(v -> binding.swLossless.toggle()); + binding.sbvRefreshRate.setOnClickListener(v -> ItemSettingsSelector.vncRefreshRate(this, MainSettingsManager.getVncRefreshRate(this), ((position, name, value) -> { MainSettingsManager.setVncRefreshRate(this, position); binding.sbvRefreshRate.setSubtitle(name); @@ -61,6 +64,7 @@ public class VNCSettingsActivity extends AppCompatActivity { binding.lnExternal.setOnClickListener( v -> startActivity(new Intent(this, ExternalVNCSettingsActivity.class))); binding.swForcerefesh.setChecked(MainSettingsManager.getForceRefeshVNCDisplay(this)); + binding.swLossless.setChecked(MainSettingsManager.getVncLosslessQuality(this)); binding.sbvRefreshRate.setSubtitle(ItemSettingsSelector.getVncRefreshRateValue(MainSettingsManager.getVncRefreshRate(this))); binding.swExternal.setChecked(MainSettingsManager.getVncExternal(this)); @@ -72,6 +76,7 @@ public class VNCSettingsActivity extends AppCompatActivity { private void uiController(boolean isEnabled) { binding.lnAllOptions.setAlpha(isEnabled ? 1f : 0.5f); binding.lnForcerefesh.setEnabled(isEnabled); + binding.lnForceLossless.setEnabled(isEnabled); binding.sbvRefreshRate.setEnabled(isEnabled); binding.lnExternal.setEnabled(isEnabled); binding.swExternal.setEnabled(isEnabled); diff --git a/app/src/main/java/com/vectras/vm/setupwizard/SetupFeatureCore.java b/app/src/main/java/com/vectras/vm/setupwizard/SetupFeatureCore.java index 2446899..3867716 100644 --- a/app/src/main/java/com/vectras/vm/setupwizard/SetupFeatureCore.java +++ b/app/src/main/java/com/vectras/vm/setupwizard/SetupFeatureCore.java @@ -203,6 +203,20 @@ public class SetupFeatureCore { } } + public static String readTextFromAssets(Context context, String fileName) throws IOException { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = new BufferedReader( + new InputStreamReader(context.getAssets().open(fileName)) + ); + + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + reader.close(); + return sb.toString(); + } + public static void setDNS(Context context) { String filesDir = context.getFilesDir().getAbsolutePath(); File rootDir = new File(filesDir + "/distro/root"); diff --git a/app/src/main/java/com/vectras/vm/utils/DeviceUtils.java b/app/src/main/java/com/vectras/vm/utils/DeviceUtils.java index fa24281..36dadb7 100644 --- a/app/src/main/java/com/vectras/vm/utils/DeviceUtils.java +++ b/app/src/main/java/com/vectras/vm/utils/DeviceUtils.java @@ -1,10 +1,15 @@ package com.vectras.vm.utils; +import static androidx.core.content.ContextCompat.registerReceiver; + import android.app.Activity; import android.app.ActivityManager; import android.app.usage.StorageStatsManager; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Configuration; +import android.os.BatteryManager; import android.os.Build; import android.os.Environment; import android.os.StatFs; @@ -111,4 +116,24 @@ public class DeviceUtils { } return Math.round(maxHz); } + + public static int getBatteryPropertyCapacity(Context context) { + BatteryManager bm= (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE); + return bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); + } + + public static int getBatteryCycleCount(Context context) { + IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + Intent batteryStatus = context.registerReceiver(null, filter); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + if (batteryStatus == null) return -1; + + return batteryStatus.getIntExtra( + BatteryManager.EXTRA_CYCLE_COUNT, -1 + ); + } + + return -1; + } } diff --git a/app/src/main/res/drawable/hide_source_24px.xml b/app/src/main/res/drawable/hide_source_24px.xml new file mode 100644 index 0000000..1aabab3 --- /dev/null +++ b/app/src/main/res/drawable/hide_source_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/activity_vm_creator.xml b/app/src/main/res/layout/activity_vm_creator.xml index 7e37d11..6c8aa20 100644 --- a/app/src/main/res/layout/activity_vm_creator.xml +++ b/app/src/main/res/layout/activity_vm_creator.xml @@ -217,6 +217,18 @@ app:title="@string/threads" app:subtitle="@string/defaulttext" /> + + + + @@ -307,7 +319,7 @@ android:paddingHorizontal="16dp" android:clickable="true" android:focusable="true" - android:hint="@string/optical_drive" + android:hint="@string/optical_drive_0" android:theme="@style/AppTheme.TextInput" app:endIconDrawable="@drawable/close_24px" app:startIconDrawable="@drawable/album_24px"> @@ -324,6 +336,30 @@ android:singleLine="true" /> + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_controlers_options.xml b/app/src/main/res/layout/fragment_controlers_options.xml index 26aa03a..f859677 100644 --- a/app/src/main/res/layout/fragment_controlers_options.xml +++ b/app/src/main/res/layout/fragment_controlers_options.xml @@ -3,39 +3,65 @@ 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:gravity="center" android:orientation="horizontal"> - + - + - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 448e2a9..e922ddf 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -579,7 +579,13 @@ Ổ cứng 0 Ổ cứng 1 Ổ quang + Ổ quang 0 + Ổ quang 1 Tệp này đã được đùng ở một nơi khác của máy ảo. Bạn cần tìm và loại bỏ hoặc đổi tên nó và chọn lại. + Chất lượng cao hơn + Chất lượng hình ảnh sẽ được cải thiện nhưng độ trễ sẽ cao hơn nếu dùng ngoài. + Pin + Đang chuẩn bị… Vterm diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 72194c0..77ee18d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -590,7 +590,13 @@ Hard drive 0 Hard drive 1 Optical drive + Optical drive 0 + Optical drive 1 This file is already in use elsewhere on the virtual machine. You need to find and remove or rename it and select it again. + Higher quality + Image quality will improve, but the latency will be greater if using external. + Battery + Preparing… diff --git a/web/data/UpdateConfig.json b/web/data/UpdateConfig.json index 1942b9e..53eb589 100644 --- a/web/data/UpdateConfig.json +++ b/web/data/UpdateConfig.json @@ -1,15 +1,15 @@ { - "versionCode":"114", - "versionName":"4.1.0", + "versionCode":"124", + "versionName":"4.2.0", "size": "45 MB", "url": "https://github.com/xoureldeen/Vectras-VM-Android/releases", "Message": "

4.1.0

\nBugs fixed.", "cancellable": true, - "versionCodeBeta":"123", - "versionNameBeta":"4.1.9", - "versionNameBetas":"4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,4.0.7,4.0.8,4.0.9,4.1.0,4.1.1,4.1.2,4.1.3,4.1.4,4.1.5,4.1.6,4.1.7,4.1.8,4.1.9", + "versionCodeBeta":"124", + "versionNameBeta":"4.2.0", + "versionNameBetas":"4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,4.0.7,4.0.8,4.0.9,4.1.1,4.1.2,4.1.3,4.1.4,4.1.5,4.1.6,4.1.7,4.1.8,4.1.9,4.2.0", "sizeBeta": "45 MB", "urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases", - "MessageBeta": "

4.1.9

Bugs fixed.", + "MessageBeta": "

4.2.0

Bugs fixed.", "cancellableBeta": true }