mirror of
https://github.com/xoureldeen/Vectras-VM-Android.git
synced 2026-05-20 09:40:18 +00:00
3.9.2
- Added 3dfx wrappers disc and VirtIO for Windows disc for mounting to optical drives and use. - Added UEFI option for virtual machines. - Asset files are only checked and extracted when necessary.
This commit is contained in:
parent
f9ed010d7e
commit
652dfc663e
26 changed files with 642 additions and 69 deletions
|
|
@ -6,12 +6,17 @@ import retrofit2.Call;
|
|||
import retrofit2.http.Body;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.Streaming;
|
||||
import retrofit2.http.Url;
|
||||
|
||||
public interface ApiService {
|
||||
@GET
|
||||
Call<ResponseBody> getRawJson(@Url String url);
|
||||
|
||||
@Streaming
|
||||
@GET
|
||||
Call<ResponseBody> downloadFile(@Url String url);
|
||||
|
||||
@POST
|
||||
Call<ResponseBody> post(@Url String url, @Body RequestBody body);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ package com.anbui.elephant.retrofit2utils;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
|
@ -85,4 +88,58 @@ public class Retrofit2Utils {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface DownloadCallback {
|
||||
void onProgress(int percent);
|
||||
void onResult(boolean success, String path, Throwable error);
|
||||
}
|
||||
|
||||
public static void download(String url, String outputPath, DownloadCallback callback) {
|
||||
api.downloadFile(url).enqueue(new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
|
||||
if (!response.isSuccessful() || response.body() == null) {
|
||||
callback.onResult(false, null, new Exception("Response error"));
|
||||
return;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
try (InputStream in = response.body().byteStream();
|
||||
OutputStream out = new FileOutputStream(outputPath)) {
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
long total = response.body().contentLength();
|
||||
long downloaded = 0;
|
||||
|
||||
int read;
|
||||
int lastPercent = 0;
|
||||
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, read);
|
||||
downloaded += read;
|
||||
|
||||
if (total > 0) {
|
||||
int percent = (int) (downloaded * 100 / total);
|
||||
if (percent != lastPercent) {
|
||||
lastPercent = percent;
|
||||
callback.onProgress(percent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.flush();
|
||||
callback.onResult(true, outputPath, null);
|
||||
|
||||
} catch (Exception e) {
|
||||
callback.onResult(false, null, e);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<ResponseBody> call, Throwable t) {
|
||||
callback.onResult(false, null, t);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,4 +117,7 @@ public class AppConfig {
|
|||
|
||||
public static String patreonLink = "https://www.patreon.com/VectrasTeam";
|
||||
|
||||
public static String virtIOWinUrl = "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.285-1/virtio-win.iso";
|
||||
public static String virtIOWinUrlMd5 = "9e650d0e7c6e017a91ca299c8f7ed766";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ public class ExportRomActivity extends AppCompatActivity {
|
|||
|
||||
vmConfigMap.put("bootFrom", current.bootFrom);
|
||||
vmConfigMap.put("isShowBootMenu", current.isShowBootMenu);
|
||||
vmConfigMap.put("isUseUefi", current.isUseUefi);
|
||||
vmConfigMap.put("qemu", current.itemExtra.replace(getRomPath, "OhnoIjustrealizeditsmidnightandIstillhavetodothis"));
|
||||
vmConfigMap.put("arch", current.itemArch);
|
||||
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ public class SplashActivity extends AppCompatActivity {
|
|||
Log.e(TAG, "Create roms-data.json file failed: ", e);
|
||||
}
|
||||
|
||||
FileInstaller.installFiles(this, true);
|
||||
//FileInstaller.installFiles(this, true);
|
||||
}
|
||||
|
||||
private void setupFolders() {
|
||||
|
|
|
|||
|
|
@ -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.setupwizard.SetupFeatureCore;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
import com.vectras.vm.utils.TextUtils;
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ public class StartVM {
|
|||
public static String cache;
|
||||
|
||||
public static String cdrompath;
|
||||
private static boolean isUseUefi;
|
||||
|
||||
public static String env(Activity activity, DataMainRoms vmData) {
|
||||
if (VMManager.isNeedLoadMigrate() && FileUtils.isFileExists(AppConfig.vmFolder + Config.vmID + "/snapshot.sh")) {
|
||||
|
|
@ -49,6 +51,7 @@ public class StartVM {
|
|||
|
||||
extraParams = bootParams + extraParams;
|
||||
cdrompath = vmData.imgCdrom;
|
||||
isUseUefi = vmData.isUseUefi;
|
||||
return env(activity, extraParams, vmData.itemPath, false);
|
||||
}
|
||||
|
||||
|
|
@ -212,7 +215,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)) {
|
||||
} else if (MainSettingsManager.getArch(activity).equals("X86_64") && (MainSettingsManager.getuseUEFI(activity) || isUseUefi)) {
|
||||
bios = "-drive ";
|
||||
bios += "file=" + AppConfig.basefiledir + "RELEASEX64_OVMF.fd,format=raw,readonly=on,if=pflash";
|
||||
bios += " -drive ";
|
||||
|
|
@ -221,6 +224,8 @@ public class StartVM {
|
|||
bios = "-bios ";
|
||||
bios += AppConfig.basefiledir + "bios-vectras.bin";
|
||||
}
|
||||
|
||||
extractFirmware(activity);
|
||||
}
|
||||
|
||||
String machine = "-M ";
|
||||
|
|
@ -279,6 +284,8 @@ public class StartVM {
|
|||
params.add("defer");
|
||||
}
|
||||
|
||||
isUseUefi = false;
|
||||
|
||||
return String.join(" ", params);
|
||||
}
|
||||
|
||||
|
|
@ -355,4 +362,28 @@ public class StartVM {
|
|||
.trim();
|
||||
}
|
||||
|
||||
public static void extractFirmware(Context context) {
|
||||
if (MainSettingsManager.useDefaultBios(context)) {
|
||||
String arch = MainSettingsManager.getArch(context);
|
||||
|
||||
FileUtils.createDirectory(AppConfig.basefiledir);
|
||||
|
||||
if (arch.equals("ARM64")) {
|
||||
if (!FileUtils.isFileExists(AppConfig.basefiledir + "QEMU_EFI.img"))
|
||||
SetupFeatureCore.copyAssetToFile(context, "roms/QEMU_EFI.img", AppConfig.basefiledir + "QEMU_EFI.img");
|
||||
|
||||
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)) {
|
||||
if (!FileUtils.isFileExists(AppConfig.basefiledir + "RELEASEX64_OVMF.fd"))
|
||||
SetupFeatureCore.copyAssetToFile(context, "roms/RELEASEX64_OVMF.fd", AppConfig.basefiledir + "RELEASEX64_OVMF.fd");
|
||||
|
||||
if (!FileUtils.isFileExists(AppConfig.basefiledir + "RELEASEX64_OVMF_VARS.fd"))
|
||||
SetupFeatureCore.copyAssetToFile(context, "roms/RELEASEX64_OVMF_VARS.fd", AppConfig.basefiledir + "RELEASEX64_OVMF_VARS.fd");
|
||||
} else {
|
||||
if (!FileUtils.isFileExists(AppConfig.basefiledir + "bios-vectras.bin"))
|
||||
SetupFeatureCore.copyAssetToFile(context, "roms/bios-vectras.bin", AppConfig.basefiledir + "bios-vectras.bin");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import static java.lang.Thread.sleep;
|
|||
import android.androidVNC.ConnectionBean;
|
||||
import android.androidVNC.VncCanvasActivity;
|
||||
import android.app.Activity;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
|
|
@ -25,7 +26,10 @@ import android.widget.Toast;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import com.anbui.elephant.retrofit2utils.Retrofit2Utils;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.google.gson.Gson;
|
||||
|
|
@ -45,9 +49,11 @@ import com.vectras.vm.main.core.MainStartVM;
|
|||
import com.vectras.vm.main.vms.DataMainRoms;
|
||||
import com.vectras.vm.settings.VNCSettingsActivity;
|
||||
import com.vectras.vm.settings.X11DisplaySettingsActivity;
|
||||
import com.vectras.vm.setupwizard.SetupFeatureCore;
|
||||
import com.vectras.vm.utils.DialogUtils;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
import com.vectras.vm.utils.JSONUtils;
|
||||
import com.vectras.vm.utils.NotificationUtils;
|
||||
import com.vectras.vm.utils.ProgressDialog;
|
||||
import com.vectras.vm.utils.TextUtils;
|
||||
import com.vectras.vterm.Terminal;
|
||||
|
|
@ -227,6 +233,7 @@ public class VMManager {
|
|||
public static boolean hideVM(String vmId) {
|
||||
return FileUtils.rename(AppConfig.vmFolder + vmId, "_" + vmId);
|
||||
}
|
||||
|
||||
public static boolean unHideVM(String vmPath) {
|
||||
return FileUtils.rename(vmPath, new File(vmPath).getName().replace("_", ""));
|
||||
}
|
||||
|
|
@ -305,7 +312,8 @@ public class VMManager {
|
|||
_activity.runOnUiThread(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||
progressDialog.reset();
|
||||
MainActivity.refeshVMListNow();
|
||||
if (!result) DialogUtils.oopsDialog(_activity, _activity.getString(R.string.an_error_occurred_while_deleting_the_vm));
|
||||
if (!result)
|
||||
DialogUtils.oopsDialog(_activity, _activity.getString(R.string.an_error_occurred_while_deleting_the_vm));
|
||||
}, 500));
|
||||
}).start();
|
||||
},
|
||||
|
|
@ -336,7 +344,8 @@ public class VMManager {
|
|||
if (isVmFilesInUse(vmId, vmList)) {
|
||||
isKeptSomeFiles = true;
|
||||
isCompleted = hideVM(vmId);
|
||||
if (isCompleted) vmList = vmList.replace(AppConfig.vmFolder + vmId, AppConfig.vmFolder + "_" + vmId);
|
||||
if (isCompleted)
|
||||
vmList = vmList.replace(AppConfig.vmFolder + vmId, AppConfig.vmFolder + "_" + vmId);
|
||||
} else {
|
||||
isCompleted = FileUtils.delete(new File(AppConfig.vmFolder + vmId));
|
||||
}
|
||||
|
|
@ -893,6 +902,11 @@ public class VMManager {
|
|||
|
||||
public static void showChangeRemovableDevicesDialog(Activity _activity, VncCanvasActivity vncCanvasActivity) {
|
||||
new Thread(() -> {
|
||||
if (FileUtils.isFileExists(AppConfig.vmFolder + Config.vmID + "/snapshot.sh")) {
|
||||
String snapshotParams = FileUtils.readAFile(AppConfig.vmFolder + Config.vmID + "/snapshot.sh");
|
||||
if (!snapshotParams.isEmpty()) lastQemuCommand = snapshotParams;
|
||||
}
|
||||
|
||||
String allDevice = getAllDevicesInQemu();
|
||||
|
||||
_activity.runOnUiThread(() -> {
|
||||
|
|
@ -901,7 +915,7 @@ public class VMManager {
|
|||
.setView(_view)
|
||||
.create();
|
||||
|
||||
_view.findViewById(R.id.ln_pause).setOnClickListener( v -> {
|
||||
_view.findViewById(R.id.ln_pause).setOnClickListener(v -> {
|
||||
DialogUtils.twoDialog(
|
||||
_activity,
|
||||
_activity.getString(R.string.pause),
|
||||
|
|
@ -978,8 +992,31 @@ public class VMManager {
|
|||
ejectCDROM(_activity);
|
||||
_dialog.dismiss();
|
||||
});
|
||||
|
||||
if (!allDevice.contains(AppConfig.basefiledir + "3dfx-wrappers.iso")) _view.findViewById(R.id.iv_eject3dfx).setVisibility(View.GONE);
|
||||
|
||||
_view.findViewById(R.id.ln_3dfx).setOnClickListener(v -> {
|
||||
if (allDevice.contains(AppConfig.basefiledir + "3dfx-wrappers.iso")) {
|
||||
ejectCDROM(_activity);
|
||||
} else {
|
||||
mount3dfxWrappersTool(_activity);
|
||||
}
|
||||
_dialog.dismiss();
|
||||
});
|
||||
|
||||
if (!allDevice.contains(AppConfig.basefiledir + "virtio-win.iso")) _view.findViewById(R.id.iv_ejectvirtio).setVisibility(View.GONE);
|
||||
|
||||
_view.findViewById(R.id.ln_virtio).setOnClickListener(v -> {
|
||||
if (allDevice.contains(AppConfig.basefiledir + "virtio-win.iso")) {
|
||||
ejectCDROM(_activity);
|
||||
} else {
|
||||
mountVirtIOWinTool(_activity);
|
||||
}
|
||||
_dialog.dismiss();
|
||||
});
|
||||
} else {
|
||||
_view.findViewById(R.id.ln_cdrom).setVisibility(View.GONE);
|
||||
_view.findViewById(R.id.ln_tools).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (allDevice.contains("floppy0")) {
|
||||
|
|
@ -1163,6 +1200,116 @@ public class VMManager {
|
|||
}, 200);
|
||||
}
|
||||
|
||||
public static void mount3dfxWrappersTool(Activity activity) {
|
||||
new Thread(() -> {
|
||||
if (!FileUtils.isFileExists(AppConfig.basefiledir + "3dfx-wrappers.iso"))
|
||||
SetupFeatureCore.copyAssetToFile(activity, "roms/3dfx-wrappers.iso", AppConfig.basefiledir + "3dfx-wrappers.iso");
|
||||
|
||||
activity.runOnUiThread(() -> changeCDROM(AppConfig.basefiledir + "3dfx-wrappers.iso", activity));
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static void mountVirtIOWinTool(Activity activity) {
|
||||
new Thread(() -> {
|
||||
if (!FileUtils.isFileExists(AppConfig.basefiledir + "virtio-win.iso")) {
|
||||
FileUtils.delete(new File(AppConfig.basefiledir + "virtio-win.bin"));
|
||||
activity.runOnUiThread(() -> DialogUtils.twoDialog(
|
||||
activity,
|
||||
activity.getString(R.string.download_required),
|
||||
activity.getString(R.string.this_tool_needs_to_be_downloaded_before_use),
|
||||
activity.getString(R.string.ok),
|
||||
activity.getString(R.string.cancel),
|
||||
true,
|
||||
R.drawable.arrow_downward_24px,
|
||||
true,
|
||||
() -> new Thread(() -> {
|
||||
|
||||
int notificationId = 30;
|
||||
|
||||
if (!NotificationUtils.isChannelExist(NotificationUtils.downloadChannelId, activity)) {
|
||||
NotificationUtils.createChannel("Download", "View the file download process.",
|
||||
NotificationUtils.downloadChannelId, NotificationManager.IMPORTANCE_DEFAULT, activity);
|
||||
}
|
||||
|
||||
NotificationManagerCompat manager = NotificationManagerCompat.from(VectrasApp.getContext());
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(VectrasApp.getContext(), NotificationUtils.downloadChannelId)
|
||||
.setSmallIcon(R.drawable.arrow_cool_down_24px)
|
||||
.setContentTitle(activity.getString(R.string.virtio_tools_for_windows))
|
||||
.setContentText("0%")
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setOngoing(true)
|
||||
.setOnlyAlertOnce(true);
|
||||
|
||||
builder.setProgress(100, 0, false);
|
||||
manager.notify(notificationId, builder.build());
|
||||
|
||||
Retrofit2Utils.download(AppConfig.virtIOWinUrl, AppConfig.basefiledir + "virtio-win.bin", new Retrofit2Utils.DownloadCallback() {
|
||||
@Override
|
||||
public void onProgress(int percent) {
|
||||
builder.setProgress(100, percent, false)
|
||||
.setContentText(percent + "%");
|
||||
manager.notify(notificationId, builder.build());
|
||||
Log.d("DL", percent + "%");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResult(boolean success, String path, Throwable error) {
|
||||
if (success) {
|
||||
FileUtils.move(AppConfig.basefiledir + "virtio-win.bin", AppConfig.basefiledir + "virtio-win.iso");
|
||||
|
||||
if (DialogUtils.isAllowShow(activity)) {
|
||||
NotificationUtils.recall(activity, notificationId);
|
||||
|
||||
activity.runOnUiThread(() -> DialogUtils.twoDialog(
|
||||
activity,
|
||||
activity.getString(R.string.virtio_tools_for_windows_is_now_ready_to_use),
|
||||
activity.getString(R.string.do_you_want_to_insert_it_into_the_optical_drive_right_now),
|
||||
activity.getString(R.string.ok),
|
||||
activity.getString(R.string.cancel),
|
||||
true,
|
||||
R.drawable.check_24px,
|
||||
true,
|
||||
() -> changeCDROM(AppConfig.basefiledir + "virtio-win.iso", activity),
|
||||
null,
|
||||
null)
|
||||
);
|
||||
} else {
|
||||
Context context = VectrasApp.getContext();
|
||||
|
||||
builder.setProgress(0, 0, false)
|
||||
.setSmallIcon(R.drawable.check_24px)
|
||||
.setContentText(context.getString(R.string.virtio_tools_for_windows_is_now_ready_to_use))
|
||||
.setOngoing(false);
|
||||
|
||||
manager.notify(notificationId, builder.build());
|
||||
}
|
||||
} else {
|
||||
FileUtils.delete(new File(AppConfig.basefiledir + "virtio-win.bin"));
|
||||
|
||||
if (DialogUtils.isAllowShow(activity)) {
|
||||
activity.runOnUiThread(() -> DialogUtils.oopsDialog(activity, activity.getString(R.string.download_failed_note)));
|
||||
} else {
|
||||
Context context = VectrasApp.getContext();
|
||||
|
||||
builder.setProgress(0, 0, false)
|
||||
.setSmallIcon(R.drawable.error_96px)
|
||||
.setContentText(context.getString(R.string.download_failed_note))
|
||||
.setOngoing(false);
|
||||
|
||||
manager.notify(notificationId, builder.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}).start(),
|
||||
null,
|
||||
null));
|
||||
} else {
|
||||
changeCDROM(AppConfig.basefiledir + "virtio-win.iso", activity);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static void changeCDROM(String _path, Activity _activity) {
|
||||
new Thread(() -> {
|
||||
if (isUsingQ35(lastQemuCommand)) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.vectras.qemu.MainSettingsManager;
|
||||
import com.vectras.vm.AppConfig;
|
||||
import com.vectras.vm.Fragment.CreateImageDialogFragment;
|
||||
|
|
@ -73,6 +74,7 @@ public class VMCreatorActivity extends AppCompatActivity {
|
|||
private String thumbnailPath = "";
|
||||
private String vmID = VMManager.idGenerator();
|
||||
private boolean isShowBootMenu = false;
|
||||
private boolean isUseUefi = false;
|
||||
private int bootFrom = 0;
|
||||
|
||||
@Override
|
||||
|
|
@ -238,12 +240,14 @@ 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.lnShowbootmenu.setOnClickListener(v -> binding.cbShowbootmenu.toggle());
|
||||
binding.cbShowbootmenu.setOnCheckedChangeListener((button, isChecked) -> isShowBootMenu = isChecked);
|
||||
binding.cbvShowbootmenu.setOnCheckedChangeListener((v, isChecked) -> isShowBootMenu = isChecked);
|
||||
|
||||
binding.lnBootfrom.setOnClickListener(v -> VMCreatorSelector.bootFrom(this, bootFrom, ((position, name, value) -> {
|
||||
binding.cbvUseuefi.setOnCheckedChangeListener((v, isChecked) -> isUseUefi = isChecked);
|
||||
if (!MainSettingsManager.getArch(this).equals("X86_64")) binding.cbvUseuefi.setVisibility(View.GONE);
|
||||
|
||||
binding.sbvBootfrom.setOnClickListener(v -> VMCreatorSelector.bootFrom(this, bootFrom, ((position, name, value) -> {
|
||||
bootFrom = position;
|
||||
binding.tvBootfrom.setText(name);
|
||||
binding.sbvBootfrom.setSubtitle(name);
|
||||
})));
|
||||
|
||||
modify = getIntent().getBooleanExtra("MODIFY", false);
|
||||
|
|
@ -564,9 +568,16 @@ public class VMCreatorActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
bootFrom = current.bootFrom;
|
||||
binding.tvBootfrom.setText(Objects.requireNonNull(VMCreatorSelector.getBootFrom(this, current.bootFrom).get("name")).toString());
|
||||
binding.sbvBootfrom.setSubtitle(Objects.requireNonNull(VMCreatorSelector.getBootFrom(this, current.bootFrom).get("name")).toString());
|
||||
isShowBootMenu = current.isShowBootMenu;
|
||||
binding.cbShowbootmenu.setChecked(isShowBootMenu);
|
||||
binding.cbvShowbootmenu.setChecked(isShowBootMenu);
|
||||
|
||||
if (MainSettingsManager.getArch(this).equals("X86_64")) {
|
||||
isUseUefi = current.isUseUefi;
|
||||
binding.cbvUseuefi.setChecked(isUseUefi);
|
||||
} else {
|
||||
binding.cbvUseuefi.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -698,6 +709,7 @@ public class VMCreatorActivity extends AppCompatActivity {
|
|||
vmConfigMap.put("imgArch", MainSettingsManager.getArch(this));
|
||||
vmConfigMap.put("bootFrom", bootFrom);
|
||||
vmConfigMap.put("isShowBootMenu", isShowBootMenu);
|
||||
vmConfigMap.put("isUseUefi", isUseUefi);
|
||||
vmConfigMap.put("vmID", vmID);
|
||||
vmConfigMap.put("qmpPort", 8080);
|
||||
return vmConfigMap;
|
||||
|
|
@ -995,7 +1007,13 @@ public class VMCreatorActivity extends AppCompatActivity {
|
|||
return;
|
||||
}
|
||||
|
||||
loadConfig(new Gson().fromJson(FileUtils.readFromFile(this, new File(AppConfig.vmFolder + vmID + "/rom-data.json")), DataMainRoms.class));
|
||||
try {
|
||||
loadConfig(new Gson().fromJson(FileUtils.readFromFile(this, new File(AppConfig.vmFolder + vmID + "/rom-data.json")), DataMainRoms.class));
|
||||
} catch (JsonSyntaxException e) {
|
||||
DialogUtils.oneDialog(this, getResources().getString(R.string.oops), getResources().getString(R.string.error_CR_CVBI4), getResources().getString(R.string.ok), true, R.drawable.warning_48px, true, null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject jObj = new JSONObject(FileUtils.readFromFile(this, new File(AppConfig.vmFolder + vmID + "/rom-data.json")));
|
||||
|
||||
if (jObj.has("vmID")) {
|
||||
|
|
|
|||
|
|
@ -48,9 +48,13 @@ public class PendingCommand {
|
|||
} else {
|
||||
com.vectras.vm.StartVM.cdrompath = "";
|
||||
Config.vmID = VMManager.idGenerator();
|
||||
String env = StartVM.env(activity, AppConfig.pendingCommand, "", true);
|
||||
MainStartVM.startNow(activity, "Quick run", env, Config.vmID, null);
|
||||
VMManager.lastQemuCommand = AppConfig.pendingCommand;
|
||||
new Thread(() -> {
|
||||
String env = StartVM.env(activity, AppConfig.pendingCommand, "", true);
|
||||
activity.runOnUiThread(() -> {
|
||||
MainStartVM.startNow(activity, "Quick run", env, Config.vmID, null);
|
||||
VMManager.lastQemuCommand = AppConfig.pendingCommand;
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
AppConfig.pendingCommand = "";
|
||||
|
|
|
|||
|
|
@ -50,4 +50,6 @@ public class DataMainRoms {
|
|||
public int bootFrom = 0;
|
||||
|
||||
public boolean isShowBootMenu = false;
|
||||
|
||||
public boolean isUseUefi = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,6 +156,12 @@ public class VmsFragment extends Fragment implements CallbackInterface.HomeCallT
|
|||
} catch (JSONException ignored) {
|
||||
romsMainData.isShowBootMenu = false;
|
||||
}
|
||||
try {
|
||||
romsMainData.isUseUefi = json_data.getBoolean("isUseUefi");
|
||||
} catch (JSONException ignored) {
|
||||
romsMainData.isUseUefi = false;
|
||||
}
|
||||
|
||||
romsMainData.itemExtra = json_data.getString("imgExtra");
|
||||
tempdata.add(romsMainData);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,8 +81,11 @@ public class VmsHomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||
Config.QMPPort = current.qmpPort;
|
||||
}
|
||||
Config.vmID = current.vmID;
|
||||
String env = StartVM.env(activity, current);
|
||||
MainStartVM.startNow(activity, current.itemName, env, current.vmID, current.itemIcon);
|
||||
|
||||
new Thread(() -> {
|
||||
String env = StartVM.env(activity, current);
|
||||
activity.runOnUiThread(() -> MainStartVM.startNow(activity, current.itemName, env, current.vmID, current.itemIcon));
|
||||
}).start();
|
||||
});
|
||||
|
||||
myHolder.cdRoms.setOnLongClickListener(v -> {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import java.io.File;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
|
@ -1047,4 +1048,37 @@ public class FileUtils {
|
|||
Log.e(TAG, "openFolder: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMd5(String filePath) {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = new FileInputStream(filePath);
|
||||
byte[] buffer = new byte[1024];
|
||||
MessageDigest digest = MessageDigest.getInstance("MD5");
|
||||
int numRead = 0;
|
||||
while (numRead != -1) {
|
||||
numRead = inputStream.read(buffer);
|
||||
if (numRead > 0)
|
||||
digest.update(buffer, 0, numRead);
|
||||
}
|
||||
byte [] md5Bytes = digest.digest();
|
||||
return convertHashToString(md5Bytes);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String convertHashToString(byte[] md5Bytes) {
|
||||
String returnVal = "";
|
||||
for (byte md5Byte : md5Bytes) {
|
||||
returnVal += Integer.toString((md5Byte & 0xff) + 0x100, 16).substring(1);
|
||||
}
|
||||
return returnVal.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,10 +37,14 @@ public class NotificationUtils {
|
|||
public static final int NO_ICON = -1;
|
||||
|
||||
public static String generalChannelId = "general";
|
||||
public static String downloadChannelId = "download";
|
||||
|
||||
public static void createAllChannel(Context context) {
|
||||
createChannel("General", "Receive new notifications.",
|
||||
"general", NotificationManager.IMPORTANCE_DEFAULT, context);
|
||||
generalChannelId, NotificationManager.IMPORTANCE_DEFAULT, context);
|
||||
|
||||
createChannel("Download", "View the file download process.",
|
||||
downloadChannelId, NotificationManager.IMPORTANCE_DEFAULT, context);
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
|
|
@ -157,6 +161,12 @@ public class NotificationUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void recall(Context context, int id) {
|
||||
if (context == null) return;
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
notificationManager.cancel(id);
|
||||
}
|
||||
|
||||
public static boolean isPermissionGranted(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
return ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED;
|
||||
|
|
|
|||
76
app/src/main/java/com/vectras/vm/view/CheckBoxView.java
Normal file
76
app/src/main/java/com/vectras/vm/view/CheckBoxView.java
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package com.vectras.vm.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.vectras.vm.R;
|
||||
|
||||
public class CheckBoxView extends LinearLayout {
|
||||
private TextView tvTitle;
|
||||
private CheckBox checkBox;
|
||||
|
||||
public interface OnCheckedChangeListener {
|
||||
void onCheckedChanged(CheckBoxView view, boolean isChecked);
|
||||
}
|
||||
|
||||
private OnCheckedChangeListener listener;
|
||||
|
||||
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public CheckBoxView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
LayoutInflater.from(context).inflate(R.layout.checkbox_view, this, true);
|
||||
|
||||
tvTitle = findViewById(R.id.tv_title);
|
||||
checkBox = findViewById(R.id.checkbox);
|
||||
|
||||
if (attrs != null) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CheckBoxView);
|
||||
|
||||
String title = typedArray.getString(R.styleable.CheckBoxView_setText);
|
||||
boolean isChecked = typedArray.getBoolean(R.styleable.CheckBoxView_setChecked, false);
|
||||
|
||||
if (title != null) tvTitle.setText(title);
|
||||
checkBox.setChecked(isChecked);
|
||||
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
findViewById(R.id.root).setOnClickListener(v -> checkBox.setChecked(!checkBox.isChecked()));
|
||||
|
||||
checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
if (listener != null) {
|
||||
listener.onCheckedChanged(this, isChecked);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setText(String title) {
|
||||
tvTitle.setText(title);
|
||||
}
|
||||
|
||||
public void setChecked(boolean isCheck) {
|
||||
checkBox.setChecked(isCheck);
|
||||
}
|
||||
|
||||
public boolean isChecked() {
|
||||
return checkBox.isChecked();
|
||||
}
|
||||
|
||||
public void setEnabled(boolean isEnabled) {
|
||||
findViewById(R.id.root).setEnabled(isEnabled);
|
||||
findViewById(R.id.root).setAlpha(0.5f);
|
||||
checkBox.setEnabled(isEnabled);
|
||||
}
|
||||
}
|
||||
52
app/src/main/java/com/vectras/vm/view/SelectBoxView.java
Normal file
52
app/src/main/java/com/vectras/vm/view/SelectBoxView.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package com.vectras.vm.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.vectras.vm.R;
|
||||
|
||||
public class SelectBoxView extends LinearLayout {
|
||||
|
||||
private TextView tvTitle, tvSubtitle;
|
||||
|
||||
public SelectBoxView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
LayoutInflater.from(context).inflate(R.layout.select_box_view, this, true);
|
||||
|
||||
tvTitle = findViewById(R.id.tv_title);
|
||||
tvSubtitle = findViewById(R.id.tv_subtitle);
|
||||
|
||||
if (attrs != null) {
|
||||
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SelectBoxView);
|
||||
|
||||
String title = typedArray.getString(R.styleable.SelectBoxView_title);
|
||||
String subtitle = typedArray.getString(R.styleable.SelectBoxView_subtitle);
|
||||
|
||||
if (title != null) tvTitle.setText(title);
|
||||
if (subtitle != null) tvSubtitle.setText(subtitle);
|
||||
|
||||
typedArray.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
tvTitle.setText(title);
|
||||
}
|
||||
|
||||
public void setSubtitle(String subtitle) {
|
||||
tvSubtitle.setText(subtitle);
|
||||
}
|
||||
|
||||
public void setEnabled(boolean isEnabled) {
|
||||
findViewById(R.id.root).setEnabled(isEnabled);
|
||||
findViewById(R.id.root).setAlpha(0.5f);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue