mirror of
https://github.com/xoureldeen/Vectras-VM-Android.git
synced 2026-07-09 17:28:54 +00:00
3.9.8
- Listen to audio from the virtual machines on Vectras VM without Termux. - Added a separate Use local time option for each virtual machine. - Removed Boot from, Use UEFI and Use local time from the general Qemu settings. - Automatically set a screenshot as the virtual machine thumbnail if the virtual machine doesn't have a thumbnail set. - Fixed a bug where update checks weren't working. - Added automatic cleanup of temporary files during setup. - Added a separate folder for Quick Run and automatically cleaned it.
This commit is contained in:
parent
bafaa4d57a
commit
febc93453c
23 changed files with 408 additions and 77 deletions
|
|
@ -12,8 +12,8 @@ android {
|
|||
applicationId "com.vectras.vm"
|
||||
minSdk minApi
|
||||
targetSdk targetApi
|
||||
versionCode 101
|
||||
versionName "3.9.7"
|
||||
versionCode 102
|
||||
versionName "3.9.8"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
multiDexEnabled true
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,11 @@ public class Config {
|
|||
return Config.getCacheDir() + "/" + vmID + "/qmpsocket";
|
||||
}
|
||||
|
||||
public static String getLocalQMPSocketPath(String vmID) {
|
||||
return Config.getCacheDir() + "/" + vmID + "/qmpsocket";
|
||||
}
|
||||
|
||||
|
||||
public static String getLocalVNCSocketPath() {
|
||||
return Config.getCacheDir()+ "/" + vmID + "/vncsocket";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ import com.vectras.vm.utils.FileUtils;
|
|||
import com.vectras.vm.utils.ListUtils;
|
||||
import com.vectras.vm.utils.NetworkUtils;
|
||||
import com.vectras.vm.utils.SimulateKeyEvent;
|
||||
import com.vectras.vm.utils.StreamAudio;
|
||||
import com.vectras.vm.utils.UIUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -105,6 +106,8 @@ public class MainVNCActivity extends VncCanvasActivity {
|
|||
private final ArrayList<HashMap<String, Object>> listmapForSendKey = new ArrayList<>();
|
||||
private boolean isConnected = false;
|
||||
|
||||
private StreamAudio streamAudio;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle b) {
|
||||
super.onCreate(b);
|
||||
|
|
@ -168,6 +171,9 @@ public class MainVNCActivity extends VncCanvasActivity {
|
|||
});
|
||||
|
||||
ConnectionBean.useLocalCursor = MainSettingsManager.getShowVirtualMouse(this) || VMManager.isNeedUseVirtualMouse();
|
||||
|
||||
streamAudio = new StreamAudio(this);
|
||||
streamAudio.setFile(AppConfig.vmFolder + Config.vmID + "/audio.raw");
|
||||
}
|
||||
|
||||
private void setDefaulViewMode() {
|
||||
|
|
@ -773,6 +779,8 @@ public class MainVNCActivity extends VncCanvasActivity {
|
|||
binding.lnNosignal.setVisibility(View.GONE);
|
||||
this.vncCanvas.setFocusableInTouchMode(true);
|
||||
// syncCursorViewWithBitmap();
|
||||
|
||||
streamAudio.play();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -782,6 +790,8 @@ public class MainVNCActivity extends VncCanvasActivity {
|
|||
isConnected = false;
|
||||
binding.lnNosignal.setVisibility(View.VISIBLE);
|
||||
if (started) isQMPPortOpening(firstConnection);
|
||||
|
||||
if (streamAudio.isPlaying()) streamAudio.stop();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ public class ExportRomActivity extends AppCompatActivity {
|
|||
vmConfigMap.put("bootFrom", current.bootFrom);
|
||||
vmConfigMap.put("isShowBootMenu", current.isShowBootMenu);
|
||||
vmConfigMap.put("isUseUefi", current.isUseUefi);
|
||||
vmConfigMap.put("isUseLocalTime", current.isUseLocalTime);
|
||||
vmConfigMap.put("qemu", current.itemExtra.replace(getRomPath, "OhnoIjustrealizeditsmidnightandIstillhavetodothis"));
|
||||
vmConfigMap.put("arch", current.itemArch);
|
||||
|
||||
|
|
@ -186,7 +187,7 @@ public class ExportRomActivity extends AppCompatActivity {
|
|||
} else if (_filelist.get(_repeat).endsWith("cqcm.json")) {
|
||||
FileUtils.writeToFile(tempFolder, "cqcm.json", FileUtils.readAFile(_filelist.get(_repeat)).replace(getRomPath, "OhnoIjustrealizeditsmidnightandIstillhavetodothis"));
|
||||
pathList.add(tempFolder + "cqcm.json");
|
||||
} else if (_filelist.get(_repeat).endsWith("screenshot.ppm")) {
|
||||
} else if (_filelist.get(_repeat).endsWith("screenshot.ppm") || _filelist.get(_repeat).endsWith("screenshot.png") || _filelist.get(_repeat).endsWith("audio.raw")) {
|
||||
//ignore
|
||||
} else {
|
||||
pathList.add(_filelist.get(_repeat));
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public class SplashActivity extends AppCompatActivity {
|
|||
setupFolders();
|
||||
MainSettingsManager.setOrientationSetting(this, 1);
|
||||
setupFiles();
|
||||
cleanUp();
|
||||
runOnUiThread(this::finishSplash);
|
||||
}).start();
|
||||
}
|
||||
|
|
@ -113,6 +114,10 @@ public class SplashActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
private void cleanUp() {
|
||||
FileUtils.delete(AppConfig.vmFolder + "QuickRun");
|
||||
}
|
||||
|
||||
private void finishSplash() {
|
||||
if (MainSettingsManager.getShowLastCrashLog(this)) {
|
||||
startActivity(new Intent(this, LastCrashActivity.class));
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ import java.util.Objects;
|
|||
|
||||
public class StartVM {
|
||||
public static String cache;
|
||||
|
||||
public static String cdrompath;
|
||||
private static boolean isUseUefi;
|
||||
private static DataMainRoms vmConfigs;
|
||||
|
||||
public static String env(Activity activity, DataMainRoms vmData) {
|
||||
vmConfigs = vmData;
|
||||
|
||||
if (VMManager.isNeedLoadMigrate() && FileUtils.isFileExists(AppConfig.vmFolder + Config.vmID + "/snapshot.sh")) {
|
||||
String snapshotParams = FileUtils.readAFile(AppConfig.vmFolder + Config.vmID + "/snapshot.sh").replace("\n", "");
|
||||
if (VMManager.isthiscommandsafe(snapshotParams, activity)) {
|
||||
|
|
@ -50,12 +50,14 @@ public class StartVM {
|
|||
}
|
||||
|
||||
extraParams = bootParams + extraParams;
|
||||
cdrompath = vmData.imgCdrom;
|
||||
isUseUefi = vmData.isUseUefi;
|
||||
return env(activity, extraParams, vmData.itemPath, false);
|
||||
}
|
||||
|
||||
public static String env(Activity activity, String extras, String img, boolean isQuickRun) {
|
||||
if (isQuickRun) {
|
||||
vmConfigs = new DataMainRoms();
|
||||
vmConfigs.isUseLocalTime = false;
|
||||
}
|
||||
|
||||
String filesDir = activity.getFilesDir().getAbsolutePath();
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ public class StartVM {
|
|||
params.add(hdd0);
|
||||
}
|
||||
|
||||
if (cdrompath.isEmpty()) {
|
||||
if (vmConfigs.imgCdrom.isEmpty()) {
|
||||
File cdromFile = new File(filesDir + "/data/Vectras/drive.iso");
|
||||
|
||||
if (cdromFile.exists()) {
|
||||
|
|
@ -143,16 +145,16 @@ public class StartVM {
|
|||
cdrom += " -device";
|
||||
cdrom += " usb-storage,bus=defaultxhci.0,drive=cdrom";
|
||||
cdrom += " -drive";
|
||||
cdrom += " if=none,id=cdrom,format=raw,media=cdrom,file='" + cdrompath + "'";
|
||||
cdrom += " if=none,id=cdrom,format=raw,media=cdrom,file='" + vmConfigs.imgCdrom + "'";
|
||||
} else {
|
||||
if (ifType.isEmpty()) {
|
||||
cdrom = "-cdrom";
|
||||
cdrom += " '" + cdrompath + "'";
|
||||
cdrom += " '" + vmConfigs.imgCdrom + "'";
|
||||
} else {
|
||||
cdrom = "-drive";
|
||||
cdrom += " index=1";
|
||||
cdrom += ",media=cdrom";
|
||||
cdrom += ",file='" + cdrompath + "'";
|
||||
cdrom += ",file='" + vmConfigs.imgCdrom + "'";
|
||||
}
|
||||
}
|
||||
params.add(cdrom);
|
||||
|
|
@ -215,7 +217,7 @@ public class StartVM {
|
|||
bios += "file=" + AppConfig.basefiledir + "QEMU_EFI.img,format=raw,readonly=on,if=pflash";
|
||||
bios += " -drive ";
|
||||
bios += "file=" + AppConfig.basefiledir + "QEMU_VARS.img,format=raw,if=pflash";
|
||||
} else if (MainSettingsManager.getArch(activity).equals("X86_64") && (MainSettingsManager.getuseUEFI(activity) || isUseUefi)) {
|
||||
} else if (MainSettingsManager.getArch(activity).equals("X86_64") && (vmConfigs.isUseUefi)) {
|
||||
bios = "-drive ";
|
||||
bios += "file=" + AppConfig.basefiledir + "RELEASEX64_OVMF.fd,format=raw,readonly=on,if=pflash";
|
||||
bios += " -drive ";
|
||||
|
|
@ -243,7 +245,7 @@ public class StartVM {
|
|||
}
|
||||
|
||||
|
||||
if (MainSettingsManager.useLocalTime(activity)) {
|
||||
if (vmConfigs.isUseLocalTime) {
|
||||
params.add("-rtc");
|
||||
params.add("base=localtime");
|
||||
}
|
||||
|
|
@ -284,8 +286,6 @@ public class StartVM {
|
|||
params.add("defer");
|
||||
}
|
||||
|
||||
isUseUefi = false;
|
||||
|
||||
return String.join(" ", params);
|
||||
}
|
||||
|
||||
|
|
@ -374,7 +374,7 @@ public class StartVM {
|
|||
|
||||
if (!FileUtils.isFileExists(AppConfig.basefiledir + "QEMU_VARS.img"))
|
||||
SetupFeatureCore.copyAssetToFile(context, "roms/QEMU_VARS.img", AppConfig.basefiledir + "QEMU_VARS.img");
|
||||
} else if (arch.equals("X86_64") && (MainSettingsManager.getuseUEFI(context) || isUseUefi)) {
|
||||
} else if (arch.equals("X86_64") && (MainSettingsManager.getuseUEFI(context) || vmConfigs.isUseUefi)) {
|
||||
if (!FileUtils.isFileExists(AppConfig.basefiledir + "RELEASEX64_OVMF.fd"))
|
||||
SetupFeatureCore.copyAssetToFile(context, "roms/RELEASEX64_OVMF.fd", AppConfig.basefiledir + "RELEASEX64_OVMF.fd");
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ import com.vectras.vm.main.core.MainStartVM;
|
|||
import com.vectras.vm.main.vms.DataMainRoms;
|
||||
import com.vectras.vm.manager.QemuConsoleDialog;
|
||||
import com.vectras.vm.manager.QmpSender;
|
||||
import com.vectras.vm.manager.VmActions;
|
||||
import com.vectras.vm.settings.VNCSettingsActivity;
|
||||
import com.vectras.vm.settings.X11DisplaySettingsActivity;
|
||||
import com.vectras.vm.setupwizard.SetupFeatureCore;
|
||||
|
|
@ -905,6 +906,7 @@ public class VMManager {
|
|||
progressDialog.setFixTextColor(true);
|
||||
progressDialog.show();
|
||||
new Thread(() -> {
|
||||
VmActions.takeScreenshot(_activity, false);
|
||||
QmpSender.pause();
|
||||
|
||||
String migrateResult = startMigrate();
|
||||
|
|
@ -1116,4 +1118,23 @@ public class VMManager {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String addAudioDevWav(String vmID, String env) {
|
||||
final String audioDevParam = ",audiodev=snd0 -audiodev wav,id=snd0,path=" + AppConfig.vmFolder + vmID + "/audio.raw ";
|
||||
String result = env;
|
||||
if (env.startsWith("-device hda-duplex ") || env.contains(" -device hda-duplex ") || env.endsWith(" -device hda-duplex")) {
|
||||
result = result.replaceFirst(" -device hda-duplex", " -device hda-duplex" + audioDevParam);
|
||||
} else if (env.startsWith("-device cs4231a ") || env.contains(" -device cs4231a ") || env.endsWith(" -device cs4231a")) {
|
||||
result = result.replaceFirst(" -device cs4231a", " -device cs4231a" + audioDevParam);
|
||||
} else if (env.startsWith("-device ac97 ") || env.contains(" -device ac97 ") || env.endsWith(" -device ac97")) {
|
||||
result = result.replaceFirst(" -device ac97", " -device ac97" + audioDevParam);
|
||||
} else if (env.startsWith("-device es1370 ") || env.contains(" -device es1370 ") || env.endsWith(" -device es1370")) {
|
||||
result = result.replaceFirst(" -device es1370", " -device es1370" + audioDevParam);
|
||||
} else if (env.startsWith("-device sb16 ") || env.contains(" -device sb16 ") || env.endsWith(" -device sb16")) {
|
||||
result = result.replaceFirst(" -device sb16", " -device sb16" + audioDevParam);
|
||||
} else if (env.startsWith("-device adlib ") || env.contains(" -device adlib ") || env.endsWith(" -device adlib")) {
|
||||
result = result.replaceFirst(" -device adlib", " -device adlib" + audioDevParam);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ public class VMCreatorActivity extends AppCompatActivity {
|
|||
private String vmID = VMManager.idGenerator();
|
||||
private boolean isShowBootMenu = false;
|
||||
private boolean isUseUefi = false;
|
||||
private boolean isUseLocalTime = true;
|
||||
private int bootFrom = 0;
|
||||
|
||||
@Override
|
||||
|
|
@ -243,6 +244,9 @@ public class VMCreatorActivity extends AppCompatActivity {
|
|||
binding.cbvShowbootmenu.setOnCheckedChangeListener((v, isChecked) -> isShowBootMenu = isChecked);
|
||||
|
||||
binding.cbvUseuefi.setOnCheckedChangeListener((v, isChecked) -> isUseUefi = isChecked);
|
||||
|
||||
binding.cbvUselocaltime.setOnCheckedChangeListener((v, isChecked) -> isUseLocalTime = isChecked);
|
||||
|
||||
if (!MainSettingsManager.getArch(this).equals("X86_64"))
|
||||
binding.cbvUseuefi.setVisibility(View.GONE);
|
||||
|
||||
|
|
@ -583,6 +587,9 @@ public class VMCreatorActivity extends AppCompatActivity {
|
|||
} else {
|
||||
binding.cbvUseuefi.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
isUseLocalTime = current.isUseLocalTime;
|
||||
binding.cbvUselocaltime.setChecked(isUseLocalTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -715,6 +722,7 @@ public class VMCreatorActivity extends AppCompatActivity {
|
|||
vmConfigMap.put("bootFrom", bootFrom);
|
||||
vmConfigMap.put("isShowBootMenu", isShowBootMenu);
|
||||
vmConfigMap.put("isUseUefi", isUseUefi);
|
||||
vmConfigMap.put("isUseLocalTime", isUseLocalTime);
|
||||
vmConfigMap.put("vmID", vmID);
|
||||
vmConfigMap.put("qmpPort", 8080);
|
||||
return vmConfigMap;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import com.vectras.vm.R;
|
|||
import com.vectras.vm.VMManager;
|
||||
import com.vectras.vm.logger.VectrasStatus;
|
||||
import com.vectras.vm.manager.QmpSender;
|
||||
import com.vectras.vm.manager.VmAudioManager;
|
||||
import com.vectras.vm.settings.ExternalVNCSettingsActivity;
|
||||
import com.vectras.vm.utils.DeviceUtils;
|
||||
import com.vectras.vm.utils.DialogUtils;
|
||||
|
|
@ -154,6 +155,7 @@ public class MainStartVM {
|
|||
if (VMManager.isVMRunning(context, finalvmID)) {
|
||||
Toast.makeText(context, "This VM is already running.", Toast.LENGTH_LONG).show();
|
||||
DisplaySystem.launch(context);
|
||||
VmAudioManager.stream(vmID);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -193,11 +195,13 @@ public class MainStartVM {
|
|||
return;
|
||||
}
|
||||
|
||||
TextView vmBootNote = showProgressDialog(context, vmName, thumbnailFile);
|
||||
FileUtils.delete(AppConfig.vmFolder + vmID + "/audio.raw");
|
||||
|
||||
TextView vmBootNote = showProgressDialog(context, vmName, thumbnailFile, vmID);
|
||||
|
||||
VMManager.isQemuStopedWithError = false;
|
||||
|
||||
String finalCommand = VMManager.addAudioDevSdl(String.format(runCommandFormat, env));
|
||||
String finalCommand = VMManager.addAudioDevWav(vmID, String.format(runCommandFormat, env));
|
||||
|
||||
if (MainSettingsManager.getVmUi(context).equals("X11")) {
|
||||
finalCommand = "export DISPLAY=:0 && " + finalCommand;
|
||||
|
|
@ -239,7 +243,8 @@ public class MainStartVM {
|
|||
if (!forceDisableMigrate && VMManager.isNeedLoadMigrate()) {
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(() -> {
|
||||
if (vmBootNote != null ) vmBootNote.setText(activity.getString(R.string.resuming));
|
||||
if (vmBootNote != null)
|
||||
vmBootNote.setText(activity.getString(R.string.resuming));
|
||||
});
|
||||
}
|
||||
String loadMigrateResponse = VMManager.loadMigrate();
|
||||
|
|
@ -263,7 +268,8 @@ public class MainStartVM {
|
|||
QmpSender.quickShutdown();
|
||||
assert activity != null;
|
||||
activity.runOnUiThread(() -> {
|
||||
if (DialogUtils.isSafeDismiss(activity, progressDialog)) progressDialog.dismiss();
|
||||
if (DialogUtils.isSafeDismiss(activity, progressDialog))
|
||||
progressDialog.dismiss();
|
||||
|
||||
DialogUtils.threeDialog(
|
||||
activity,
|
||||
|
|
@ -314,7 +320,10 @@ public class MainStartVM {
|
|||
// //This feature is not available yet.
|
||||
} else if (MainSettingsManager.getVmUi(context).equals("X11") && !DisplaySystem.isUseBuiltInX11()) {
|
||||
assert activity != null;
|
||||
activity.runOnUiThread(() -> DisplaySystem.launch(context));
|
||||
activity.runOnUiThread(() -> {
|
||||
DisplaySystem.launch(context);
|
||||
VmAudioManager.stream(vmID);
|
||||
});
|
||||
}
|
||||
|
||||
FileUtils.writeToFile(AppConfig.vmFolder + vmID, "snapshot.sh", env);
|
||||
|
|
@ -323,13 +332,15 @@ public class MainStartVM {
|
|||
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(() -> {
|
||||
if (DialogUtils.isSafeDismiss(activity, progressDialog)) progressDialog.dismiss();
|
||||
if (DialogUtils.isSafeDismiss(activity, progressDialog))
|
||||
progressDialog.dismiss();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
} else {
|
||||
assert activity != null;
|
||||
if (DialogUtils.isSafeDismiss(activity, progressDialog)) progressDialog.dismiss();
|
||||
if (DialogUtils.isSafeDismiss(activity, progressDialog))
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
|
||||
skipIDEwithARM64DialogInStartVM = false;
|
||||
|
|
@ -364,7 +375,7 @@ public class MainStartVM {
|
|||
setDefault();
|
||||
}
|
||||
|
||||
public static TextView showProgressDialog(Context context, String _content, String thumbnailFile) {
|
||||
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);
|
||||
|
|
@ -373,18 +384,20 @@ public class MainStartVM {
|
|||
if (thumbnailFile != null) {
|
||||
ImageView ivThumbnail = progressView.findViewById(R.id.iv_thumbnail);
|
||||
|
||||
if (thumbnailFile.isEmpty()) {
|
||||
VMManager.setIconWithName(ivThumbnail, _content);
|
||||
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)
|
||||
.into(ivThumbnail);
|
||||
} else if (FileUtils.isFileExists(AppConfig.vmFolder + vmID + "/screenshot.png")) {
|
||||
Glide.with(context.getApplicationContext())
|
||||
.load(new File(AppConfig.vmFolder + vmID + "/screenshot.png"))
|
||||
.placeholder(R.drawable.ic_computer_180dp_with_padding)
|
||||
.error(R.drawable.ic_computer_180dp_with_padding)
|
||||
.into(ivThumbnail);
|
||||
} else {
|
||||
if (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)
|
||||
.into(ivThumbnail);
|
||||
} else {
|
||||
VMManager.setIconWithName(ivThumbnail, _content);
|
||||
}
|
||||
VMManager.setIconWithName(ivThumbnail, _content);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.vectras.vm.R;
|
|||
import com.vectras.vm.StartVM;
|
||||
import com.vectras.vm.VMManager;
|
||||
import com.vectras.vm.utils.DialogUtils;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
import com.vectras.vterm.Terminal;
|
||||
|
||||
public class PendingCommand {
|
||||
|
|
@ -56,10 +57,10 @@ public class PendingCommand {
|
|||
} else {
|
||||
Log.i(TAG, "Run VM...");
|
||||
|
||||
com.vectras.vm.StartVM.cdrompath = "";
|
||||
Config.vmID = VMManager.idGenerator();
|
||||
Config.vmID = "QuickRun/" + VMManager.idGenerator();
|
||||
new Thread(() -> {
|
||||
String env = StartVM.env(activity, command, "", true);
|
||||
FileUtils.createDirectory(AppConfig.vmFolder + Config.vmID);
|
||||
activity.runOnUiThread(() -> {
|
||||
MainStartVM.startNow(activity, "Quick run", env, Config.vmID, null);
|
||||
VMManager.lastQemuCommand = command;
|
||||
|
|
|
|||
|
|
@ -52,4 +52,6 @@ public class DataMainRoms {
|
|||
public boolean isShowBootMenu = false;
|
||||
|
||||
public boolean isUseUefi = false;
|
||||
|
||||
public boolean isUseLocalTime = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,6 +161,11 @@ public class VmsFragment extends Fragment implements CallbackInterface.HomeCallT
|
|||
} catch (JSONException ignored) {
|
||||
romsMainData.isUseUefi = false;
|
||||
}
|
||||
try {
|
||||
romsMainData.isUseLocalTime = json_data.getBoolean("isUseLocalTime");
|
||||
} catch (JSONException ignored) {
|
||||
romsMainData.isUseLocalTime = true;
|
||||
}
|
||||
|
||||
romsMainData.itemExtra = json_data.getString("imgExtra");
|
||||
tempdata.add(romsMainData);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.vectras.qemu.Config;
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.StartVM;
|
||||
import com.vectras.vm.VMManager;
|
||||
|
|
@ -57,9 +58,7 @@ public class VmsHomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||
final DataMainRoms current = data.get(position);
|
||||
myHolder.textName.setText(current.itemName);
|
||||
myHolder.textArch.setText(current.itemArch);
|
||||
if (current.itemIcon.isEmpty()){
|
||||
VMManager.setIconWithName(myHolder.ivIcon, current.itemName);
|
||||
} else if (FileUtils.isFileExists(current.itemIcon)){
|
||||
if (!current.itemIcon.isEmpty() && FileUtils.isFileExists(current.itemIcon)){
|
||||
Glide.with(activity.getApplicationContext())
|
||||
.load(new File(current.itemIcon))
|
||||
.placeholder(R.drawable.ic_computer_180dp_with_padding)
|
||||
|
|
@ -67,14 +66,21 @@ public class VmsHomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.into(myHolder.ivIcon);
|
||||
} else if (FileUtils.isFileExists(AppConfig.vmFolder + current.vmID + "/screenshot.png")) {
|
||||
Glide.with(activity.getApplicationContext())
|
||||
.load(new File(AppConfig.vmFolder + current.vmID + "/screenshot.png"))
|
||||
.placeholder(R.drawable.ic_computer_180dp_with_padding)
|
||||
.error(R.drawable.ic_computer_180dp_with_padding)
|
||||
.skipMemoryCache(true)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.into(myHolder.ivIcon);
|
||||
} else {
|
||||
myHolder.ivIcon.setImageResource(R.drawable.ic_computer_180dp_with_padding);
|
||||
VMManager.setIconWithName(myHolder.ivIcon, current.itemName);
|
||||
}
|
||||
myHolder.optionsBtn.setOnClickListener(view -> RomOptionsDialog.showNow(activity, position, current.vmID, current.itemName));
|
||||
|
||||
myHolder.cdRoms.setOnClickListener(view -> {
|
||||
VMManager.setArch(current.itemArch, activity);
|
||||
StartVM.cdrompath = current.imgCdrom;
|
||||
if (current.qmpPort == 0) {
|
||||
Config.setDefault();
|
||||
} else {
|
||||
|
|
|
|||
42
app/src/main/java/com/vectras/vm/manager/VmActions.java
Normal file
42
app/src/main/java/com/vectras/vm/manager/VmActions.java
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package com.vectras.vm.manager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.vectras.qemu.Config;
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
import com.vectras.vm.utils.ImageUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class VmActions {
|
||||
public static boolean takeScreenshot(Context context, boolean isSaveToGallery) {
|
||||
boolean isSaved = QmpSender.takeScreenshot();
|
||||
if (isSaved) {
|
||||
try {
|
||||
if (isSaveToGallery) {
|
||||
Uri imageFile = ImageUtils.saveToGallery(
|
||||
context,
|
||||
ImageUtils.ppmToBitmap(new File(AppConfig.vmFolder + Config.vmID + "/screenshot.ppm")),
|
||||
String.valueOf(System.currentTimeMillis())
|
||||
);
|
||||
|
||||
FileUtils.copyFileFromUri(context, imageFile, AppConfig.vmFolder + Config.vmID + "/screenshot.png");
|
||||
} else {
|
||||
ImageUtils.saveBitmapToPNGFile(
|
||||
ImageUtils.ppmToBitmap(new File(AppConfig.vmFolder + Config.vmID + "/screenshot.ppm")),
|
||||
AppConfig.vmFolder + Config.vmID,
|
||||
"screenshot.png"
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
isSaved = false;
|
||||
}
|
||||
}
|
||||
|
||||
FileUtils.delete(AppConfig.vmFolder + Config.vmID + "/screenshot.ppm");
|
||||
|
||||
return isSaved;
|
||||
}
|
||||
}
|
||||
30
app/src/main/java/com/vectras/vm/manager/VmAudioManager.java
Normal file
30
app/src/main/java/com/vectras/vm/manager/VmAudioManager.java
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package com.vectras.vm.manager;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.vectras.qemu.Config;
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.vectras.vm.VectrasApp;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
import com.vectras.vm.utils.StreamAudio;
|
||||
|
||||
public class VmAudioManager {
|
||||
private static final String TAG = "VmAudioManager";
|
||||
public static final StreamAudio streamAudio = new StreamAudio(VectrasApp.getContext());
|
||||
|
||||
public static void stream(String vmID) {
|
||||
if (streamAudio.isPlaying()) streamAudio.stop();
|
||||
|
||||
streamAudio.setFile(AppConfig.vmFolder + vmID + "/audio.raw");
|
||||
streamAudio.play();
|
||||
|
||||
new Thread(() -> {
|
||||
while (FileUtils.isFileExists(Config.getLocalQMPSocketPath(vmID))) {
|
||||
try { Thread.sleep(2000); } catch (InterruptedException ignored) { return; }
|
||||
}
|
||||
|
||||
Log.d(TAG, "Stoped.");
|
||||
streamAudio.stop();
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.vectras.vm.manager;
|
|||
import android.androidVNC.ConnectionBean;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
|
@ -228,26 +229,12 @@ public class VmControllerDialog extends DialogFragment {
|
|||
progressDialog.show();
|
||||
|
||||
new Thread(() -> {
|
||||
boolean isSaved = QmpSender.takeScreenshot();
|
||||
if (isSaved) {
|
||||
try {
|
||||
ImageUtils.saveToGallery(
|
||||
requireActivity(),
|
||||
ImageUtils.ppmToBitmap(new File (AppConfig.vmFolder + Config.vmID + "/screenshot.ppm")),
|
||||
String.valueOf(System.currentTimeMillis())
|
||||
);
|
||||
} catch (Exception e) {
|
||||
isSaved = false;
|
||||
}
|
||||
}
|
||||
boolean isSaved = VmActions.takeScreenshot(requireActivity(), true);
|
||||
|
||||
FileUtils.delete(AppConfig.vmFolder + Config.vmID + "/screenshot.ppm");
|
||||
|
||||
boolean finalIsSaved = isSaved;
|
||||
new Handler(Looper.getMainLooper()).post(() -> {
|
||||
if (!isAdded()) return;
|
||||
progressDialog.reset();
|
||||
Toast.makeText(requireActivity().getApplicationContext(), getString(finalIsSaved ? R.string.saved_to_the_gallery : R.string.unable_to_take_a_screenshot), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(requireActivity().getApplicationContext(), getString(isSaved ? R.string.saved_to_the_gallery : R.string.unable_to_take_a_screenshot), Toast.LENGTH_SHORT).show();
|
||||
dismiss();
|
||||
});
|
||||
}).start();
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import android.net.Uri;
|
|||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
|
|
@ -24,10 +25,13 @@ import com.vectras.vm.utils.PackageUtils;
|
|||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public class UpdaterActivity extends AppCompatActivity {
|
||||
|
||||
private final String TAG ="UpdaterActivity";
|
||||
|
||||
ActivityUpdaterBinding binding;
|
||||
String downloadUrl = "";
|
||||
|
||||
|
|
@ -78,7 +82,7 @@ public class UpdaterActivity extends AppCompatActivity {
|
|||
int versionCode = PackageUtils.getThisVersionCode(getApplicationContext());
|
||||
String versionName = PackageUtils.getThisVersionName(getApplicationContext());
|
||||
|
||||
Retrofit2Utils.get(AppConfig.vectrasRaw + "software-store.json", ((isSuccess, body, status, error) -> {
|
||||
Retrofit2Utils.get(AppConfig.vectrasRaw + "UpdateConfig.json", ((isSuccess, body, status, error) -> {
|
||||
if (isSuccess) {
|
||||
binding.lpiProgressbar.setVisibility(View.GONE);
|
||||
binding.lnBottombar.setVisibility(View.VISIBLE);
|
||||
|
|
@ -140,12 +144,15 @@ public class UpdaterActivity extends AppCompatActivity {
|
|||
null);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.d(TAG, "JSONException: ", e);
|
||||
whenUpToDate();
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Empty body.");
|
||||
whenUpToDate();
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Failed.");
|
||||
whenUpToDate();
|
||||
}
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import android.content.Context;
|
|||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.VMManager;
|
||||
import com.vectras.vm.utils.DeviceUtils;
|
||||
|
|
@ -60,13 +59,30 @@ public class SetupFeatureCore {
|
|||
lastErrorLog = "";
|
||||
|
||||
File distroDir = new File(filesDir + "/distro");
|
||||
if (!distroDir.exists()) {
|
||||
if (!distroDir.mkdir()) {
|
||||
lastErrorLog = "Failed to create directory: " + filesDir + "/distro";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
File binDir = new File(distroDir + "/bin");
|
||||
if (!binDir.exists()) {
|
||||
if (!isInstalledProot(context)) {
|
||||
if (!extractSystemFiles(context, "bootstrap", "")) return false;
|
||||
if (!extractSystemFiles(context, "bootstrap", "", false)) {
|
||||
if (!extractSystemFiles(context, "bootstrap", "", true)) return false;
|
||||
}
|
||||
|
||||
/*if (!mkSymlinks(filesDir + "/usr/lib/")) {
|
||||
lastErrorLog = "Failed to create symlinks.";
|
||||
return false;
|
||||
}*/
|
||||
}
|
||||
|
||||
if (isInstalledDistro(context)) return true;
|
||||
if (isInstalledDistro(context)) {
|
||||
lastErrorLog = "Installed proot.";
|
||||
return true;
|
||||
}
|
||||
|
||||
File tmpDir = new File(context.getFilesDir(), "usr/tmp");
|
||||
if (!tmpDir.isDirectory()) {
|
||||
|
|
@ -77,12 +93,17 @@ public class SetupFeatureCore {
|
|||
}
|
||||
}
|
||||
|
||||
return extractSystemFiles(context, "alpine19", "distro");
|
||||
if (!extractSystemFiles(context, "alpine19", "distro", false)) {
|
||||
return extractSystemFiles(context, "alpine19", "distro", true);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean extractSystemFiles(Context context, String fromAsset, String extractTo) {
|
||||
public static boolean extractSystemFiles(Context context, String fromAsset, String extractTo, boolean tryNoSameOwner) {
|
||||
String randomFileName = VMManager.startRamdomVMID();
|
||||
String filesDir = context.getFilesDir().getAbsolutePath();
|
||||
String abi = Build.SUPPORTED_ABIS[0];
|
||||
|
|
@ -99,9 +120,11 @@ public class SetupFeatureCore {
|
|||
// Step 2: Run tar extraction
|
||||
if (isCompleted) {
|
||||
String[] cmdline = {"tar", "xf", extractedFilePath, "-C", filesDir + "/" + extractTo};
|
||||
String[] cmdline2 = {"tar", "xf", extractedFilePath, "-C", filesDir + "/" + extractTo, "--no-same-owner"};
|
||||
|
||||
Process process = null;
|
||||
try {
|
||||
process = Runtime.getRuntime().exec(cmdline);
|
||||
process = Runtime.getRuntime().exec(tryNoSameOwner ? cmdline2 : cmdline);
|
||||
|
||||
// Capture standard error output (stderr)
|
||||
BufferedReader errorReader =
|
||||
|
|
@ -121,11 +144,18 @@ public class SetupFeatureCore {
|
|||
}
|
||||
|
||||
// If there was any output in stderr, treat it as an error
|
||||
return exitCode == 0 && errorOutput.length() <= 0;
|
||||
if (exitCode == 0 && errorOutput.length() <= 0) {
|
||||
return true;
|
||||
} else {
|
||||
lastErrorLog = errorOutput.toString();
|
||||
return false;
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
lastErrorLog = lastErrorLog.isEmpty() ? e.toString() : lastErrorLog + "\n" + e;
|
||||
Log.e(TAG, "extractSystemFiles: ", e);
|
||||
} finally {
|
||||
FileUtils.delete(extractedFilePath);
|
||||
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
|
|
@ -134,6 +164,27 @@ public class SetupFeatureCore {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static boolean mkSymlinks(String libPath) {
|
||||
Log.d(TAG, "mkSymlinks: Creating...");
|
||||
return FileUtils.symlink(libPath + "libtalloc.so.2.4.2", libPath + "libtalloc.so.2") &&
|
||||
FileUtils.symlink(libPath + "libtalloc.so.2", libPath + "libtalloc.so");
|
||||
}
|
||||
|
||||
public static void fixPermissions(String distroPath) {
|
||||
File binDir = new File(distroPath + "/bin");
|
||||
File usrBinDir = new File(distroPath + "/usr/bin");
|
||||
File sbinDir = new File(distroPath + "/sbin");
|
||||
File usrSbinDir = new File(distroPath + "/usr/sbin");
|
||||
|
||||
for (File dir : new File[]{binDir, usrBinDir, sbinDir, usrSbinDir}) {
|
||||
if (dir.exists() && dir.listFiles() != null) {
|
||||
for (File f : dir.listFiles()) {
|
||||
f.setExecutable(true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean copyAssetToFile(Context context, String assetPath, String outputPath) {
|
||||
try (InputStream in = context.getAssets().open(assetPath);
|
||||
OutputStream out = new FileOutputStream(outputPath)) {
|
||||
|
|
|
|||
|
|
@ -856,6 +856,26 @@ public class FileUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean symlink(String targetPath, String linkPath) {
|
||||
File target = new File(targetPath);
|
||||
File linkFile = new File(linkPath);
|
||||
|
||||
if (target.exists()) {
|
||||
if (linkFile.exists() && !linkFile.delete()) return false;
|
||||
try {
|
||||
Os.symlink(target.getAbsolutePath(), linkFile.getAbsolutePath());
|
||||
Log.d(TAG, "Symlink: " + linkFile.getAbsolutePath() + " → " + target.getAbsolutePath());
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, "Symlink failed: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static String getMd5(String filePath) {
|
||||
try (InputStream inputStream = new FileInputStream(filePath)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
|
|
|
|||
110
app/src/main/java/com/vectras/vm/utils/StreamAudio.java
Normal file
110
app/src/main/java/com/vectras/vm/utils/StreamAudio.java
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
package com.vectras.vm.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioTrack;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
public class StreamAudio {
|
||||
private final String TAG = "StreamAudio";
|
||||
private Context context;
|
||||
private boolean isPlay;
|
||||
private String filePath = "";
|
||||
|
||||
public StreamAudio(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
return isPlay;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
isPlay = false;
|
||||
}
|
||||
|
||||
public void play() {
|
||||
if (!filePath.isEmpty()) streamFromFile();
|
||||
}
|
||||
|
||||
public void setFile(String path) {
|
||||
filePath = path;
|
||||
}
|
||||
|
||||
public void streamFromFile() {
|
||||
isPlay = true;
|
||||
|
||||
new Thread(() -> {
|
||||
Log.d(TAG, "Play: " + filePath);
|
||||
|
||||
while (isPlay && !FileUtils.isFileExists(filePath)) {
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
isPlay = false;
|
||||
Log.d(TAG, "Failed to wait for: " + filePath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPlay) return;
|
||||
|
||||
Log.d(TAG, "Preparing to play: " + filePath);
|
||||
|
||||
int minBuf = AudioTrack.getMinBufferSize(44100,
|
||||
AudioFormat.CHANNEL_OUT_STEREO,
|
||||
AudioFormat.ENCODING_PCM_16BIT);
|
||||
|
||||
AudioTrack audioTrack = new AudioTrack.Builder()
|
||||
.setAudioAttributes(new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.build())
|
||||
.setAudioFormat(new AudioFormat.Builder()
|
||||
.setSampleRate(44100)
|
||||
.setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
|
||||
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
||||
.build())
|
||||
.setBufferSizeInBytes(minBuf * 4)
|
||||
.setTransferMode(AudioTrack.MODE_STREAM)
|
||||
.build();
|
||||
|
||||
audioTrack.play();
|
||||
|
||||
try (RandomAccessFile raf = new RandomAccessFile(filePath, "r")) {
|
||||
byte[] buffer = new byte[minBuf];
|
||||
long lastPos = raf.length();
|
||||
|
||||
Log.d(TAG, "Playing: " + filePath);
|
||||
|
||||
while (isPlay && !isContextDestroyed(context)) {
|
||||
long fileSize = raf.length();
|
||||
|
||||
if (fileSize > lastPos) {
|
||||
raf.seek(lastPos);
|
||||
int bytesRead = raf.read(buffer);
|
||||
if (bytesRead > 0) {
|
||||
audioTrack.write(buffer, 0, bytesRead);
|
||||
lastPos += bytesRead;
|
||||
}
|
||||
} else {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
isPlay = false;
|
||||
Log.e(TAG, e.getMessage());
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private boolean isContextDestroyed(Context context) {
|
||||
if (context instanceof Activity activity) {
|
||||
return activity.isDestroyed() || activity.isFinishing();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -285,6 +285,13 @@
|
|||
android:layout_height="wrap_content"
|
||||
app:setText="@string/use_uefi_for_x64_only"/>
|
||||
|
||||
<com.vectras.vm.view.CheckBoxView
|
||||
android:id="@+id/cbv_uselocaltime"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:setText="@string/use_local_time"
|
||||
app:setChecked="true"/>
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
android:key="ifType"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:icon="@drawable/round_dns_24" />
|
||||
<ListPreference
|
||||
<!--<ListPreference
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:key="boot"
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
android:entryValues="@array/bootValues"
|
||||
android:defaultValue="c"
|
||||
app:icon="@drawable/ic_b"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
app:useSimpleSummaryProvider="true" />-->
|
||||
<SwitchPreferenceCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -102,13 +102,13 @@
|
|||
android:key="useDefaultBios"
|
||||
android:title="@string/use_default_bios_uefi"
|
||||
app:icon="@drawable/hard_disk_24px" />
|
||||
<SwitchPreferenceCompat
|
||||
<!--<SwitchPreferenceCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:defaultValue="false"
|
||||
android:key="useUEFI"
|
||||
android:title="@string/use_uefi_for_x64_only"
|
||||
app:icon="@drawable/hard_disk_24px" />
|
||||
app:icon="@drawable/hard_disk_24px" />-->
|
||||
<SwitchPreferenceCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -116,13 +116,13 @@
|
|||
android:key="useMemoryOvercommit"
|
||||
android:title="@string/use_memory_overcommit"
|
||||
app:icon="@drawable/memory_alt_24px" />
|
||||
<SwitchPreferenceCompat
|
||||
<!--<SwitchPreferenceCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:defaultValue="true"
|
||||
android:key="useLocalTime"
|
||||
android:title="@string/use_local_time"
|
||||
app:icon="@drawable/schedule_24px" />
|
||||
app:icon="@drawable/schedule_24px" />-->
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
"url": "https://github.com/xoureldeen/Vectras-VM-Android/releases",
|
||||
"Message": "<h2>3.9.0</h2>\nBugs fixed.",
|
||||
"cancellable": true,
|
||||
"versionCodeBeta":"101",
|
||||
"versionNameBeta":"3.9.7",
|
||||
"versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8,3.3.9,3.4.1,3.4.2,3.4.3,3.4.4,3.4.5,3.4.6,3.4.7,3.4.8,3.4.9,3.5.1,3.5.2,3.5.3,3.5.4,3.5.5,3.5.6,3.5.7,3.5.8,3.5.9,3.6.1,3.6.2,3.6.3,3.6.4,3.6.5,3.6.6,3.6.7,3.6.8,3.6.9,3.7.1,3.7.2,3.7.3,3.7.4,3.7.5,3.7.6,3.7.7,3.7.8,3.7.9,3.8.0,3.8.1,3.8.2,3.8.3,3.8.4,3.8.5,3.8.6,3.8.7,3.8.8,3.8.9,3.9.0,3.9.1,3.9.2,3.9.3,3.9.4,3.9.5,3.9.6,3.9.7",
|
||||
"versionCodeBeta":"102",
|
||||
"versionNameBeta":"3.9.8",
|
||||
"versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8,3.3.9,3.4.1,3.4.2,3.4.3,3.4.4,3.4.5,3.4.6,3.4.7,3.4.8,3.4.9,3.5.1,3.5.2,3.5.3,3.5.4,3.5.5,3.5.6,3.5.7,3.5.8,3.5.9,3.6.1,3.6.2,3.6.3,3.6.4,3.6.5,3.6.6,3.6.7,3.6.8,3.6.9,3.7.1,3.7.2,3.7.3,3.7.4,3.7.5,3.7.6,3.7.7,3.7.8,3.7.9,3.8.0,3.8.1,3.8.2,3.8.3,3.8.4,3.8.5,3.8.6,3.8.7,3.8.8,3.8.9,3.9.0,3.9.1,3.9.2,3.9.3,3.9.4,3.9.5,3.9.6,3.9.7,3.9.8",
|
||||
"sizeBeta": "45 MB",
|
||||
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
|
||||
"MessageBeta": "<h2>3.9.7</h2>Bugs fixed.",
|
||||
"MessageBeta": "<h2>3.9.8</h2>Bugs fixed.",
|
||||
"cancellableBeta": true
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue