Compare commits
2 Commits
c86c17c0be
...
5eac64be96
| Author | SHA1 | Date |
|---|---|---|
|
|
5eac64be96 | |
|
|
1393a288c7 |
|
|
@ -66,12 +66,15 @@
|
|||
</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" />
|
||||
</application>
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
package org.iiab.controller;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
public class BatteryUtils {
|
||||
|
||||
// Previously at MainActivity
|
||||
public static void checkAndPromptOptimizations(Activity activity, ActivityResultLauncher<Intent> launcher) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
PowerManager pm = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
|
||||
if (pm != null && !pm.isIgnoringBatteryOptimizations(activity.getPackageName())) {
|
||||
String manufacturer = Build.MANUFACTURER.toLowerCase();
|
||||
String message = activity.getString(R.string.battery_opt_msg);
|
||||
|
||||
if (manufacturer.contains("oppo") || manufacturer.contains("realme") || manufacturer.contains("xiaomi")) {
|
||||
|
||||
if (manufacturer.contains("oppo") || manufacturer.contains("realme")) {
|
||||
message += activity.getString(R.string.battery_opt_oppo_extra);
|
||||
} else if (manufacturer.contains("xiaomi")) {
|
||||
message += activity.getString(R.string.battery_opt_xiaomi_extra);
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.battery_opt_title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.go_to_settings, (dialog, which) -> openBatterySettings(activity, manufacturer))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
} else {
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||
intent.setData(Uri.parse("package:" + activity.getPackageName()));
|
||||
launcher.launch(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void openBatterySettings(Activity activity, String manufacturer) {
|
||||
boolean success = false;
|
||||
String packageName = activity.getPackageName();
|
||||
|
||||
if (manufacturer.contains("oppo") || manufacturer.contains("realme")) {
|
||||
try {
|
||||
Intent intent = new Intent();
|
||||
intent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity"));
|
||||
activity.startActivity(intent);
|
||||
success = true;
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
Intent intent = new Intent();
|
||||
intent.setComponent(new ComponentName("com.coloros.oppoguardelf", "com.coloros.oppoguardelf.Permission.BackgroundAllowAppListActivity"));
|
||||
activity.startActivity(intent);
|
||||
success = true;
|
||||
} catch (Exception e2) {}
|
||||
}
|
||||
} else if (manufacturer.contains("xiaomi")) {
|
||||
try {
|
||||
Intent intent = new Intent("miui.intent.action.APP_BATTERY_SAVER_SETTINGS");
|
||||
intent.setComponent(new ComponentName("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity"));
|
||||
intent.putExtra("package_name", packageName);
|
||||
intent.putExtra("package_label", activity.getString(R.string.app_name));
|
||||
activity.startActivity(intent);
|
||||
success = true;
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
try {
|
||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
intent.setData(Uri.parse("package:" + packageName));
|
||||
activity.startActivity(intent);
|
||||
} catch (Exception ex) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package org.iiab.controller;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.biometric.BiometricManager;
|
||||
import androidx.biometric.BiometricPrompt;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class BiometricHelper {
|
||||
|
||||
// This is the "phone line" that tells MainActivity the user succeeded
|
||||
public interface AuthCallback {
|
||||
void onSuccess();
|
||||
}
|
||||
|
||||
public static boolean isDeviceSecure(Context context) {
|
||||
BiometricManager bm = BiometricManager.from(context);
|
||||
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) context.getSystemService(Context.KEYGUARD_SERVICE);
|
||||
|
||||
return bm.canAuthenticate(auth) == BiometricManager.BIOMETRIC_SUCCESS || (km != null && km.isDeviceSecure());
|
||||
}
|
||||
|
||||
public static void showEnrollmentDialog(Context context) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.security_required_title)
|
||||
.setMessage(R.string.security_required_msg)
|
||||
.setPositiveButton(R.string.go_to_settings, (dialog, which) -> {
|
||||
Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
|
||||
context.startActivity(intent);
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
public static void prompt(AppCompatActivity activity, String title, String subtitle, AuthCallback callback) {
|
||||
Executor executor = ContextCompat.getMainExecutor(activity);
|
||||
BiometricPrompt biometricPrompt = new BiometricPrompt(activity, executor, new BiometricPrompt.AuthenticationCallback() {
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
|
||||
super.onAuthenticationSucceeded(result);
|
||||
// Call back to MainActivity!
|
||||
callback.onSuccess();
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(title)
|
||||
.setSubtitle(subtitle)
|
||||
.setAllowedAuthenticators(auth)
|
||||
.build();
|
||||
|
||||
biometricPrompt.authenticate(promptInfo);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package org.iiab.controller;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.provider.Settings;
|
||||
import android.transition.AutoTransition;
|
||||
import android.transition.TransitionManager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
public class DashboardManager {
|
||||
|
||||
private final Activity activity;
|
||||
private final LinearLayout dashboardContainer;
|
||||
|
||||
private final View dashWifi, dashHotspot, dashTunnel;
|
||||
private final View ledWifi, ledHotspot, ledTunnel;
|
||||
private final View standaloneEspwButton;
|
||||
private final View standaloneEspwDescription;
|
||||
|
||||
// We pass a Callback so the Dashboard can tell MainActivity to start/stop the VPN
|
||||
public interface DashboardActionCallback {
|
||||
void onToggleEspwRequested();
|
||||
}
|
||||
|
||||
public DashboardManager(Activity activity, View rootView, DashboardActionCallback callback) {
|
||||
this.activity = activity;
|
||||
|
||||
// Bind all the views
|
||||
dashboardContainer = (LinearLayout) rootView.findViewById(R.id.dashboard_container);
|
||||
dashWifi = rootView.findViewById(R.id.dash_wifi);
|
||||
dashHotspot = rootView.findViewById(R.id.dash_hotspot);
|
||||
dashTunnel = rootView.findViewById(R.id.dash_tunnel);
|
||||
|
||||
ledWifi = rootView.findViewById(R.id.led_wifi);
|
||||
ledHotspot = rootView.findViewById(R.id.led_hotspot);
|
||||
ledTunnel = rootView.findViewById(R.id.led_tunnel);
|
||||
|
||||
standaloneEspwButton = rootView.findViewById(R.id.control);
|
||||
standaloneEspwDescription = rootView.findViewById(R.id.control_description);
|
||||
|
||||
setupListeners(callback);
|
||||
}
|
||||
|
||||
private void setupListeners(DashboardActionCallback callback) {
|
||||
// Single tap opens Settings directly (No wrench icons needed!)
|
||||
dashWifi.setOnClickListener(v -> activity.startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)));
|
||||
|
||||
dashHotspot.setOnClickListener(v -> {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setClassName("com.android.settings", "com.android.settings.TetherSettings");
|
||||
activity.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
activity.startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
|
||||
}
|
||||
});
|
||||
|
||||
// The Tunnel/ESPW toggle logic
|
||||
View.OnClickListener toggleEspw = v -> callback.onToggleEspwRequested();
|
||||
standaloneEspwButton.setOnClickListener(toggleEspw);
|
||||
dashTunnel.setOnClickListener(toggleEspw);
|
||||
}
|
||||
|
||||
// Updates the LED graphics based on actual OS connectivity states
|
||||
public void updateConnectivityLeds(boolean isWifiOn, boolean isHotspotOn) {
|
||||
ledWifi.setBackgroundResource(isWifiOn ? R.drawable.led_on_green : R.drawable.led_off);
|
||||
ledHotspot.setBackgroundResource(isHotspotOn ? R.drawable.led_on_green : R.drawable.led_off);
|
||||
}
|
||||
|
||||
// The Magic Morphing Animation!
|
||||
public void setTunnelState(boolean isTunnelActive, boolean isDegraded) {
|
||||
// Tells Android to smoothly animate any layout changes we make next
|
||||
TransitionManager.beginDelayedTransition((ViewGroup) dashboardContainer.getParent(), new AutoTransition().setDuration(300));
|
||||
|
||||
if (isTunnelActive) {
|
||||
// Morph into 33% / 33% / 33% Dashboard mode
|
||||
standaloneEspwButton.setVisibility(View.GONE);
|
||||
standaloneEspwDescription.setVisibility(View.GONE);
|
||||
dashTunnel.setVisibility(View.VISIBLE);
|
||||
ledTunnel.setBackgroundResource(isDegraded ? R.drawable.led_on_orange : R.drawable.led_on_green);
|
||||
|
||||
// Force recalculate
|
||||
dashboardContainer.setWeightSum(3f);
|
||||
} else {
|
||||
// Morph back into 50% / 50% mode
|
||||
dashTunnel.setVisibility(View.GONE);
|
||||
standaloneEspwButton.setVisibility(View.VISIBLE);
|
||||
standaloneEspwDescription.setVisibility(View.VISIBLE);
|
||||
// The LED turns off implicitly since the whole dash_tunnel hides, but we can enforce it:
|
||||
ledTunnel.setBackgroundResource(R.drawable.led_off);
|
||||
// Force recalculate
|
||||
dashboardContainer.setWeightSum(2f);
|
||||
}
|
||||
// Force recalculate
|
||||
dashboardContainer.requestLayout();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package org.iiab.controller;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Locale;
|
||||
|
||||
public class LogManager {
|
||||
private static final String LOG_FILE_NAME = "watchdog_heartbeat_log.txt";
|
||||
|
||||
// Callbacks to communicate with MainActivity
|
||||
public interface LogReadCallback {
|
||||
void onResult(String logContent, boolean isRapidGrowth);
|
||||
}
|
||||
|
||||
public interface LogClearCallback {
|
||||
void onSuccess();
|
||||
void onError(String message);
|
||||
}
|
||||
|
||||
// Read the file in the background and return the result to the main thread
|
||||
public static void readLogsAsync(Context context, LogReadCallback callback) {
|
||||
new Thread(() -> {
|
||||
File logFile = new File(context.getFilesDir(), LOG_FILE_NAME);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (!logFile.exists()) {
|
||||
sb.append(context.getString(R.string.no_blackbox_found)).append("\n");
|
||||
} else {
|
||||
sb.append(context.getString(R.string.loading_history)).append("\n");
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(logFile))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
sb.append(context.getString(R.string.error_reading_history, e.getMessage())).append("\n");
|
||||
}
|
||||
sb.append(context.getString(R.string.end_of_history)).append("\n");
|
||||
}
|
||||
|
||||
SharedPreferences internalPrefs = context.getSharedPreferences("IIAB_Internal", Context.MODE_PRIVATE);
|
||||
boolean isRapid = internalPrefs.getBoolean(IIABWatchdog.PREF_RAPID_GROWTH, false);
|
||||
String result = sb.toString();
|
||||
|
||||
// We return the call on the main UI thread
|
||||
new Handler(Looper.getMainLooper()).post(() -> callback.onResult(result, isRapid));
|
||||
}).start();
|
||||
}
|
||||
|
||||
// Delete the file securely
|
||||
public static void clearLogs(Context context, LogClearCallback callback) {
|
||||
File logFile = new File(context.getFilesDir(), LOG_FILE_NAME);
|
||||
try (PrintWriter pw = new PrintWriter(logFile)) {
|
||||
pw.print("");
|
||||
context.getSharedPreferences("IIAB_Internal", Context.MODE_PRIVATE)
|
||||
.edit().putBoolean(IIABWatchdog.PREF_RAPID_GROWTH, false).apply();
|
||||
callback.onSuccess();
|
||||
} catch (IOException e) {
|
||||
callback.onError(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the file size
|
||||
public static String getFormattedSize(Context context) {
|
||||
File logFile = new File(context.getFilesDir(), LOG_FILE_NAME);
|
||||
long size = logFile.exists() ? logFile.length() : 0;
|
||||
|
||||
if (size < 1024) {
|
||||
return size + " B";
|
||||
} else if (size < 1024 * 1024) {
|
||||
return String.format(Locale.getDefault(), "%.1f KB", size / 1024.0);
|
||||
} else {
|
||||
return String.format(Locale.getDefault(), "%.2f MB", size / (1024.0 * 1024.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -87,10 +87,19 @@ public class PortalActivity extends AppCompatActivity {
|
|||
|
||||
Preferences prefs = new Preferences(this);
|
||||
boolean isVpnActive = prefs.getEnable();
|
||||
String targetUrl = isVpnActive ? "http://box/" : "http://localhost:8085/home";
|
||||
|
||||
String rawUrl = getIntent().getStringExtra("TARGET_URL");
|
||||
|
||||
// If for some strange reason the URL arrives empty, we use the security fallback
|
||||
if (rawUrl == null || rawUrl.isEmpty()) {
|
||||
rawUrl = "http://localhost:8085/home";
|
||||
}
|
||||
|
||||
// 1. Damos alcance global seguro a la URL para todos los lambdas de aquí en adelante
|
||||
final String finalTargetUrl = rawUrl;
|
||||
|
||||
btnHome.setOnClickListener(v -> {
|
||||
webView.loadUrl(targetUrl);
|
||||
webView.loadUrl(finalTargetUrl); // Usamos la variable final
|
||||
resetTimer.run();
|
||||
});
|
||||
|
||||
|
|
@ -146,6 +155,22 @@ public class PortalActivity extends AppCompatActivity {
|
|||
// Restore cache for normal browsing speed
|
||||
view.getSettings().setCacheMode(android.webkit.WebSettings.LOAD_DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, android.webkit.WebResourceRequest request, android.webkit.WebResourceError error) {
|
||||
super.onReceivedError(view, request, error);
|
||||
|
||||
if (request.isForMainFrame()) {
|
||||
String customErrorHtml = "<html><body style='background-color:#1A1A1A;color:#FFFFFF;text-align:center;padding-top:50px;font-family:sans-serif;'>"
|
||||
+ "<h2>⚠️ Connection Failed</h2>"
|
||||
+ "<p>Unable to reach the secure environment.</p>"
|
||||
+ "<p style='color:#888;font-size:12px;'>Error: " + error.getDescription() + "</p>"
|
||||
+ "</body></html>";
|
||||
view.loadData(customErrorHtml, "text/html", "UTF-8");
|
||||
isPageLoading = false;
|
||||
btnReload.setText("↻");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// --- MANUALLY CLOSE BAR LOGIC ---
|
||||
|
|
@ -163,10 +188,10 @@ public class PortalActivity extends AppCompatActivity {
|
|||
int tempPort = prefs.getSocksPort();
|
||||
if (tempPort <= 0) tempPort = 1080;
|
||||
|
||||
// Variable safe to read in lambda
|
||||
// 3. Restauramos la variable segura para el puerto
|
||||
final int finalProxyPort = tempPort;
|
||||
|
||||
// 3. Proxy block (ONLY IF VPN IS ACTIVE)
|
||||
// 4. Proxy block (ONLY IF VPN IS ACTIVE)
|
||||
if (isVpnActive) {
|
||||
if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) {
|
||||
ProxyConfig proxyConfig = new ProxyConfig.Builder()
|
||||
|
|
@ -178,16 +203,16 @@ public class PortalActivity extends AppCompatActivity {
|
|||
ProxyController.getInstance().setProxyOverride(proxyConfig, executor, () -> {
|
||||
Log.d(TAG, "Proxy configured on port: " + finalProxyPort);
|
||||
// Load HTML only when proxy is ready
|
||||
webView.loadUrl(targetUrl);
|
||||
webView.loadUrl(finalTargetUrl);
|
||||
});
|
||||
} else {
|
||||
// Fallback for older devices
|
||||
Log.w(TAG, "Proxy Override not supported");
|
||||
webView.loadUrl(targetUrl);
|
||||
webView.loadUrl(finalTargetUrl);
|
||||
}
|
||||
} else {
|
||||
// VPN is OFF. Do NOT use proxy. Just load localhost directly.
|
||||
webView.loadUrl(targetUrl);
|
||||
webView.loadUrl(finalTargetUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,8 @@ public class WatchdogService extends Service {
|
|||
public static final String ACTION_START = "org.iiab.controller.WATCHDOG_START";
|
||||
public static final String ACTION_STOP = "org.iiab.controller.WATCHDOG_STOP";
|
||||
public static final String ACTION_HEARTBEAT = "org.iiab.controller.HEARTBEAT";
|
||||
|
||||
public static final String ACTION_STATE_STARTED = "org.iiab.controller.WATCHDOG_STARTED";
|
||||
public static final String ACTION_STATE_STOPPED = "org.iiab.controller.WATCHDOG_STOPPED";
|
||||
private static final int HEARTBEAT_INTERVAL_MS = 20 * 1000;
|
||||
|
||||
@Override
|
||||
|
|
@ -36,12 +37,8 @@ public class WatchdogService extends Service {
|
|||
String action = intent.getAction();
|
||||
if (ACTION_START.equals(action)) {
|
||||
startWatchdog();
|
||||
} else if (ACTION_STOP.equals(action)) {
|
||||
stopWatchdog();
|
||||
return START_NOT_STICKY;
|
||||
} else if (ACTION_HEARTBEAT.equals(action)) {
|
||||
IIABWatchdog.performHeartbeat(this);
|
||||
// CRITICAL: Reschedule for the next pulse to create an infinite loop
|
||||
scheduleHeartbeat();
|
||||
}
|
||||
}
|
||||
|
|
@ -58,15 +55,26 @@ public class WatchdogService extends Service {
|
|||
|
||||
IIABWatchdog.logSessionStart(this);
|
||||
scheduleHeartbeat();
|
||||
|
||||
Intent startIntent = new Intent(ACTION_STATE_STARTED);
|
||||
startIntent.setPackage(getPackageName());
|
||||
sendBroadcast(startIntent);
|
||||
}
|
||||
|
||||
private void stopWatchdog() {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
// 1. Avisamos inmediatamente a la UI que nos estamos apagando
|
||||
Intent stopIntent = new Intent(ACTION_STATE_STOPPED);
|
||||
stopIntent.setPackage(getPackageName());
|
||||
sendBroadcast(stopIntent);
|
||||
|
||||
// 2. Limpiamos la basura
|
||||
cancelHeartbeat();
|
||||
IIABWatchdog.logSessionStop(this);
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
private PendingIntent getHeartbeatPendingIntent() {
|
||||
Intent intent = new Intent(this, WatchdogService.class);
|
||||
intent.setAction(ACTION_HEARTBEAT);
|
||||
|
|
@ -101,12 +109,6 @@ public class WatchdogService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
stopWatchdog();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<solid android:color="#333333" /> <stroke android:width="1dp" android:color="#222222" />
|
||||
</shape>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<solid android:color="#00FF00" /> <stroke android:width="2dp" android:color="#4400FF00" />
|
||||
</shape>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<solid android:color="#FF9800" />
|
||||
<stroke android:width="2dp" android:color="#44FF9800" />
|
||||
</shape>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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"
|
||||
|
|
@ -56,8 +68,88 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- HR below Watchdog -->
|
||||
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/divider_color" android:layout_marginBottom="20dp"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/dashboard_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="#1A1A1A"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dash_wifi"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:padding="12dp">
|
||||
<View
|
||||
android:layout_width="10dp"
|
||||
android:id="@+id/led_wifi"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/led_off" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Wi-Fi"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dash_hotspot"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:padding="12dp">
|
||||
<View
|
||||
android:id="@+id/led_hotspot"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/led_off" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hotspot"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dash_tunnel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:padding="12dp"
|
||||
android:visibility="gone">
|
||||
<View
|
||||
android:id="@+id/led_tunnel"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/led_on_green" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Tunnel"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- VPN Control Section -->
|
||||
<Button
|
||||
|
|
@ -73,6 +165,7 @@
|
|||
android:textAllCaps="false"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/control_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/vpn_description"
|
||||
|
|
@ -97,7 +190,7 @@
|
|||
android:backgroundTint="@color/btn_explore_disabled"
|
||||
android:textAllCaps="false"
|
||||
android:elevation="4dp"
|
||||
android:visibility="gone" />
|
||||
android:enabled="false" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/config_label"
|
||||
|
|
@ -238,19 +331,45 @@
|
|||
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/divider_color" android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Watchdog Control Section -->
|
||||
<Button
|
||||
android:id="@+id/watchdog_control"
|
||||
<LinearLayout
|
||||
android:id="@+id/deck_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="90dp"
|
||||
android:text="@string/watchdog_enable"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#FFFFFF"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="@color/btn_watchdog_off"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:textAllCaps="false"/>
|
||||
android:background="#00000000"
|
||||
android:padding="3dp"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false"
|
||||
android:weightSum="2">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_server_control"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:text="Launch Server"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#FFFFFF"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="@color/btn_success"
|
||||
android:textAllCaps="false"/>
|
||||
<Button
|
||||
android:id="@+id/watchdog_control"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="4dp"
|
||||
android:text="@string/watchdog_enable"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#FFFFFF"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="@color/btn_watchdog_off"
|
||||
android:textAllCaps="false"/>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -18,8 +25,8 @@
|
|||
<string name="control_enable">Activar Safe Pocket Web</string>
|
||||
<string name="control_disable">Desactivar Safe Pocket Web</string>
|
||||
<string name="vpn_description">Habilite URLs amigables. Bloquee las amenazas.</string>
|
||||
<string name="watchdog_enable">Activar Watchdog Maestro</string>
|
||||
<string name="watchdog_disable">Desactivar Watchdog Maestro</string>
|
||||
<string name="watchdog_enable">Activar\nWatchdog Maestro</string>
|
||||
<string name="watchdog_disable">Desactivar\nWatchdog Maestro</string>
|
||||
<string name="log_reset_confirm_title">¿Reiniciar historial de log?</string>
|
||||
<string name="log_reset_confirm_msg">Esto borrará permanentemente todos los logs de conexión guardados. Esta acción no se puede deshacer.</string>
|
||||
<string name="log_warning_rapid_growth">El archivo de log está creciendo demasiado rápido, verifique si algo está fallando</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>
|
||||
|
|
|
|||
|
|
@ -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\nMaster Watchdog</string>
|
||||
<string name="watchdog_disable">Disable\nMaster 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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -18,8 +25,8 @@
|
|||
<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="watchdog_enable">Enable\nMaster Watchdog</string>
|
||||
<string name="watchdog_disable">Disable\nMaster 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue