diff --git a/apk/controller/app/src/main/java/org/iiab/controller/MainActivity.java b/apk/controller/app/src/main/java/org/iiab/controller/MainActivity.java index d6ef819..ce566d4 100644 --- a/apk/controller/app/src/main/java/org/iiab/controller/MainActivity.java +++ b/apk/controller/app/src/main/java/org/iiab/controller/MainActivity.java @@ -10,6 +10,8 @@ package org.iiab.controller; import android.os.Bundle; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AlertDialog; @@ -22,7 +24,6 @@ import android.content.ClipboardManager; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.CheckBox; @@ -73,8 +74,6 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe private Button watchdogControl; private TextView connectionLog; private LinearLayout logActions; - private Button btnClearLog; - private Button btnCopyLog; private LinearLayout configLayout; private TextView configLabel; private LinearLayout advancedConfig; @@ -83,7 +82,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe private ImageButton themeToggle; private TextView versionFooter; - private BroadcastReceiver logReceiver = new BroadcastReceiver() { + private ActivityResultLauncher vpnPermissionLauncher; + + private final BroadcastReceiver logReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (IIABWatchdog.ACTION_LOG_MESSAGE.equals(intent.getAction())) { @@ -100,45 +101,55 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe prefs = new Preferences(this); setContentView(R.layout.main); - edittext_socks_addr = (EditText) findViewById(R.id.socks_addr); - edittext_socks_udp_addr = (EditText) findViewById(R.id.socks_udp_addr); - edittext_socks_port = (EditText) findViewById(R.id.socks_port); - edittext_socks_user = (EditText) findViewById(R.id.socks_user); - edittext_socks_pass = (EditText) findViewById(R.id.socks_pass); - edittext_dns_ipv4 = (EditText) findViewById(R.id.dns_ipv4); - edittext_dns_ipv6 = (EditText) findViewById(R.id.dns_ipv6); - checkbox_ipv4 = (CheckBox) findViewById(R.id.ipv4); - checkbox_ipv6 = (CheckBox) findViewById(R.id.ipv6); - checkbox_global = (CheckBox) findViewById(R.id.global); - checkbox_udp_in_tcp = (CheckBox) findViewById(R.id.udp_in_tcp); - checkbox_remote_dns = (CheckBox) findViewById(R.id.remote_dns); - button_apps = (Button) findViewById(R.id.apps); - button_save = (Button) findViewById(R.id.save); - button_control = (Button) findViewById(R.id.control); + // Initialize the VPN permission launcher + vpnPermissionLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == RESULT_OK && prefs.getEnable()) { + connectVpn(); + } + } + ); + + edittext_socks_addr = findViewById(R.id.socks_addr); + edittext_socks_udp_addr = findViewById(R.id.socks_udp_addr); + edittext_socks_port = findViewById(R.id.socks_port); + edittext_socks_user = findViewById(R.id.socks_user); + edittext_socks_pass = findViewById(R.id.socks_pass); + edittext_dns_ipv4 = findViewById(R.id.dns_ipv4); + edittext_dns_ipv6 = findViewById(R.id.dns_ipv6); + checkbox_ipv4 = findViewById(R.id.ipv4); + checkbox_ipv6 = findViewById(R.id.ipv6); + checkbox_global = findViewById(R.id.global); + checkbox_udp_in_tcp = findViewById(R.id.udp_in_tcp); + checkbox_remote_dns = findViewById(R.id.remote_dns); + button_apps = findViewById(R.id.apps); + button_save = findViewById(R.id.save); + button_control = findViewById(R.id.control); - watchdogControl = (Button) findViewById(R.id.watchdog_control); + watchdogControl = findViewById(R.id.watchdog_control); watchdogControl.setOnClickListener(this); - logActions = (LinearLayout) findViewById(R.id.log_actions); - btnClearLog = (Button) findViewById(R.id.btn_clear_log); - btnCopyLog = (Button) findViewById(R.id.btn_copy_log); + logActions = findViewById(R.id.log_actions); + Button btnClearLog = findViewById(R.id.btn_clear_log); + Button btnCopyLog = findViewById(R.id.btn_copy_log); btnClearLog.setOnClickListener(this); btnCopyLog.setOnClickListener(this); - connectionLog = (TextView) findViewById(R.id.connection_log); + connectionLog = findViewById(R.id.connection_log); connectionLog.setMovementMethod(new ScrollingMovementMethod()); // Enable text selection for copying large logs connectionLog.setTextIsSelectable(true); - configLayout = (LinearLayout) findViewById(R.id.config_layout); - configLabel = (TextView) findViewById(R.id.config_label); + configLayout = findViewById(R.id.config_layout); + configLabel = findViewById(R.id.config_label); configLabel.setOnClickListener(v -> toggleVisibility(configLayout, configLabel, "Configuration")); - advancedConfig = (LinearLayout) findViewById(R.id.advanced_config); - advConfigLabel = (TextView) findViewById(R.id.adv_config_label); + advancedConfig = findViewById(R.id.advanced_config); + advConfigLabel = findViewById(R.id.adv_config_label); advConfigLabel.setOnClickListener(v -> toggleVisibility(advancedConfig, advConfigLabel, "Advanced Settings")); - logLabel = (TextView) findViewById(R.id.log_label); + logLabel = findViewById(R.id.log_label); logLabel.setOnClickListener(v -> { if (connectionLog.getVisibility() == View.GONE) { readBlackBoxLogs(); // Load logs from file when expanding @@ -147,11 +158,11 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe logActions.setVisibility(connectionLog.getVisibility()); }); - themeToggle = (ImageButton) findViewById(R.id.theme_toggle); + themeToggle = findViewById(R.id.theme_toggle); themeToggle.setOnClickListener(v -> toggleTheme()); applySavedTheme(); - versionFooter = (TextView) findViewById(R.id.version_text); + versionFooter = findViewById(R.id.version_text); setVersionFooter(); checkbox_udp_in_tcp.setOnClickListener(this); @@ -164,15 +175,22 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe /* Request VPN permission */ Intent intent = VpnService.prepare(MainActivity.this); - if (intent != null) - startActivityForResult(intent, 0); - else - onActivityResult(0, RESULT_OK, null); + if (intent != null) { + vpnPermissionLauncher.launch(intent); + } else if (prefs.getEnable()) { + connectVpn(); + } checkBatteryOptimizations(); addToLog("Application Started"); } + private void connectVpn() { + Intent intent = new Intent(this, TProxyService.class); + startService(intent.setAction(TProxyService.ACTION_CONNECT)); + addToLog("VPN Permission Granted. Connecting..."); + } + private void readBlackBoxLogs() { File logFile = new File(getFilesDir(), "watchdog_heartbeat_log.txt"); if (!logFile.exists()) { @@ -290,7 +308,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe super.onStart(); IntentFilter filter = new IntentFilter(IIABWatchdog.ACTION_LOG_MESSAGE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - registerReceiver(logReceiver, filter, Context.RECEIVER_EXPORTED); + registerReceiver(logReceiver, filter, Context.RECEIVER_NOT_EXPORTED); } else { registerReceiver(logReceiver, filter); } @@ -306,16 +324,6 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe } } - @Override - protected void onActivityResult(int request, int result, Intent data) { - super.onActivityResult(request, result, data); - if ((result == RESULT_OK) && prefs.getEnable()) { - Intent intent = new Intent(this, TProxyService.class); - startService(intent.setAction(TProxyService.ACTION_CONNECT)); - addToLog("VPN Permission Granted. Connecting..."); - } - } - @Override public void onClick(View view) { if (view == checkbox_global || view == checkbox_remote_dns) { @@ -377,9 +385,12 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe showBiometricPrompt(); } else { BiometricManager biometricManager = BiometricManager.from(this); - int authenticators = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ? - (BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL) : - BiometricManager.Authenticators.BIOMETRIC_WEAK; + + int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + // For older versions, DEVICE_CREDENTIAL behaves differently, but androidx.biometric handles fallback + authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK | BiometricManager.Authenticators.DEVICE_CREDENTIAL; + } boolean isSecure = false; android.app.KeyguardManager km = (android.app.KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); @@ -418,17 +429,15 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe } }); - BiometricPrompt.PromptInfo.Builder promptBuilder = new BiometricPrompt.PromptInfo.Builder() - .setTitle("Authentication required") - .setSubtitle("Authenticate to disable the secure environment"); + int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - promptBuilder.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG | - BiometricManager.Authenticators.DEVICE_CREDENTIAL); - } else { - promptBuilder.setDeviceCredentialAllowed(true); - } - biometricPrompt.authenticate(promptBuilder.build()); + BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder() + .setTitle("Authentication required") + .setSubtitle("Authenticate to disable the secure environment") + .setAllowedAuthenticators(authenticators) + .build(); + + biometricPrompt.authenticate(promptInfo); } private void showWatchdogBiometricPrompt() { @@ -441,17 +450,15 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe } }); - BiometricPrompt.PromptInfo.Builder promptBuilder = new BiometricPrompt.PromptInfo.Builder() - .setTitle("Unlock Master Watchdog") - .setSubtitle("Authentication required to stop Termux protection"); + int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - promptBuilder.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG | - BiometricManager.Authenticators.DEVICE_CREDENTIAL); - } else { - promptBuilder.setDeviceCredentialAllowed(true); - } - biometricPrompt.authenticate(promptBuilder.build()); + BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder() + .setTitle("Unlock Master Watchdog") + .setSubtitle("Authentication required to stop Termux protection") + .setAllowedAuthenticators(authenticators) + .build(); + + biometricPrompt.authenticate(promptInfo); } private void toggleService(boolean isEnable) { @@ -481,10 +488,10 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe } if (watchdogActive) { - watchdogControl.setText("Disable Master Watchdog"); + watchdogControl.setText(R.string.watchdog_disable); watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on)); } else { - watchdogControl.setText("Enable Master Watchdog"); + watchdogControl.setText(R.string.watchdog_enable); watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_off)); } diff --git a/apk/controller/app/src/main/res/values/strings.xml b/apk/controller/app/src/main/res/values/strings.xml index e797ba3..9ec9fb3 100644 --- a/apk/controller/app/src/main/res/values/strings.xml +++ b/apk/controller/app/src/main/res/values/strings.xml @@ -18,4 +18,6 @@ Enable Safe Pocket Web Disable Safe Pocket Web Enable friendly URLs. Lock out the threats. + Enable Master Watchdog + Disable Master Watchdog