- Fixed the black screen issue when returning to the built-in VNC screen.
- Added bubble to hide and show buttons when using an external mouse.
- Bugs fixed.
This commit is contained in:
An Bui 2026-06-19 22:05:01 +07:00
parent bee7d78d17
commit aeff1f4bae
10 changed files with 222 additions and 36 deletions

View file

@ -12,8 +12,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
versionCode 132
versionName "4.2.8"
versionCode 133
versionName "4.2.9"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true

View file

@ -465,15 +465,21 @@ public class VncCanvas extends AppCompatImageView {
}
}
boolean fullUpdateNeeded = false;
if (pendingColorModel != null) {
setPixelFormat();
fullUpdateNeeded = true;
}
setEncodings(true);
bitmapData.writeFullUpdateRequest(!fullUpdateNeeded);
if (isNeedWriteFullUpdateRequest) {
bitmapData.writeFullUpdateRequest(false);
isNeedWriteFullUpdateRequest = false;
} else {
boolean fullUpdateNeeded = false;
if (pendingColorModel != null) {
setPixelFormat();
fullUpdateNeeded = true;
}
bitmapData.writeFullUpdateRequest(!fullUpdateNeeded);
}
break;
@ -2092,10 +2098,14 @@ public class VncCanvas extends AppCompatImageView {
bitmapData.updateBitmap(x, y, w, h);
}
boolean isNeedWriteFullUpdateRequest = true;
public void connected() {
// Request initial framebuffer update
isNeedWriteFullUpdateRequest = true;
VncCanvasActivity activity = (VncCanvasActivity) VncCanvas.this.getContext();
activity.onConnected();
}
public void disconnected() {

View file

@ -6,6 +6,7 @@ import android.androidVNC.VncCanvasActivity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.fragment.app.FragmentTransaction;
import android.annotation.SuppressLint;
@ -74,6 +75,7 @@ import com.vectras.vm.utils.ListUtils;
import com.vectras.vm.utils.SimulateKeyEvent;
import com.vectras.vm.utils.StreamAudio;
import com.vectras.vm.utils.UIUtils;
import com.vectras.vm.view.DynamicBubble;
import java.util.ArrayList;
import java.util.HashMap;
@ -128,7 +130,7 @@ public class MainVNCActivity extends VncCanvasActivity {
new InputManager.InputDeviceListener() {
@Override
public void onInputDeviceAdded(int deviceId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (isConnected && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
InputDevice device = inputManager.getInputDevice(deviceId);
if (device != null && DeviceUtils.isMouseSource(device.getSources()) && Config.mouseMode == Config.MouseMode.Trackpad) {
setUIModeDesktop();
@ -261,6 +263,12 @@ public class MainVNCActivity extends VncCanvasActivity {
});
if (!isConnected) tryReconnect(false);
DynamicBubble dynamicBubble = new DynamicBubble(binding.lnBubbleContainer, binding.btnBubble);
dynamicBubble.onClicked(() -> {
bindingControls.mainControl.setVisibility(bindingControls.mainControl.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
binding.btnBubble.setIcon(AppCompatResources.getDrawable(this, bindingControls.mainControl.getVisibility() == View.GONE ? R.drawable.radio_button_unchecked_24px : R.drawable.hide_source_24px));
});
}
private void setDefaulViewMode() {
@ -540,23 +548,16 @@ public class MainVNCActivity extends VncCanvasActivity {
invalidateOptionsMenu();
UIUtils.setMouseVisible(binding.main);
if (firstConnection) bindingControls.mainControl.setVisibility(View.VISIBLE);
binding.lnBubbleContainer.setVisibility(View.GONE);
} catch (Exception ex) {
if (Config.debug)
Log.e(TAG, "setUIModeMobile: ", ex);
}
//Apply settings when connection is successful.
// try {
// vncCanvas.setupScaleMode(binding.main);
// if (MainSettingsManager.getVNCScaleMode(this) == VNCConfig.oneToOne) {
// vncCanvas.setScaleMode(vncCanvas.ONE_TO_ONE_MODE);
// } else if (MainSettingsManager.getVNCScaleMode(this) == VNCConfig.scaleToFitScreen) {
// vncCanvas.setScaleMode(vncCanvas.STRETCH_TO_FIT_MODE);
// }
// } catch (Exception e) {
// MainSettingsManager.setVNCScaleMode(this, VNCConfig.fitToScreen);
// Log.e(TAG, "oneToOne: ", e);
// }
applyScaleMode();
}
private void promptSetUIModeDesktop(final Activity activity, final boolean mouseMethodAlt) {
@ -595,10 +596,17 @@ public class MainVNCActivity extends VncCanvasActivity {
UIUtils.toastShort(MainVNCActivity.this, "External Mouse Enabled");
//onNormalScreen();
//AbstractScaling.getById(R.id.itemOneToOne).setScaleTypeForActivity(MainVNCActivity.this);
applyScaleMode();
showPanningState();
onMouse();
UIUtils.setMouseInvisible(binding.main);
if (firstConnection) bindingControls.mainControl.setVisibility(View.GONE);
binding.lnBubbleContainer.setVisibility(View.VISIBLE);
binding.btnBubble.setIcon(AppCompatResources.getDrawable(this, bindingControls.mainControl.getVisibility() == View.GONE ? R.drawable.radio_button_unchecked_24px : R.drawable.hide_source_24px));
} catch (Exception e) {
if (Config.debug)
Log.e(TAG, "setUIModeDesktop: ", e);
@ -607,6 +615,20 @@ public class MainVNCActivity extends VncCanvasActivity {
invalidateOptionsMenu();
}
private void applyScaleMode() {
try {
vncCanvas.setupScaleMode(binding.main);
if (MainSettingsManager.getVNCScaleMode(this) == VNCConfig.oneToOne) {
vncCanvas.setScaleMode(vncCanvas.ONE_TO_ONE_MODE);
} else if (MainSettingsManager.getVNCScaleMode(this) == VNCConfig.scaleToFitScreen) {
vncCanvas.setScaleMode(vncCanvas.STRETCH_TO_FIT_MODE);
}
} catch (Exception e) {
MainSettingsManager.setVNCScaleMode(this, VNCConfig.fitToScreen);
Log.e(TAG, "oneToOne: ", e);
}
}
private boolean toggleFullScreen() {
UIUtils.toastShort(this, "VNC Fullscreen not supported");
@ -841,7 +863,7 @@ public class MainVNCActivity extends VncCanvasActivity {
public void onBackPressed() {
if (bindingSendKey.sendkeylayout.getVisibility() == View.VISIBLE) {
bindingSendKey.sendkeylayout.setVisibility(View.GONE);
} else if (bindingControls.mainControl.getVisibility() == View.GONE) {
} else if (binding.lnBubbleContainer.getVisibility() == View.GONE && bindingControls.mainControl.getVisibility() == View.GONE) {
bindingControls.mainControl.setVisibility(View.VISIBLE);
} else {
FrameLayout l = findViewById(R.id.mainControl);
@ -863,14 +885,13 @@ public class MainVNCActivity extends VncCanvasActivity {
runOnUiThread(() -> {
isConnected = true;
this.resumeVM();
if (!firstConnection)
UIUtils.showHints(this);
firstConnection = true;
// if (!firstConnection)
// UIUtils.showHints(this);
if (Config.mouseMode == Config.MouseMode.External)
setUIModeDesktop();
else
setUIModeMobile(screenMode == VNCScreenMode.FitToScreen);
// if (Config.mouseMode == Config.MouseMode.External)
// setUIModeDesktop();
// else
// setUIModeMobile(screenMode == VNCScreenMode.FitToScreen);
binding.lnNosignal.setVisibility(View.GONE);
binding.lnConnecting.setVisibility(View.GONE);
@ -896,6 +917,8 @@ public class MainVNCActivity extends VncCanvasActivity {
setUIModeMobile(false);
}
}
firstConnection = true;
});
}

View file

@ -543,7 +543,15 @@ public class VmControllerDialog extends DialogFragment {
if (uri != null) {
try {
new Thread(() -> {
File selectedFilePath = new File(Objects.requireNonNull(FileUtils.getPath(getContext(), uri)));
String path = FileUtils.getPath(getContext(), uri);
if (path == null) {
showErrorSelectedFileDialog();
dismiss();
return;
}
File selectedFilePath = new File(path);
if (isAdded()) {
requireActivity().runOnUiThread(() -> {

View file

@ -0,0 +1,90 @@
package com.vectras.vm.view;
import android.view.MotionEvent;
import android.view.View;
public class DynamicBubble {
View container;
View bubble;
private float dX, dY, startX, startY;
boolean isDragging;
final int CLICK_ACTION_THRESHOLD = 10;
Runnable onClicked;
public DynamicBubble(View container, View bubble) {
this.container = container;
this.bubble = bubble;
setup();
}
public void onClicked(Runnable onClicked) {
this.onClicked = onClicked;
}
private void setup() {
bubble.setOnTouchListener((v, event) -> {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// Save the View's initial position relative to your finger.
dX = v.getX() - event.getRawX();
dY = v.getY() - event.getRawY();
// Save the starting position to check if it's a click.
startX = event.getRawX();
startY = event.getRawY();
isDragging = false;
break;
case MotionEvent.ACTION_MOVE:
// Calculate the distance moved.
float movedX = event.getRawX() - startX;
float movedY = event.getRawY() - startY;
// If the displacement exceeds the threshold, it is determined that pulling is occurring.
if (Math.abs(movedX) > CLICK_ACTION_THRESHOLD || Math.abs(movedY) > CLICK_ACTION_THRESHOLD) {
isDragging = true;
// Move the view using your finger.
v.setX(event.getRawX() + dX);
v.setY(event.getRawY() + dY);
}
break;
case MotionEvent.ACTION_UP:
if (!isDragging) {
// Click event.
v.performClick();
if (onClicked != null) onClicked.run();
} else {
onDropped();
}
isDragging = false;
break;
case MotionEvent.ACTION_CANCEL:
isDragging = false;
break;
default:
return false;
}
return true;
});
}
private void onDropped() {
if (bubble.getX() > (float) container.getWidth() / 2) {
bubble.animate().x(container.getWidth() - bubble.getWidth());
} else {
bubble.animate().x(0);
}
if (bubble.getY() < bubble.getHeight()) {
bubble.animate().y(0);
} else if (bubble.getY() > (float) container.getHeight() - bubble.getHeight()) {
bubble.animate().y(container.getHeight() - bubble.getHeight());
}
}
}

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M120,720L120,640L840,640L840,720L120,720ZM120,520L120,440L840,440L840,520L120,520ZM120,320L120,240L840,240L840,320L120,320Z"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800Z"/>
</vector>

View file

@ -43,6 +43,21 @@
<include
android:id="@+id/sendkeysdialog"
layout="@layout/send_key_dialog" />
<LinearLayout
android:id="@+id/ln_bubble_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:visibility="visible">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_bubble"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/radio_button_unchecked_24px"/>
</LinearLayout>
</RelativeLayout>
<LinearLayout

View file

@ -5,11 +5,11 @@
"url": "https://github.com/xoureldeen/Vectras-VM-Android/releases",
"Message": "<h2>4.1.0</h2>\nBugs fixed.",
"cancellable": true,
"versionCodeBeta":"132",
"versionNameBeta":"4.2.8",
"versionNameBetas":"4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,4.0.7,4.0.8,4.0.9,4.1.1,4.1.2,4.1.3,4.1.4,4.1.5,4.1.6,4.1.7,4.1.8,4.1.9,4.2.0,4.2.1,4.2.2,4.2.3,4.2.4,4.2.5,4.2.6,4.2.7,4.2.8",
"versionCodeBeta":"133",
"versionNameBeta":"4.2.9",
"versionNameBetas":"4.0.1,4.0.2,4.0.3,4.0.4,4.0.5,4.0.6,4.0.7,4.0.8,4.0.9,4.1.1,4.1.2,4.1.3,4.1.4,4.1.5,4.1.6,4.1.7,4.1.8,4.1.9,4.2.0,4.2.1,4.2.2,4.2.3,4.2.4,4.2.5,4.2.6,4.2.7,4.2.8,4.2.9",
"sizeBeta": "45 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
"MessageBeta": "<h2>4.2.8</h2>Bugs fixed.",
"MessageBeta": "<h2>4.2.9</h2>Bugs fixed.",
"cancellableBeta": true
}

View file

@ -1641,6 +1641,26 @@
"vecid": "windows1124h2cvbi",
"id": ""
},
{
"rom_name": "Windows 11 24H2 ARM64",
"rom_icon": "https://www.getmyos.com/app_public/files/t/1/2021/06/windows_11_logo_by_getmyos.png",
"rom_url": "https://youtu.be/WL1uN41VKP0",
"rom_path": "Windows 11 24H2 ARM64.cvbi",
"rom_avail": true,
"gui": true,
"rom_size": "2024 Update",
"rom_arch": "ARM64",
"rom_kernel": "windows",
"rom_extra": "",
"final_rom_file_name": "",
"desc": "This Rom is from Nguyen Bao An Bui. You can get it on An Bui app: https://play.google.com/store/apps/details?id=com.anbui.app\n\nWindows 11 is the latest major release of Microsoft's Windows NT operating system, released on October 5, 2021 as the successor to Windows 10 (2015). It is provided free for any Windows 10 devices that meet the new Windows 11 system requirements. A server version, Windows Server 2025, was released in 2024. Windows 11 is the first major version of Windows NT without a companion mobile version following the discontinuations of Windows Phone with Windows 10 Mobile.",
"file_size": "10 GB",
"containsAds": true,
"creator": "Nguyen Bao An Bui",
"verified": true,
"vecid": "windows1124h2arm64cvbi",
"id": "99630"
},
{
"rom_name": "Windows 11 25H2",
"rom_icon": "https://www.getmyos.com/app_public/files/t/1/2021/06/windows_11_logo_by_getmyos.png",