- Fixed the issue of being unable to insert or change optical discs, floppy drives, and memory cards when filenames contained spaces.
- Added equalizer.
- Bubbles on the built-in VNC screen will automatically fade when there is no interaction.
- The default setting for automatically switching to external mouse mode on the built-in VNC screen is now off.
This commit is contained in:
An Bui 2026-07-09 23:38:17 +07:00
parent 5074600e8b
commit 3990bbb4c8
22 changed files with 879 additions and 15 deletions

View file

@ -12,8 +12,8 @@ android {
applicationId "com.vectras.vm"
minSdk minApi
targetSdk targetApi
versionCode 136
versionName "4.3.2"
versionCode 137
versionName "4.3.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true

View file

@ -299,6 +299,11 @@
<meta-data
android:name="android.max_aspect"
android:value="10.0" />
<activity
android:name=".settings.EqualizerActivity"
android:exported="false"
android:windowSoftInputMode="adjustResize" />
</application>
</manifest>

View file

@ -1341,6 +1341,6 @@ public class MainSettingsManager extends AppCompatActivity
public static Boolean getAutoSwitchToExternalMouse(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean("autoSwitchToExternalMouse", true);
return prefs.getBoolean("autoSwitchToExternalMouse", false);
}
}

View file

@ -397,7 +397,7 @@ public class MainVNCActivity extends VncCanvasActivity {
public void onDestroy() {
super.onDestroy();
this.stopTimeListener();
if (streamAudio != null) streamAudio.stop();
if (streamAudio != null) streamAudio.release();
//Terminal.killQemuProcess();
}

View file

@ -363,7 +363,7 @@ public class QmpSender {
public static String changeDevice(String deviceId, String filePath) {
if (filePath.trim().isEmpty()) return ejectDevice(deviceId);
return send("change " + deviceId + " " + filePath);
return send("change " + deviceId + " \"" + filePath + "\"");
}
public static void quickEjectDevice(Context context, String deviceId) {

View file

@ -0,0 +1,195 @@
package com.vectras.vm.settings;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import com.vectras.vm.R;
import com.vectras.vm.databinding.ActivityEqualizerBinding;
import com.vectras.vm.sound.AudioSettingsData;
import com.vectras.vm.sound.SoundEffect;
import java.text.DecimalFormat;
import java.util.Objects;
public class EqualizerActivity extends AppCompatActivity {
ActivityEqualizerBinding binding;
AudioSettingsData audioSettingsData;
float[] lastValues = new float[5];
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.undo) {
binding.sliderUpperTreble.setValue(lastValues[0]);
binding.sliderTreble.setValue(lastValues[1]);
binding.sliderMid.setValue(lastValues[2]);
binding.sliderBass.setValue(lastValues[3]);
binding.sliderLowBass.setValue(lastValues[4]);
return true;
} else if (id == R.id.reset) {
binding.sliderUpperTreble.setValue(0);
binding.sliderTreble.setValue(0);
binding.sliderMid.setValue(0);
binding.sliderBass.setValue(0);
binding.sliderLowBass.setValue(0);
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.equalizer_menu, menu);
return true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
binding = ActivityEqualizerBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
binding.toolbar.setNavigationOnClickListener(view -> finish());
initialize();
}
@Override
public void onPause() {
super.onPause();
if (audioSettingsData != null) {
audioSettingsData.setEqualizerEnabled(binding.swEnabled.isChecked());
audioSettingsData.setUpperTreble(binding.sliderUpperTreble.getValue());
audioSettingsData.setTreble(binding.sliderTreble.getValue());
audioSettingsData.setMid(binding.sliderMid.getValue());
audioSettingsData.setBass(binding.sliderBass.getValue());
audioSettingsData.setLowBass(binding.sliderLowBass.getValue());
}
}
private void initialize() {
SoundEffect soundEffect = new SoundEffect(this, 0);
short bands = soundEffect.equalizer.getNumberOfBands();
if (bands < 2) {
binding.lnUnsupport.setVisibility(View.VISIBLE);
binding.appbar.setVisibility(View.GONE);
binding.lnEnabled.setVisibility(View.GONE);
binding.lnAllOptions.setVisibility(View.GONE);
binding.btnExit.setOnClickListener(v -> finish());
return;
}
audioSettingsData = new AudioSettingsData(this);
lastValues[0] = audioSettingsData.getUpperTreble();
lastValues[1] = audioSettingsData.getTreble();
lastValues[2] = audioSettingsData.getMid();
lastValues[3] = audioSettingsData.getBass();
lastValues[4] = audioSettingsData.getLowBass();
// 1dB = 100mB
int minBandLevelRange = soundEffect.getMinBandLevelRange() / 100;
int maxBandLevelRange = soundEffect.getMaxBandLevelRange() / 100;
if (bands > 4) {
binding.sliderLowBass.setValueFrom(minBandLevelRange);
binding.sliderLowBass.setValueTo(maxBandLevelRange);
binding.sliderLowBass.setValue(audioSettingsData.getLowBass());
binding.tvLowBassValue.setText(getFormattedValue(binding.sliderLowBass.getValue()));
binding.sliderLowBass.addOnChangeListener((slider, value, fromUser) -> binding.tvLowBassValue.setText(getFormattedValue(value)));
} else {
binding.lnLowBass.setVisibility(View.GONE);
binding.tvUpperTreble.setText(R.string.upper_treble);
binding.tvTreble.setText(R.string.treble);
binding.tvMid.setText(R.string.bass);
binding.tvBass.setText(R.string.low_bass);
}
if (bands > 3) {
binding.sliderBass.setValueFrom(minBandLevelRange);
binding.sliderBass.setValueTo(maxBandLevelRange);
binding.sliderBass.setValue(audioSettingsData.getBass());
binding.tvBassValue.setText(getFormattedValue(binding.sliderBass.getValue()));
binding.sliderBass.addOnChangeListener((slider, value, fromUser) -> binding.tvBassValue.setText(getFormattedValue(value)));
} else {
binding.lnBass.setVisibility(View.GONE);
binding.tvUpperTreble.setText(R.string.treble);
binding.tvTreble.setText(R.string.mid);
binding.tvMid.setText(R.string.bass);
}
if (bands > 2) {
binding.sliderMid.setValueFrom(minBandLevelRange);
binding.sliderMid.setValueTo(maxBandLevelRange);
binding.sliderMid.setValue(audioSettingsData.getMid());
binding.tvMidValue.setText(getFormattedValue(binding.sliderMid.getValue()));
binding.sliderMid.addOnChangeListener((slider, value, fromUser) -> binding.tvMidValue.setText(getFormattedValue(value)));
} else {
binding.lnMid.setVisibility(View.GONE);
binding.tvUpperTreble.setText(R.string.treble);
binding.tvTreble.setText(R.string.bass);
}
binding.sliderUpperTreble.setValueFrom(minBandLevelRange);
binding.sliderUpperTreble.setValueTo(maxBandLevelRange);
binding.sliderTreble.setValueFrom(minBandLevelRange);
binding.sliderTreble.setValueTo(maxBandLevelRange);
binding.sliderUpperTreble.setValue(audioSettingsData.getUpperTreble());
binding.sliderTreble.setValue(audioSettingsData.getTreble());
binding.tvUpperTrebleValue.setText(getFormattedValue(binding.sliderUpperTreble.getValue()));
binding.tvTrebleValue.setText(getFormattedValue(binding.sliderTreble.getValue()));
binding.sliderUpperTreble.addOnChangeListener((slider, value, fromUser) -> binding.tvUpperTrebleValue.setText(getFormattedValue(value)));
binding.sliderTreble.addOnChangeListener((slider, value, fromUser) -> binding.tvTrebleValue.setText(getFormattedValue(value)));
binding.lnEnabled.setOnClickListener(v -> binding.swEnabled.toggle());
binding.swEnabled.setChecked(audioSettingsData.isEqualizerEnabled());
binding.swEnabled.setOnCheckedChangeListener((buttonView, isChecked) -> setEnabled(isChecked));
setEnabled(binding.swEnabled.isChecked());
}
private void setEnabled(boolean isEnabled) {
binding.lnAllOptions.setAlpha(isEnabled ? 1 : 0.5f);
binding.sliderUpperTreble.setEnabled(isEnabled);
binding.sliderTreble.setEnabled(isEnabled);
binding.sliderMid.setEnabled(isEnabled);
binding.sliderBass.setEnabled(isEnabled);
binding.sliderLowBass.setEnabled(isEnabled);
}
DecimalFormat decimalFormat = new DecimalFormat("#.#");
private String getFormattedValue(float value) {
return decimalFormat.format(value) + " dB";
}
}

View file

@ -42,5 +42,7 @@ public class Settings2Activity extends AppCompatActivity {
binding.lnVnc.setOnClickListener(v -> startActivity(new Intent(this, VNCSettingsActivity.class)));
binding.lnX11.setOnClickListener(v -> startActivity(new Intent(this, X11DisplaySettingsActivity.class)));
binding.lnEqualizer.setOnClickListener(v -> startActivity(new Intent(this, EqualizerActivity.class)));
}
}

View file

@ -0,0 +1,62 @@
package com.vectras.vm.sound;
import android.content.Context;
import android.content.SharedPreferences;
public class AudioSettingsData {
Context context;
SharedPreferences sharedPreferences;
public AudioSettingsData(Context context) {
this.context = context;
sharedPreferences = context.getSharedPreferences("audio_settings", Context.MODE_PRIVATE);
}
public void setEqualizerEnabled(boolean enabled) {
sharedPreferences.edit().putBoolean("isEnabled", enabled).apply();
}
public boolean isEqualizerEnabled() {
return sharedPreferences.getBoolean("isEnabled", false);
}
public void setUpperTreble(float value) {
sharedPreferences.edit().putFloat("upperTreble", value).apply();
}
public float getUpperTreble() {
return sharedPreferences.getFloat("upperTreble", 0);
}
public void setTreble(float value) {
sharedPreferences.edit().putFloat("treble", value).apply();
}
public float getTreble() {
return sharedPreferences.getFloat("treble", 0);
}
public void setMid(float value) {
sharedPreferences.edit().putFloat("mid", value).apply();
}
public float getMid() {
return sharedPreferences.getFloat("mid", 0);
}
public void setBass(float value) {
sharedPreferences.edit().putFloat("bass", value).apply();
}
public float getBass() {
return sharedPreferences.getFloat("bass", 0);
}
public void setLowBass(float value) {
sharedPreferences.edit().putFloat("lowBass", value).apply();
}
public float getLowBass() {
return sharedPreferences.getFloat("lowBass", 0);
}
}

View file

@ -0,0 +1,118 @@
package com.vectras.vm.sound;
import android.content.Context;
import android.media.audiofx.Equalizer;
import android.util.Log;
public class SoundEffect {
final String TAG = "SoundEffect";
Context context;
public int session;
public Equalizer equalizer;
public SoundEffect(Context context, int session) {
this.context = context;
this.session = session;
equalizer = new Equalizer(1000, session);
}
public AudioFrequencyData getDeviceAudioFrequencies() {
AudioFrequencyData data = new AudioFrequencyData();
int[] targets = { 60, 230, 910, 4000, 14000 };
short bands = equalizer.getNumberOfBands();
int[] hzList = new int[bands];
for (short i = 0; i < bands; i++) {
int center = equalizer.getCenterFreq(i);
hzList[i] = center / 1000;
}
if (bands <= targets.length) {
int[] finalIds = new int[bands];
for (int i = 0; i < bands; i++) {
finalIds[i] = i;
}
data.frequencies = hzList;
data.ids = finalIds;
Log.d(TAG, "Equalizer bands: " + bands);
Log.d(TAG, "Equalizer frequencies: " + hzList[0] + ", " + hzList[1] + ", " + hzList[2] + ", " + hzList[3] + ", " + hzList[4]);
Log.d(TAG, "Equalizer ids: " + finalIds[0] + ", " + finalIds[1] + ", " + finalIds[2] + ", " + finalIds[3] + ", " + finalIds[4]);
return data;
}
int[] finalHzList = new int[5];
int[] finalIds = new int[5];
for (int i = 0; i < targets.length; i++) {
int losest = Integer.MAX_VALUE;
int bandId = 0;
for (int ii = 0; ii < hzList.length; ii++) {
if (Math.abs(targets[i] - hzList[ii]) < losest) {
losest = hzList[ii];
bandId = ii;
}
finalHzList[i] = losest;
finalIds[i] = bandId;
}
}
data.frequencies = finalHzList;
data.ids = finalIds;
Log.d(TAG, "Equalizer bands: " + bands);
Log.d(TAG, "Equalizer frequencies: " + hzList[0] + ", " + hzList[1] + ", " + hzList[2] + ", " + hzList[3] + ", " + hzList[4]);
Log.d(TAG, "Equalizer ids: " + finalIds[0] + ", " + finalIds[1] + ", " + finalIds[2] + ", " + finalIds[3] + ", " + finalIds[4]);
return data;
}
public void applyEffect(float[] dBData) {
short bands = equalizer.getNumberOfBands();
if (bands < 2) {
Log.d(TAG, "Equalizer bands: " + bands);
return;
}
AudioFrequencyData data = getDeviceAudioFrequencies();
equalizer.setBandLevel((short) data.ids[0], (short) dBData[0]);
equalizer.setBandLevel((short) data.ids[1], (short) dBData[1]);
if (bands > 2) equalizer.setBandLevel((short) data.ids[2], (short) dBData[2]);
if (bands > 3) equalizer.setBandLevel((short) data.ids[3], (short) dBData[3]);
if (bands > 4) equalizer.setBandLevel((short) data.ids[4], (short) dBData[4]);
Log.d(TAG, "Equalizer dB: " + dBData[0] + ", " + dBData[1] + ", " + dBData[2] + ", " + dBData[3] + ", " + dBData[4]);
}
public void release() {
if (equalizer != null) {
equalizer.release();
}
}
public void setEnabled(boolean isEnabled) {
equalizer.setEnabled(isEnabled);
}
public short getMinBandLevelRange() {
return equalizer.getBandLevelRange()[0];
}
public short getMaxBandLevelRange() {
return equalizer.getBandLevelRange()[1];
}
public static class AudioFrequencyData {
int[] frequencies;
int[] ids;
}
}

View file

@ -5,6 +5,7 @@ import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.media.audiofx.Equalizer;
import android.util.Log;
import com.vectras.vm.utils.FileUtils;
@ -19,6 +20,7 @@ public class StreamAudio {
private int sampleRate = 48000;
public boolean isDestroyed;
private StreamAudio cross;
private SoundEffect soundEffect;
public StreamAudio(Context context) {
this.context = context;
@ -56,6 +58,10 @@ public class StreamAudio {
sampleRate = 48000;
}
public void release() {
isPlay = false;
releaseSoundEffect();
}
public void streamFromFile() {
if (isPlay) return;
@ -102,6 +108,8 @@ public class StreamAudio {
.setTransferMode(AudioTrack.MODE_STREAM)
.build();
applyEffect(audioTrack);
audioTrack.play();
if (cross != null && !cross.isDestroyed && cross.isPlaying()) cross.stop();
@ -135,6 +143,63 @@ public class StreamAudio {
}).start();
}
float[] currentdBData = new float[5];
public void applyEffect(AudioTrack audioTrack) {
if (soundEffect != null && soundEffect.equalizer.getNumberOfBands() < 2) return;
AudioSettingsData audioSettingsData = new AudioSettingsData(context);
if (!audioSettingsData.isEqualizerEnabled()) {
releaseSoundEffect();
return;
}
if (soundEffect == null || soundEffect.session != audioTrack.getAudioSessionId())
soundEffect = new SoundEffect(context, audioTrack.getAudioSessionId());
if (soundEffect.equalizer.getNumberOfBands() < 2) return;
float[] dBData = new float[5];
//Bass -> Mid -> Treble
dBData[0] = audioSettingsData.getLowBass();
dBData[1] = audioSettingsData.getBass();
dBData[2] = audioSettingsData.getMid();
dBData[3] = audioSettingsData.getTreble();
dBData[4] = audioSettingsData.getUpperTreble();
if (
currentdBData[0] == dBData[0] &&
currentdBData[1] == dBData[1] &&
currentdBData[2] == dBData[2] &&
currentdBData[3] == dBData[3] &&
currentdBData[4] == dBData[4]) {
return;
}
this.currentdBData = dBData;
// Convert dB to mB
for (int i = 0; i < dBData.length; i++) {
dBData[i] = dBData[i] * 100;
}
soundEffect.applyEffect(dBData);
soundEffect.setEnabled(audioSettingsData.isEqualizerEnabled());
Log.d(TAG, "Equalizer enabled: " + audioSettingsData.isEqualizerEnabled());
}
public void releaseSoundEffect() {
if (soundEffect != null) {
soundEffect.setEnabled(false);
soundEffect.release();
soundEffect = null;
}
}
private boolean isContextDestroyed(Context context) {
if (context instanceof Activity activity) {
isDestroyed = activity.isDestroyed() || activity.isFinishing();

View file

@ -23,6 +23,8 @@ public class DynamicBubble {
}
private void setup() {
bubble.animate().alpha(0.5f).setDuration(200);
bubble.setOnTouchListener((v, event) -> {
switch (event.getActionMasked()) {
@ -44,6 +46,9 @@ public class DynamicBubble {
// If the displacement exceeds the threshold, it is determined that pulling is occurring.
if (Math.abs(movedX) > CLICK_ACTION_THRESHOLD || Math.abs(movedY) > CLICK_ACTION_THRESHOLD) {
if (!isDragging) bubble.animate().alpha(1).setDuration(200);
isDragging = true;
// Move the view using your finger.
@ -75,6 +80,8 @@ public class DynamicBubble {
}
private void onDropped() {
bubble.animate().alpha(0.5f).setDuration(200);
if (bubble.getX() > (float) container.getWidth() / 2) {
bubble.animate().x(container.getWidth() - bubble.getWidth());
} else {

View file

@ -752,7 +752,7 @@ public class X11Activity extends AppCompatActivity implements View.OnApplyWindow
@Override
protected void onDestroy() {
unregisterReceiver(receiver);
if (streamAudio != null) streamAudio.stop();
if (streamAudio != null) streamAudio.release();
super.onDestroy();
}

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="M280,720L280,240L360,240L360,720L280,720ZM440,880L440,80L520,80L520,880L440,880ZM120,560L120,400L200,400L200,560L120,560ZM600,720L600,240L680,240L680,720L600,720ZM760,560L760,400L840,400L840,560L760,560Z"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="96dp"
android:height="96dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M791,905L520,634L520,880L440,880L440,554L360,474L360,720L280,720L280,394L55,169L112,112L848,848L791,905ZM120,560L120,400L200,400L200,560L120,560ZM520,406L440,326L440,80L520,80L520,406ZM680,566L600,486L600,240L680,240L680,566ZM760,560L760,400L840,400L840,560L760,560Z"/>
</vector>

View file

@ -0,0 +1,11 @@
<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"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M280,760L280,680L564,680Q627,680 673.5,640Q720,600 720,540Q720,480 673.5,440Q627,400 564,400L312,400L416,504L360,560L160,360L360,160L416,216L312,320L564,320Q661,320 730.5,383Q800,446 800,540Q800,634 730.5,697Q661,760 564,760L280,760Z"/>
</vector>

View file

@ -0,0 +1,308 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
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:fitsSystemWindows="true"
android:orientation="vertical"
tools:context=".settings.EqualizerActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$Behavior">
<com.google.android.material.appbar.CollapsingToolbarLayout
style="@style/App.CollapsingToolbarLarge"
app:title="@string/equalizer">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
style="@style/App.CollapsingMaterialToolbar"
app:navigationIcon="@drawable/arrow_back_rounded_24px" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/ln_all_options"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="100dp"
android:paddingHorizontal="16dp"
android:animateLayoutChanges="true">
<LinearLayout
android:id="@+id/ln_upper_treble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingHorizontal="16dp">
<TextView
android:id="@+id/tv_upper_treble"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/upper_treble"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_upper_treble_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0 dB"
android:textSize="18sp" />
</LinearLayout>
<com.google.android.material.slider.Slider
android:id="@+id/slider_upper_treble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:value="0.0"
android:valueFrom="-10"
android:valueTo="10" />
</LinearLayout>
<LinearLayout
android:id="@+id/ln_treble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingHorizontal="16dp">
<TextView
android:id="@+id/tv_treble"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/treble"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_treble_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0 dB"
android:textSize="18sp" />
</LinearLayout>
<com.google.android.material.slider.Slider
android:id="@+id/slider_treble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:value="0.0"
android:valueFrom="-10"
android:valueTo="10" />
</LinearLayout>
<LinearLayout
android:id="@+id/ln_mid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingHorizontal="16dp">
<TextView
android:id="@+id/tv_mid"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/mid"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_mid_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0 dB"
android:textSize="18sp" />
</LinearLayout>
<com.google.android.material.slider.Slider
android:id="@+id/slider_mid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:value="0.0"
android:valueFrom="-10"
android:valueTo="10" />
</LinearLayout>
<LinearLayout
android:id="@+id/ln_bass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingHorizontal="16dp">
<TextView
android:id="@+id/tv_bass"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/bass"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_bass_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0 dB"
android:textSize="18sp" />
</LinearLayout>
<com.google.android.material.slider.Slider
android:id="@+id/slider_bass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:value="0.0"
android:valueFrom="-10"
android:valueTo="10" />
</LinearLayout>
<LinearLayout
android:id="@+id/ln_low_bass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingHorizontal="16dp">
<TextView
android:id="@+id/tv_low_bass"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/low_bass"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_low_bass_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0 dB"
android:textSize="18sp" />
</LinearLayout>
<com.google.android.material.slider.Slider
android:id="@+id/slider_low_bass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:value="0.0"
android:valueFrom="-10"
android:valueTo="10" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginVertical="16dp" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:src="@drawable/info_24px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/equalizer_note" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<View
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="?attr/colorSurfaceContainerLow"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.card.MaterialCardView
style="@style/Widget.Material3.CardView.Filled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:backgroundTint="?attr/colorSecondaryContainer"
app:cardCornerRadius="50dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/ln_enabled"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingVertical="16dp"
android:paddingLeft="32dp"
android:paddingRight="20dp" >
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/use_equalizer"
android:textSize="16sp">
</TextView>
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/sw_enabled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/colorAccent"
android:clickable="false"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:id="@+id/ln_unsupport"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:background="?attr/colorSurfaceContainerLow"
android:visibility="gone">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/graphic_eq_off_96px"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:gravity="center"
android:text="@string/your_device_is_not_supported"
android:textSize="18sp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_exit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ok"/>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -6,9 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.drawerlayout.widget.DrawerLayout 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"
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -28,7 +26,8 @@
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/home_drawer_menu" />
app:menu="@menu/home_drawer_menu"
app:itemTextAppearanceActiveBoldEnabled="false" />
</androidx.drawerlayout.widget.DrawerLayout>

View file

@ -302,7 +302,7 @@
android:id="@+id/ln_x11"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_marginBottom="18dp"
android:background="@drawable/object_shape_bottom_high"
android:padding="16dp"
android:gravity="center_vertical">
@ -334,6 +334,43 @@
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ln_equalizer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:background="@drawable/object_shape_single_high"
android:padding="16dp"
android:gravity="center_vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="@drawable/settings_system_background_icon"
android:src="@drawable/graphic_eq_24px" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/equalizer"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/equalizer_settings_note"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View file

@ -0,0 +1,13 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/undo"
android:icon="@drawable/undo_24px"
android:title="@string/undo"
app:showAsAction="always"/>
<item
android:id="@+id/reset"
android:icon="@drawable/refresh_24px"
android:title="@string/reset"
app:showAsAction="always"/>
</menu>

View file

@ -770,6 +770,17 @@
<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>
<string name="x11_general_setting_note">Bạn cần khởi động lại ứng dụng khi chuyển đổi giữa tích hợp sẵn và ngoài.</string>
<string name="equalizer">Bộ chỉnh âm</string>
<string name="equalizer_settings_note">Bổng, trung và trầm.</string>
<string name="equalizer_note">Bạn có thể cần khởi động lại máy ảo để áp dụng các thay đổi. Hiệu ứng âm thanh có thể chỉ có tác dụng nếu bạn sử dụng các phụ kiện như tai nghe.</string>
<string name="use_equalizer">Dùng bộ chỉnh âm</string>
<string name="upper_treble">Bổng cao</string>
<string name="treble">Bổng</string>
<string name="mid">Trung</string>
<string name="bass">Trầm</string>
<string name="low_bass">Trầm sâu</string>
<string name="your_device_is_not_supported">Thiết bị của bạn không được hỗ trợ.</string>
<string name="undo">Hoàn tác</string>
<!-- CPUs -->

View file

@ -629,6 +629,17 @@
<string name="auto_switch_to_external_mouse_note">Automatically switches when the mouse is connected and disconnected.</string>
<string name="the_necessary_packages_have_been_installed">The necessary packages have been installed.</string>
<string name="an_error_occurred_while_creating_the_virtual_drive">An error occurred while creating the virtual drive.</string>
<string name="equalizer">Equalizer</string>
<string name="equalizer_settings_note">Treble, mid and bass.</string>
<string name="equalizer_note">You may need to restart the virtual machine for the changes to take effect. Sound effects may be affected if you use accessories such as headphones.</string>
<string name="use_equalizer">Use equalizer</string>
<string name="upper_treble">Upper treble</string>
<string name="treble">Treble</string>
<string name="mid">Mid</string>
<string name="bass">Bass</string>
<string name="low_bass">Low bass</string>
<string name="your_device_is_not_supported">Your device is not supported.</string>
<string name="undo">Undo</string>
<!--======================TERMUX STRINGS====================-->

View file

@ -5,11 +5,11 @@
"url": "https://github.com/xoureldeen/Vectras-VM-Android/releases",
"Message": "<h2>4.3.0</h2>\nBugs fixed.",
"cancellable": true,
"versionCodeBeta":"136",
"versionNameBeta":"4.3.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.1,4.2.2,4.2.3,4.2.4,4.2.5,4.2.6,4.2.7,4.2.8,4.2.9,4.3.0,4.3.1,4.3.2",
"versionCodeBeta":"137",
"versionNameBeta":"4.3.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.1,4.2.2,4.2.3,4.2.4,4.2.5,4.2.6,4.2.7,4.2.8,4.2.9,4.3.0,4.3.1,4.3.2,4.3.3",
"sizeBeta": "45 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
"MessageBeta": "<h2>4.3.2</h2>Bugs fixed.",
"MessageBeta": "<h2>4.3.3</h2>Bugs fixed.",
"cancellableBeta": true
}