[controller] agregar wizard para petición de permisos

This commit is contained in:
Luis Guzmán 2026-03-31 21:13:54 -06:00
parent c86c17c0be
commit 1393a288c7
8 changed files with 393 additions and 17 deletions

View File

@ -66,6 +66,7 @@
</intent-filter>
</activity>
<activity android:name=".AppListActivity" android:label="@string/app_name"/>
<activity android:name=".SetupActivity" android:exported="false" />
<activity
android:name=".PortalActivity"
android:theme="@style/Theme.AppCompat.NoActionBar" />

View File

@ -101,6 +101,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
private TextView logWarning;
private TextView logSizeText;
private ImageButton themeToggle;
private ImageButton btnSettings;
private TextView versionFooter;
private ProgressBar logProgress;
@ -134,6 +135,14 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Intercept launch and redirect to Setup Wizard if first time
SharedPreferences internalPrefs = getSharedPreferences("IIAB_Internal", Context.MODE_PRIVATE);
if (!internalPrefs.getBoolean("setup_complete", false)) {
startActivity(new Intent(this, SetupActivity.class));
finish();
return;
}
prefs = new Preferences(this);
setContentView(R.layout.main);
@ -200,6 +209,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
logWarning = findViewById(R.id.log_warning_text);
logSizeText = findViewById(R.id.log_size_text);
themeToggle = findViewById(R.id.theme_toggle);
btnSettings = findViewById(R.id.btn_settings);
versionFooter = findViewById(R.id.version_text);
configLayout = findViewById(R.id.config_layout);
configLabel = findViewById(R.id.config_label);
@ -212,6 +222,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
btnClearLog.setOnClickListener(this);
btnCopyLog.setOnClickListener(this);
themeToggle.setOnClickListener(v -> toggleTheme());
btnSettings.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, SetupActivity.class)));
configLabel.setOnClickListener(v -> handleConfigToggle());
advConfigLabel.setOnClickListener(v -> toggleVisibility(advancedConfig, advConfigLabel, getString(R.string.advanced_settings_label)));
logLabel.setOnClickListener(v -> handleLogToggle());
@ -241,7 +252,6 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
applySavedTheme();
setVersionFooter();
updateUI();
initiatePermissionChain();
addToLog(getString(R.string.app_started));

View File

@ -0,0 +1,179 @@
package org.iiab.controller;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.net.VpnService;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.widget.Button;
import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SwitchCompat;
import androidx.core.content.ContextCompat;
public class SetupActivity extends AppCompatActivity {
private static final String TERMUX_PERMISSION = "com.termux.permission.RUN_COMMAND";
private SwitchCompat switchNotif, switchTermux, switchVpn, switchBattery;
private Button btnContinue;
private ActivityResultLauncher<String> requestPermissionLauncher;
private ActivityResultLauncher<Intent> vpnLauncher;
private ActivityResultLauncher<Intent> batteryLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setup);
TextView welcomeText = findViewById(R.id.setup_welcome_text);
welcomeText.setText(getString(R.string.setup_welcome, getString(R.string.app_name)));
switchNotif = findViewById(R.id.switch_perm_notifications);
switchTermux = findViewById(R.id.switch_perm_termux);
switchVpn = findViewById(R.id.switch_perm_vpn);
switchBattery = findViewById(R.id.switch_perm_battery);
btnContinue = findViewById(R.id.btn_setup_continue);
// Hide Notification switch if Android < 13
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
switchNotif.setVisibility(android.view.View.GONE);
}
setupLaunchers();
setupListeners();
checkAllPermissions();
btnContinue.setOnClickListener(v -> {
// Save flag so we don't show this screen again
SharedPreferences prefs = getSharedPreferences("IIAB_Internal", Context.MODE_PRIVATE);
prefs.edit().putBoolean("setup_complete", true).apply();
finish();
});
}
private void setupLaunchers() {
requestPermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
isGranted -> checkAllPermissions()
);
vpnLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> checkAllPermissions()
);
batteryLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> checkAllPermissions()
);
}
private void setupListeners() {
switchNotif.setOnClickListener(v -> {
if (switchNotif.isChecked()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
}
}
switchNotif.setChecked(hasNotifPermission()); // Revert visual state pending system response
});
switchTermux.setOnClickListener(v -> {
if (switchTermux.isChecked()) {
requestPermissionLauncher.launch(TERMUX_PERMISSION);
}
switchTermux.setChecked(hasTermuxPermission());
});
switchVpn.setOnClickListener(v -> {
if (switchVpn.isChecked()) {
Intent intent = VpnService.prepare(this);
if (intent != null) {
vpnLauncher.launch(intent);
} else {
checkAllPermissions(); // Already granted
}
}
switchVpn.setChecked(hasVpnPermission());
});
switchBattery.setOnClickListener(v -> {
if (switchBattery.isChecked()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + getPackageName()));
batteryLauncher.launch(intent);
}
}
switchBattery.setChecked(hasBatteryPermission());
});
}
@Override
protected void onResume() {
super.onResume();
checkAllPermissions(); // Refresh state if user returns from settings
}
private void checkAllPermissions() {
boolean notif = hasNotifPermission();
boolean termux = hasTermuxPermission();
boolean vpn = hasVpnPermission();
boolean battery = hasBatteryPermission();
switchNotif.setChecked(notif);
switchTermux.setChecked(termux);
switchVpn.setChecked(vpn);
switchBattery.setChecked(battery);
// Lock switches if already granted to prevent user confusion
if (notif) switchNotif.setEnabled(false);
if (termux) switchTermux.setEnabled(false);
if (vpn) switchVpn.setEnabled(false);
if (battery) switchBattery.setEnabled(false);
boolean allGranted = termux && vpn && battery;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
allGranted = allGranted && notif;
}
btnContinue.setEnabled(allGranted);
btnContinue.setBackgroundTintList(ContextCompat.getColorStateList(this,
allGranted ? R.color.btn_explore_ready : R.color.btn_explore_disabled));
}
private boolean hasNotifPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED;
}
return true;
}
private boolean hasTermuxPermission() {
return ContextCompat.checkSelfPermission(this, TERMUX_PERMISSION) == PackageManager.PERMISSION_GRANTED;
}
private boolean hasVpnPermission() {
return VpnService.prepare(this) == null;
}
private boolean hasBatteryPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm != null && pm.isIgnoringBatteryOptimizations(getPackageName());
}
return true;
}
}

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
android:background="?android:attr/windowBackground">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/setup_title"
android:textSize="28sp"
android:textStyle="bold"
android:textColor="?android:attr/textColorPrimary"
android:layout_marginTop="32dp"
android:layout_marginBottom="16dp"/>
<TextView
android:id="@+id/setup_welcome_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="?android:attr/textColorSecondary"
android:layout_marginBottom="32dp"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_perm_notifications"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/setup_perm_notifications"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:paddingVertical="12dp"
android:layout_marginBottom="8dp"/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_perm_termux"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/setup_perm_termux"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:paddingVertical="12dp"
android:layout_marginBottom="8dp"/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_perm_vpn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/setup_perm_vpn"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:paddingVertical="12dp"
android:layout_marginBottom="8dp"/>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switch_perm_battery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/setup_perm_battery"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:paddingVertical="12dp"
android:layout_marginBottom="8dp"/>
</LinearLayout>
<Button
android:id="@+id/btn_setup_continue"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="@string/setup_continue"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#FFFFFF"
android:background="@drawable/rounded_button"
android:backgroundTint="@color/btn_success"
android:textAllCaps="false"
android:enabled="false" />
</LinearLayout>

View File

@ -31,6 +31,18 @@
android:textSize="18sp"
android:textStyle="bold" />
<ImageButton
android:id="@+id/btn_settings"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_toStartOf="@id/theme_toggle"
android:layout_centerVertical="true"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@android:drawable/ic_menu_preferences"
android:contentDescription="Settings"
android:padding="10dp"
android:scaleType="fitCenter" />
<!-- Triple Toggle Theme ImageButton -->
<ImageButton
android:id="@+id/theme_toggle"

View File

@ -1,5 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="setup_title">Initial Setup</string>
<string name="setup_welcome">Welcome to the %1$s setup wizard.\n\nIn order to work properly, we need the following permissions:</string>
<string name="setup_perm_notifications">Push Notifications</string>
<string name="setup_perm_termux">Termux Execution</string>
<string name="setup_perm_vpn">Safe Pocket Web (VPN)</string>
<string name="setup_perm_battery">Disable Battery Optimization</string>
<string name="setup_continue">Continue</string>
<string name="app_name">IIAB-oA Controller</string>
<string name="socks_addr">Dirección Socks:</string>
<string name="socks_udp_addr">Dirección UDP Socks:</string>
@ -31,7 +38,7 @@
<string name="copy_all">Copiar Todo</string>
<string name="system_ready">Sistema listo...\n</string>
<string name="configuration_label">Configuración</string>
<string name="advanced_settings_label">Ajustes Avanzados</string>
<string name="advanced_settings_label">Ajustes del Túnel</string>
<string name="connection_log_label">Log de Conexión</string>
<string name="app_started">Aplicación Iniciada</string>
<string name="no_blackbox_found">--- No se encontró el archivo BlackBox ---</string>

View File

@ -1,20 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">SocksTun</string>
<string name="socks_addr">Адрес Socks:</string>
<string name="socks_udp_addr">UDP-aдрес Socks:</string>
<string name="socks_port">Порт Socks:</string>
<string name="socks_user">Имя пользователя Socks:</string>
<string name="socks_pass">Пароль Socks:</string>
<string name="setup_title">Initial Setup</string>
<string name="setup_welcome">Welcome to the %1$s setup wizard.\n\nIn order to work properly, we need the following permissions:</string>
<string name="setup_perm_notifications">Push Notifications</string>
<string name="setup_perm_termux">Termux Execution</string>
<string name="setup_perm_vpn">Safe Pocket Web (VPN)</string>
<string name="setup_perm_battery">Disable Battery Optimization</string>
<string name="setup_continue">Continue</string>
<string name="app_name">IIAB-oA Controller</string>
<string name="socks_addr">Socks Address:</string>
<string name="socks_udp_addr">Socks UDP Address:</string>
<string name="socks_port">Socks Port:</string>
<string name="socks_user">Socks Username:</string>
<string name="socks_pass">Socks Password:</string>
<string name="dns_ipv4">DNS IPv4:</string>
<string name="dns_ipv6">DNS IPv6:</string>
<string name="udp_in_tcp">UDP через TCP</string>
<string name="remote_dns">Удалённый DNS</string>
<string name="udp_in_tcp">UDP relay over TCP</string>
<string name="remote_dns">Remote DNS</string>
<string name="ipv4">IPv4</string>
<string name="ipv6">IPv6</string>
<string name="global">Глобально</string>
<string name="apps">Приложения</string>
<string name="save">Сохранить</string>
<string name="control_enable">Включить</string>
<string name="control_disable">Отключить</string>
<string name="global">Global</string>
<string name="apps">Apps</string>
<string name="save">Save</string>
<string name="control_enable">Enable Safe Pocket Web</string>
<string name="control_disable">Disable Safe Pocket Web</string>
<string name="vpn_description">Enable friendly URLs. Lock out the threats.</string>
<string name="watchdog_enable">Enable Master Watchdog</string>
<string name="watchdog_disable">Disable Master Watchdog</string>
<string name="log_reset_confirm_title">Reset Log History?</string>
<string name="log_reset_confirm_msg">This will permanently delete all stored connection logs. This action cannot be undone.</string>
<string name="log_warning_rapid_growth">The logging file is growing too rapidly, you might want to check if something is failing</string>
<!-- New strings for translatability -->
<string name="browse_content">🚀 Explore Content</string>
<string name="watchdog_description">Protects Termux from Doze mode and keeps Wi-Fi active.</string>
<string name="reset_log">Reset Log</string>
<string name="copy_all">Copy All</string>
<string name="system_ready">System ready...\n</string>
<string name="configuration_label">Configuration</string>
<string name="advanced_settings_label">Tunnel Settings</string>
<string name="connection_log_label">Connection Log</string>
<string name="app_started">Application Started</string>
<string name="no_blackbox_found">--- No BlackBox file found ---</string>
<string name="loading_history">--- Loading History ---</string>
<string name="error_reading_history">Error reading history: %s</string>
<string name="end_of_history">--- End of History ---</string>
<string name="recovery_pulse_received">Recovery Pulse Received from System. Enforcing VPN...</string>
<string name="battery_opt_title">Battery Optimization</string>
<string name="battery_opt_msg">For the Watchdog to work reliably, please disable battery optimizations for this app.</string>
<string name="go_to_settings">Go to Settings</string>
<string name="cancel">Cancel</string>
<string name="saved_toast">Saved</string>
<string name="settings_saved">Settings Saved</string>
<string name="log_reset_log">Log reset</string>
<string name="log_reset_user">Log reset by user</string>
<string name="log_copied_toast">Log copied to clipboard</string>
<string name="watchdog_stopped">Watchdog Stopped</string>
<string name="watchdog_started">Watchdog Started</string>
<string name="vpn_stopping">VPN Stopping...</string>
<string name="vpn_starting">VPN Starting...</string>
<string name="log_cleared_toast">Log cleared</string>
<string name="failed_reset_log">Failed to reset log: %s</string>
<string name="unlock_watchdog_title">Unlock Master Watchdog</string>
<string name="unlock_watchdog_subtitle">Authentication required to stop Termux protection</string>
<string name="auth_success_disconnect">Authentication Success. Disconnecting...</string>
<string name="auth_required_title">Authentication required</string>
<string name="auth_required_subtitle">Authenticate to disable the secure environment</string>
<string name="security_required_title">Security Required</string>
<string name="security_required_msg">You must set up a PIN, Pattern, or Fingerprint on your device before enabling the secure environment.</string>
<string name="user_initiated_conn">User initiated connection</string>
<string name="vpn_permission_granted">VPN Permission Granted. Connecting...</string>
<!-- IIABWatchdog strings -->
<string name="pulse_stimulating">Pulse: Stimulating Termux...</string>
<string name="critical_os_blocked">CRITICAL: OS blocked Termux stimulus (SecurityException).</string>
<string name="ping_ok">PING 8085: OK</string>
<string name="ping_fail">PING 8085: FAIL (%s)</string>
<string name="session_started">HEARTBEAT SESSION STARTED</string>
<string name="session_stopped">HEARTBEAT SESSION STOPPED</string>
<!-- TermuxCallbackReceiver strings -->
<string name="termux_stimulus_ok">[Termux] Stimulus OK (exit 0)</string>
<string name="termux_pulse_error">[Termux] Pulse Error (exit %1$d): %2$s</string>
<string name="log_size_format">Size: %1$s / 10MB</string>
<!-- Brand specific battery warnings -->
<string name="battery_opt_oppo_extra">\n\nOPPO/Realme detected: Please ensure you also enable \'Allow background activity\' in this app\'s settings.</string>
<string name="battery_opt_xiaomi_extra">\n\nXiaomi detected: Please set battery saver to \'No restrictions\' in settings.</string>
<string name="battery_opt_denied">For the app to work 100%, please disable battery optimization.</string>
<string name="fix_action">FIX</string>
</resources>

View File

@ -1,5 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="setup_title">Initial Setup</string>
<string name="setup_welcome">Welcome to the %1$s setup wizard.\n\nIn order to work properly, we need the following permissions:</string>
<string name="setup_perm_notifications">Push Notifications</string>
<string name="setup_perm_termux">Termux Execution</string>
<string name="setup_perm_vpn">Safe Pocket Web (VPN)</string>
<string name="setup_perm_battery">Disable Battery Optimization</string>
<string name="setup_continue">Continue</string>
<string name="app_name">IIAB-oA Controller</string>
<string name="socks_addr">Socks Address:</string>
<string name="socks_udp_addr">Socks UDP Address:</string>
@ -31,7 +38,7 @@
<string name="copy_all">Copy All</string>
<string name="system_ready">System ready...\n</string>
<string name="configuration_label">Configuration</string>
<string name="advanced_settings_label">Advanced Settings</string>
<string name="advanced_settings_label">Tunnel Settings</string>
<string name="connection_log_label">Connection Log</string>
<string name="app_started">Application Started</string>
<string name="no_blackbox_found">--- No BlackBox file found ---</string>