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 }