From 62886033f4b5535e768e1cb2681f57353dd2d31c Mon Sep 17 00:00:00 2001
From: An Bui <91354810+AnBui2004@users.noreply.github.com>
Date: Mon, 27 Apr 2026 18:54:03 +0700
Subject: [PATCH] 4.0.5
- Improved VNC display.
- Bugs fixed.
---
app/build.gradle | 4 +-
.../androidVNC/FullBufferBitmapData.java | 7 +-
.../java/android/androidVNC/VncCanvas.java | 15 +++
.../com/vectras/qemu/MainVNCActivity.java | 93 +++++++++++++++----
.../vm/manager/VmControllerDialog.java | 29 ++++--
app/src/main/res/layout/activity_vnc.xml | 13 +--
web/data/UpdateConfig.json | 8 +-
7 files changed, 125 insertions(+), 44 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index fbec7cc..eb15065 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -12,8 +12,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
- versionCode 108
- versionName "4.0.4"
+ versionCode 109
+ versionName "4.0.5"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
diff --git a/app/src/main/java/android/androidVNC/FullBufferBitmapData.java b/app/src/main/java/android/androidVNC/FullBufferBitmapData.java
index 2611a3b..6e64932 100644
--- a/app/src/main/java/android/androidVNC/FullBufferBitmapData.java
+++ b/app/src/main/java/android/androidVNC/FullBufferBitmapData.java
@@ -86,7 +86,7 @@ class FullBufferBitmapData extends AbstractBitmapData {
// this fixes the issue with Nougat Devices displaying black screen for 24bit color mode C24bit
Bitmap bitmapTmp = Bitmap.createBitmap(framebufferwidth, framebufferheight, Config.bitmapConfig);
bitmapTmp.setPixels(bitmapPixels, offset(xo, yo), data.framebufferwidth, 0, 0, drawWidth, drawHeight);
- canvas.drawBitmap(bitmapTmp, (float) vncCanvas.getWidth() / -2 + (float) framebufferwidth / 2, (float) vncCanvas.getHeight() / 2 - (float) framebufferheight / 2, null);
+ canvas.drawBitmap(bitmapTmp, 0, 0, null);
/*
}
@@ -105,8 +105,8 @@ class FullBufferBitmapData extends AbstractBitmapData {
if(data.vncCanvas.connection.getUseLocalCursor())
{
// OneToOne
- int locationX = vncCanvas.getWidth() / -2 + framebufferwidth / 2 + data.vncCanvas.mouseX;
- int locationY = vncCanvas.getHeight() / 2 - framebufferheight / 2 + data.vncCanvas.mouseY;
+ int locationX = data.vncCanvas.mouseX;
+ int locationY = data.vncCanvas.mouseY;
if (vncCanvas.getScaleType() == ImageView.ScaleType.FIT_CENTER) {
// Full screen
@@ -115,6 +115,7 @@ class FullBufferBitmapData extends AbstractBitmapData {
} else if (vncCanvas.getScaleType() == ImageView.ScaleType.FIT_XY){
// Scale to fit screen
locationX = vncCanvas.getWidth() / 2 + framebufferwidth / -2 + data.vncCanvas.mouseX;
+ locationY = vncCanvas.getHeight() / 2 - framebufferheight / 2 + data.vncCanvas.mouseY;
}
setCursorRect(locationX, locationY);
diff --git a/app/src/main/java/android/androidVNC/VncCanvas.java b/app/src/main/java/android/androidVNC/VncCanvas.java
index 03483a4..d511e59 100644
--- a/app/src/main/java/android/androidVNC/VncCanvas.java
+++ b/app/src/main/java/android/androidVNC/VncCanvas.java
@@ -2193,6 +2193,9 @@ public class VncCanvas extends AppCompatImageView {
setScaleX((float) getWidth() / getImageWidth());
setScaleY((float) getHeight() / getImageHeight());
});
+ } else if (getScaleType() == ScaleType.FIT_CENTER || getScaleType() == ScaleType.FIT_START) {
+ setScaleX(1);
+ setScaleY(1);
}
}
@@ -2208,6 +2211,18 @@ public class VncCanvas extends AppCompatImageView {
setScaleX((float) parent.getWidth() / getImageWidth());
setScaleY((float) parent.getHeight() / getImageHeight());
});
+ } else if (getScaleType() == ScaleType.FIT_CENTER || getScaleType() == ScaleType.FIT_START) {
+ setScaleX(1);
+ setScaleY(1);
}
}
+
+ public final ScaleType FULL_SCREEN_MODE = ScaleType.FIT_CENTER;
+ public final ScaleType STRETCH_TO_FIT_MODE = ScaleType.FIT_XY;
+ public final ScaleType ONE_TO_ONE_MODE = ScaleType.CENTER;
+
+ public void setScaleMode(ScaleType mode) {
+ setScaleType(mode);
+ setupScaleMode();
+ }
}
diff --git a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java
index 3bdda66..fa58397 100644
--- a/app/src/main/java/com/vectras/qemu/MainVNCActivity.java
+++ b/app/src/main/java/com/vectras/qemu/MainVNCActivity.java
@@ -21,7 +21,10 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -115,7 +118,7 @@ public class MainVNCActivity extends VncCanvasActivity {
getContext = this;
UIUtils.edgeToEdge(this);
- UIUtils.setOnApplyWindowInsetsListener(binding.vncMainLayout);
+ UIUtils.setOnApplyWindowInsetsListener(binding.vncControlLayout);
initializeControlFragment();
initializeDesktopControl();
initializeGameControl();
@@ -170,7 +173,11 @@ public class MainVNCActivity extends VncCanvasActivity {
binding.lnNosignal.setOnClickListener(v -> {
// In VNCCanvasActivity.
// Do not attempt to reconnect while connected.
- tryReconnect();
+ tryReconnect(false);
+ });
+
+ binding.lnConnecting.setOnClickListener(v -> {
+
});
ConnectionBean.useLocalCursor = MainSettingsManager.getShowVirtualMouse(this) || VMManager.isNeedUseVirtualMouse();
@@ -178,7 +185,7 @@ public class MainVNCActivity extends VncCanvasActivity {
streamAudio = new StreamAudio(this);
streamAudio.setFile(VmFileManager.findAudioRaw(this, Config.vmID));
- if (!isConnected) tryReconnect();
+ if (!isConnected) tryReconnect(false);
}
private void setDefaulViewMode() {
@@ -448,12 +455,11 @@ public class MainVNCActivity extends VncCanvasActivity {
//Apply settings when connection is successful.
try {
+ vncCanvas.setupScaleMode(binding.main);
if (MainSettingsManager.getVNCScaleMode(this) == VNCConfig.oneToOne) {
- AbstractScaling.getById(R.id.itemOneToOne)
- .setScaleTypeForActivity(this);
+ vncCanvas.setScaleMode(vncCanvas.ONE_TO_ONE_MODE);
} else if (MainSettingsManager.getVNCScaleMode(this) == VNCConfig.scaleToFitScreen) {
- vncCanvas.setScaleType(ImageView.ScaleType.FIT_XY);
- vncCanvas.setupScaleMode(binding.main);
+ vncCanvas.setScaleMode(vncCanvas.STRETCH_TO_FIT_MODE);
}
} catch (Exception e) {
MainSettingsManager.setVNCScaleMode(this, VNCConfig.fitToScreen);
@@ -780,6 +786,8 @@ public class MainVNCActivity extends VncCanvasActivity {
if (VmAudioManager.currentVmId.equals(Config.vmID) && VmAudioManager.streamAudio.isPlaying())
streamAudio.setCross(VmAudioManager.streamAudio);
if (!streamAudio.isPlaying()) streamAudio.play();
+
+ unBlurLayout();
});
}
@@ -792,6 +800,8 @@ public class MainVNCActivity extends VncCanvasActivity {
if (!VmAudioManager.currentVmId.equals(Config.vmID)) streamAudio.setCross(null);
if (streamAudio.isPlaying()) streamAudio.stop();
+
+ blurLayout();
});
}
@@ -819,19 +829,13 @@ public class MainVNCActivity extends VncCanvasActivity {
// }
} else {
// Try reconnect.
- if (Config.forceRefeshVNCDisplay) {
- startActivity(new Intent(this, MainVNCActivity.class));
- overridePendingTransition(0, 0);
- finish();
- } else {
- tryReconnect();
- }
+ tryReconnect(true);
}
}
private boolean isTrying;
- private void tryReconnect() {
+ private void tryReconnect(boolean forceRefeshVNCDisplay) {
if (isTrying) return;
isTrying = true;
@@ -855,7 +859,7 @@ public class MainVNCActivity extends VncCanvasActivity {
binding.lnNosignal.setVisibility(View.VISIBLE);
binding.lnConnecting.setVisibility(View.GONE);
} else {
- if (Config.forceRefeshVNCDisplay) {
+ if (forceRefeshVNCDisplay && Config.forceRefeshVNCDisplay) {
runOnUiThread(() -> {
startActivity(new Intent(MainVNCActivity.this, MainVNCActivity.class));
overridePendingTransition(0, 0);
@@ -867,6 +871,12 @@ public class MainVNCActivity extends VncCanvasActivity {
binding.lnConnecting.setVisibility(View.GONE);
}
}
+
+ if (!isConnected) {
+ blurLayout();
+ } else {
+ unBlurLayout();
+ }
}
}
}, 0);
@@ -911,19 +921,32 @@ public class MainVNCActivity extends VncCanvasActivity {
// Create and show the dialog.
LoggerDialogFragment newFragment = new LoggerDialogFragment();
newFragment.show(ft, "Logger");
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ getSupportFragmentManager().executePendingTransactions();
+ if (newFragment.getDialog() == null) return;
+ blurLayout();
+ newFragment.getDialog().setOnDismissListener(d -> unBlurLayout());
+ }
});
- bindingControls.shutdownBtn.setOnClickListener(v -> DialogUtils.threeDialog(this, getString(R.string.power), getString(R.string.shutdown_or_reset_content_vnc), getString(R.string.shutdown), getString(R.string.reset), getString(R.string.power), true, R.drawable.power_settings_new_24px, true,
- this::shutdownthisvm, QmpSender::quickReset, VMManager::pressPowerButton, null));
+ bindingControls.shutdownBtn.setOnClickListener(v -> {
+ blurLayout();
+
+ DialogUtils.threeDialog(this, getString(R.string.power), getString(R.string.shutdown_or_reset_content_vnc), getString(R.string.shutdown), getString(R.string.reset), getString(R.string.power), true, R.drawable.power_settings_new_24px, true,
+ this::shutdownthisvm, QmpSender::quickReset, VMManager::pressPowerButton, this::unBlurLayout);
+ });
bindingControls.shutdownBtn.setOnLongClickListener(view -> {
+ blurLayout();
+
DialogUtils.twoDialog(this, "Exit", "You will be left here but the virtual machine will continue to run.", "Exit", getString(R.string.cancel), true, R.drawable.exit_to_app_24px, true,
() -> {
started = false;
if (!VmAudioManager.currentVmId.equals(Config.vmID))
streamAudio.setCross(null);
finish();
- }, null, null);
+ }, null, this::unBlurLayout);
return false;
});
@@ -957,9 +980,41 @@ public class MainVNCActivity extends VncCanvasActivity {
vmControllerDialog.vncCanvas = vncCanvas;
vmControllerDialog.streamAudio = streamAudio;
vmControllerDialog.show(getSupportFragmentManager(), "VmControllerDialog");
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ getSupportFragmentManager().executePendingTransactions();
+ blurLayout();
+ vmControllerDialog.setOnDismissCallback(this::unBlurLayout);
+ }
});
}
+ boolean isBlurring;
+
+ private void blurLayout() {
+ if (isBlurring) return;
+ isBlurring = true;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ RenderEffect blurEffect = RenderEffect.createBlurEffect(
+ 25f, 25f,
+ Shader.TileMode.CLAMP
+ );
+ binding.vncCanvasLayout.setRenderEffect(blurEffect);
+ binding.vncControlLayout.setRenderEffect(blurEffect);
+ }
+ }
+
+ private void unBlurLayout() {
+ if (!isBlurring) return;
+ isBlurring = false;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ binding.vncCanvasLayout.setRenderEffect(null);
+ binding.vncControlLayout.setRenderEffect(null);
+ }
+ }
+
private void initializeDesktopControl() {
bindingDesktopControls.upBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
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 5ef276f..dd9210b 100644
--- a/app/src/main/java/com/vectras/vm/manager/VmControllerDialog.java
+++ b/app/src/main/java/com/vectras/vm/manager/VmControllerDialog.java
@@ -3,6 +3,7 @@ package com.vectras.vm.manager;
import android.androidVNC.ConnectionBean;
import android.androidVNC.VncCanvas;
import android.app.Dialog;
+import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
@@ -17,7 +18,6 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
-import com.google.android.material.color.MaterialColors;
import com.vectras.qemu.Config;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.qemu.MainVNCActivity;
@@ -117,6 +117,7 @@ public class VmControllerDialog extends DialogFragment {
}
if (streamAudio == null ||
+ !isAdded() ||
FileUtils.getFileSize(VmFileManager.findAudioRaw(requireContext(), Config.vmID)) == 0 ||
(isAdded() && (!(requireActivity() instanceof MainVNCActivity)) && !VmAudioManager.currentVmId.equals(Config.vmID))
) {
@@ -299,9 +300,7 @@ public class VmControllerDialog extends DialogFragment {
if (MainSettingsManager.getVNCScaleMode(requireActivity()) == VNCConfig.oneToOne) return;
MainSettingsManager.setVNCScaleMode(requireActivity(), VNCConfig.oneToOne);
- requireActivity().startActivity(new Intent(requireActivity(), MainVNCActivity.class));
- requireActivity().overridePendingTransition(0, 0);
- requireActivity().finish();
+ requireActivity().recreate();
dismiss();
});
@@ -309,9 +308,7 @@ public class VmControllerDialog extends DialogFragment {
if (MainSettingsManager.getVNCScaleMode(requireActivity()) == VNCConfig.fitToScreen) return;
MainSettingsManager.setVNCScaleMode(requireActivity(), VNCConfig.fitToScreen);
- requireActivity().startActivity(new Intent(requireActivity(), MainVNCActivity.class));
- requireActivity().overridePendingTransition(0, 0);
- requireActivity().finish();
+ requireActivity().recreate();
dismiss();
});
@@ -319,9 +316,7 @@ public class VmControllerDialog extends DialogFragment {
if (MainSettingsManager.getVNCScaleMode(requireActivity()) == VNCConfig.scaleToFitScreen) return;
MainSettingsManager.setVNCScaleMode(requireActivity(), VNCConfig.scaleToFitScreen);
- requireActivity().startActivity(new Intent(requireActivity(), MainVNCActivity.class));
- requireActivity().overridePendingTransition(0, 0);
- requireActivity().finish();
+ requireActivity().recreate();
dismiss();
});
} else {
@@ -333,6 +328,20 @@ public class VmControllerDialog extends DialogFragment {
return dialog;
}
+ private Runnable onDismissCallback;
+
+ public void setOnDismissCallback(Runnable callback) {
+ this.onDismissCallback = callback;
+ }
+
+ @Override
+ public void onDismiss(@NonNull DialogInterface dialog) {
+ super.onDismiss(dialog);
+ if (onDismissCallback != null) {
+ onDismissCallback.run();
+ }
+ }
+
private void takeScreenshot() {
ProgressDialog progressDialog = new ProgressDialog(requireActivity());
progressDialog.setText(getString(R.string.taking_a_screenshot));
diff --git a/app/src/main/res/layout/activity_vnc.xml b/app/src/main/res/layout/activity_vnc.xml
index 875afd2..db3e9f3 100644
--- a/app/src/main/res/layout/activity_vnc.xml
+++ b/app/src/main/res/layout/activity_vnc.xml
@@ -9,6 +9,7 @@
android:orientation="vertical">
@@ -31,7 +32,7 @@
@@ -71,7 +72,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.8"
- android:background="?attr/colorSurfaceContainer" />
+ android:background="#E6000000" />
+ app:tint="@android:color/white" />
+ android:textColor="@android:color/white" />
@@ -112,7 +113,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.8"
- android:background="?attr/colorSurfaceContainer" />
+ android:background="#E6000000" />
4.0.0\nBugs fixed.",
"cancellable": true,
- "versionCodeBeta":"108",
- "versionNameBeta":"4.0.4",
- "versionNameBetas":"4.0.0,4.0.1,4.0.2,4.0.3,4.0.4",
+ "versionCodeBeta":"109",
+ "versionNameBeta":"4.0.5",
+ "versionNameBetas":"4.0.0,4.0.1,4.0.2,4.0.3,4.0.4,4.0.5",
"sizeBeta": "45 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
- "MessageBeta": "4.0.4
Bugs fixed.",
+ "MessageBeta": "4.0.5
Bugs fixed.",
"cancellableBeta": true
}