diff --git a/app/build.gradle b/app/build.gradle
index c30de84..d49107d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -12,8 +12,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
- versionCode 105
- versionName "4.0.1"
+ versionCode 106
+ versionName "4.0.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e8f787e..787ced8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -156,7 +156,7 @@
android:name="com.vectras.qemu.MainVNCActivity"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize"
android:launchMode="singleTask"
- android:screenOrientation="landscape"
+ android:screenOrientation="sensorLandscape"
android:theme="@style/Theme.FullScreen"
android:windowSoftInputMode="adjustPan" />
+
+
diff --git a/app/src/main/java/android/androidVNC/VncCanvas.java b/app/src/main/java/android/androidVNC/VncCanvas.java
index ef60e97..b3dc0eb 100644
--- a/app/src/main/java/android/androidVNC/VncCanvas.java
+++ b/app/src/main/java/android/androidVNC/VncCanvas.java
@@ -303,22 +303,8 @@ public class VncCanvas extends AppCompatImageView {
Log.i(TAG, "Desktop name is " + rfb.desktopName);
Log.i(TAG, "Desktop size is " + rfb.framebufferWidth + " x " + rfb.framebufferHeight);
- boolean useFull = false;
- int capacity = BCFactory.getInstance().getBCActivityManager()
- .getMemoryClass(Utils.getActivityManager(getContext()));
- if (connection.getForceFull() == BitmapImplHint.AUTO) {
- if (rfb.framebufferWidth * rfb.framebufferHeight * FullBufferBitmapData.CAPACITY_MULTIPLIER <= capacity
- * 1024 * 1024) {
- useFull = true;
- }
- } else {
- useFull = (connection.getForceFull() == BitmapImplHint.FULL);
- }
- if (!useFull) {
- bitmapData = new LargeBitmapData(rfb, this, dx, dy, capacity);
- } else {
- bitmapData = new FullBufferBitmapData(rfb, this, capacity);
- }
+ initBitmapData(dx, dy);
+
mouseX = rfb.framebufferWidth / 2;
mouseY = rfb.framebufferHeight / 2;
@@ -333,6 +319,29 @@ public class VncCanvas extends AppCompatImageView {
pendingColorModel = null;
}
+ void initBitmapData(int dx, int dy) {
+ int capacity = BCFactory.getInstance().getBCActivityManager()
+ .getMemoryClass(Utils.getActivityManager(getContext()));
+
+ boolean useFull = false;
+ if (connection.getForceFull() == BitmapImplHint.AUTO) {
+ if (rfb.framebufferWidth * rfb.framebufferHeight * FullBufferBitmapData.CAPACITY_MULTIPLIER
+ <= capacity * 1024 * 1024) {
+ useFull = true;
+ }
+ } else {
+ useFull = (connection.getForceFull() == BitmapImplHint.FULL);
+ }
+
+ if (bitmapData != null) bitmapData.dispose();
+
+ if (!useFull) {
+ bitmapData = new LargeBitmapData(rfb, this, dx, dy, capacity);
+ } else {
+ bitmapData = new FullBufferBitmapData(rfb, this, capacity);
+ }
+ }
+
public void setColorModel(COLORMODEL cm) {
// Only update if color model changes
if (colorModel == null || !colorModel.equals(cm)) {
@@ -395,6 +404,11 @@ public class VncCanvas extends AppCompatImageView {
rfb.setFramebufferSize(rw, rh);
// - updateFramebufferSize();
Log.v(TAG, "rfb.EncodingNewFBSize");
+
+ Point size = new Point();
+ VncCanvasActivity.display.getSize(size);
+ initBitmapData(size.x, size.y);
+
reload();
break;
}
diff --git a/app/src/main/java/android/androidVNC/VncCanvasActivity.java b/app/src/main/java/android/androidVNC/VncCanvasActivity.java
index b7fd01a..a8f9c41 100644
--- a/app/src/main/java/android/androidVNC/VncCanvasActivity.java
+++ b/app/src/main/java/android/androidVNC/VncCanvasActivity.java
@@ -1821,6 +1821,12 @@ public abstract class VncCanvasActivity extends AppCompatActivity {
// Remove old canvas
ViewGroup parent = findViewById(R.id.vnc_canvas_layout);
+
+ if (vncCanvas != null) {
+ vncCanvas.closeConnection();
+ vncCanvas.onDestroy();
+ }
+
parent.removeView(vncCanvas);
// Create new canvas
diff --git a/app/src/main/java/com/vectras/qemu/MainSettingsManager.java b/app/src/main/java/com/vectras/qemu/MainSettingsManager.java
index 9888beb..cf2cd89 100644
--- a/app/src/main/java/com/vectras/qemu/MainSettingsManager.java
+++ b/app/src/main/java/com/vectras/qemu/MainSettingsManager.java
@@ -1046,7 +1046,7 @@ public class MainSettingsManager extends AppCompatActivity
public static Boolean getForceRefeshVNCDisplay(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- return prefs.getBoolean("forceRefeshVNCDisplay", true);
+ return prefs.getBoolean("forceRefeshVNCDisplay", false);
}
public static void setQuickStart(Context context, Boolean _boolean) {
diff --git a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java
index 393af80..3c477bf 100644
--- a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java
+++ b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java
@@ -114,6 +114,8 @@ public class MainVNCActivity extends VncCanvasActivity {
super.onCreate(b);
getContext = this;
+ UIUtils.edgeToEdge(this);
+ UIUtils.setOnApplyWindowInsetsListener(binding.vncMainLayout);
initializeControlFragment();
initializeDesktopControl();
initializeGameControl();
@@ -175,6 +177,8 @@ public class MainVNCActivity extends VncCanvasActivity {
streamAudio = new StreamAudio(this);
streamAudio.setFile(VmFileManager.findAudioRaw(this, Config.vmID));
+
+ if (!isConnected) tryReconnect();
}
private void setDefaulViewMode() {
@@ -187,7 +191,7 @@ public class MainVNCActivity extends VncCanvasActivity {
// screenMode = VNCScreenMode.FitToScreen;
setLayout(getResources().getConfiguration());
- UIUtils.setOrientation(this);
+ //UIUtils.setOrientation(this);
}
@Override
@@ -766,12 +770,14 @@ public class MainVNCActivity extends VncCanvasActivity {
setUIModeMobile(screenMode == VNCScreenMode.FitToScreen);
binding.lnNosignal.setVisibility(View.GONE);
+ binding.lnConnecting.setVisibility(View.GONE);
this.vncCanvas.setFocusableInTouchMode(true);
// syncCursorViewWithBitmap();
if (VmAudioManager.currentVmId.equals(Config.vmID) && VmAudioManager.streamAudio.isPlaying())
streamAudio.setCross(VmAudioManager.streamAudio);
if (!streamAudio.isPlaying()) streamAudio.play();
+
});
}
@@ -847,14 +853,16 @@ public class MainVNCActivity extends VncCanvasActivity {
binding.lnNosignal.setVisibility(View.VISIBLE);
binding.lnConnecting.setVisibility(View.GONE);
} else {
- isTrying = false;
-
if (Config.forceRefeshVNCDisplay) {
runOnUiThread(() -> {
startActivity(new Intent(MainVNCActivity.this, MainVNCActivity.class));
overridePendingTransition(0, 0);
finish();
});
+ } else {
+ isTrying = false;
+ binding.lnNosignal.setVisibility(View.GONE);
+ binding.lnConnecting.setVisibility(View.GONE);
}
}
}
diff --git a/app/src/main/java/com/vectras/vm/RomInfo.java b/app/src/main/java/com/vectras/vm/RomInfo.java
index 700f22e..59f8934 100644
--- a/app/src/main/java/com/vectras/vm/RomInfo.java
+++ b/app/src/main/java/com/vectras/vm/RomInfo.java
@@ -25,6 +25,7 @@ import com.vectras.vm.databinding.ActivityRomInfoBinding;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.ImageUtils;
+import com.vectras.vm.utils.IntentUtils;
import com.vectras.vm.utils.PackageUtils;
import java.io.File;
@@ -165,7 +166,13 @@ public class RomInfo extends AppCompatActivity {
});
if (getIntent().hasExtra("isRomInfo") && getIntent().getBooleanExtra("isRomInfo", false)) {
- binding.btnPick.setOnClickListener(v -> romPicker.launch("*/*"));
+ binding.btnPick.setOnClickListener(v -> {
+ try {
+ romPicker.launch("*/*");
+ } catch (Exception e) {
+ IntentUtils.showErrorDialog(this);
+ }
+ });
} else {
binding.btnPick.setVisibility(View.GONE);
}
@@ -326,7 +333,8 @@ public class RomInfo extends AppCompatActivity {
String likeContent = getString(R.string.like);
boolean isLiked = interaction.isLiked();
likeContent = (likes == 0) ? getString(R.string.like) : interaction.getFormatedLikeCount();
- if (isLiked) binding.btnLike.setIcon(ContextCompat.getDrawable(RomInfo.this, R.drawable.thumb_up_filled_24px));
+ if (isLiked)
+ binding.btnLike.setIcon(ContextCompat.getDrawable(RomInfo.this, R.drawable.thumb_up_filled_24px));
binding.btnLike.setText(likeContent);
binding.lnAllViews.setVisibility(View.VISIBLE);
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 482a106..0f88376 100644
--- a/app/src/main/java/com/vectras/vm/creator/VMCreatorActivity.java
+++ b/app/src/main/java/com/vectras/vm/creator/VMCreatorActivity.java
@@ -44,6 +44,7 @@ import com.vectras.vm.utils.ImageUtils;
import com.vectras.vm.utils.IntentUtils;
import com.vectras.vm.utils.JSONUtils;
import com.vectras.vm.utils.PackageUtils;
+import com.vectras.vm.utils.ProgressDialog;
import com.vectras.vm.utils.UIUtils;
import org.json.JSONException;
@@ -87,7 +88,11 @@ public class VMCreatorActivity extends AppCompatActivity {
finish();
return true;
} else if (id == R.id.add_file) {
+ try {
filePicker.launch("*/*");
+ } catch (Exception e) {
+ IntentUtils.showErrorDialog(this);
+ }
return true;
} else if (id == R.id.show_in_folder) {
FileUtils.openFolder(this, VmFileManager.getPath(vmID));
@@ -126,8 +131,20 @@ public class VMCreatorActivity extends AppCompatActivity {
binding.btnCreate.setOnClickListener(v -> startCreateVM());
- binding.drive.setOnClickListener(v -> diskPicker.launch("*/*"));
- binding.driveField.setOnClickListener(v -> diskPicker.launch("*/*"));
+ binding.drive.setOnClickListener(v -> {
+ try {
+ diskPicker.launch("*/*");
+ } catch (Exception e) {
+ IntentUtils.showErrorDialog(this);
+ }
+ });
+ binding.driveField.setOnClickListener(v -> {
+ try {
+ diskPicker.launch("*/*");
+ } catch (Exception e) {
+ IntentUtils.showErrorDialog(this);
+ }
+ });
binding.driveField.setEndIconOnClickListener(v -> {
if (Objects.requireNonNull(binding.drive.getText()).toString().isEmpty()) {
@@ -147,10 +164,21 @@ public class VMCreatorActivity extends AppCompatActivity {
true,
R.drawable.hard_drive_24px,
true,
- () -> diskPicker.launch("*/*"),
+ () -> {
+ try {
+ diskPicker.launch("*/*");
+ } catch (Exception e) {
+ IntentUtils.showErrorDialog(this);
+ }
+ },
() -> {
if (binding.drive.getText().toString().contains(VmFileManager.quickGetPath(vmID))) {
- FileUtils.delete(new File(Objects.requireNonNull(binding.drive.getText()).toString()));
+ ProgressDialog progressDialog1 = new ProgressDialog(this);
+ progressDialog1.show();
+ new Thread(() -> {
+ FileUtils.delete(new File(Objects.requireNonNull(binding.drive.getText()).toString()));
+ runOnUiThread(progressDialog1::reset);
+ }).start();
}
binding.drive.setText("");
binding.driveField.setEndIconDrawable(R.drawable.add_24px);
@@ -168,7 +196,13 @@ public class VMCreatorActivity extends AppCompatActivity {
}
});
- View.OnClickListener cdromClickListener = v -> isoPicker.launch("*/*");
+ View.OnClickListener cdromClickListener = v -> {
+ try {
+ isoPicker.launch("*/*");
+ } catch (Exception e) {
+ IntentUtils.showErrorDialog(this);
+ }
+ };
binding.cdrom.setOnClickListener(cdromClickListener);
binding.cdromField.setOnClickListener(cdromClickListener);
@@ -310,7 +344,11 @@ public class VMCreatorActivity extends AppCompatActivity {
} else if (getIntent().hasExtra("importcvbinow")) {
setDefault();
- cvbiPicker.launch("*/*");
+ try {
+ cvbiPicker.launch("*/*");
+ } catch (Exception e) {
+ IntentUtils.showErrorDialog(this);
+ }
} else {
setDefault();
if (MainSettingsManager.autoCreateDisk(this)) {
@@ -777,10 +815,14 @@ public class VMCreatorActivity extends AppCompatActivity {
}
private void selectedDiskFile(Uri _content_describer, boolean _addtodrive) {
+ ProgressDialog progressDialog1 = new ProgressDialog(this);
+ progressDialog1.show();
new Thread(() -> {
if (FileUtils.isValidFilePath(this, FileUtils.getPath(this, _content_describer), false)) {
File selectedFilePath = new File(getPath(_content_describer));
runOnUiThread(() -> {
+ progressDialog1.reset();
+
if (VMManager.isADiskFile(selectedFilePath.getPath())) {
startProcessingHardDriveFile(_content_describer, _addtodrive);
} else {
@@ -849,9 +891,17 @@ public class VMCreatorActivity extends AppCompatActivity {
null);
return;
}
- File selectedFilePath = new File(getPath(_content_describer));
- binding.drive.setText(selectedFilePath.getPath());
- binding.driveField.setEndIconDrawable(R.drawable.more_vert_24px);
+
+ ProgressDialog progressDialog1 = new ProgressDialog(this);
+ progressDialog1.show();
+ new Thread(() -> {
+ File selectedFilePath = new File(getPath(_content_describer));
+ runOnUiThread(() -> {
+ progressDialog1.reset();
+ binding.drive.setText(selectedFilePath.getPath());
+ binding.driveField.setEndIconDrawable(R.drawable.more_vert_24px);
+ });
+ }).start();
}
}
@@ -1030,7 +1080,7 @@ public class VMCreatorActivity extends AppCompatActivity {
if (jObj.has("vmID")) {
if (!jObj.isNull("vmID")) {
if (!jObj.getString("vmID").isEmpty()) {
- FileUtils.move(VmFileManager.getConfigFile(vmID), VmFileManager.getConfigFile( jObj.getString("vmID")));
+ FileUtils.move(VmFileManager.getConfigFile(vmID), VmFileManager.getConfigFile(jObj.getString("vmID")));
vmID = jObj.getString("vmID");
}
}
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 d88b188..1ba1124 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
@@ -18,6 +18,7 @@ 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;
@@ -388,12 +389,16 @@ public class MainStartVM {
.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);
diff --git a/app/src/main/java/com/vectras/vm/manager/VmControllerDialog.java b/app/src/main/java/com/vectras/vm/manager/VmControllerDialog.java
index b452e4d..23fa551 100644
--- a/app/src/main/java/com/vectras/vm/manager/VmControllerDialog.java
+++ b/app/src/main/java/com/vectras/vm/manager/VmControllerDialog.java
@@ -22,6 +22,7 @@ import com.vectras.qemu.VNCConfig;
import com.vectras.vm.AppConfig;
import com.vectras.vm.R;
import com.vectras.vm.VMManager;
+import com.vectras.vm.VectrasApp;
import com.vectras.vm.creator.VMCreatorActivity;
import com.vectras.vm.databinding.DialogChangeRemovableDevicesBinding;
import com.vectras.vm.main.core.DisplaySystem;
@@ -108,6 +109,7 @@ public class VmControllerDialog extends DialogFragment {
});
if (isAdded() && (!(requireActivity() instanceof MainVNCActivity))) {
+ if (streamAudio == null) streamAudio = new StreamAudio(requireActivity().getApplicationContext());
if (!streamAudio.isPlaying()) VmAudioManager.set(Config.vmID);
}
@@ -143,7 +145,8 @@ public class VmControllerDialog extends DialogFragment {
dismiss();
});
- if (!isHavingSecondaryOpticalDisc()) binding.tvCdrom.setText(R.string.cdrom);
+ if (!isHavingSecondaryOpticalDisc())
+ binding.tvCdrom.setText(R.string.cdrom);
} else {
binding.lnCdrom.setVisibility(View.GONE);
}
@@ -156,7 +159,8 @@ public class VmControllerDialog extends DialogFragment {
dismiss();
});
- if (!isHavingOpticalDisc()) binding.tvSecondaryCdrom.setText(R.string.cdrom);
+ if (!isHavingOpticalDisc())
+ binding.tvSecondaryCdrom.setText(R.string.cdrom);
} else {
binding.lnSecondaryCdrom.setVisibility(View.GONE);
}
@@ -351,13 +355,20 @@ public class VmControllerDialog extends DialogFragment {
registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
if (uri != null) {
try {
- File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(requireActivity(), uri)));
- QmpSender.changeOpticalDisc(requireActivity(), selectedFilePath.getAbsolutePath(), infoBlock);
+ new Thread(() -> {
+ File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(getContext(), uri)));
+
+ if (isAdded()) {
+ requireActivity().runOnUiThread(() -> {
+ QmpSender.changeOpticalDisc(requireActivity(), selectedFilePath.getAbsolutePath(), infoBlock);
+ dismiss();
+ });
+ }
+ }).start();
} catch (Exception e) {
showErrorSelectedFileDialog();
+ dismiss();
}
-
- dismiss();
}
});
@@ -365,13 +376,20 @@ public class VmControllerDialog extends DialogFragment {
registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
if (uri != null) {
try {
- File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(requireActivity(), uri)));
- QmpSender.changeSecondaryOpticalDisc(requireActivity(), selectedFilePath.getAbsolutePath(), infoBlock);
+ new Thread(() -> {
+ File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(getContext(), uri)));
+
+ if (isAdded()) {
+ requireActivity().runOnUiThread(() -> {
+ QmpSender.changeSecondaryOpticalDisc(requireActivity(), selectedFilePath.getAbsolutePath(), infoBlock);
+ dismiss();
+ });
+ }
+ }).start();
} catch (Exception e) {
showErrorSelectedFileDialog();
+ dismiss();
}
-
- dismiss();
}
});
@@ -379,13 +397,20 @@ public class VmControllerDialog extends DialogFragment {
registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
if (uri != null) {
try {
- File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(requireActivity(), uri)));
- QmpSender.changeFloppyDiskA(requireActivity(), selectedFilePath.getAbsolutePath());
+ new Thread(() -> {
+ File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(getContext(), uri)));
+
+ if (isAdded()) {
+ requireActivity().runOnUiThread(() -> {
+ QmpSender.changeFloppyDiskA(requireActivity(), selectedFilePath.getAbsolutePath());
+ dismiss();
+ });
+ }
+ }).start();
} catch (Exception e) {
showErrorSelectedFileDialog();
+ dismiss();
}
-
- dismiss();
}
});
@@ -393,13 +418,20 @@ public class VmControllerDialog extends DialogFragment {
registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
if (uri != null) {
try {
- File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(requireActivity(), uri)));
- QmpSender.changeFloppyDiskB(requireActivity(), selectedFilePath.getAbsolutePath());
+ new Thread(() -> {
+ File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(getContext(), uri)));
+
+ if (isAdded()) {
+ requireActivity().runOnUiThread(() -> {
+ QmpSender.changeFloppyDiskB(requireActivity(), selectedFilePath.getAbsolutePath());
+ dismiss();
+ });
+ }
+ }).start();
} catch (Exception e) {
showErrorSelectedFileDialog();
+ dismiss();
}
-
- dismiss();
}
});
@@ -407,13 +439,20 @@ public class VmControllerDialog extends DialogFragment {
registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> {
if (uri != null) {
try {
- File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(requireActivity(), uri)));
- QmpSender.changeMemoryCard(requireActivity(), selectedFilePath.getAbsolutePath());
+ new Thread(() -> {
+ File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(getContext(), uri)));
+
+ if (isAdded()) {
+ requireActivity().runOnUiThread(() -> {
+ QmpSender.changeMemoryCard(requireActivity(), selectedFilePath.getAbsolutePath());
+ dismiss();
+ });
+ }
+ }).start();
} catch (Exception e) {
showErrorSelectedFileDialog();
+ dismiss();
}
-
- dismiss();
}
});
diff --git a/app/src/main/res/layout/activity_vnc.xml b/app/src/main/res/layout/activity_vnc.xml
index fe7a69a..875afd2 100644
--- a/app/src/main/res/layout/activity_vnc.xml
+++ b/app/src/main/res/layout/activity_vnc.xml
@@ -30,70 +30,103 @@
android:keepScreenOn="true" />
-
-
-
-
-
-
-
-
-
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
4.0.0\nBugs fixed.",
"cancellable": true,
- "versionCodeBeta":"105",
- "versionNameBeta":"4.0.1",
- "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,3.9.9,4.0.0,4.0.1",
+ "versionCodeBeta":"106",
+ "versionNameBeta":"4.0.2",
+ "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,3.9.9,4.0.0,4.0.1,4.0.2",
"sizeBeta": "45 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
- "MessageBeta": "4.0.1
Bugs fixed.",
+ "MessageBeta": "4.0.2
Bugs fixed.",
"cancellableBeta": true
}