- The built-in VNC display has been improved.
- Bugs fixed.
This commit is contained in:
An Bui 2026-06-29 13:54:03 +07:00
parent 812b850a79
commit b99565396b
13 changed files with 204 additions and 47 deletions

View file

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

View file

@ -339,14 +339,14 @@ public class RfbProto {
localSocket.connect(localSocketAddr);
//localSocket.setSoTimeout(10_000);
is = new DataInputStream(new BufferedInputStream(localSocket.getInputStream(),
32768));
65536));
sos = localSocket.getOutputStream();
} else {
sock = new Socket(host, port);
//sock.setSoTimeout(10_000);
is = new DataInputStream(new BufferedInputStream(sock.getInputStream(),
16384));
32768));
sos = sock.getOutputStream();
}

View file

@ -50,10 +50,15 @@ import android.view.View;
import com.antlersoft.android.bc.BCFactory;
import com.vectras.qemu.Config;
import com.vectras.vm.R;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.UIUtils;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.Inflater;
import androidx.appcompat.widget.AppCompatImageView;
@ -1955,6 +1960,8 @@ public class VncCanvas extends AppCompatImageView {
for (int i = 0; i < count; i++) {
dst[i] = (int) readPixelsBuffer[i] & 0xFF;
}
totalBytesReceived.addAndGet(count);
} else {
final int l = count * 3;
if (l > readPixelsBuffer.length) {
@ -1967,6 +1974,8 @@ public class VncCanvas extends AppCompatImageView {
| ((readPixelsBuffer[idx + 2] & 0xFF) << 16 | (readPixelsBuffer[idx + 1] & 0xFF) << 8
| (readPixelsBuffer[idx] & 0xFF));
}
totalBytesReceived.addAndGet(l);
}
}
@ -2104,15 +2113,53 @@ public class VncCanvas extends AppCompatImageView {
// Request initial framebuffer update
isNeedWriteFullUpdateRequest = true;
startDeadCheck();
VncCanvasActivity activity = (VncCanvasActivity) VncCanvas.this.getContext();
activity.onConnected();
}
public void disconnected() {
stopDeadCheck();
VncCanvasActivity activity = (VncCanvasActivity) VncCanvas.this.getContext();
activity.onDisconnected();
}
private final AtomicLong totalBytesReceived = new AtomicLong(0);
private long lastCheckedBytes = 0;
ScheduledExecutorService deadCheckScheduler;
boolean isNeedDeadCheck = DeviceUtils.isColorOS(getContext());
private void startDeadCheck() {
if (!isNeedDeadCheck) return;
totalBytesReceived.set(0);
lastCheckedBytes = totalBytesReceived.get();
deadCheckScheduler = Executors.newSingleThreadScheduledExecutor();
deadCheckScheduler.scheduleWithFixedDelay(() -> {
long currentBytes = totalBytesReceived.get();
if (currentBytes == lastCheckedBytes) {
isNeedWriteFullUpdateRequest = true;
Log.d(TAG, "No response was detected and a WriteFullUpdateRequest was sent.");
} else {
lastCheckedBytes = currentBytes;
}
}, 5000, 5000, TimeUnit.SECONDS);
}
private void stopDeadCheck() {
if (!isNeedDeadCheck) return;
if (deadCheckScheduler != null) {
deadCheckScheduler.shutdownNow();
deadCheckScheduler = null;
}
}
public float getFramebufferWidth() {
return bitmapData.framebufferwidth;
}

View file

@ -531,7 +531,7 @@ public class MainVNCActivity extends VncCanvasActivity {
}
private void setUIModeMobile(boolean fitToScreen) {
public void setUIModeMobile(boolean fitToScreen) {
try {
MotionEvent a = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
@ -586,7 +586,7 @@ public class MainVNCActivity extends VncCanvasActivity {
}
private void setUIModeDesktop() {
public void setUIModeDesktop() {
try {
MotionEvent a = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);

View file

@ -84,6 +84,11 @@ public class VmControllerDialog extends DialogFragment {
infoBlock = "";
}
if (!isAdded()) {
progressDialog.reset();
return;
}
long audioFileSize = FileUtils.getFileSize(VmFileManager.findAudioRaw(requireContext(), Config.vmID));
isGotInfo.set(true);
@ -332,7 +337,7 @@ public class VmControllerDialog extends DialogFragment {
});
if (isAdded() && requireActivity() instanceof MainVNCActivity) {
if (isAdded() && requireActivity() instanceof MainVNCActivity mainVNCActivity) {
binding.lnRefresh.setOnClickListener(v -> {
requireActivity().startActivity(new Intent(requireActivity(), MainVNCActivity.class));
requireActivity().overridePendingTransition(0, 0);
@ -349,10 +354,10 @@ public class VmControllerDialog extends DialogFragment {
binding.lnVirtualmouse.setOnClickListener(v -> binding.swVirtualmouse.toggle());
binding.lnMouse.setOnClickListener(v -> {
MainVNCActivity.getContext.onMouseMode();
dismiss();
});
// binding.lnMouse.setOnClickListener(v -> {
// MainVNCActivity.getContext.onMouseMode();
// dismiss();
// });
binding.lnSettings.setOnClickListener(v -> {
requireActivity().startActivity(new Intent(requireActivity(), VNCSettingsActivity.class));
@ -362,6 +367,7 @@ public class VmControllerDialog extends DialogFragment {
TypedValue typedValue = new TypedValue();
requireActivity().getTheme().resolveAttribute(androidx.appcompat.R.attr.colorPrimary, typedValue, true);
int colorPrimary = typedValue.data;
int colorControlNormal = binding.tvMute.getCurrentTextColor();
if (MainSettingsManager.getVNCScaleMode(requireActivity()) == VNCConfig.oneToOne) {
binding.ivScreenOneToOne.setBackgroundResource(R.drawable.dialog_shape_single_button);
@ -434,6 +440,32 @@ public class VmControllerDialog extends DialogFragment {
});
binding.lnPinchToZoom.setOnClickListener(v -> binding.swPinchToZoom.toggle());
if (Config.mouseMode == Config.MouseMode.Trackpad) {
binding.ivTrackpadMode.setBackgroundResource(R.drawable.dialog_shape_single_button);
binding.ivTrackpadMode.setColorFilter(colorPrimary);
} else {
binding.ivExternalMouseMode.setBackgroundResource(R.drawable.dialog_shape_single_button);
binding.ivExternalMouseMode.setColorFilter(colorPrimary);
}
binding.ivTrackpadMode.setOnClickListener(v -> {
if (Config.mouseMode == Config.MouseMode.Trackpad) return;
mainVNCActivity.setUIModeMobile(false);
binding.ivTrackpadMode.setBackgroundResource(R.drawable.dialog_shape_single_button);
binding.ivTrackpadMode.setColorFilter(colorPrimary);
binding.ivExternalMouseMode.setBackgroundResource(R.drawable.dialog_shape_click_effect_button);
binding.ivExternalMouseMode.setColorFilter(colorControlNormal);
});
binding.ivExternalMouseMode.setOnClickListener(v -> {
if (Config.mouseMode == Config.MouseMode.External) return;
mainVNCActivity.setUIModeDesktop();
binding.ivTrackpadMode.setBackgroundResource(R.drawable.dialog_shape_click_effect_button);
binding.ivTrackpadMode.setColorFilter(colorControlNormal);
binding.ivExternalMouseMode.setBackgroundResource(R.drawable.dialog_shape_single_button);
binding.ivExternalMouseMode.setColorFilter(colorPrimary);
});
} else {
binding.lnUserInterface.setVisibility(View.GONE);
}

View file

@ -214,8 +214,7 @@ public class SetupWizard2Activity extends AppCompatActivity {
edit.apply();
} else if (currentStep == STEP_PATERON) {
uiControllerFinalSteps(currentStep + 1);
Intent intent = new Intent(ACTION_VIEW, Uri.parse(AppConfig.patreonLink));
startActivity(intent);
IntentUtils.openUrl(this, AppConfig.patreonLink);
} else {
startActivity(new Intent(this, MainActivity.class));
finish();

View file

@ -1,7 +1,5 @@
package com.vectras.vm.utils;
import static androidx.core.content.ContextCompat.registerReceiver;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.usage.StorageStatsManager;
@ -20,12 +18,8 @@ import android.util.Log;
import android.view.Display;
import android.view.InputDevice;
import com.vectras.vm.VectrasApp;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.UUID;
@ -159,4 +153,8 @@ public class DeviceUtils {
return (sources & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE
|| (sources & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE;
}
public static boolean isColorOS(Context context) {
return PackageUtils.isInstalled("com.nearme.themespace", context);
}
}

View file

@ -2,6 +2,7 @@ package com.vectras.vm.utils;
import static android.content.Intent.ACTION_VIEW;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@ -30,8 +31,21 @@ public class IntentUtils {
PackageManager packagemanager = context.getPackageManager();
if (intent.resolveActivity(packagemanager) != null) {
context.startActivity(intent);
return true;
try {
context.startActivity(intent);
return true;
} catch (SecurityException e) {
try {
Intent chooserIntent = Intent.createChooser(intent, null);
if (!(context instanceof Activity)) {
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(chooserIntent);
return true;
} catch (Exception ex) {
return false;
}
}
} else {
return false;
}

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,880Q364,880 282,798Q200,716 200,600L200,360Q200,244 282,162Q364,80 480,80Q596,80 678,162Q760,244 760,360L760,600Q760,716 678,798Q596,880 480,880ZM520,360L680,360Q680,288 634.5,233Q589,178 520,164L520,360ZM280,360L440,360L440,164Q371,178 325.5,233Q280,288 280,360ZM480,800Q563,800 621.5,741.5Q680,683 680,600L680,440L280,440L280,600Q280,683 338.5,741.5Q397,800 480,800ZM480,440Q480,440 480,440Q480,440 480,440L480,440L480,440L480,440L480,440L480,440L480,440ZM520,360L520,360Q520,360 520,360Q520,360 520,360ZM440,360Q440,360 440,360Q440,360 440,360L440,360ZM480,440Q480,440 480,440Q480,440 480,440L480,440L480,440L480,440Q480,440 480,440Q480,440 480,440Z"/>
</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="M593,880Q569,880 547,871Q525,862 508,845L304,640L337,606Q351,592 371,587Q391,582 411,587L480,607L480,320Q480,303 491.5,291.5Q503,280 520,280Q537,280 548.5,291.5Q560,303 560,320L560,713L462,685L565,788Q571,794 578,797Q585,800 593,800L760,800Q793,800 816.5,776.5Q840,753 840,720L840,560Q840,543 851.5,531.5Q863,520 880,520Q897,520 908.5,531.5Q920,543 920,560L920,720Q920,786 873,833Q826,880 760,880L593,880ZM600,600L600,440Q600,423 611.5,411.5Q623,400 640,400Q657,400 668.5,411.5Q680,423 680,440L680,600L600,600ZM720,600L720,480Q720,463 731.5,451.5Q743,440 760,440Q777,440 788.5,451.5Q800,463 800,480L800,600L720,600ZM760,800Q760,800 760,800Q760,800 760,800L593,800Q593,800 589.5,800Q586,800 565,800L593,800L593,800L593,800L760,800ZM160,760Q127,760 103.5,736.5Q80,713 80,680L80,200Q80,167 103.5,143.5Q127,120 160,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,360L760,360L760,200Q760,200 760,200Q760,200 760,200L160,200Q160,200 160,200Q160,200 160,200L160,680Q160,680 160,680Q160,680 160,680L232,680L311,760L160,760Z"/>
</vector>

View file

@ -553,6 +553,33 @@
android:background="@drawable/dialog_shape_click_effect_button"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ln_mouse_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingVertical="8dp"
android:paddingHorizontal="16dp">
<ImageView
android:id="@+id/iv_trackpad_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:layout_weight="1"
android:src="@drawable/trackpad_input_24px"
android:background="@drawable/dialog_shape_click_effect_button"/>
<ImageView
android:id="@+id/iv_external_mouse_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:padding="8dp"
android:layout_weight="1"
android:src="@drawable/mouse_24px"
android:background="@drawable/dialog_shape_click_effect_button"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ln_virtualmouse"
android:layout_width="match_parent"
@ -657,25 +684,25 @@
android:text="@string/refresh"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ln_mouse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingVertical="8dp"
android:paddingHorizontal="16dp"
android:background="?attr/selectableItemBackground">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/touchpad_mouse_24px"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:textColor="?attr/colorControlNormal"
android:text="@string/mouse_mode"/>
</LinearLayout>
<!-- <LinearLayout-->
<!-- android:id="@+id/ln_mouse"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:gravity="center_vertical"-->
<!-- android:paddingVertical="8dp"-->
<!-- android:paddingHorizontal="16dp"-->
<!-- android:background="?attr/selectableItemBackground">-->
<!-- <ImageView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:src="@drawable/touchpad_mouse_24px"/>-->
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:padding="8dp"-->
<!-- android:textColor="?attr/colorControlNormal"-->
<!-- android:text="@string/mouse_mode"/>-->
<!-- </LinearLayout>-->
<LinearLayout
android:id="@+id/ln_settings"