Merge pull request #658 from AnBui2004/master

4.2.3
This commit is contained in:
An Bui 2026-06-08 17:09:26 +07:00 committed by GitHub
commit 3b16fd8b7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 704 additions and 475 deletions

View file

@ -12,8 +12,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
versionCode 126
versionName "4.2.2"
versionCode 127
versionName "4.2.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true

View file

@ -1271,4 +1271,28 @@ public class MainSettingsManager extends AppCompatActivity
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean("suggestionsAndTipsNotification", true);
}
public static void setExternalX11(Context context, Boolean _boolean) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("externalX11", _boolean);
edit.apply();
}
public static Boolean getExternalX11(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean("externalX11", false);
}
public static void setBlurEffect(Context context, Boolean _boolean) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("blurEffect", _boolean);
edit.apply();
}
public static Boolean getBlurEffect(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean("blurEffect", true);
}
}

View file

@ -1066,7 +1066,7 @@ public class MainVNCActivity extends VncCanvasActivity {
boolean isBlurring;
private void blurLayout() {
if (isBlurring) return;
if (isBlurring || !MainSettingsManager.getBlurEffect(this)) return;
isBlurring = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {

View file

@ -26,35 +26,20 @@ public class ControlersOptionsFragment extends DialogFragment {
alertDialog.setContentView(R.layout.fragment_controlers_options);
alertDialog.findViewById(R.id.gamepadBtn).setOnClickListener(v -> {
MainSettingsManager.setControlMode(getActivity(), "G");
if (MainSettingsManager.getVmUi(getActivity()).equals("X11")) {
if (X11Activity.desktop != null ) X11Activity.desktop.setVisibility(View.GONE);
if (X11Activity.gamepad != null ) X11Activity.gamepad.setVisibility(View.VISIBLE);
} else if (MainSettingsManager.getVmUi(getActivity()).equals("VNC")) {
binding.desktop.setVisibility(View.GONE);
binding.gamepad.setVisibility(View.VISIBLE);
}
alertDialog.cancel();
});
alertDialog.findViewById(R.id.desktopBtn).setOnClickListener(v -> {
MainSettingsManager.setControlMode(getActivity(), "D");
if (MainSettingsManager.getVmUi(getActivity()).equals("X11")) {
if (X11Activity.desktop != null ) X11Activity.desktop.setVisibility(View.VISIBLE);
if (X11Activity.gamepad != null ) X11Activity.gamepad.setVisibility(View.GONE);
} else if (MainSettingsManager.getVmUi(getActivity()).equals("VNC")) {
binding.desktop.setVisibility(View.VISIBLE);
binding.gamepad.setVisibility(View.GONE);
}
alertDialog.cancel();
});
alertDialog.findViewById(R.id.hideBtn).setOnClickListener(v -> {
MainSettingsManager.setControlMode(getActivity(), "H");
if (MainSettingsManager.getVmUi(getActivity()).equals("X11")) {
if (X11Activity.desktop != null) X11Activity.desktop.setVisibility(View.GONE);
if (X11Activity.gamepad != null) X11Activity.gamepad.setVisibility(View.GONE);
} else if (MainSettingsManager.getVmUi(getActivity()).equals("VNC")) {
binding.desktop.setVisibility(View.GONE);
binding.gamepad.setVisibility(View.GONE);
}
alertDialog.cancel();
});

View file

@ -396,10 +396,7 @@ public class RomInfo extends AppCompatActivity {
openAnBuiContentUrl(anbuiContentUrls);
}
} else {
Intent openurl = new Intent();
openurl.setAction(Intent.ACTION_VIEW);
openurl.setData(Uri.parse(getIntent().getStringExtra("getrom")));
startActivity(openurl);
IntentUtils.openUrl(this, getIntent().getStringExtra("getrom"));
}
}

View file

@ -678,12 +678,10 @@ public class VMManager {
},
null, null);
isQemuStopedWithError = true;
return true;
} else if (_result.contains(") exists") && _result.contains("drive with bus")) {
//Error code: DRIVE_INDEX_0_EXISTS
DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_DRIVE_INDEX_0_EXISTS) + "\n\n" + _result, R.drawable.hard_drive_24px);
isQemuStopedWithError = true;
return true;
} else if (_result.contains("gtk initialization failed") || _result.contains("x11 not available")) {
//Error code: X11_NOT_AVAILABLE
DialogUtils.twoDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_X11_NOT_AVAILABLE), _activity.getString(R.string.switch_to_vnc), _activity.getString(R.string.cancel), true, R.drawable.cast_24px, true,
@ -693,7 +691,6 @@ public class VMManager {
},
null, null);
isQemuStopedWithError = true;
return true;
} else if (_result.contains("Couldn't connect to XServer")) {
if (isTryAgain) {
DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.x11_display_cannot_be_used_at_this_time_content) + "\n\n" + _result, R.drawable.cast_warning_24px);
@ -704,19 +701,16 @@ public class VMManager {
MainStartVM.startTryAgain(_activity);
isTryAgain = true;
}
return true;
} else if (_result.contains("No such file or directory")) {
//Error code: NO_SUCH_FILE_OR_DIRECTORY
DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_NO_SUCH_FILE_OR_DIRECTORY) + "\n\n" + _result, R.drawable.file_copy_24px);
_activity.stopService(new Intent(_activity, MainService.class));
isQemuStopedWithError = true;
return true;
} else if (_result.contains("another process using")) {
//Error code: ANOTHER_PROCESS_USING_IMAGE
DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.error_ANOTHER_PROCESS_USING_IMAGE) + "\n\n" + _result, R.drawable.file_copy_24px);
_activity.stopService(new Intent(_activity, MainService.class));
isQemuStopedWithError = true;
return true;
} else if (_result.contains("mesapt: invalid sdl display")) {
DialogUtils.twoDialog(_activity,
_activity.getResources().getString(R.string.problem_has_been_detected),
@ -738,11 +732,15 @@ public class VMManager {
DialogUtils.oneDialog(_activity, _activity.getString(R.string.problem_has_been_detected), _activity.getString(R.string.vm_could_not_be_run_content) + "\n\n" + _result, R.drawable.error_96px);
_activity.stopService(new Intent(_activity, MainService.class));
isQemuStopedWithError = true;
return true;
} else {
isQemuStopedWithError = false;
return false;
}
if (isQemuStopedWithError) {
MainStartVM.dismissDialog();
}
return isQemuStopedWithError;
}
public static boolean isRomsDataJsonValid(Boolean _needfix, Activity _context) {
@ -807,37 +805,38 @@ public class VMManager {
}
public static boolean isthiscommandsafeimg(@NonNull String _command, Context _context) {
if (!_command.contains("qcow2")) {
String _getsize = _command.substring(_command.lastIndexOf(" ") + 1);
if (_getsize.toLowerCase().endsWith("t") || _getsize.toLowerCase().endsWith("p") || _getsize.toLowerCase().endsWith("e")) {
latestUnsafeCommandReason = _context.getString(R.string.size_too_large_try_qcow2_format);
return false;
}
if (_getsize.toLowerCase().endsWith("g")) {
if (_getsize.length() <= 2) {
return true;
} else {
latestUnsafeCommandReason = _context.getString(R.string.size_too_large_try_qcow2_format);
return false;
}
}
if (_getsize.toLowerCase().endsWith("m")) {
if (_getsize.length() <= 4) {
return true;
} else {
latestUnsafeCommandReason = _context.getString(R.string.size_too_large_try_qcow2_format);
return false;
}
}
if (_getsize.toLowerCase().endsWith("k")) {
if (_getsize.length() <= 8) {
return true;
} else {
latestUnsafeCommandReason = _context.getString(R.string.size_too_large_try_qcow2_format);
return false;
}
}
}
// This is unnecessary because the Android filesystem only counts the space used within the .img file.
// if (!_command.contains("qcow2")) {
// String _getsize = _command.trim().substring(_command.lastIndexOf(" ") + 1);
// if (_getsize.toLowerCase().endsWith("t") || _getsize.toLowerCase().endsWith("p") || _getsize.toLowerCase().endsWith("e")) {
// latestUnsafeCommandReason = _context.getString(R.string.size_too_large_try_qcow2_format);
// return false;
// }
// if (_getsize.toLowerCase().endsWith("g")) {
// if (_getsize.length() <= 2) {
// return true;
// } else {
// latestUnsafeCommandReason = _context.getString(R.string.size_too_large_try_qcow2_format);
// return false;
// }
// }
// if (_getsize.toLowerCase().endsWith("m")) {
// if (_getsize.length() <= 4) {
// return true;
// } else {
// latestUnsafeCommandReason = _context.getString(R.string.size_too_large_try_qcow2_format);
// return false;
// }
// }
// if (_getsize.toLowerCase().endsWith("k")) {
// if (_getsize.length() <= 8) {
// return true;
// } else {
// latestUnsafeCommandReason = _context.getString(R.string.size_too_large_try_qcow2_format);
// return false;
// }
// }
// }
return true;
}

View file

@ -6,7 +6,6 @@ import static com.vectras.vm.utils.LibraryChecker.isPackageInstalled2;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import com.termux.app.TermuxService;
@ -14,15 +13,16 @@ import com.vectras.qemu.Config;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.qemu.MainVNCActivity;
import com.vectras.vm.R;
import com.vectras.vm.VectrasApp;
import com.vectras.vm.core.ShellExecutor;
import com.vectras.vm.core.TermuxX11;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.PackageUtils;
import com.vectras.vm.x11.X11Activity;
import com.vectras.vterm.Terminal;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
@ -31,7 +31,8 @@ public class DisplaySystem {
private static boolean isTermuxClassLoaded = false;
public static boolean isUseBuiltInX11() {
return SDK_INT < 34 && DeviceUtils.isArm();
//return SDK_INT < 34 && DeviceUtils.isArm();
return !MainSettingsManager.getExternalX11(VectrasApp.getContext());
}
public static void launch(Context context) {
@ -124,6 +125,11 @@ public class DisplaySystem {
Log.d(TAG, "startTermuxX11...");
if (isUseBuiltInX11()) {
if (SDK_INT >= 34) {
File loaderApk = new File(TermuxService.PREFIX_PATH + "/libexec/termux-x11/loader.apk");
loaderApk.setWritable(false, false);
}
ShellExecutor shellExec = new ShellExecutor();
shellExec.exec(TermuxService.PREFIX_PATH + "/bin/termux-x11 :0");
} else {

View file

@ -25,6 +25,7 @@ import com.vectras.vm.manager.QmpSender;
import com.vectras.vm.manager.VmFileManager;
import com.vectras.vm.manager.VmAudioManager;
import com.vectras.vm.settings.ExternalVNCSettingsActivity;
import com.vectras.vm.utils.DeviceUtils;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.NetworkUtils;
@ -59,6 +60,8 @@ public class MainStartVM {
Config.vmID = vmConfig.vmID;
VMManager.setArch(vmConfig.itemArch, activity);
if (!(MainSettingsManager.getVmUi(activity).equals("X11") && DisplaySystem.isUseBuiltInX11())) {
showDialog(activity, vmConfig.vmID, vmConfig.itemName, vmConfig.itemIcon, activity.getString(R.string.preparing));
}
@ -136,6 +139,15 @@ public class MainStartVM {
Config.vmID = finalvmID;
if (VMManager.isVMRunning(context, finalvmID)) {
Toast.makeText(context, "This VM is already running.", Toast.LENGTH_LONG).show();
DisplaySystem.launch(context);
if (!MainSettingsManager.getVmUi(context).equals("VNC")) VmAudioManager.stream(vmID);
dismissDialog();
return;
}
File romDir = new File(Config.getCacheDir() + "/" + finalvmID);
if (!romDir.exists()) {
if (!romDir.mkdirs()) {
@ -163,37 +175,7 @@ public class MainStartVM {
return;
}
if (env.contains(FileUtils.getExternalFilesDirectory(context).getPath() + "/SharedFolder") && FileUtils.getFolderSize(FileUtils.getExternalFilesDirectory(context).getPath() + "/SharedFolder") * Math.pow(10, -6) > 516) {
DialogUtils.twoDialog(
context,
context.getString(R.string.problem_has_been_detected),
context.getString(R.string.shared_folder_is_too_large_content),
context.getString(R.string.open_shared_folder),
context.getString(R.string.close),
true,
R.drawable.warning_48px,
true,
() -> FileUtils.openFolder(context, FileUtils.getExternalFilesDirectory(context).getPath() + "/SharedFolder"),
null,
null
);
dismissDialog();
return;
}
VMManager.lastQemuCommand = env;
if (VMManager.isVMRunning(context, finalvmID)) {
Toast.makeText(context, "This VM is already running.", Toast.LENGTH_LONG).show();
DisplaySystem.launch(context);
if (!MainSettingsManager.getVmUi(context).equals("VNC")) VmAudioManager.stream(vmID);
dismissDialog();
return;
}
if (AppConfig.getSetupFiles().contains("arm") && !AppConfig.getSetupFiles().contains("arm64")) {
if (!DeviceUtils.is64bit()) {
if (env.contains("tcg,thread=multi")) {
StartVmDialog finalDialog1 = dialog;
DialogUtils.twoDialog(context, context.getResources().getString(R.string.problem_has_been_detected), context.getResources().getString(R.string.can_not_use_mttcg), context.getString(R.string.ok), context.getString(R.string.cancel), true, R.drawable.warning_48px, true,
@ -219,12 +201,39 @@ public class MainStartVM {
return;
}
new Thread(() -> {
boolean isExceeded = env.contains(FileUtils.getExternalFilesDirectory(context).getPath() + "/SharedFolder") && FileUtils.getFolderSize(FileUtils.getExternalFilesDirectory(context).getPath() + "/SharedFolder") * Math.pow(10, -6) > 516;
new Handler(Looper.getMainLooper()).post(() -> {
if (isExceeded) {
DialogUtils.twoDialog(
context,
context.getString(R.string.problem_has_been_detected),
context.getString(R.string.shared_folder_is_too_large_content),
context.getString(R.string.open_shared_folder),
context.getString(R.string.close),
true,
R.drawable.warning_48px,
true,
() -> FileUtils.openFolder(context, FileUtils.getExternalFilesDirectory(context).getPath() + "/SharedFolder"),
null,
null
);
dismissDialog();
return;
}
VMManager.lastQemuCommand = env;
if (breakNow) {
dismissDialog();
return;
}
startVm(context, vmName, env, finalvmID, thumbnailFile);
});
}).start();
}
public static void startVm(
@ -410,7 +419,7 @@ public class MainStartVM {
if (status != null) dialog.setStatus(status);
}
private static void dismissDialog() {
public static void dismissDialog() {
if (dialog != null) dialog.dismiss();
if (breakNow) {
new Thread(QmpSender::shutdown).start();

View file

@ -3,38 +3,88 @@ package com.vectras.vm.main.core;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.fragment.app.FragmentActivity;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.vectras.vm.creator.VMCreatorActivity;
import com.vectras.vm.ExportRomActivity;
import com.vectras.vm.R;
import com.vectras.vm.VMManager;
import com.vectras.vm.main.vms.DataMainRoms;
import com.vectras.vm.manager.VmAudioManager;
import com.vectras.vm.manager.VmControllerDialog;
import com.vectras.vm.manager.VmFileManager;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.ProgressDialog;
import java.io.File;
public class RomOptionsDialog {
public static void show(Activity activity, int position, String vmID, String vmName) {
if (VMManager.isVMRunning(activity, vmID)) {
public static void show(Activity activity, int position, DataMainRoms vmConfig) {
if (VMManager.isVMRunning(activity, vmConfig.vmID)) {
VmControllerDialog vmControllerDialog = new VmControllerDialog();
vmControllerDialog.streamAudio = VmAudioManager.streamAudio;
vmControllerDialog.position = position;
vmControllerDialog.show(((FragmentActivity) activity).getSupportFragmentManager(), "VmControllerDialog");
} else {
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(activity);
bottomSheetDialog.setOnShowListener(d -> {
BottomSheetBehavior<FrameLayout> behavior = bottomSheetDialog.getBehavior();
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
behavior.setSkipCollapsed(true);
});
View v = activity.getLayoutInflater().inflate(R.layout.rom_options_dialog, null);
bottomSheetDialog.setContentView(v);
Button modifyRomBtn = v.findViewById(R.id.modifyRomBtn);
modifyRomBtn.setOnClickListener(v3 -> {
activity.startActivity(new Intent(activity, VMCreatorActivity.class).putExtra("POS", position).putExtra("MODIFY", true).putExtra("VMID", vmID));
ImageView thumbnail = v.findViewById(R.id.ivIcon);
TextView name = v.findViewById(R.id.textName);
TextView arch = v.findViewById(R.id.textArch);
name.setText(vmConfig.itemName);
arch.setText(vmConfig.itemArch);
if (!vmConfig.itemIcon.isEmpty() && FileUtils.isFileExists(vmConfig.itemIcon)){
Glide.with(activity.getApplicationContext())
.load(new File(vmConfig.itemIcon))
.placeholder(R.drawable.ic_computer_180dp_with_padding)
.error(R.drawable.ic_computer_180dp_with_padding)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(thumbnail);
} else if (VmFileManager.isScreenshotPngExists(vmConfig.vmID)) {
Glide.with(activity.getApplicationContext())
.load(new File(VmFileManager.getScreenshotPng(vmConfig.vmID )))
.placeholder(R.drawable.ic_computer_180dp_with_padding)
.error(R.drawable.ic_computer_180dp_with_padding)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(thumbnail);
} else {
VMManager.setIconWithName(thumbnail, vmConfig.itemName);
}
v.findViewById(R.id.btn_start).setOnClickListener(v4 -> {
MainStartVM.startNow(activity, vmConfig);
bottomSheetDialog.cancel();
});
Button exportRomBtn = v.findViewById(R.id.exportRomBtn);
exportRomBtn.setOnClickListener(v2 -> {
v.findViewById(R.id.ln_edit).setOnClickListener(v3 -> {
activity.startActivity(new Intent(activity, VMCreatorActivity.class).putExtra("POS", position).putExtra("MODIFY", true).putExtra("VMID", vmConfig.vmID));
bottomSheetDialog.cancel();
});
v.findViewById(R.id.ln_export).setOnClickListener(v2 -> {
Intent intent = new Intent();
intent.setClass(activity, ExportRomActivity.class);
intent.putExtra("POS", position);
@ -42,9 +92,23 @@ public class RomOptionsDialog {
bottomSheetDialog.cancel();
});
Button removeRomBtn = v.findViewById(R.id.removeRomBtn);
removeRomBtn.setOnClickListener(v1 -> {
VMManager.deleteVMDialog(vmName, position, activity);
v.findViewById(R.id.ln_cleanup).setOnClickListener(v5 -> {
ProgressDialog progressDialog = new ProgressDialog(activity);
progressDialog.setText(activity.getString(R.string.just_a_sec));
progressDialog.show();
new Thread(() -> {
VmFileManager.removeTemp(activity, vmConfig.vmID);
activity.runOnUiThread(() -> {
progressDialog.reset();
Toast.makeText(activity, activity.getString(R.string.done), Toast.LENGTH_SHORT).show();
bottomSheetDialog.cancel();
});
}).start();
});
v.findViewById(R.id.ln_remove).setOnClickListener(v1 -> {
VMManager.deleteVMDialog(vmConfig.itemName, position, activity);
bottomSheetDialog.cancel();
});

View file

@ -85,9 +85,7 @@ public class StartVmDialog {
vmBootNote.setText(R.string.shutting_down);
new Thread(() -> {
QmpSender.shutdown();
new Handler(Looper.getMainLooper()).post(() -> {
callBack.onStop();
});
new Handler(Looper.getMainLooper()).post(callBack::onStop);
boolean isVmRuning = VMManager.isVMRunning(activity, vmId);
@ -98,7 +96,7 @@ public class StartVmDialog {
QmpSender.shutdown();
if (!isVmRuning) {
if (!isVmRuning || VMManager.isQemuStopedWithError) {
new Handler(Looper.getMainLooper()).post(() -> {
isShuttingDown = false;
dismiss();

View file

@ -134,7 +134,7 @@ public class VmsFragment extends Fragment implements CallbackInterface.HomeCallT
List<DataMainRoms> finalTempdata = tempdata;
requireActivity().runOnUiThread(() -> {
binding.lnLoad.setVisibility(View.GONE);
if (finalTempdata.isEmpty()) {
if (finalTempdata == null || finalTempdata.isEmpty()) {
binding.rvRomlist.setVisibility(View.GONE);
binding.lnNothinghere.setVisibility(View.VISIBLE);
} else {

View file

@ -17,7 +17,6 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.vectras.qemu.Config;
import com.vectras.vm.R;
import com.vectras.vm.StartVM;
import com.vectras.vm.VMManager;
import com.vectras.vm.main.core.MainStartVM;
import com.vectras.vm.main.core.RomOptionsDialog;
@ -78,18 +77,10 @@ public class VmsHomeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
VMManager.setIconWithName(myHolder.ivIcon, current.itemName);
}
myHolder.cdRoms.setOnClickListener(view -> {
VMManager.setArch(current.itemArch, activity);
if (current.qmpPort == 0) {
Config.setDefault();
} else {
Config.QMPPort = current.qmpPort;
}
MainStartVM.startNow(activity, current);
});
myHolder.cdRoms.setOnClickListener(view -> MainStartVM.startNow(activity, current));
myHolder.cdRoms.setOnLongClickListener(v -> {
RomOptionsDialog.show(activity, position, current.vmID, current.itemName);
RomOptionsDialog.show(activity, position, current);
return true;
});
}

View file

@ -2,7 +2,9 @@ package com.vectras.vm.settings;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.activity.OnBackPressedCallback;
@ -91,6 +93,14 @@ public class ThemeActivity extends AppCompatActivity {
binding.lnDynamiccolor.setEnabled(false);
binding.lnDynamiccolor.setAlpha(0.5f);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
binding.swBlurEffect.setChecked(MainSettingsManager.getBlurEffect(this));
binding.lnBlurEffect.setOnClickListener(v -> binding.swBlurEffect.toggle());
binding.swBlurEffect.setOnCheckedChangeListener((buttonView, isChecked) -> MainSettingsManager.setBlurEffect(this, isChecked));
} else {
binding.lnBlurEffect.setVisibility(View.GONE);
}
}
private void onBack() {

View file

@ -40,6 +40,7 @@ public class X11DisplaySettingsActivity extends AppCompatActivity {
binding.swEnabled.setChecked(MainSettingsManager.getVmUi(this).equals("X11"));
binding.swRunQemuWithXterm.setChecked(MainSettingsManager.getRunQemuWithXterm(this));
binding.swUseSdl.setChecked(MainSettingsManager.getUseSdl(this));
binding.swUseExternal.setChecked(MainSettingsManager.getExternalX11(this));
binding.swEnabled.setOnCheckedChangeListener((buttonView, isChecked) -> {
MainSettingsManager.setVmUi(this, isChecked ? "X11" : "VNC");
@ -71,6 +72,9 @@ public class X11DisplaySettingsActivity extends AppCompatActivity {
binding.swUseSdl.setOnCheckedChangeListener((buttonView, isChecked) -> MainSettingsManager.setUseSdl(this, isChecked));
binding.lnUseSdl.setOnClickListener(v -> binding.swUseSdl.toggle());
binding.swUseExternal.setOnCheckedChangeListener((buttonView, isChecked) -> MainSettingsManager.setExternalX11(this, isChecked));
binding.lnUseExternal.setOnClickListener(v -> binding.swUseExternal.toggle());
isInitialized = true;
uiController(binding.swEnabled.isChecked());

View file

@ -18,5 +18,4 @@ public class NumberUtils {
return String.format(Locale.US, "%.2f GB", gb);
}
}
}

View file

@ -1,7 +1,7 @@
package com.vectras.vm.x11;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import android.app.Activity;
import android.app.Dialog;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION.SDK_INT;
@ -13,7 +13,6 @@ import static android.view.KeyEvent.*;
import static android.view.WindowManager.LayoutParams.*;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.activity.OnBackPressedCallback;
import androidx.appcompat.content.res.AppCompatResources;
@ -23,13 +22,14 @@ import com.vectras.qemu.Config;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.Fragment.ControlersOptionsFragment;
import com.vectras.vm.Fragment.LoggerDialogFragment;
import com.vectras.vm.MainService;
import com.vectras.vm.VMManager;
import com.vectras.vm.databinding.ActivityX11Binding;
import com.vectras.vm.databinding.ControlsFragmentBinding;
import com.vectras.vm.databinding.DesktopControlsBinding;
import com.vectras.vm.databinding.GameControlsBinding;
import com.vectras.vm.main.core.MainStartVM;
import com.vectras.vm.manager.QmpSender;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.UIUtils;
import com.vectras.vm.widgets.JoystickView;
import static com.vectras.vm.x11.CmdEntryPoint.ACTION_START;
import static com.vectras.vm.x11.LoriePreferences.ACTION_PREFERENCES_CHANGED;
@ -69,7 +69,6 @@ import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
@ -97,10 +96,10 @@ import java.util.Objects;
@SuppressLint("ApplySharedPref")
@SuppressWarnings({"deprecation", "unused"})
public class X11Activity extends AppCompatActivity implements View.OnApplyWindowInsetsListener {
private final String TAG = "X11Activity";
static final String ACTION_STOP = "com.vectras.vm.x11.ACTION_STOP";
public static Handler handler = new Handler();
FrameLayout frm;
private TouchInputHandler mInputHandler;
private ICmdEntryInterface service = null;
public TermuxX11ExtraKeys mExtraKeys;
@ -112,22 +111,18 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
private boolean hideEKOnVolDown = false;
private boolean toggleIMEUsingBackKey = false;
private boolean useTermuxEKBarBehaviour = false;
private static final int KEY_BACK = 158;
private final int KEY_BACK = 158;
public LinearLayout menuLayout;
private boolean oldFullscreen = false;
private boolean oldHideCutout = false;
private static boolean oldFullscreen = false;
private static boolean oldHideCutout = false;
public TextView keyEventStatusTextView;
public static LinearLayout desktop;
public static LinearLayout gamepad;
public boolean ctrlClicked = false;
public boolean altClicked = false;
private ImageButton qmpBtn;
public static Activity activity;
ActivityX11Binding binding;
ControlsFragmentBinding bindingControls;
DesktopControlsBinding bindingDesktopControls;
GameControlsBinding bindingGameControls;
private final BroadcastReceiver receiver =
new BroadcastReceiver() {
@ -215,12 +210,14 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
| FLAG_TRANSLUCENT_STATUS,
0);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_x11);
activity = this;
binding = ActivityX11Binding.inflate(getLayoutInflater());
bindingControls = binding.controlsfragment;
bindingDesktopControls = binding.controlsfragment.desktopcontrols;
bindingGameControls = binding.controlsfragment.gamecontrols;
setContentView(binding.getRoot());
frm = findViewById(R.id.frame);
findViewById(R.id.preferences_button)
binding.preferencesButton
.setOnClickListener(
(l) ->
startActivity(
@ -229,7 +226,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
setAction(Intent.ACTION_MAIN);
}
}));
findViewById(R.id.help_button)
binding.helpButton
.setOnClickListener(
(l) ->
startActivity(
@ -238,8 +235,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
Uri.parse(
"https://github.com/termux/termux-x11/blob/master/README.md#running-graphical-applications"))));
LorieView lorieView = findViewById(R.id.lorieView);
View lorieParent = (View) lorieView.getParent();
View lorieParent = (View) binding.lorieView.getParent();
mInputHandler =
new TouchInputHandler(
@ -250,7 +246,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
toggleExtraKeys();
}
},
new InputEventSender(lorieView));
new InputEventSender(binding.lorieView));
mLorieKeyListener =
(v, k, e) -> {
@ -268,16 +264,19 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
if (k == KEYCODE_BACK) {
if (e.isFromSource(InputDevice.SOURCE_MOUSE)
|| e.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) {
if (e.getRepeatCount() != 0) // ignore auto-repeat
if (e.getRepeatCount() != 0) { // ignore auto-repeat
Log.d(TAG, "KEYCODE_BACK: auto-repeat");
return true;
}
if (e.getAction() == ACTION_UP
|| e.getAction() == ACTION_DOWN)
lorieView.sendMouseEvent(
binding.lorieView.sendMouseEvent(
-1,
-1,
InputStub.BUTTON_RIGHT,
e.getAction() == ACTION_DOWN,
true);
Log.d(TAG, "KEYCODE_BACK: sendMouseEvent");
return true;
}
@ -286,8 +285,14 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
!= KEYBOARD_TYPE_ALPHABETIC
|| e.getScanCode() == 0) {
if (toggleIMEUsingBackKey && e.getAction() == ACTION_UP) {
activity.finish();
Log.d(TAG, "KEYCODE_BACK: toggleIMEUsingBackKey");
}
if (e.getAction() == ACTION_UP) {
onBack();
}
Log.d(TAG, "KEYCODE_BACK: toggleIMEUsingBackKey");
return true;
}
}
@ -303,39 +308,39 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
};
lorieParent.setOnTouchListener(
(v, e) -> mInputHandler.handleTouchEvent(lorieParent, lorieView, e));
(v, e) -> mInputHandler.handleTouchEvent(lorieParent, binding.lorieView, e));
lorieParent.setOnHoverListener(
(v, e) -> mInputHandler.handleTouchEvent(lorieParent, lorieView, e));
(v, e) -> mInputHandler.handleTouchEvent(lorieParent, binding.lorieView, e));
lorieParent.setOnGenericMotionListener(
(v, e) -> mInputHandler.handleTouchEvent(lorieParent, lorieView, e));
(v, e) -> mInputHandler.handleTouchEvent(lorieParent, binding.lorieView, e));
if (SDK_INT >= VERSION_CODES.O) {
lorieView.setOnCapturedPointerListener(
(v, e) -> mInputHandler.handleTouchEvent(lorieView, lorieView, e));
binding.lorieView.setOnCapturedPointerListener(
(v, e) -> mInputHandler.handleTouchEvent(binding.lorieView, binding.lorieView, e));
}
if (SDK_INT >= VERSION_CODES.O) {
lorieParent.setOnCapturedPointerListener(
(v, e) -> mInputHandler.handleTouchEvent(lorieView, lorieView, e));
(v, e) -> mInputHandler.handleTouchEvent(binding.lorieView, binding.lorieView, e));
}
lorieView.setOnKeyListener(mLorieKeyListener);
binding.lorieView.setOnKeyListener(mLorieKeyListener);
lorieView.setCallback(
binding.lorieView.setCallback(
(sfc, surfaceWidth, surfaceHeight, screenWidth, screenHeight) -> {
int framerate =
(int)
((lorieView.getDisplay() != null)
? lorieView.getDisplay().getRefreshRate()
((binding.lorieView.getDisplay() != null)
? binding.lorieView.getDisplay().getRefreshRate()
: 30);
mInputHandler.handleHostSizeChanged(surfaceWidth, surfaceHeight);
mInputHandler.handleClientSizeChanged(screenWidth, screenHeight);
LorieView.sendWindowChange(screenWidth, screenHeight, framerate);
binding.lorieView.sendWindowChange(screenWidth, screenHeight, framerate);
if (service != null) {
try {
service.windowChanged(
sfc,
lorieView.getDisplay() != null
? lorieView.getDisplay().getName()
binding.lorieView.getDisplay() != null
? binding.lorieView.getDisplay().getName()
: "screen");
} catch (RemoteException e) {
Log.e("X11Activity", "failed to send windowChanged request", e);
@ -370,54 +375,19 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
initStylusAuxButtons();
initMouseAuxButtons();
desktop = findViewById(R.id.desktop);
gamepad = findViewById(R.id.gamepad);
if (Objects.equals(MainSettingsManager.getControlMode(activity), "D")) {
desktop.setVisibility(View.VISIBLE);
gamepad.setVisibility(View.GONE);
} else if (Objects.equals(MainSettingsManager.getControlMode(activity), "G")) {
desktop.setVisibility(View.GONE);
gamepad.setVisibility(View.VISIBLE);
} else if (Objects.equals(MainSettingsManager.getControlMode(activity), "H")) {
desktop.setVisibility(View.GONE);
gamepad.setVisibility(View.GONE);
if (Objects.equals(MainSettingsManager.getControlMode(this), "D")) {
bindingControls.desktop.setVisibility(View.VISIBLE);
bindingControls.gamepad.setVisibility(View.GONE);
} else if (Objects.equals(MainSettingsManager.getControlMode(this), "G")) {
bindingControls.desktop.setVisibility(View.GONE);
bindingControls.gamepad.setVisibility(View.VISIBLE);
} else if (Objects.equals(MainSettingsManager.getControlMode(this), "H")) {
bindingControls.desktop.setVisibility(View.GONE);
bindingControls.gamepad.setVisibility(View.GONE);
}
ImageButton shutdownBtn = findViewById(R.id.shutdownBtn);
ImageButton settingBtn = findViewById(R.id.btnSettings);
ImageButton btnFit = findViewById(R.id.btnFit);
ImageButton keyboardBtn = findViewById(R.id.kbdBtn);
ImageButton controllersBtn = findViewById(R.id.btnMode);
ImageButton upBtn = findViewById(R.id.upBtn);
ImageButton leftBtn = findViewById(R.id.leftBtn);
ImageButton downBtn = findViewById(R.id.downBtn);
ImageButton rightBtn = findViewById(R.id.rightBtn);
ImageButton enterBtn = findViewById(R.id.enterBtn);
ImageButton escBtn = findViewById(R.id.escBtn);
ImageButton ctrlBtn = findViewById(R.id.ctrlBtn);
ImageButton altBtn = findViewById(R.id.altBtn);
ImageButton delBtn = findViewById(R.id.delBtn);
ImageButton btnVterm = findViewById(R.id.btnVterm);
Button eBtn = findViewById(R.id.eBtn);
Button rBtn = findViewById(R.id.rBtn);
Button qBtn = findViewById(R.id.qBtn);
Button xBtn = findViewById(R.id.xBtn);
ImageButton ctrlGameBtn = findViewById(R.id.ctrlGameBtn);
Button spaceBtn = findViewById(R.id.spaceBtn);
Button tabGameBtn = findViewById(R.id.tabGameBtn);
Button tabBtn = findViewById(R.id.tabBtn);
ImageButton upGameBtn = findViewById(R.id.upGameBtn);
ImageButton downGameBtn = findViewById(R.id.downGameBtn);
ImageButton leftGameBtn = findViewById(R.id.leftGameBtn);
ImageButton rightGameBtn = findViewById(R.id.rightGameBtn);
ImageButton enterGameBtn = findViewById(R.id.enterGameBtn);
ImageButton appsBtn = findViewById(R.id.btnPrograms);
qmpBtn = findViewById(R.id.btnQmp);
final boolean[] isFullScreen = {false};
btnFit.setOnClickListener(view -> {
bindingControls.btnFit.setOnClickListener(view -> {
sendKey(KEYCODE_CTRL_LEFT, false);
sendKey(KEYCODE_ALT_LEFT, false);
sendKey(KEYCODE_F, false);
@ -426,16 +396,16 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
sendKey(KEYCODE_F, true);
if (isFullScreen[0]) {
btnFit.setImageDrawable(AppCompatResources.getDrawable(X11Activity.this, R.drawable.close_fullscreen_24px));
bindingControls.btnFit.setImageDrawable(AppCompatResources.getDrawable(X11Activity.this, R.drawable.close_fullscreen_24px));
isFullScreen[0] = false;
} else {
btnFit.setImageDrawable(AppCompatResources.getDrawable(X11Activity.this, R.drawable.open_in_full_24px));
bindingControls.btnFit.setImageDrawable(AppCompatResources.getDrawable(X11Activity.this, R.drawable.open_in_full_24px));
isFullScreen[0] = true;
}
});
appsBtn.setOnClickListener(v -> {
Dialog dialog = new Dialog(activity);
bindingControls.btnPrograms.setOnClickListener(v -> {
Dialog dialog = new Dialog(this);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.dialog_programs);
Objects.requireNonNull(dialog.getWindow()).setBackgroundDrawableResource(android.R.color.transparent);
@ -449,28 +419,28 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
ImageButton glxGearsBtn = dialog.findViewById(R.id.btnGlxGears);
termBtn.setOnClickListener(v1 -> {
new Terminal(activity).executeShellCommand2("xfce4-terminal", false, activity);
new Terminal(this).executeShellCommand2("xfce4-terminal", false, this);
dialog.dismiss();
});
glxGearsBtn.setOnClickListener(v1 -> {
new Terminal(activity).executeShellCommand2("glxgears", false, activity);
new Terminal(this).executeShellCommand2("glxgears", false, this);
dialog.dismiss();
});
vkCubeBtn.setOnClickListener(v1 -> {
new Terminal(activity).executeShellCommand2("vkcube", false, activity);
new Terminal(this).executeShellCommand2("vkcube", false, this);
dialog.dismiss();
});
try {
dialog.show();
} catch (WindowManager.BadTokenException e) {
e.printStackTrace();
Log.e(TAG, "Failed to show dialog", e);
}
});
upGameBtn.setOnTouchListener((v, event) -> {
bindingGameControls.upGameBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KEYCODE_DPAD_UP, false);
v.animate().scaleXBy(-0.2f).setDuration(200).start();
@ -485,7 +455,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
return false;
});
leftGameBtn.setOnTouchListener((v, event) -> {
bindingGameControls.leftGameBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KEYCODE_DPAD_LEFT, false);
v.animate().scaleXBy(-0.2f).setDuration(200).start();
@ -500,7 +470,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
return false;
});
downGameBtn.setOnTouchListener((v, event) -> {
bindingGameControls.downGameBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KEYCODE_DPAD_DOWN, false);
v.animate().scaleXBy(-0.2f).setDuration(200).start();
@ -515,7 +485,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
return false;
});
rightGameBtn.setOnTouchListener((v, event) -> {
bindingGameControls.rightGameBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KEYCODE_DPAD_RIGHT, false);
v.animate().scaleXBy(-0.2f).setDuration(200).start();
@ -530,33 +500,34 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
return false;
});
JoystickView joystick = findViewById(R.id.joyStick);
joystick.setVisibility(View.GONE);
tabBtn.setOnClickListener(v -> keyDownUp(KEYCODE_TAB));
tabGameBtn.setOnClickListener(v -> keyDownUp(KEYCODE_TAB));
enterGameBtn.setOnClickListener(v -> keyDownUp(KEYCODE_ENTER));
eBtn.setOnClickListener(v -> keyDownUp(KEYCODE_E));
rBtn.setOnClickListener(v -> keyDownUp(KEYCODE_R));
qBtn.setOnClickListener(v -> keyDownUp(KEYCODE_Q));
xBtn.setOnClickListener(v -> keyDownUp(KEYCODE_X));
ctrlGameBtn.setOnClickListener(v -> keyDownUp(KEYCODE_CTRL_LEFT));
spaceBtn.setOnClickListener(v -> keyDownUp(KEYCODE_SPACE));
btnVterm.setOnClickListener(v -> {
bindingGameControls.joyStick.setVisibility(View.GONE);
bindingDesktopControls.tabBtn.setOnClickListener(v -> keyDownUp(KEYCODE_TAB));
bindingGameControls.tabGameBtn.setOnClickListener(v -> keyDownUp(KEYCODE_TAB));
bindingGameControls.enterGameBtn.setOnClickListener(v -> keyDownUp(KEYCODE_ENTER));
bindingGameControls.eBtn.setOnClickListener(v -> keyDownUp(KEYCODE_E));
bindingGameControls.rBtn.setOnClickListener(v -> keyDownUp(KEYCODE_R));
bindingGameControls.qBtn.setOnClickListener(v -> keyDownUp(KEYCODE_Q));
bindingGameControls.xBtn.setOnClickListener(v -> keyDownUp(KEYCODE_X));
bindingGameControls.ctrlGameBtn.setOnClickListener(v -> keyDownUp(KEYCODE_CTRL_LEFT));
bindingGameControls.spaceBtn.setOnClickListener(v -> keyDownUp(KEYCODE_SPACE));
bindingControls.btnVterm.setOnClickListener(v -> {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// Create and show the dialog.
LoggerDialogFragment newFragment = new LoggerDialogFragment();
newFragment.show(ft, "Logger");
});
shutdownBtn.setOnClickListener(v -> finish());
bindingControls.shutdownBtn.setOnClickListener(v -> finish());
keyboardBtn.setOnClickListener(v -> new Handler(Looper.getMainLooper()).postDelayed(() -> toggleKeyboardVisibility(X11Activity.this), 200));
controllersBtn.setOnClickListener(v -> {
bindingControls.kbdBtn.setOnClickListener(v -> new Handler(Looper.getMainLooper()).postDelayed(() -> toggleKeyboardVisibility(X11Activity.this), 200));
bindingControls.btnMode.setOnClickListener(v -> {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// Create and show the dialog.
ControlersOptionsFragment newFragment = new ControlersOptionsFragment();
newFragment.binding = binding.controlsfragment;
newFragment.show(ft, "Controllers");
});
findViewById(R.id.btnSettings)
bindingControls.btnSettings
.setOnClickListener(
(l) ->
startActivity(
@ -565,7 +536,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
setAction(Intent.ACTION_MAIN);
}
}));
upBtn.setOnTouchListener((v, event) -> {
bindingDesktopControls.upBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KEYCODE_DPAD_UP, false);
v.animate().scaleXBy(-0.2f).setDuration(200).start();
@ -580,7 +551,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
return false;
});
leftBtn.setOnTouchListener((v, event) -> {
bindingDesktopControls.leftBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KEYCODE_DPAD_LEFT, false);
v.animate().scaleXBy(-0.2f).setDuration(200).start();
@ -595,7 +566,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
return false;
});
downBtn.setOnTouchListener((v, event) -> {
bindingDesktopControls.downBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KEYCODE_DPAD_DOWN, false);
v.animate().scaleXBy(-0.2f).setDuration(200).start();
@ -610,7 +581,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
return false;
});
rightBtn.setOnTouchListener((v, event) -> {
bindingDesktopControls.rightBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KEYCODE_DPAD_RIGHT, false);
v.animate().scaleXBy(-0.2f).setDuration(200).start();
@ -626,44 +597,45 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
return false;
});
escBtn.setOnClickListener(v -> keyDownUp(KEYCODE_ESCAPE));
bindingDesktopControls.escBtn.setOnClickListener(v -> keyDownUp(KEYCODE_ESCAPE));
enterBtn.setOnClickListener(v -> keyDownUp(KEYCODE_ENTER));
bindingDesktopControls.enterBtn.setOnClickListener(v -> keyDownUp(KEYCODE_ENTER));
ctrlBtn.setOnClickListener(new View.OnClickListener() {
bindingDesktopControls.ctrlBtn.setOnClickListener(new View.OnClickListener() {
@SuppressLint("UseCompatLoadingForDrawables")
@Override
public void onClick(View v) {
if (!ctrlClicked) {
sendKey(KEYCODE_CTRL_LEFT, false);
ctrlBtn.setBackground(getResources().getDrawable(R.drawable.controls_button2));
bindingDesktopControls.ctrlBtn.setBackground(getResources().getDrawable(R.drawable.controls_button2));
ctrlClicked = true;
} else {
sendKey(KEYCODE_CTRL_LEFT, true);
ctrlBtn.setBackground(getResources().getDrawable(R.drawable.controls_button1));
bindingDesktopControls.ctrlBtn.setBackground(getResources().getDrawable(R.drawable.controls_button1));
ctrlClicked = false;
}
}
});
altBtn.setOnClickListener(new View.OnClickListener() {
bindingDesktopControls.altBtn.setOnClickListener(new View.OnClickListener() {
@SuppressLint("UseCompatLoadingForDrawables")
@Override
public void onClick(View v) {
if (!altClicked) {
sendKey(KEYCODE_ALT_LEFT, false);
altBtn.setBackground(getResources().getDrawable(R.drawable.controls_button2));
bindingDesktopControls.altBtn.setBackground(getResources().getDrawable(R.drawable.controls_button2));
altClicked = true;
} else {
sendKey(KEYCODE_ALT_LEFT, true);
altBtn.setBackground(getResources().getDrawable(R.drawable.controls_button1));
bindingDesktopControls.altBtn.setBackground(getResources().getDrawable(R.drawable.controls_button1));
altClicked = false;
}
}
});
delBtn.setOnClickListener(v -> keyDownUp(KEYCODE_DEL));
qmpBtn.setVisibility(View.GONE);
qmpBtn.setOnClickListener(v -> {
bindingDesktopControls.delBtn.setOnClickListener(v -> keyDownUp(KEYCODE_DEL));
bindingControls.btnQmp.setVisibility(View.GONE);
bindingControls.btnQmp.setOnClickListener(v -> {
// if (monitorMode) {
// onVNC();
// qmpBtn.setImageDrawable(getResources().getDrawable(R.drawable.round_terminal_24));
@ -673,18 +645,13 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
// }
});
Button rightClickBtn = findViewById(R.id.rightClickBtn);
Button middleClickBtn = findViewById(R.id.middleBtn);
Button leftClickBtn = findViewById(R.id.leftClickBtn);
ImageButton winBtn = findViewById(R.id.winBtn);
if (SDK_INT >= VERSION_CODES.N) {
Map.of(
leftClickBtn,
bindingDesktopControls.leftClickBtn,
InputStub.BUTTON_LEFT,
middleClickBtn,
bindingDesktopControls.middleBtn,
InputStub.BUTTON_MIDDLE,
rightClickBtn,
bindingDesktopControls.rightClickBtn,
InputStub.BUTTON_RIGHT)
.forEach(
(v, b) ->
@ -707,32 +674,31 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
return true;
}));
} else {
leftClickBtn.setVisibility(View.GONE);
middleClickBtn.setVisibility(View.GONE);
rightClickBtn.setVisibility(View.GONE);
bindingDesktopControls.leftClickBtn.setVisibility(View.GONE);
bindingDesktopControls.middleBtn.setVisibility(View.GONE);
bindingDesktopControls.rightClickBtn.setVisibility(View.GONE);
}
winBtn.setOnClickListener(v -> {
bindingDesktopControls.winBtn.setOnClickListener(v -> {
sendKey(KEYCODE_CTRL_LEFT, false);
sendKey(KEYCODE_ESCAPE, false);
sendKey(KEYCODE_CTRL_LEFT, false);
sendKey(KEYCODE_ESCAPE, false);
});
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (findViewById(R.id.mainControl).getVisibility() == View.GONE) {
findViewById(R.id.mainControl).setVisibility(View.VISIBLE);
} else if (findViewById(R.id.mainControl).getVisibility() == View.VISIBLE) {
findViewById(R.id.mainControl).setVisibility(View.GONE);
}
}
});
bindingDesktopControls.ctrlaltdelBtn.setVisibility(View.GONE);
MainStartVM.startPending(this);
}
private void onBack() {
if (bindingControls.mainControl.getVisibility() == View.GONE) {
bindingControls.mainControl.setVisibility(View.VISIBLE);
} else {
finish();
}
}
private void keyDownUp(int keyEventCode) {
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
@ -757,105 +723,99 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
boolean stylusMenuEnabled = p.getBoolean("showStylusClickOverride", false);
final float menuUnselectedTrasparency = 0.66f;
final float menuSelectedTrasparency = 1.0f;
Button left = findViewById(R.id.button_left_click);
Button right = findViewById(R.id.button_right_click);
Button middle = findViewById(R.id.button_middle_click);
Button visibility = findViewById(R.id.button_visibility);
LinearLayout overlay = findViewById(R.id.mouse_helper_visibility);
LinearLayout buttons = findViewById(R.id.mouse_helper_secondary_layer);
overlay.setOnTouchListener((v, e) -> true);
overlay.setOnHoverListener((v, e) -> true);
overlay.setOnGenericMotionListener((v, e) -> true);
binding.mouseHelperVisibility.setOnTouchListener((v, e) -> true);
binding.mouseHelperVisibility.setOnHoverListener((v, e) -> true);
binding.mouseHelperVisibility.setOnGenericMotionListener((v, e) -> true);
if (SDK_INT >= VERSION_CODES.O) {
overlay.setOnCapturedPointerListener((v, e) -> true);
binding.mouseHelperVisibility.setOnCapturedPointerListener((v, e) -> true);
}
overlay.setVisibility(stylusMenuEnabled ? View.VISIBLE : View.GONE);
binding.mouseHelperVisibility.setVisibility(stylusMenuEnabled ? View.VISIBLE : View.GONE);
View.OnClickListener listener =
view -> {
TouchInputHandler.STYLUS_INPUT_HELPER_MODE =
(view.equals(left)
(view.equals(binding.buttonLeftClick)
? 1
: (view.equals(middle) ? 2 : (view.equals(right) ? 3 : 0)));
left.setAlpha(
: (view.equals(binding.buttonMiddleClick) ? 2 : (view.equals(binding.buttonRightClick) ? 3 : 0)));
binding.buttonLeftClick.setAlpha(
(TouchInputHandler.STYLUS_INPUT_HELPER_MODE == 1)
? menuSelectedTrasparency
: menuUnselectedTrasparency);
middle.setAlpha(
binding.buttonMiddleClick.setAlpha(
(TouchInputHandler.STYLUS_INPUT_HELPER_MODE == 2)
? menuSelectedTrasparency
: menuUnselectedTrasparency);
right.setAlpha(
binding.buttonRightClick.setAlpha(
(TouchInputHandler.STYLUS_INPUT_HELPER_MODE == 3)
? menuSelectedTrasparency
: menuUnselectedTrasparency);
visibility.setAlpha(menuUnselectedTrasparency);
binding.buttonVisibility.setAlpha(menuUnselectedTrasparency);
};
left.setOnClickListener(listener);
middle.setOnClickListener(listener);
right.setOnClickListener(listener);
binding.buttonLeftClick.setOnClickListener(listener);
binding.buttonMiddleClick.setOnClickListener(listener);
binding.buttonRightClick.setOnClickListener(listener);
visibility.setOnClickListener(
binding.buttonVisibility.setOnClickListener(
view -> {
if (buttons.getVisibility() == View.VISIBLE) {
buttons.setVisibility(View.GONE);
visibility.setAlpha(menuUnselectedTrasparency);
if (binding.mouseButtonsSecondaryLayer.getVisibility() == View.VISIBLE) {
binding.mouseButtonsSecondaryLayer.setVisibility(View.GONE);
binding.buttonVisibility.setAlpha(menuUnselectedTrasparency);
int m = TouchInputHandler.STYLUS_INPUT_HELPER_MODE;
visibility.setText(m == 1 ? "L" : (m == 2 ? "M" : (m == 3 ? "R" : "U")));
binding.buttonVisibility.setText(m == 1 ? "L" : (m == 2 ? "M" : (m == 3 ? "R" : "U")));
} else {
buttons.setVisibility(View.VISIBLE);
visibility.setAlpha(menuUnselectedTrasparency);
visibility.setText("X");
binding.mouseButtonsSecondaryLayer.setVisibility(View.VISIBLE);
binding.buttonVisibility.setAlpha(menuUnselectedTrasparency);
binding.buttonVisibility.setText("X");
// Calculate screen border making sure btn is fully inside the view
float maxX = frm.getWidth() - 4 * left.getWidth();
float maxY = frm.getHeight() - 4 * left.getHeight();
float maxX = binding.frame.getWidth() - 4 * binding.buttonLeftClick.getWidth();
float maxY = binding.frame.getHeight() - 4 * binding.buttonLeftClick.getHeight();
// Make sure the Stylus menu is fully inside the screen
overlay.setX(MathUtils.clamp(overlay.getX(), 0, maxX));
overlay.setY(MathUtils.clamp(overlay.getY(), 0, maxY));
binding.mouseHelperVisibility.setX(MathUtils.clamp(binding.mouseHelperVisibility.getX(), 0, maxX));
binding.mouseHelperVisibility.setY(MathUtils.clamp(binding.mouseHelperVisibility.getY(), 0, maxY));
int m = TouchInputHandler.STYLUS_INPUT_HELPER_MODE;
listener.onClick(
m == 1 ? left : (m == 2 ? middle : (m == 3 ? right : left)));
m == 1 ? binding.buttonLeftClick : (m == 2 ? binding.buttonMiddleClick : (m == 3 ? binding.buttonRightClick : binding.buttonLeftClick)));
}
});
// Simulated mouse click 1 = left , 2 = middle , 3 = right
TouchInputHandler.STYLUS_INPUT_HELPER_MODE = 1;
listener.onClick(left);
listener.onClick(binding.buttonLeftClick);
visibility.setOnLongClickListener(
binding.buttonVisibility.setOnLongClickListener(
v -> {
if (SDK_INT >= VERSION_CODES.N) {
v.startDragAndDrop(
ClipData.newPlainText("", ""),
new View.DragShadowBuilder(visibility) {
new View.DragShadowBuilder(binding.buttonVisibility) {
public void onDrawShadow(@NonNull Canvas canvas) {}
},
null,
View.DRAG_FLAG_GLOBAL);
}
frm.setOnDragListener(
binding.frame.setOnDragListener(
(v2, event) -> {
// Calculate screen border making sure btn is fully inside the view
float maxX = frm.getWidth() - visibility.getWidth();
float maxY = frm.getHeight() - visibility.getHeight();
float maxX = binding.frame.getWidth() - binding.buttonVisibility.getWidth();
float maxY = binding.frame.getHeight() - binding.buttonVisibility.getHeight();
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_LOCATION:
// Center touch location with btn icon
float dX = event.getX() - visibility.getWidth() / 2.0f;
float dY = event.getY() - visibility.getHeight() / 2.0f;
float dX = event.getX() - binding.buttonVisibility.getWidth() / 2.0f;
float dY = event.getY() - binding.buttonVisibility.getHeight() / 2.0f;
// Make sure the dragged btn is inside the view with clamp
overlay.setX(MathUtils.clamp(dX, 0, maxX));
overlay.setY(MathUtils.clamp(dY, 0, maxY));
binding.mouseHelperVisibility.setX(MathUtils.clamp(dX, 0, maxX));
binding.mouseHelperVisibility.setY(MathUtils.clamp(dY, 0, maxY));
break;
case DragEvent.ACTION_DRAG_ENDED:
// Make sure the dragged btn is inside the view
overlay.setX(MathUtils.clamp(overlay.getX(), 0, maxX));
overlay.setY(MathUtils.clamp(overlay.getY(), 0, maxY));
binding.mouseHelperVisibility.setX(MathUtils.clamp(binding.mouseHelperVisibility.getX(), 0, maxX));
binding.mouseHelperVisibility.setY(MathUtils.clamp(binding.mouseHelperVisibility.getY(), 0, maxY));
break;
}
return true;
@ -876,58 +836,51 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
@SuppressLint("ClickableViewAccessibility")
void initMouseAuxButtons() {
Button left = findViewById(R.id.mouse_button_left_click);
Button right = findViewById(R.id.mouse_button_right_click);
Button middle = findViewById(R.id.mouse_button_middle_click);
ImageButton pos = findViewById(R.id.mouse_buttons_position);
LinearLayout primaryLayer = findViewById(R.id.mouse_buttons);
LinearLayout secondaryLayer = findViewById(R.id.mouse_buttons_secondary_layer);
SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this);
boolean mouseHelperEnabled =
p.getBoolean("showMouseHelper", false) && "1".equals(p.getString("touchMode", "1"));
primaryLayer.setVisibility(mouseHelperEnabled ? View.VISIBLE : View.GONE);
binding.mouseButtons.setVisibility(mouseHelperEnabled ? View.VISIBLE : View.GONE);
pos.setOnClickListener(
binding.mouseButtonsPosition.setOnClickListener(
(v) -> {
if (secondaryLayer.getOrientation() == LinearLayout.HORIZONTAL) {
setSize(left, 48, 96);
setSize(right, 48, 96);
secondaryLayer.setOrientation(LinearLayout.VERTICAL);
if (binding.mouseButtonsSecondaryLayer.getOrientation() == LinearLayout.HORIZONTAL) {
setSize(binding.mouseButtonLeftClick, 48, 96);
setSize(binding.mouseButtonRightClick, 48, 96);
binding.mouseButtonsSecondaryLayer.setOrientation(LinearLayout.VERTICAL);
} else {
setSize(left, 96, 48);
setSize(right, 96, 48);
secondaryLayer.setOrientation(LinearLayout.HORIZONTAL);
setSize(binding.mouseButtonLeftClick, 96, 48);
setSize(binding.mouseButtonRightClick, 96, 48);
binding.mouseButtonsSecondaryLayer.setOrientation(LinearLayout.HORIZONTAL);
}
handler.postDelayed(
() -> {
int[] offset = new int[2];
frm.getLocationOnScreen(offset);
primaryLayer.setX(
binding.frame.getLocationOnScreen(offset);
binding.mouseButtons.setX(
MathUtils.clamp(
primaryLayer.getX(),
binding.mouseButtons.getX(),
offset[0],
offset[0]
+ frm.getWidth()
- primaryLayer.getWidth()));
primaryLayer.setY(
+ binding.frame.getWidth()
- binding.mouseButtons.getWidth()));
binding.mouseButtons.setY(
MathUtils.clamp(
primaryLayer.getY(),
binding.mouseButtons.getY(),
offset[1],
offset[1]
+ frm.getHeight()
- primaryLayer.getHeight()));
+ binding.frame.getHeight()
- binding.mouseButtons.getHeight()));
},
10);
});
if (SDK_INT >= VERSION_CODES.N) {
Map.of(
left,
binding.mouseButtonLeftClick,
InputStub.BUTTON_LEFT,
middle,
binding.mouseButtonMiddleClick,
InputStub.BUTTON_MIDDLE,
right,
binding.mouseButtonRightClick,
InputStub.BUTTON_RIGHT)
.forEach(
(v, b) ->
@ -951,7 +904,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}));
}
pos.setOnTouchListener(
binding.mouseButtonsPosition.setOnTouchListener(
new View.OnTouchListener() {
final int touchSlop =
(int)
@ -968,45 +921,45 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
public boolean onTouch(View v, MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
primaryLayer.getLocationOnScreen(startPosition);
binding.mouseButtons.getLocationOnScreen(startPosition);
startOffset[0] = e.getX();
startOffset[1] = e.getY();
startTime = SystemClock.uptimeMillis();
pos.setPressed(true);
binding.mouseButtonsPosition.setPressed(true);
break;
case MotionEvent.ACTION_MOVE:
{
int[] offset = new int[2];
int[] offset2 = new int[2];
primaryLayer.getLocationOnScreen(offset);
frm.getLocationOnScreen(offset2);
primaryLayer.setX(
binding.mouseButtons.getLocationOnScreen(offset);
binding.frame.getLocationOnScreen(offset2);
binding.mouseButtons.setX(
MathUtils.clamp(
offset[0] - startOffset[0] + e.getX(),
offset2[0],
offset2[0]
+ frm.getWidth()
- primaryLayer.getWidth()));
primaryLayer.setY(
+ binding.frame.getWidth()
- binding.mouseButtons.getWidth()));
binding.mouseButtons.setY(
MathUtils.clamp(
offset[1] - startOffset[1] + e.getY(),
offset2[1],
offset2[1]
+ frm.getHeight()
- primaryLayer.getHeight()));
+ binding.frame.getHeight()
- binding.mouseButtons.getHeight()));
break;
}
case MotionEvent.ACTION_UP:
{
final int[] _pos = new int[2];
primaryLayer.getLocationOnScreen(_pos);
binding.mouseButtons.getLocationOnScreen(_pos);
int deltaX =
(int) (startOffset[0] - e.getX())
+ (startPosition[0] - _pos[0]);
int deltaY =
(int) (startOffset[1] - e.getY())
+ (startPosition[1] - _pos[1]);
pos.setPressed(false);
binding.mouseButtonsPosition.setPressed(false);
if (deltaX * deltaX + deltaY * deltaY < touchSlop
&& SystemClock.uptimeMillis() - startTime
@ -1111,26 +1064,26 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
if (getRequestedOrientation() != requestedOrientation)
setRequestedOrientation(requestedOrientation);
findViewById(R.id.mouse_buttons)
binding.mouseButtons
.setVisibility(
p.getBoolean("showMouseHelper", false)
&& "1".equals(p.getString("touchMode", "1"))
&& mClientConnected
? View.VISIBLE
: View.GONE);
LinearLayout buttons = findViewById(R.id.mouse_helper_visibility);
if (p.getBoolean("showStylusClickOverride", false)) {
buttons.setVisibility(View.VISIBLE);
binding.mouseHelperVisibility.setVisibility(View.VISIBLE);
} else {
// Reset default input back to normal
TouchInputHandler.STYLUS_INPUT_HELPER_MODE = 1;
final float menuUnselectedTrasparency = 0.66f;
final float menuSelectedTrasparency = 1.0f;
findViewById(R.id.button_left_click).setAlpha(menuSelectedTrasparency);
findViewById(R.id.button_right_click).setAlpha(menuUnselectedTrasparency);
findViewById(R.id.button_middle_click).setAlpha(menuUnselectedTrasparency);
findViewById(R.id.button_visibility).setAlpha(menuUnselectedTrasparency);
buttons.setVisibility(View.GONE);
binding.buttonLeftClick.setAlpha(menuSelectedTrasparency);
binding.buttonRightClick.setAlpha(menuUnselectedTrasparency);
binding.buttonMiddleClick.setAlpha(menuUnselectedTrasparency);
binding.buttonVisibility.setAlpha(menuUnselectedTrasparency);
binding.mouseHelperVisibility.setVisibility(View.GONE);
}
getTerminalToolbarViewPager().setAlpha(((float) p.getInt("opacityEKBar", 100)) / 100);
@ -1160,11 +1113,11 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
public LorieView getLorieView() {
return findViewById(R.id.lorieView);
return binding.lorieView;
}
public ViewPager getTerminalToolbarViewPager() {
return findViewById(R.id.terminal_toolbar_view_pager);
return binding.terminalToolbarViewPager;
}
private void setTerminalToolbarView() {
@ -1181,7 +1134,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
boolean showNow = enabled && preferences.getBoolean("additionalKbdVisible", true);
terminalToolbarViewPager.setVisibility(showNow ? View.VISIBLE : View.GONE);
findViewById(R.id.terminal_toolbar_view_pager).requestFocus();
terminalToolbarViewPager.requestFocus();
handler.postDelayed(
() -> {
@ -1200,7 +1153,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
.length));
terminalToolbarViewPager.setLayoutParams(layoutParams);
}
frm.setPadding(
binding.frame.setPadding(
0,
0,
0,
@ -1231,7 +1184,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
parent.addView(pager, 0);
if (mExtraKeys != null) mExtraKeys.unsetSpecialKeys();
}
frm.setPadding(
binding.frame.setPadding(
0,
0,
0,
@ -1370,12 +1323,14 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
android.os.Process.myUid(),
context.getPackageName())
== AppOpsManager.MODE_ALLOWED;
else
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
return appOpsManager.checkOpNoThrow(
AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
android.os.Process.myUid(),
context.getPackageName())
== AppOpsManager.MODE_ALLOWED;
else
return false;
}
@Override
@ -1426,14 +1381,14 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this);
mClientConnected = connected;
toggleExtraKeys(connected && p.getBoolean("additionalKbdVisible", true), true);
findViewById(R.id.mouse_buttons)
binding.mouseButtons
.setVisibility(
p.getBoolean("showMouseHelper", false)
&& "1".equals(p.getString("touchMode", "1"))
&& mClientConnected
? View.VISIBLE
: View.GONE);
findViewById(R.id.stub)
binding.stub
.setVisibility(connected ? View.INVISIBLE : View.VISIBLE);
getLorieView().setVisibility(connected ? View.VISIBLE : View.INVISIBLE);
getLorieView().regenerate();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

View file

@ -2,6 +2,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"

View file

@ -130,6 +130,48 @@
android:clickable="false"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/ln_blur_effect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/object_shape_single_high"
android:layout_marginTop="16dp"
android:padding="16dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/sw_blur_effect"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/blur_effect"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/blur_effect_note"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp" />
</LinearLayout>
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/sw_blur_effect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:clickable="false"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -26,7 +26,9 @@
android:visibility="visible"
android:layout_gravity="center" />
<include layout="@layout/controls_fragment" />
<include
android:id="@+id/controlsfragment"
layout="@layout/controls_fragment" />
<TextView
android:id="@+id/keyEventStatusTextView"

View file

@ -117,7 +117,8 @@
android:id="@+id/ln_use_sdl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/object_shape_bottom_high"
android:background="@drawable/object_shape_middle_high"
android:layout_marginBottom="2dp"
android:padding="16dp" >
<LinearLayout
@ -153,6 +154,47 @@
app:layout_constraintTop_toTopOf="parent"
android:clickable="false"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/ln_use_external"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/object_shape_bottom_high"
android:padding="16dp" >
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/sw_use_external"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/external"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/use_external_x11_note"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp" />
</LinearLayout>
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/sw_use_external"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:clickable="false"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -9,7 +9,9 @@
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:requiresFadingEdge="vertical"
android:fadingEdgeLength="48dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"

View file

@ -28,7 +28,9 @@
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="32dp"
android:layout_weight="1">
android:layout_weight="1"
android:requiresFadingEdge="vertical"
android:fadingEdgeLength="48dp">
<TextView
android:id="@+id/tv_content"

View file

@ -9,7 +9,9 @@
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:requiresFadingEdge="vertical"
android:fadingEdgeLength="48dp">
<LinearLayout
android:layout_width="match_parent"

View file

@ -3,86 +3,175 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:paddingTop="0dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingBottom="20dp">
android:orientation="vertical">
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:id="@+id/drag_handle"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<!-- <TextView-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="@string/options"-->
<!-- android:textSize="22sp"/>-->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/linear4"
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="0dp"
android:orientation="horizontal">
<Button
android:id="@+id/modifyRomBtn"
style="@style/MaterialAlertDialog.NeutralButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit"
app:icon="@drawable/edit_24px"
app:iconTint="?attr/colorSurfaceInverse"
android:textColor="?attr/colorSurfaceInverse"
app:iconGravity="top"
app:iconPadding="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/exportRomBtn"/>
android:requiresFadingEdge="vertical"
android:fadingEdgeLength="48dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/exportRomBtn"
style="@style/MaterialAlertDialog.NegativeButtonStyle"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/export"
app:icon="@drawable/arrow_upward_24px"
app:iconTint="?attr/colorSurfaceInverse"
android:textColor="?attr/colorSurfaceInverse"
app:iconGravity="top"
app:iconPadding="8dp"
app:layout_constraintStart_toEndOf="@id/modifyRomBtn"
app:layout_constraintEnd_toStartOf="@id/deviceManagerBtn"/>
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingHorizontal="16dp">
<com.google.android.material.card.MaterialCardView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
style="?attr/materialCardViewFilledStyle"
app:cardBackgroundColor="?attr/colorSurface">
<ImageView
android:id="@+id/ivIcon"
android:layout_width="70dp"
android:layout_height="70dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_computer_180dp_with_padding"/>
</com.google.android.material.card.MaterialCardView>
<Button
android:id="@+id/deviceManagerBtn"
style="@style/MaterialAlertDialog.NegativeButtonStyle"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/device_manager"
app:icon="@drawable/devices_other_24px"
app:iconTint="?attr/colorSurfaceInverse"
android:textColor="?attr/colorSurfaceInverse"
app:iconGravity="top"
app:iconPadding="8dp"
app:layout_constraintStart_toEndOf="@id/exportRomBtn"
app:layout_constraintEnd_toStartOf="@id/removeRomBtn"
android:visibility="gone"
tools:visibility="visible"/>
android:layout_weight="1"
android:orientation="vertical"
android:paddingHorizontal="16dp">
<Button
android:id="@+id/removeRomBtn"
style="@style/MaterialAlertDialog.NegativeButtonStyle"
<TextView
android:id="@+id/textName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
android:text="@string/new_vm"
android:textSize="18sp" />
<TextView
android:id="@+id/textArch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/remove"
app:icon="@drawable/delete_24px"
app:iconTint="@color/red_error"
android:textColor="@color/red_error"
app:iconGravity="top"
app:iconPadding="8dp"
app:layout_constraintStart_toEndOf="@id/deviceManagerBtn"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
android:maxLines="1"
android:text="@string/x86_64"
android:textSize="12sp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"/>
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_start"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:icon="@drawable/play_arrow_24px"/>
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="16dp"/>
<LinearLayout
android:id="@+id/ln_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingVertical="8dp"
android:paddingHorizontal="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="2dp"
android:background="@drawable/object_shape_top_high">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/edit_24px"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:padding="8dp"
android:textColor="?attr/colorControlNormal"
android:text="@string/edit"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ln_export"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingVertical="8dp"
android:paddingHorizontal="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="2dp"
android:background="@drawable/object_shape_middle_high">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/arrow_upward_24px"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:padding="8dp"
android:textColor="?attr/colorControlNormal"
android:text="@string/export"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ln_cleanup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingVertical="8dp"
android:paddingHorizontal="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="2dp"
android:background="@drawable/object_shape_middle_high">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/cleaning_services_24px"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:padding="8dp"
android:textColor="?attr/colorControlNormal"
android:text="@string/clean_up"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ln_remove"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingVertical="8dp"
android:paddingHorizontal="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="16dp"
android:background="@drawable/object_shape_bottom_high">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/delete_24px"
app:tint="?attr/colorError"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:padding="8dp"
android:textColor="?attr/colorError"
android:text="@string/remove"/>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View file

@ -382,7 +382,7 @@
<string name="tap_to_try_again">Chạm để thử lại.</string>
<string name="force_refresh">Buộc làm mới</string>
<string name="force_refresh_vnc_description">Màn hình VNC sẽ khởi động lại mỗi khi có thay đổi, điều này giúp khắc phục một số sự cố.</string>
<string name="settings_for_vnc">Các cài đặt cho VNC.</string>
<string name="settings_for_vnc">Chất lượng, tần số làm mới và ngoài.</string>
<string name="imported">Đã nhập</string>
<string name="file_imported_successfully">Tệp đã được nhập.</string>
<string name="file_import_failed">Nhập tệp thất bại.</string>
@ -747,6 +747,9 @@
<string name="settings_tag_keyboard">Bàn phím</string>
<string name="settings_tag_other">Khác</string>
<string name="executing_command_please_wait">Đang thực thi, vui lòng đợi…</string>
<string name="use_external_x11_note">Dùng ứng dụng Termux:X11 thay vì màn hình có sẵn.</string>
<string name="blur_effect">Hiệu ứng mờ</string>
<string name="blur_effect_note">Làm mờ xung quanh để tăng khả năng tập trung vào một vùng cần được chú ý.</string>
<!-- CPUs -->

View file

@ -393,7 +393,7 @@
<string name="tap_to_try_again">Tap to try again.</string>
<string name="force_refresh">Force refresh</string>
<string name="force_refresh_vnc_description">The VNC screen will restart every time there is a change, which helps troubleshoot problems.</string>
<string name="settings_for_vnc">Settings for VNC.</string>
<string name="settings_for_vnc">Quality, refresh rate and external.</string>
<string name="imported">Imported</string>
<string name="file_imported_successfully">File imported successfully.</string>
<string name="file_import_failed">File import failed.</string>
@ -759,6 +759,9 @@
<string name="settings_tag_keyboard">Keyboard</string>
<string name="settings_tag_other">Other</string>
<string name="executing_command_please_wait">Executing command, please wait…</string>
<string name="use_external_x11_note">Use the Termux:X11 app instead of the built-in screen.</string>
<string name="blur_effect">Blur effect</string>
<string name="blur_effect_note">Blur the surrounding area to increase focus on a specific area that needs attention.</string>
<!-- CPUs -->
<string name="max_cpu" translatable="false">Max</string>

View file

@ -7,7 +7,7 @@ comGoogleFirebase = "firebase-crashlytics"
commonsCompress = "1.28.0"
commonsIo = "2.22.0"
constraintlayout = "2.2.1"
coreKtx = "1.18.0"
coreKtx = "1.19.0"
documentfile = "1.1.0"
drawerlayout = "1.2.0"
espressoCore = "3.7.0"

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":"126",
"versionNameBeta":"4.2.2",
"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",
"versionCodeBeta":"127",
"versionNameBeta":"4.2.3",
"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",
"sizeBeta": "45 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
"MessageBeta": "<h2>4.2.2</h2>Bugs fixed.",
"MessageBeta": "<h2>4.2.3</h2>Bugs fixed.",
"cancellableBeta": true
}