- Fixed an issue with the confusing virtual machine control dialog box.
- Fixed the frozen virtual machine startup dialog box.
- Added marking of content containing advertisements.
- Added select and display of the virtual machine control dialog box to the built-in X11 screen.
- Removed the quick software launch shortcut button from the built-in X11 screen.
- Added a blur effect to the built-in X11 screen.
- Several minor interface improvements.
- Bugs fixed.
This commit is contained in:
An Bui 2026-06-11 17:17:33 +07:00
parent 6dbf2f711e
commit a227f5b8a6
27 changed files with 391 additions and 98 deletions

View file

@ -12,8 +12,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
versionCode 128
versionName "4.2.4"
versionCode 129
versionName "4.2.5"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true

View file

@ -136,7 +136,7 @@
android:exported="false"
android:label="@string/mini_tools" />
<activity
android:name=".RomInfo"
android:name=".store.RomInfo"
android:configChanges="orientation|screenSize|keyboardHidden|smallestScreenSize|screenLayout"
android:exported="false" />
<activity

View file

@ -189,8 +189,8 @@ public class RfbProto {
class VectrasOutputStream {
OutputStream os;
HandlerThread rfbQueueThread = null;
Handler handler = null;
HandlerThread rfbQueueThread;
Handler handler;
public VectrasOutputStream(OutputStream sos) {
os = sos;
@ -203,14 +203,11 @@ public class RfbProto {
public void write(final int data) throws IOException {
final int data0 = data;
handler.post(new Runnable() {
@Override
public void run() {
try {
sos.write(data0);
} catch (IOException e) {
e.printStackTrace();
}
handler.post(() -> {
try {
sos.write(data0);
} catch (IOException e) {
e.printStackTrace();
}
});
}
@ -218,16 +215,13 @@ public class RfbProto {
public void write(final byte[] bytes) throws IOException {
final byte [] buffer0 = java.util.Arrays.copyOf(bytes, bytes.length);
handler.post(new Runnable() {
@Override
public void run() {
try {
sos.write(buffer0);
} catch (IOException e) {
Log.w(TAG, "Error while sending VNC data");
if(Config.debug)
e.printStackTrace();
}
handler.post(() -> {
try {
sos.write(buffer0);
} catch (IOException e) {
Log.w(TAG, "Error while sending VNC data");
if(Config.debug)
e.printStackTrace();
}
});
}
@ -237,14 +231,11 @@ public class RfbProto {
final int offset0 = offset;
final int count0 = count;
handler.post(new Runnable() {
@Override
public void run() {
try {
sos.write(buffer0, offset0, count0);
} catch (IOException e) {
e.printStackTrace();
}
handler.post(() -> {
try {
sos.write(buffer0, offset0, count0);
} catch (IOException e) {
e.printStackTrace();
}
});
}
@ -370,8 +361,19 @@ public class RfbProto {
try {
os.close();
} catch (Exception ex) {
ex.printStackTrace();
Log.e(TAG, "Error while closing VectrasOutputStream", ex);
}
if (localSocket != null) {
try {
localSocket.close();
closed = true;
Log.v(TAG, "RFB local socket closed");
} catch (Exception e) {
Log.e(TAG, "Error while closing RFB local socket", e);
}
}
if (sock != null) {
try {
sock.close();
@ -385,7 +387,7 @@ public class RfbProto {
} */
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Error while closing RFB socket", e);
}
}

View file

@ -1202,6 +1202,8 @@ public class MainVNCActivity extends VncCanvasActivity {
}
});
bindingControls.btnVmManager.setVisibility(View.GONE);
bindingDesktopControls.rightClickBtn.setOnClickListener(v -> {
try {
MotionEvent e = MotionEvent.obtain(1000, 1000, MotionEvent.ACTION_DOWN, vncCanvas.mouseX, vncCanvas.mouseY,

View file

@ -842,15 +842,22 @@ public class VMManager {
public static boolean isVMRunning(Context context, String vmID) {
String result = Terminal.executeShellCommandWithResult("ps -e", context);
if (result.contains(Config.getCacheDir() + "/" + vmID + "/qmpsocket")) {
Log.d("VMManager.isThisVMRunning", "Yes");
if (result.contains(" -qmp unix:" + Config.getLocalQMPSocketPath(vmID))) {
Log.d("VMManager.isThisVMRunning", "Yes.\n\n" + result);
return true;
} else {
Log.d("VMManager.isThisVMRunning", "No");
Log.d("VMManager.isThisVMRunning", "No.\n\n" + result);
return false;
}
}
public static boolean isVMRunning(String vmID) {
File qmpSocket = new File(Config.getLocalQMPSocketPath(vmID));
boolean exists = qmpSocket.exists();
Log.d("VMManager.isThisVMRunning", exists ? "Yes" : "No");
return exists;
}
public static boolean isQemuRunning(Activity activity) {
Terminal vterm = new Terminal(activity);
vterm.executeShellCommand2("ps -e", false, activity);

View file

@ -29,7 +29,7 @@ import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.AppConfig;
import com.vectras.vm.Fragment.CreateImageDialogFragment;
import com.vectras.vm.R;
import com.vectras.vm.RomInfo;
import com.vectras.vm.store.RomInfo;
import com.vectras.vm.SplashActivity;
import com.vectras.vm.VMManager;
import com.vectras.vm.main.vms.DataMainRoms;

View file

@ -14,6 +14,7 @@ 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.qemu.Config;
import com.vectras.vm.creator.VMCreatorActivity;
import com.vectras.vm.ExportRomActivity;
import com.vectras.vm.R;
@ -30,6 +31,8 @@ import java.io.File;
public class RomOptionsDialog {
public static void show(Activity activity, int position, DataMainRoms vmConfig) {
if (VMManager.isVMRunning(activity, vmConfig.vmID)) {
Config.vmID = vmConfig.vmID;
VmControllerDialog vmControllerDialog = new VmControllerDialog();
vmControllerDialog.streamAudio = VmAudioManager.streamAudio;
vmControllerDialog.position = position;

View file

@ -87,6 +87,9 @@ public class StartVmDialog {
QmpSender.shutdown();
new Handler(Looper.getMainLooper()).post(callBack::onStop);
final int MAX_TRY = 10;
int triedCount = 0;
boolean isVmRuning = VMManager.isVMRunning(activity, vmId);
while (isVmRuning) {
@ -96,7 +99,9 @@ public class StartVmDialog {
QmpSender.shutdown();
if (!isVmRuning || VMManager.isQemuStopedWithError) {
if (!isVmRuning || VMManager.isQemuStopedWithError || triedCount == MAX_TRY) {
if (triedCount < MAX_TRY) FileUtils.delete(Config.getLocalQMPSocketPath(vmId));
new Handler(Looper.getMainLooper()).post(() -> {
isShuttingDown = false;
dismiss();
@ -110,6 +115,8 @@ public class StartVmDialog {
} catch (InterruptedException ignored) {
}
triedCount++;
}
}).start();
});

View file

@ -35,4 +35,5 @@ public class DataRoms {
public String id;
@SerializedName("vecid")
public String vecid;
public boolean containsAds;
}

View file

@ -16,7 +16,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.vectras.vm.R;
import com.vectras.vm.RomInfo;
import com.vectras.vm.store.RomInfo;
import com.vectras.vm.utils.UIUtils;
import java.util.ArrayList;
@ -76,10 +76,13 @@ public class RomStoreHomeAdpater extends RecyclerView.Adapter<RecyclerView.ViewH
intent.putExtra("id", current.id);
intent.putExtra("vecid", current.vecid);
intent.putExtra("isRomInfo", true);
intent.putExtra("containsAds", current.containsAds);
context.startActivity(intent);
});
myHolder.textAvail.setText(context.getString(R.string.available) + (current.containsAds ? "" + context.getString(R.string.contains_ads) : ""));
} else {
myHolder.textAvail.setText(context.getString(R.string.unavailable));
myHolder.textAvail.setText(context.getString(R.string.unavailable) + (current.containsAds ? "" + context.getString(R.string.contains_ads) : ""));
myHolder.textAvail.setTextColor(Color.RED);
}

View file

@ -16,7 +16,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.vectras.vm.R;
import com.vectras.vm.RomInfo;
import com.vectras.vm.store.RomInfo;
import com.vectras.vm.main.romstore.DataRoms;
import com.vectras.vm.utils.UIUtils;
@ -77,10 +77,12 @@ public class SoftwareStoreHomeAdapter extends RecyclerView.Adapter<RecyclerView.
intent.putExtra("id", current.id);
intent.putExtra("vecid", current.vecid);
intent.putExtra("isRomInfo", false);
intent.putExtra("containsAds", current.containsAds);
context.startActivity(intent);
});
myHolder.textAvail.setText(context.getString(R.string.available) + (current.containsAds ? "" + context.getString(R.string.contains_ads) : ""));
} else {
myHolder.textAvail.setText(context.getString(R.string.unavailable));
myHolder.textAvail.setText(context.getString(R.string.unavailable) + (current.containsAds ? "" + context.getString(R.string.contains_ads) : ""));
myHolder.textAvail.setTextColor(Color.RED);
}

View file

@ -33,6 +33,7 @@ import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.ProgressDialog;
import com.vectras.vm.utils.StreamAudio;
import com.vectras.vm.x11.X11Activity;
import java.io.File;
import java.util.ArrayList;
@ -121,7 +122,8 @@ public class VmControllerDialog extends DialogFragment {
binding.lnSwitch.setOnClickListener(v -> {
if (isAdded()) {
VmPicker vmPicker = new VmPicker(requireActivity());
vmPicker.currentVmId= Config.vmID;
vmPicker.currentVmId = Config.vmID;
vmPicker.title = getString(R.string.switch_to);
vmPicker.listVm = list;
vmPicker.pick((position, name, value) -> {
if (Config.vmID.equals(value)) return;
@ -182,7 +184,7 @@ public class VmControllerDialog extends DialogFragment {
dismiss();
});
if (isAdded() && (!(requireActivity() instanceof MainVNCActivity))) {
if (isAdded() && (!(requireActivity() instanceof MainVNCActivity)) && (!(requireActivity() instanceof X11Activity))) {
if (streamAudio == null)
streamAudio = new StreamAudio(requireActivity().getApplicationContext());
if (!streamAudio.isPlaying()) VmAudioManager.set(Config.vmID);
@ -191,11 +193,16 @@ public class VmControllerDialog extends DialogFragment {
if (streamAudio == null ||
!isAdded() ||
audioFileSize == 0 ||
(isAdded() && (!(requireActivity() instanceof MainVNCActivity)) && !VmAudioManager.currentVmId.equals(Config.vmID))
(isAdded() && (!(requireActivity() instanceof MainVNCActivity)) && !(requireActivity() instanceof X11Activity) && !VmAudioManager.currentVmId.equals(Config.vmID))
) {
binding.lnMute.setVisibility(View.GONE);
} else {
if (!streamAudio.isPlaying()) {
if (isAdded() && requireActivity() instanceof X11Activity) {
if (!streamAudio.getFile().equals(VmFileManager.findAudioRaw(getContext(), Config.vmID)) || !streamAudio.isPlaying()) {
binding.ivMute.setImageResource(R.drawable.volume_up_24px);
binding.tvMute.setText(R.string.unmute);
}
} else if (!streamAudio.isPlaying()) {
binding.ivMute.setImageResource(R.drawable.volume_up_24px);
binding.tvMute.setText(R.string.unmute);
}
@ -491,7 +498,21 @@ public class VmControllerDialog extends DialogFragment {
}
private void mute() {
if (streamAudio.isPlaying()) {
if (isAdded() && requireActivity() instanceof X11Activity) {
if (streamAudio.isPlaying() && streamAudio.getFile().equals(VmFileManager.findAudioRaw(getContext(), Config.vmID))) {
streamAudio.stop();
binding.ivMute.setImageResource(R.drawable.volume_up_24px);
binding.tvMute.setText(R.string.unmute);
} else {
if (!streamAudio.getFile().equals(VmFileManager.findAudioRaw(getContext(), Config.vmID))) {
streamAudio.stop();
streamAudio.setFile(VmFileManager.findAudioRaw(getContext(), Config.vmID));
}
streamAudio.play();
binding.ivMute.setImageResource(R.drawable.volume_off_24px);
binding.tvMute.setText(R.string.mute);
}
} else if (streamAudio.isPlaying()) {
streamAudio.stop();
binding.ivMute.setImageResource(R.drawable.volume_up_24px);
binding.tvMute.setText(R.string.unmute);

View file

@ -18,8 +18,21 @@ import java.util.List;
import java.util.Objects;
public class VmListManager {
public static ArrayList<HashMap<String, Object>> getAllVmForPickRunningNoVncSocketOnly(Context context) {
ArrayList<HashMap<String, Object>> listVm = getAllVmForPickRunningOnly(context);
ArrayList<HashMap<String, Object>> list = new ArrayList<>();
for (int i = 0; i < listVm.size(); i++) {
if (!FileUtils.isFileExists(Config.getLocalVNCSocketPath(Objects.requireNonNull(listVm.get(i).get("value")).toString()))) {
list.add(listVm.get(i));
}
}
return list;
}
public static ArrayList<HashMap<String, Object>> getAllVmForPickRunningVncSocketOnly(Context context) {
ArrayList<HashMap<String, Object>> listVm = getAllVmForPick(context);
ArrayList<HashMap<String, Object>> listVm = getAllVmForPickRunningOnly(context);
ArrayList<HashMap<String, Object>> list = new ArrayList<>();
for (int i = 0; i < listVm.size(); i++) {

View file

@ -4,6 +4,7 @@ import android.app.Activity;
import com.vectras.vm.R;
import com.vectras.vm.creator.VMCreatorSelector;
import com.vectras.vm.utils.UniversalPickerDialog;
import java.util.ArrayList;
import java.util.HashMap;
@ -12,8 +13,9 @@ import java.util.Objects;
public class VmPicker {
public boolean isRunningOnly;
public String currentVmId = "";
public String title;
private Activity activity;
ArrayList<HashMap<String, Object>> listVm;
public ArrayList<HashMap<String, Object>> listVm;
public interface VMPickerCallback {
void onSelected(int position, String name, String vmId);
@ -39,11 +41,11 @@ public class VmPicker {
}
}
VMCreatorSelector.showDialog(
UniversalPickerDialog.show(
activity,
list,
position,
(callback::onSelected),
activity.getString(R.string.switch_to));
title == null || title.isEmpty() ? activity.getString(R.string.vm_picker) : title);
}
}

View file

@ -1,4 +1,4 @@
package com.vectras.vm;
package com.vectras.vm.store;
import android.content.Intent;
import android.graphics.Bitmap;
@ -22,6 +22,9 @@ import com.anbui.elephant.interaction.Interaction;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import com.vectras.vm.ImagePrvActivity;
import com.vectras.vm.R;
import com.vectras.vm.VMManager;
import com.vectras.vm.creator.VMCreatorActivity;
import com.vectras.vm.databinding.ActivityRomInfoBinding;
import com.vectras.vm.utils.DialogUtils;
@ -177,9 +180,15 @@ public class RomInfo extends AppCompatActivity {
if (getIntent().hasExtra("title")) {
binding.textName.setText(getIntent().getStringExtra("title"));
}
if (getIntent().hasExtra("shortdesc")) {
binding.textSize.setText(getIntent().getStringExtra("shortdesc"));
}
if (getIntent().hasExtra("containsAds")) {
binding.tvContainsAds.setVisibility(getIntent().getBooleanExtra("containsAds", false) ? View.VISIBLE : View.GONE);
}
if (getIntent().hasExtra("desc")) {
binding.descTxt.setText(getIntent().getStringExtra("desc"));
}

View file

@ -38,6 +38,10 @@ public class StreamAudio {
filePath = path;
}
public String getFile() {
return filePath;
}
public void setCross(StreamAudio streamAudio) {
cross = streamAudio;
}

View file

@ -69,7 +69,7 @@ public class UniversalPickerDialog {
public RecyclerviewAdapter(Activity activity, AlertDialog alertDialog, ArrayList<HashMap<String, Object>> arr, int position, UniversalPickerDialogCallback callback) {
this.activity = activity;
data = arr;
currentPosition = position > -1 ? position : 0;
currentPosition = position;
dialog = alertDialog;
this.callback = callback;
}

View file

@ -8,6 +8,8 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION.SDK_INT;
import android.content.pm.PackageManager;
import android.graphics.RenderEffect;
import android.graphics.Shader;
import android.os.Looper;
import static android.view.InputDevice.KEYBOARD_TYPE_ALPHABETIC;
@ -30,7 +32,11 @@ import com.vectras.vm.databinding.GameControlsBinding;
import com.vectras.vm.main.core.MainStartVM;
import com.vectras.vm.manager.QmpSender;
import com.vectras.vm.manager.VmAudioManager;
import com.vectras.vm.manager.VmControllerDialog;
import com.vectras.vm.manager.VmFileManager;
import com.vectras.vm.manager.VmListManager;
import com.vectras.vm.manager.VmPicker;
import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.StreamAudio;
import com.vectras.vm.utils.UIUtils;
@ -94,6 +100,8 @@ import com.vectras.vm.x11.utils.X11ToolbarViewPager;
import com.vectras.vm.R;
import com.vectras.vterm.Terminal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@ -410,41 +418,41 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
}
});
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);
WindowManager.LayoutParams layoutParams = dialog.getWindow().getAttributes();
layoutParams.alpha = 1f;
dialog.getWindow().setAttributes(layoutParams);
ImageButton termBtn = dialog.findViewById(R.id.btnTerminal);
ImageButton vkCubeBtn = dialog.findViewById(R.id.btnVkCube);
ImageButton glxGearsBtn = dialog.findViewById(R.id.btnGlxGears);
termBtn.setOnClickListener(v1 -> {
new Terminal(this).executeShellCommand2("xfce4-terminal", false, this);
dialog.dismiss();
});
glxGearsBtn.setOnClickListener(v1 -> {
new Terminal(this).executeShellCommand2("glxgears", false, this);
dialog.dismiss();
});
vkCubeBtn.setOnClickListener(v1 -> {
new Terminal(this).executeShellCommand2("vkcube", false, this);
dialog.dismiss();
});
try {
dialog.show();
} catch (WindowManager.BadTokenException e) {
Log.e(TAG, "Failed to show dialog", e);
}
});
// 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);
//
// WindowManager.LayoutParams layoutParams = dialog.getWindow().getAttributes();
// layoutParams.alpha = 1f;
// dialog.getWindow().setAttributes(layoutParams);
//
// ImageButton termBtn = dialog.findViewById(R.id.btnTerminal);
// ImageButton vkCubeBtn = dialog.findViewById(R.id.btnVkCube);
// ImageButton glxGearsBtn = dialog.findViewById(R.id.btnGlxGears);
//
// termBtn.setOnClickListener(v1 -> {
// new Terminal(this).executeShellCommand2("xfce4-terminal", false, this);
// dialog.dismiss();
// });
//
// glxGearsBtn.setOnClickListener(v1 -> {
// new Terminal(this).executeShellCommand2("glxgears", false, this);
// dialog.dismiss();
// });
//
// vkCubeBtn.setOnClickListener(v1 -> {
// new Terminal(this).executeShellCommand2("vkcube", false, this);
// dialog.dismiss();
// });
//
// try {
// dialog.show();
// } catch (WindowManager.BadTokenException e) {
// Log.e(TAG, "Failed to show dialog", e);
// }
// });
bindingGameControls.upGameBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
@ -517,12 +525,21 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
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");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
getSupportFragmentManager().executePendingTransactions();
if (newFragment.getDialog() == null) return;
blurLayout();
newFragment.getDialog().setOnDismissListener(d -> unBlurLayout());
}
});
bindingControls.shutdownBtn.setOnClickListener(v -> finish());
bindingControls.kbdBtn.setOnClickListener(v -> new Handler(Looper.getMainLooper()).postDelayed(() -> toggleKeyboardVisibility(X11Activity.this), 200));
@ -533,6 +550,8 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
newFragment.binding = binding.controlsfragment;
newFragment.show(ft, "Controllers");
});
bindingControls.btnSettings
.setOnClickListener(
(l) ->
@ -542,6 +561,9 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
setAction(Intent.ACTION_MAIN);
}
}));
bindingControls.btnVmManager.setOnClickListener(v -> vmController());
bindingDesktopControls.upBtn.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KEYCODE_DPAD_UP, false);
@ -1486,4 +1508,72 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
Config.setDefault();
finish();
}
private void vmController() {
ArrayList<HashMap<String, Object>> list = VmListManager.getAllVmForPickRunningNoVncSocketOnly(this);
if (list.isEmpty()) {
DialogUtils.oopsDialog(this, getString(R.string.no_vms_are_available));
} else if (list.size() == 1) {
Config.vmID = Objects.requireNonNull(list.get(0).get("value")).toString();
VmControllerDialog vmControllerDialog = new VmControllerDialog();
vmControllerDialog.streamAudio = streamAudio;
vmControllerDialog.show(getSupportFragmentManager(), "VmControllerDialog");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
getSupportFragmentManager().executePendingTransactions();
blurLayout();
vmControllerDialog.setOnDismissCallback(this::unBlurLayout);
}
} else {
VmPicker vmPicker = new VmPicker(this);
vmPicker.currentVmId = "";
vmPicker.listVm = list;
vmPicker.pick((position, name, value) -> {
if (position < 0) {
DialogUtils.oopsDialog(this, getString(R.string.no_vms_are_available));
return;
}
Config.vmID = value;
VmControllerDialog vmControllerDialog = new VmControllerDialog();
vmControllerDialog.streamAudio = streamAudio;
vmControllerDialog.show(getSupportFragmentManager(), "VmControllerDialog");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
getSupportFragmentManager().executePendingTransactions();
blurLayout();
vmControllerDialog.setOnDismissCallback(this::unBlurLayout);
}
});
}
}
boolean isBlurring;
private void blurLayout() {
if (isBlurring || !MainSettingsManager.getBlurEffect(this)) return;
isBlurring = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
RenderEffect blurEffect = RenderEffect.createBlurEffect(
25f, 25f,
Shader.TileMode.CLAMP
);
binding.main.setRenderEffect(blurEffect);
binding.lorieView.setRenderEffect(blurEffect);
}
}
private void unBlurLayout() {
if (!isBlurring) return;
isBlurring = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
binding.main.setRenderEffect(null);
binding.lorieView.setRenderEffect(null);
}
}
}

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,306L480,306L480,306L480,306L480,306ZM654,480L654,480L654,480L654,480L654,480ZM306,480L306,480L306,480L306,480L306,480ZM480,654L480,654L480,654L480,654L480,654ZM480,420L360,300L360,80L600,80L600,300L480,420ZM660,600L540,480L660,360L880,360L880,600L660,600ZM80,600L80,360L300,360L420,480L300,600L80,600ZM360,880L360,660L480,540L600,660L600,880L360,880ZM480,306L520,266L520,160L440,160L440,266L480,306ZM160,520L266,520L306,480L266,440L160,440L160,520ZM440,800L520,800L520,694L480,654L440,694L440,800ZM694,520L800,520L800,440L694,440L654,480L694,520Z"/>
</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="M240,560L240,640L160,640Q127,640 103.5,616.5Q80,593 80,560L80,160Q80,127 103.5,103.5Q127,80 160,80L560,80Q593,80 616.5,103.5Q640,127 640,160L640,240L560,240L560,160Q560,160 560,160Q560,160 560,160L160,160Q160,160 160,160Q160,160 160,160L160,560Q160,560 160,560Q160,560 160,560L240,560ZM400,880Q367,880 343.5,856.5Q320,833 320,800L320,400Q320,367 343.5,343.5Q367,320 400,320L800,320Q833,320 856.5,343.5Q880,367 880,400L880,800Q880,833 856.5,856.5Q833,880 800,880L400,880ZM400,800L800,800Q800,800 800,800Q800,800 800,800L800,400Q800,400 800,400Q800,400 800,400L400,400Q400,400 400,400Q400,400 400,400L400,800Q400,800 400,800Q400,800 400,800ZM600,600Q600,600 600,600Q600,600 600,600L600,600Q600,600 600,600Q600,600 600,600L600,600Q600,600 600,600Q600,600 600,600L600,600Q600,600 600,600Q600,600 600,600Z"/>
</vector>

View file

@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.vectras.vm.RomInfo">
tools:context="com.vectras.vm.store.RomInfo">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
@ -96,6 +96,14 @@
android:layout_height="wrap_content"
android:text="size"
android:textSize="14sp" />
<TextView
android:id="@+id/tv_contains_ads"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/contains_ads"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>

View file

@ -3,6 +3,7 @@
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"
android:background="@android:color/black">

View file

@ -84,7 +84,17 @@
android:layout_marginRight="8dp"
android:background="@drawable/controls_button"
android:backgroundTint="?attr/colorPrimary"
android:src="@drawable/round_settings_24"
android:src="@drawable/settings_24px"
app:tint="?attr/colorPrimary" />
<ImageButton
android:id="@+id/btn_vm_manager"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:background="@drawable/controls_button"
android:backgroundTint="?attr/colorPrimary"
android:src="@drawable/stack_24px"
app:tint="?attr/colorPrimary" />
<ImageButton
@ -95,7 +105,9 @@
android:background="@drawable/controls_button"
android:backgroundTint="?attr/colorPrimary"
android:src="@drawable/apps_24px"
app:tint="?attr/colorPrimary" />
app:tint="?attr/colorPrimary"
android:visibility="gone"
tools:visibility="visible"/>
<ImageButton
android:id="@+id/btnQmp"
@ -104,7 +116,7 @@
android:layout_marginRight="8dp"
android:background="@drawable/controls_button"
android:backgroundTint="?attr/colorPrimary"
android:src="@drawable/round_terminal_24"
android:src="@drawable/terminal_24px"
app:tint="?attr/colorPrimary" />
<ImageButton
@ -124,7 +136,7 @@
android:layout_marginRight="8dp"
android:background="@drawable/controls_button"
android:backgroundTint="?attr/colorPrimary"
android:src="@drawable/round_games_24"
android:src="@drawable/gamepad_24px"
app:tint="?attr/colorPrimary" />
<ImageButton
@ -134,7 +146,7 @@
android:layout_marginLeft="0dp"
android:background="@drawable/controls_button"
android:backgroundTint="?attr/colorPrimary"
android:src="@drawable/round_keyboard_24"
android:src="@drawable/keyboard_24px"
app:tint="?attr/colorPrimary" />
<ImageButton

View file

@ -592,6 +592,7 @@
<string name="in_system_settings">Ở cài đặt hệ thống.</string>
<string name="missing_packages">Một số gói đang bị thiếu</string>
<string name="missing_packages_content">Đây là những gói đang bị thiếu và bạn nên cài đặt chúng:</string>
<string name="contains_ads">Có chứa quảng cáo</string>
<!--======================TERMUX STRINGS====================-->
<string name="application_name" translatable="false">Vterm</string>

View file

@ -604,6 +604,7 @@
<string name="missing_packages">Missing packages</string>
<string name="missing_packages_content">These are the missing packages that you should install:</string>
<string name="x11_general_setting_note">You need to restart the app when switching between built-in and external.</string>
<string name="contains_ads">Contains ads</string>
<!--======================TERMUX STRINGS====================-->