[UI/MM] agregar Maintentance Mode para administrar la puerta del Walled Garden

This commit is contained in:
Luis Guzmán 2026-03-13 09:57:43 -06:00
parent 33dc5c910b
commit 9348fe438a
4 changed files with 165 additions and 44 deletions

View File

@ -78,6 +78,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
private CheckBox checkbox_udp_in_tcp; private CheckBox checkbox_udp_in_tcp;
private CheckBox checkbox_remote_dns; private CheckBox checkbox_remote_dns;
private CheckBox checkbox_global; private CheckBox checkbox_global;
private CheckBox checkbox_maintenance;
private CheckBox checkbox_ipv4; private CheckBox checkbox_ipv4;
private CheckBox checkbox_ipv6; private CheckBox checkbox_ipv6;
private Button button_apps; private Button button_apps;
@ -170,6 +171,8 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
checkbox_global = findViewById(R.id.global); checkbox_global = findViewById(R.id.global);
checkbox_udp_in_tcp = findViewById(R.id.udp_in_tcp); checkbox_udp_in_tcp = findViewById(R.id.udp_in_tcp);
checkbox_remote_dns = findViewById(R.id.remote_dns); checkbox_remote_dns = findViewById(R.id.remote_dns);
checkbox_maintenance = findViewById(R.id.checkbox_maintenance);
checkbox_maintenance.setOnClickListener(this);
button_apps = findViewById(R.id.apps); button_apps = findViewById(R.id.apps);
button_save = findViewById(R.id.save); button_save = findViewById(R.id.save);
button_control = findViewById(R.id.control); button_control = findViewById(R.id.control);
@ -196,7 +199,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
btnClearLog.setOnClickListener(this); btnClearLog.setOnClickListener(this);
btnCopyLog.setOnClickListener(this); btnCopyLog.setOnClickListener(this);
themeToggle.setOnClickListener(v -> toggleTheme()); themeToggle.setOnClickListener(v -> toggleTheme());
configLabel.setOnClickListener(v -> toggleVisibility(configLayout, configLabel, getString(R.string.configuration_label))); configLabel.setOnClickListener(v -> handleConfigToggle());
advConfigLabel.setOnClickListener(v -> toggleVisibility(advancedConfig, advConfigLabel, getString(R.string.advanced_settings_label))); advConfigLabel.setOnClickListener(v -> toggleVisibility(advancedConfig, advConfigLabel, getString(R.string.advanced_settings_label)));
logLabel.setOnClickListener(v -> handleLogToggle()); logLabel.setOnClickListener(v -> handleLogToggle());
checkbox_udp_in_tcp.setOnClickListener(this); checkbox_udp_in_tcp.setOnClickListener(this);
@ -525,7 +528,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (view == checkbox_global || view == checkbox_remote_dns) { if (view == checkbox_global || view == checkbox_remote_dns || view == checkbox_maintenance) {
savePrefs(); savePrefs();
updateUI(); updateUI();
} else if (view == button_apps) { } else if (view == button_apps) {
@ -571,8 +574,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
} }
private void handleWatchdogClick() { private void handleWatchdogClick() {
if (prefs.getWatchdogEnable()) showWatchdogBiometricPrompt(); // if (prefs.getWatchdogEnable()) showWatchdogBiometricPrompt();
else toggleWatchdog(false); // else toggleWatchdog(false);
toggleWatchdog(prefs.getWatchdogEnable());
} }
private void toggleWatchdog(boolean stop) { private void toggleWatchdog(boolean stop) {
@ -629,6 +633,53 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
bp.authenticate(new BiometricPrompt.PromptInfo.Builder().setTitle(getString(R.string.auth_required_title)).setSubtitle(getString(R.string.auth_required_subtitle)).setAllowedAuthenticators(auth).build()); bp.authenticate(new BiometricPrompt.PromptInfo.Builder().setTitle(getString(R.string.auth_required_title)).setSubtitle(getString(R.string.auth_required_subtitle)).setAllowedAuthenticators(auth).build());
} }
// --- Secure Advanced Settings Menu ---
private void handleConfigToggle() {
if (configLayout.getVisibility() == View.GONE) {
// The menu is closed. We want to open it, so we check for security first.
BiometricManager bm = BiometricManager.from(this);
int auth = BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
auth = BiometricManager.Authenticators.BIOMETRIC_WEAK | BiometricManager.Authenticators.DEVICE_CREDENTIAL;
}
android.app.KeyguardManager km = (android.app.KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (bm.canAuthenticate(auth) == BiometricManager.BIOMETRIC_SUCCESS || (km != null && km.isDeviceSecure())) {
showConfigBiometricPrompt();
} else {
showEnrollmentDialog(); // Forces user to set a PIN if device has no security
}
} else {
// The menu is open. Just close it without asking for fingerprint.
toggleVisibility(configLayout, configLabel, getString(R.string.advanced_settings_label));
}
}
private void showConfigBiometricPrompt() {
Executor ex = ContextCompat.getMainExecutor(this);
BiometricPrompt bp = new BiometricPrompt(this, ex, new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
// Auth successful! Open the menu.
toggleVisibility(configLayout, configLabel, getString(R.string.advanced_settings_label));
}
});
int auth = BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
auth = BiometricManager.Authenticators.BIOMETRIC_WEAK | BiometricManager.Authenticators.DEVICE_CREDENTIAL;
}
// Reusing your existing strings to avoid compilation errors
bp.authenticate(new BiometricPrompt.PromptInfo.Builder()
.setTitle(getString(R.string.auth_required_title))
.setSubtitle(getString(R.string.auth_required_subtitle))
.setAllowedAuthenticators(auth)
.build());
}
// ------------------------------------------------
private void showWatchdogBiometricPrompt() { private void showWatchdogBiometricPrompt() {
Executor ex = ContextCompat.getMainExecutor(this); Executor ex = ContextCompat.getMainExecutor(this);
BiometricPrompt bp = new BiometricPrompt(this, new BiometricPrompt.AuthenticationCallback() { BiometricPrompt bp = new BiometricPrompt(this, new BiometricPrompt.AuthenticationCallback() {
@ -685,25 +736,49 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
checkbox_global.setChecked(prefs.getGlobal()); checkbox_global.setChecked(prefs.getGlobal());
checkbox_udp_in_tcp.setChecked(prefs.getUdpInTcp()); checkbox_udp_in_tcp.setChecked(prefs.getUdpInTcp());
checkbox_remote_dns.setChecked(prefs.getRemoteDns()); checkbox_remote_dns.setChecked(prefs.getRemoteDns());
checkbox_maintenance.setChecked(prefs.getMaintenanceMode());
boolean editable = !vpnActive; boolean editable = !vpnActive;
edittext_socks_addr.setEnabled(editable); edittext_socks_addr.setEnabled(editable);
edittext_socks_port.setEnabled(editable); edittext_socks_port.setEnabled(editable);
button_save.setEnabled(editable); button_save.setEnabled(editable);
} }
//DEFAULT VALUES ON ORIGINAL INTERFACE
// private void savePrefs() {
// prefs.setSocksAddress(edittext_socks_addr.getText().toString());
// prefs.setSocksPort(Integer.parseInt(edittext_socks_port.getText().toString()));
// prefs.setSocksUdpAddress(edittext_socks_udp_addr.getText().toString());
// prefs.setSocksUsername(edittext_socks_user.getText().toString());
// prefs.setSocksPassword(edittext_socks_pass.getText().toString());
// prefs.setDnsIpv4(edittext_dns_ipv4.getText().toString());
// prefs.setDnsIpv6(edittext_dns_ipv6.getText().toString());
// prefs.setIpv4(checkbox_ipv4.isChecked());
// prefs.setIpv6(checkbox_ipv6.isChecked());
// prefs.setGlobal(checkbox_global.isChecked());
// prefs.setUdpInTcp(checkbox_udp_in_tcp.isChecked());
// prefs.setRemoteDns(checkbox_remote_dns.isChecked());
// prefs.setMaintenanceMode(checkbox_maintenance.isChecked());
// }
private void savePrefs() { private void savePrefs() {
prefs.setSocksAddress(edittext_socks_addr.getText().toString()); // 1. Hardcoded / Hidden Secure Values (Walled Garden defaults)
prefs.setSocksPort(Integer.parseInt(edittext_socks_port.getText().toString())); prefs.setSocksAddress("127.0.0.1");
prefs.setSocksUdpAddress(edittext_socks_udp_addr.getText().toString()); prefs.setSocksPort(1080);
prefs.setSocksUsername(edittext_socks_user.getText().toString()); prefs.setSocksUdpAddress(""); // Empty by default
prefs.setSocksPassword(edittext_socks_pass.getText().toString()); prefs.setSocksUsername("");
prefs.setSocksPassword("");
prefs.setIpv4(true);
prefs.setIpv6(true);
prefs.setUdpInTcp(false);
prefs.setRemoteDns(true);
// CRITICAL: Force Global to TRUE so the tunnel catches ALL system traffic
prefs.setGlobal(true);
// 2. User Editable Values (The only things read from the UI)
prefs.setDnsIpv4(edittext_dns_ipv4.getText().toString()); prefs.setDnsIpv4(edittext_dns_ipv4.getText().toString());
prefs.setDnsIpv6(edittext_dns_ipv6.getText().toString()); prefs.setDnsIpv6(edittext_dns_ipv6.getText().toString());
prefs.setIpv4(checkbox_ipv4.isChecked()); prefs.setMaintenanceMode(checkbox_maintenance.isChecked());
prefs.setIpv6(checkbox_ipv6.isChecked());
prefs.setGlobal(checkbox_global.isChecked());
prefs.setUdpInTcp(checkbox_udp_in_tcp.isChecked());
prefs.setRemoteDns(checkbox_remote_dns.isChecked());
} }
private void addToLog(String message) { private void addToLog(String message) {

View File

@ -32,6 +32,7 @@ public class Preferences
public static final String APPS = "Apps"; public static final String APPS = "Apps";
public static final String ENABLE = "Enable"; public static final String ENABLE = "Enable";
public static final String WATCHDOG_ENABLE = "WatchdogEnable"; public static final String WATCHDOG_ENABLE = "WatchdogEnable";
public static final String MAINTENANCE_MODE = "MaintenanceMode";
private SharedPreferences prefs; private SharedPreferences prefs;
@ -193,6 +194,16 @@ public class Preferences
editor.commit(); editor.commit();
} }
public boolean getMaintenanceMode() {
return prefs.getBoolean(MAINTENANCE_MODE, false);
}
public void setMaintenanceMode(boolean enable) {
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(MAINTENANCE_MODE, enable);
editor.commit();
}
public int getTunnelMtu() { public int getTunnelMtu() {
return 8500; return 8500;
} }

View File

@ -226,6 +226,10 @@ public class TProxyService extends VpnService {
String selfName = getApplicationContext().getPackageName(); String selfName = getApplicationContext().getPackageName();
try { try {
builder.addDisallowedApplication(selfName); builder.addDisallowedApplication(selfName);
if (prefs.getMaintenanceMode()) { // Verify if the maintenance mode is enabled
builder.addDisallowedApplication("com.termux");
Log.i(TAG, "Maintenance mode enabled: Termux has direct Internet access");
}
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
} }
} }

View File

@ -125,23 +125,11 @@
android:elevation="4dp" android:elevation="4dp"
android:visibility="gone" /> android:visibility="gone" />
<Button
android:id="@+id/apps"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/apps"
android:layout_marginTop="4dp"
android:background="@drawable/rounded_button"
android:backgroundTint="#444444"
android:textColor="#FFFFFF"
android:textAllCaps="false"/>
<!-- Config Section (Below VPN Button) -->
<TextView <TextView
android:id="@+id/config_label" android:id="@+id/config_label"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/configuration_label" android:text="@string/advanced_settings_label"
android:textStyle="bold" android:textStyle="bold"
android:padding="12dp" android:padding="12dp"
android:background="?attr/sectionHeaderBackground" android:background="?attr/sectionHeaderBackground"
@ -159,14 +147,63 @@
android:padding="12dp" android:padding="12dp"
android:background="?attr/sectionBackground"> android:background="?attr/sectionBackground">
<!-- Primary Fields --> <Button
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_addr" android:textColor="?android:attr/textColorSecondary" android:textSize="12sp"/> android:id="@+id/apps"
<EditText android:id="@+id/socks_addr" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary"/> android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/apps"
android:layout_marginBottom="12dp"
android:background="@drawable/rounded_button"
android:backgroundTint="#444444"
android:textColor="#FFFFFF"
android:textAllCaps="false"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_port" android:textColor="?android:attr/textColorSecondary" android:textSize="12sp"/> <TextView android:layout_width="wrap_content"
<EditText android:id="@+id/socks_port" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:inputType="number" android:textColor="?android:attr/textColorPrimary"/> android:layout_height="wrap_content"
android:text="@string/dns_ipv4"
android:textColor="?android:attr/textColorSecondary"
android:textSize="11sp"/>
<EditText android:id="@+id/dns_ipv4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dns_ipv6"
android:textColor="?android:attr/textColorSecondary"
android:textSize="11sp"/>
<EditText android:id="@+id/dns_ipv6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"/>
<CheckBox
android:id="@+id/checkbox_maintenance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="Maintenance Mode" />
<Button android:id="@+id/save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/save"
android:layout_marginTop="8dp"
android:background="@drawable/rounded_button"
android:backgroundTint="#555555"
android:textColor="#FFFFFF"
android:textAllCaps="false"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_addr" android:textColor="?android:attr/textColorSecondary" android:textSize="12sp" android:visibility="gone"/>
<EditText android:id="@+id/socks_addr" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary" android:visibility="gone"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_port" android:textColor="?android:attr/textColorSecondary" android:textSize="12sp" android:visibility="gone"/>
<EditText android:id="@+id/socks_port" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:inputType="number" android:textColor="?android:attr/textColorPrimary" android:visibility="gone"/>
<!-- Advanced Sub-section -->
<TextView <TextView
android:id="@+id/adv_config_label" android:id="@+id/adv_config_label"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -176,7 +213,8 @@
android:padding="8dp" android:padding="8dp"
android:textSize="13sp" android:textSize="13sp"
android:clickable="true" android:clickable="true"
android:focusable="true"/> android:focusable="true"
android:visibility="gone"/>
<LinearLayout <LinearLayout
android:id="@+id/advanced_config" android:id="@+id/advanced_config"
@ -194,12 +232,6 @@
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_pass" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_pass" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
<EditText android:id="@+id/socks_pass" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:inputType="textPassword" android:textColor="?android:attr/textColorPrimary"/> <EditText android:id="@+id/socks_pass" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:inputType="textPassword" android:textColor="?android:attr/textColorPrimary"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/dns_ipv4" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
<EditText android:id="@+id/dns_ipv4" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/dns_ipv6" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
<EditText android:id="@+id/dns_ipv6" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary"/>
<CheckBox android:id="@+id/udp_in_tcp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/udp_in_tcp" android:textColor="?android:attr/textColorSecondary"/> <CheckBox android:id="@+id/udp_in_tcp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/udp_in_tcp" android:textColor="?android:attr/textColorSecondary"/>
<CheckBox android:id="@+id/ipv4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ipv4" android:textColor="?android:attr/textColorSecondary"/> <CheckBox android:id="@+id/ipv4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ipv4" android:textColor="?android:attr/textColorSecondary"/>
<CheckBox android:id="@+id/ipv6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ipv6" android:textColor="?android:attr/textColorSecondary"/> <CheckBox android:id="@+id/ipv6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ipv6" android:textColor="?android:attr/textColorSecondary"/>
@ -209,12 +241,11 @@
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp"> android:layout_marginTop="8dp"
android:visibility="gone">
<CheckBox android:id="@+id/remote_dns" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/remote_dns" android:textColor="?android:attr/textColorSecondary"/> <CheckBox android:id="@+id/remote_dns" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/remote_dns" android:textColor="?android:attr/textColorSecondary"/>
<CheckBox android:id="@+id/global" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/global" android:textColor="?android:attr/textColorSecondary"/> <CheckBox android:id="@+id/global" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/global" android:textColor="?android:attr/textColorSecondary"/>
</LinearLayout> </LinearLayout>
<Button android:id="@+id/save" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/save" android:layout_marginTop="8dp" android:background="@drawable/rounded_button" android:backgroundTint="#555555" android:textColor="#FFFFFF" android:textAllCaps="false"/>
</LinearLayout> </LinearLayout>
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="#444444" android:layout_marginVertical="20dp"/> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#444444" android:layout_marginVertical="20dp"/>