Compare commits
No commits in common. "b69389621dc7467524f6030308c75ae2aa9f3eeb" and "c1f9152f9f605f80eec24494552dbf3a228453e8" have entirely different histories.
b69389621d
...
c1f9152f9f
|
|
@ -4,7 +4,7 @@
|
|||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2026-03-07T02:01:50.591889196Z">
|
||||
<DropdownSelection timestamp="2026-03-06T16:09:16.786936459Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=69K7MB899PKJGQBI" />
|
||||
|
|
|
|||
|
|
@ -48,13 +48,6 @@
|
|||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Termux Result Callback Receiver -->
|
||||
<receiver android:name=".TermuxCallbackReceiver" android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="org.iiab.controller.TERMUX_OUTPUT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<activity android:name=".MainActivity" android:label="@string/app_name"
|
||||
android:excludeFromRecents="true"
|
||||
android:launchMode="singleTop"
|
||||
|
|
|
|||
|
|
@ -1,26 +1,16 @@
|
|||
package org.iiab.controller;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
|
|
@ -32,14 +22,11 @@ public class IIABWatchdog {
|
|||
|
||||
public static final String ACTION_LOG_MESSAGE = "org.iiab.controller.LOG_MESSAGE";
|
||||
public static final String EXTRA_MESSAGE = "org.iiab.controller.EXTRA_MESSAGE";
|
||||
public static final String ACTION_TERMUX_OUTPUT = "org.iiab.controller.TERMUX_OUTPUT";
|
||||
|
||||
public static final String PREF_RAPID_GROWTH = "log_rapid_growth";
|
||||
|
||||
// --- TEMPORARY DEBUG FLAGS ---
|
||||
private static final boolean DEBUG_ENABLED = true;
|
||||
private static final String BLACKBOX_FILE = "watchdog_heartbeat_log.txt";
|
||||
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
||||
private static final int MAX_DAYS = 5;
|
||||
// ----------------------------
|
||||
|
||||
/**
|
||||
* Performs a full heartbeat pulse: sending stimulus and debug ping.
|
||||
|
|
@ -50,51 +37,35 @@ public class IIABWatchdog {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sends a keep-alive command to Termux via Intent.
|
||||
* Sends a command to Termux to keep it active.
|
||||
* This is the real keep-alive mechanism.
|
||||
* @param context The context to use for sending the intent.
|
||||
*/
|
||||
public static void sendStimulus(Context context) {
|
||||
if (DEBUG_ENABLED) {
|
||||
writeToBlackBox(context, "Pulse: Stimulating Termux...");
|
||||
writeToBlackBox(context, "Sending Intent (true) to Termux to keep it awake...");
|
||||
}
|
||||
|
||||
// Build the intent for Termux with exact payload requirements
|
||||
Intent intent = new Intent();
|
||||
Intent intent = new Intent("com.termux.service.RUN_COMMAND");
|
||||
intent.setClassName("com.termux", "com.termux.app.RunCommandService");
|
||||
intent.setAction("com.termux.RUN_COMMAND");
|
||||
|
||||
// 1. Absolute path to the command (String)
|
||||
intent.putExtra("com.termux.RUN_COMMAND_PATH", "/data/data/com.termux/files/usr/bin/true");
|
||||
// 2. Execute silently in the background (Boolean, critical)
|
||||
intent.putExtra("com.termux.RUN_COMMAND_BACKGROUND", true);
|
||||
// 3. Avoid saving session history (String "0" = no action)
|
||||
intent.putExtra("com.termux.RUN_COMMAND_SESSION_ACTION", "0");
|
||||
|
||||
// Callback mechanism to confirm execution
|
||||
Intent callbackIntent = new Intent(context, TermuxCallbackReceiver.class);
|
||||
callbackIntent.setAction(ACTION_TERMUX_OUTPUT);
|
||||
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
flags |= PendingIntent.FLAG_IMMUTABLE;
|
||||
}
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, callbackIntent, flags);
|
||||
intent.putExtra("com.termux.service.RUN_COMMAND_CALLBACK", pendingIntent);
|
||||
intent.putExtra("com.termux.service.RUN_COMMAND_PATH", "/data/data/com.termux/files/usr/bin/true");
|
||||
intent.putExtra("com.termux.service.RUN_COMMAND_BACKGROUND", true);
|
||||
|
||||
try {
|
||||
context.startService(intent);
|
||||
} catch (SecurityException e) {
|
||||
// This catches specific permission errors on newer Android versions
|
||||
Log.e(TAG, "Permission Denied: Ensure manifest has RUN_COMMAND and app is not restricted.", e);
|
||||
writeToBlackBox(context, "CRITICAL: OS blocked Termux stimulus (SecurityException).");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unexpected error sending intent to Termux", e);
|
||||
writeToBlackBox(context, "Pulse Error: " + e.getMessage());
|
||||
if (DEBUG_ENABLED) {
|
||||
Log.e(TAG, "[DEBUG_DEEP_SLEEP] Failed to send 'true' command to Termux", e);
|
||||
writeToBlackBox(context, "ERROR sending Intent: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings the Termux NGINX server to check responsiveness.
|
||||
* Pings the Termux NGINX server to check if it's responsive.
|
||||
* This is a temporary debugging tool.
|
||||
* // TODO: REMOVE AFTER HANS DEBUGGING
|
||||
* @param context The context for writing to the blackbox log.
|
||||
*/
|
||||
public static void performDebugPing(Context context) {
|
||||
final String NGINX_IP = "127.0.0.1";
|
||||
|
|
@ -104,11 +75,13 @@ public class IIABWatchdog {
|
|||
try (Socket socket = new Socket()) {
|
||||
socket.connect(new InetSocketAddress(NGINX_IP, NGINX_PORT), 2000);
|
||||
if (DEBUG_ENABLED) {
|
||||
writeToBlackBox(context, "PING 8085: OK");
|
||||
Log.e(TAG, "[DEBUG_DEEP_SLEEP] PING 8085 SUCCESSFUL: Termux is alive.");
|
||||
writeToBlackBox(context, "PING 8085 SUCCESSFUL: Termux is alive.");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (DEBUG_ENABLED) {
|
||||
writeToBlackBox(context, "PING 8085: FAIL (" + e.getMessage() + ")");
|
||||
Log.w(TAG, "[DEBUG_DEEP_SLEEP] PING 8085 FAILED: " + e.getMessage());
|
||||
writeToBlackBox(context, "PING 8085 FAILED: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
|
@ -116,7 +89,7 @@ public class IIABWatchdog {
|
|||
|
||||
public static void logSessionStart(Context context) {
|
||||
if (DEBUG_ENABLED) {
|
||||
writeToBlackBox(context, "HEARTBEAT SESSION STARTED");
|
||||
writeToBlackBox(context, "HEARTBEAT SESSION STARTED (Thread based)");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,93 +99,21 @@ public class IIABWatchdog {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a message to the local log file and broadcasts it for UI update.
|
||||
*/
|
||||
public static void writeToBlackBox(Context context, String message) {
|
||||
private static void writeToBlackBox(Context context, String message) {
|
||||
try {
|
||||
File logFile = new File(context.getFilesDir(), BLACKBOX_FILE);
|
||||
|
||||
// 1. Perform maintenance if file size is nearing limit
|
||||
if (logFile.exists() && logFile.length() > MAX_FILE_SIZE * 0.9) {
|
||||
maintenance(context, logFile);
|
||||
}
|
||||
|
||||
try (FileWriter writer = new FileWriter(logFile, true)) {
|
||||
FileWriter writer = new FileWriter(logFile, true);
|
||||
String datePrefix = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
|
||||
writer.append(datePrefix).append(" - ").append(message).append("\n");
|
||||
writer.close();
|
||||
|
||||
// Also broadcast for UI update
|
||||
broadcastLog(context, message);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to write to BlackBox", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles log rotation based on date (5 days) and size (10MB).
|
||||
*/
|
||||
private static void maintenance(Context context, File logFile) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.add(Calendar.DAY_OF_YEAR, -MAX_DAYS);
|
||||
Date cutoffDate = cal.getTime();
|
||||
|
||||
boolean deletedByDate = false;
|
||||
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(logFile))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.length() > 19) {
|
||||
try {
|
||||
Date lineDate = sdf.parse(line.substring(0, 19));
|
||||
if (lineDate != null && lineDate.after(cutoffDate)) {
|
||||
lines.add(line);
|
||||
} else {
|
||||
deletedByDate = true;
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
lines.add(line);
|
||||
}
|
||||
} else {
|
||||
lines.add(line);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If after date cleanup it's still too large, trim the oldest 20%
|
||||
if (calculateSize(lines) > MAX_FILE_SIZE) {
|
||||
int toRemove = lines.size() / 5;
|
||||
if (toRemove > 0) {
|
||||
lines = lines.subList(toRemove, lines.size());
|
||||
}
|
||||
// If deleting by size but not by date, it indicates rapid log growth
|
||||
if (!deletedByDate) {
|
||||
setRapidGrowthFlag(context, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Write cleaned logs back to file
|
||||
try (PrintWriter pw = new PrintWriter(new FileWriter(logFile))) {
|
||||
for (String l : lines) {
|
||||
pw.println(l);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Maintenance write failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static long calculateSize(List<String> lines) {
|
||||
long size = 0;
|
||||
for (String s : lines) size += s.length() + 1;
|
||||
return size;
|
||||
}
|
||||
|
||||
private static void setRapidGrowthFlag(Context context, boolean enabled) {
|
||||
SharedPreferences prefs = context.getSharedPreferences("IIAB_Internal", Context.MODE_PRIVATE);
|
||||
prefs.edit().putBoolean(PREF_RAPID_GROWTH, enabled).apply();
|
||||
}
|
||||
|
||||
private static void broadcastLog(Context context, String message) {
|
||||
Intent intent = new Intent(ACTION_LOG_MESSAGE);
|
||||
intent.putExtra(EXTRA_MESSAGE, message);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.Manifest;
|
||||
import android.os.Bundle;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
|
|
@ -25,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.view.MotionEvent;
|
||||
import android.widget.Button;
|
||||
|
|
@ -33,36 +31,30 @@ import android.widget.CheckBox;
|
|||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.net.VpnService;
|
||||
import android.net.Uri;
|
||||
import android.text.method.ScrollingMovementMethod;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.biometric.BiometricManager;
|
||||
import androidx.biometric.BiometricPrompt;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
|
||||
private static final String TAG = "IIAB-MainActivity";
|
||||
private static final String TERMUX_PERMISSION = "com.termux.permission.RUN_COMMAND";
|
||||
private Preferences prefs;
|
||||
private EditText edittext_socks_addr;
|
||||
private EditText edittext_socks_udp_addr;
|
||||
|
|
@ -88,19 +80,10 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
private LinearLayout advancedConfig;
|
||||
private TextView advConfigLabel;
|
||||
private TextView logLabel;
|
||||
private TextView logWarning;
|
||||
private TextView logSizeText;
|
||||
private ImageButton themeToggle;
|
||||
private TextView versionFooter;
|
||||
private ProgressBar logProgress;
|
||||
|
||||
private ActivityResultLauncher<Intent> vpnPermissionLauncher;
|
||||
private ActivityResultLauncher<String> requestPermissionLauncher;
|
||||
private ActivityResultLauncher<String> notificationPermissionLauncher;
|
||||
|
||||
private boolean isReadingLogs = false;
|
||||
private Handler sizeUpdateHandler = new Handler();
|
||||
private Runnable sizeUpdateRunnable;
|
||||
|
||||
private final BroadcastReceiver logReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
|
|
@ -108,7 +91,6 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
if (IIABWatchdog.ACTION_LOG_MESSAGE.equals(intent.getAction())) {
|
||||
String message = intent.getStringExtra(IIABWatchdog.EXTRA_MESSAGE);
|
||||
addToLog(message);
|
||||
updateLogSizeUI();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -120,7 +102,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
prefs = new Preferences(this);
|
||||
setContentView(R.layout.main);
|
||||
|
||||
// Initialize Result Launchers
|
||||
// Initialize the VPN permission launcher
|
||||
vpnPermissionLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
|
|
@ -130,28 +112,6 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
}
|
||||
);
|
||||
|
||||
requestPermissionLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.RequestPermission(),
|
||||
isGranted -> {
|
||||
if (isGranted) {
|
||||
addToLog("Termux RUN_COMMAND permission granted");
|
||||
} else {
|
||||
addToLog("Termux permission denied. Watchdog stimulus may fail.");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
notificationPermissionLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.RequestPermission(),
|
||||
isGranted -> {
|
||||
if (isGranted) {
|
||||
addToLog("Notification permission granted");
|
||||
} else {
|
||||
addToLog("Notification permission denied. Status visibility may be limited.");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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);
|
||||
|
|
@ -179,41 +139,35 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
|
||||
connectionLog = findViewById(R.id.connection_log);
|
||||
connectionLog.setMovementMethod(new ScrollingMovementMethod());
|
||||
// Enable text selection for copying large logs
|
||||
connectionLog.setTextIsSelectable(true);
|
||||
|
||||
logProgress = findViewById(R.id.log_progress);
|
||||
logWarning = findViewById(R.id.log_warning_text);
|
||||
logSizeText = findViewById(R.id.log_size_text);
|
||||
|
||||
// Allow internal scrolling by disabling parent intercept
|
||||
// FIX: Allow internal scrolling by disabling parent intercept
|
||||
connectionLog.setOnTouchListener((v, event) -> {
|
||||
if (v.getId() == R.id.connection_log) {
|
||||
v.getParent().requestDisallowInterceptTouchEvent(true);
|
||||
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
|
||||
v.getParent().requestDisallowInterceptTouchEvent(false);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
configLayout = findViewById(R.id.config_layout);
|
||||
configLabel = findViewById(R.id.config_label);
|
||||
configLabel.setOnClickListener(v -> toggleVisibility(configLayout, configLabel, getString(R.string.configuration_label)));
|
||||
configLabel.setOnClickListener(v -> toggleVisibility(configLayout, configLabel, "Configuration"));
|
||||
|
||||
advancedConfig = findViewById(R.id.advanced_config);
|
||||
advConfigLabel = findViewById(R.id.adv_config_label);
|
||||
advConfigLabel.setOnClickListener(v -> toggleVisibility(advancedConfig, advConfigLabel, getString(R.string.advanced_settings_label)));
|
||||
advConfigLabel.setOnClickListener(v -> toggleVisibility(advancedConfig, advConfigLabel, "Advanced Settings"));
|
||||
|
||||
logLabel = findViewById(R.id.log_label);
|
||||
logLabel.setOnClickListener(v -> {
|
||||
boolean isOpening = connectionLog.getVisibility() == View.GONE;
|
||||
if (isOpening) {
|
||||
readBlackBoxLogs();
|
||||
startLogSizeUpdates();
|
||||
} else {
|
||||
stopLogSizeUpdates();
|
||||
if (connectionLog.getVisibility() == View.GONE) {
|
||||
readBlackBoxLogs(); // Load logs from file when expanding
|
||||
}
|
||||
toggleVisibility(connectionLog, logLabel, getString(R.string.connection_log_label));
|
||||
toggleVisibility(connectionLog, logLabel, "Connection Log");
|
||||
logActions.setVisibility(connectionLog.getVisibility());
|
||||
if (logSizeText != null) logSizeText.setVisibility(connectionLog.getVisibility());
|
||||
});
|
||||
|
||||
themeToggle = findViewById(R.id.theme_toggle);
|
||||
|
|
@ -231,11 +185,6 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
button_control.setOnClickListener(this);
|
||||
updateUI();
|
||||
|
||||
/* Request permissions */
|
||||
checkNotificationPermission();
|
||||
checkTermuxPermission();
|
||||
checkBatteryOptimizations();
|
||||
|
||||
/* Request VPN permission */
|
||||
Intent intent = VpnService.prepare(MainActivity.this);
|
||||
if (intent != null) {
|
||||
|
|
@ -244,109 +193,33 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
connectVpn();
|
||||
}
|
||||
|
||||
addToLog(getString(R.string.app_started));
|
||||
|
||||
sizeUpdateRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateLogSizeUI();
|
||||
sizeUpdateHandler.postDelayed(this, 10000);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void checkNotificationPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkTermuxPermission() {
|
||||
if (ContextCompat.checkSelfPermission(this, TERMUX_PERMISSION) != PackageManager.PERMISSION_GRANTED) {
|
||||
requestPermissionLauncher.launch(TERMUX_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
private void startLogSizeUpdates() {
|
||||
sizeUpdateHandler.removeCallbacks(sizeUpdateRunnable);
|
||||
sizeUpdateHandler.post(sizeUpdateRunnable);
|
||||
}
|
||||
|
||||
private void stopLogSizeUpdates() {
|
||||
sizeUpdateHandler.removeCallbacks(sizeUpdateRunnable);
|
||||
}
|
||||
|
||||
private void updateLogSizeUI() {
|
||||
if (logSizeText == null) return;
|
||||
File logFile = new File(getFilesDir(), "watchdog_heartbeat_log.txt");
|
||||
long size = logFile.exists() ? logFile.length() : 0;
|
||||
String sizeStr;
|
||||
if (size < 1024) {
|
||||
sizeStr = size + " B";
|
||||
} else if (size < 1024 * 1024) {
|
||||
sizeStr = String.format(Locale.getDefault(), "%.1f KB", size / 1024.0);
|
||||
} else {
|
||||
sizeStr = String.format(Locale.getDefault(), "%.2f MB", size / (1024.0 * 1024.0));
|
||||
}
|
||||
logSizeText.setText(getString(R.string.log_size_format, sizeStr));
|
||||
checkBatteryOptimizations();
|
||||
addToLog("Application Started");
|
||||
}
|
||||
|
||||
private void connectVpn() {
|
||||
Intent intent = new Intent(this, TProxyService.class);
|
||||
startService(intent.setAction(TProxyService.ACTION_CONNECT));
|
||||
addToLog(getString(R.string.vpn_permission_granted));
|
||||
addToLog("VPN Permission Granted. Connecting...");
|
||||
}
|
||||
|
||||
private void readBlackBoxLogs() {
|
||||
if (isReadingLogs) return;
|
||||
isReadingLogs = true;
|
||||
|
||||
if (logProgress != null) {
|
||||
logProgress.setVisibility(View.VISIBLE);
|
||||
File logFile = new File(getFilesDir(), "watchdog_heartbeat_log.txt");
|
||||
if (!logFile.exists()) {
|
||||
addToLog("--- No BlackBox file found ---");
|
||||
return;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
File logFile = new File(getFilesDir(), "watchdog_heartbeat_log.txt");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (!logFile.exists()) {
|
||||
sb.append(getString(R.string.no_blackbox_found)).append("\n");
|
||||
} else {
|
||||
sb.append(getString(R.string.loading_history)).append("\n");
|
||||
addToLog("--- Loading BlackBox Logs ---");
|
||||
try (BufferedReader br = new BufferedReader(new FileReader(logFile))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
addToLog("[FILE] " + line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
sb.append(getString(R.string.error_reading_history, e.getMessage())).append("\n");
|
||||
addToLog("Error reading BlackBox: " + e.getMessage());
|
||||
}
|
||||
sb.append(getString(R.string.end_of_history)).append("\n");
|
||||
}
|
||||
|
||||
final String result = sb.toString();
|
||||
|
||||
// Check rapid growth flag
|
||||
SharedPreferences internalPrefs = getSharedPreferences("IIAB_Internal", Context.MODE_PRIVATE);
|
||||
final boolean isRapid = internalPrefs.getBoolean(IIABWatchdog.PREF_RAPID_GROWTH, false);
|
||||
|
||||
runOnUiThread(() -> {
|
||||
if (connectionLog != null) {
|
||||
connectionLog.setText(result);
|
||||
scrollToBottom();
|
||||
}
|
||||
if (logProgress != null) {
|
||||
logProgress.setVisibility(View.GONE);
|
||||
}
|
||||
if (logWarning != null) {
|
||||
logWarning.setVisibility(isRapid ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
updateLogSizeUI();
|
||||
isReadingLogs = false;
|
||||
});
|
||||
}).start();
|
||||
addToLog("--- End of File ---");
|
||||
}
|
||||
|
||||
private void setVersionFooter() {
|
||||
|
|
@ -359,24 +232,15 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
stopLogSizeUpdates();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (getIntent() != null && getIntent().getBooleanExtra(VpnRecoveryReceiver.EXTRA_RECOVERY, false)) {
|
||||
addToLog(getString(R.string.recovery_pulse_received));
|
||||
addToLog("Recovery Pulse Received from System. Enforcing VPN...");
|
||||
Intent vpnIntent = new Intent(this, TProxyService.class);
|
||||
startService(vpnIntent.setAction(TProxyService.ACTION_CONNECT));
|
||||
setIntent(null);
|
||||
}
|
||||
if (connectionLog != null && connectionLog.getVisibility() == View.VISIBLE) {
|
||||
startLogSizeUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -390,14 +254,14 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
if (pm != null && !pm.isIgnoringBatteryOptimizations(getPackageName())) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.battery_opt_title)
|
||||
.setMessage(R.string.battery_opt_msg)
|
||||
.setPositiveButton(R.string.go_to_settings, (dialog, which) -> {
|
||||
.setTitle("Battery Optimization")
|
||||
.setMessage("For the Watchdog to work reliably, please disable battery optimizations for this app.")
|
||||
.setPositiveButton("Go to Settings", (dialog, which) -> {
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||
intent.setData(Uri.parse("package:" + getPackageName()));
|
||||
startActivity(intent);
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
|
@ -470,7 +334,6 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
stopLogSizeUpdates();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -480,54 +343,28 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
updateUI();
|
||||
} else if (view == button_apps) {
|
||||
startActivity(new Intent(this, AppListActivity.class));
|
||||
} else if (view.getId() == R.id.save) {
|
||||
} else if (view == button_save) {
|
||||
savePrefs();
|
||||
Toast.makeText(this, R.string.saved_toast, Toast.LENGTH_SHORT).show();
|
||||
addToLog(getString(R.string.settings_saved));
|
||||
Context context = getApplicationContext();
|
||||
Toast.makeText(context, "Saved", Toast.LENGTH_SHORT).show();
|
||||
addToLog("Settings Saved");
|
||||
} else if (view.getId() == R.id.control) {
|
||||
handleControlClick();
|
||||
} else if (view.getId() == R.id.watchdog_control) {
|
||||
handleWatchdogClick();
|
||||
} else if (view.getId() == R.id.btn_clear_log) {
|
||||
showResetLogConfirmation();
|
||||
connectionLog.setText("");
|
||||
addToLog("Log reset");
|
||||
} else if (view.getId() == R.id.btn_copy_log) {
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("IIAB Log", connectionLog.getText().toString());
|
||||
if (clipboard != null) {
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast.makeText(this, R.string.log_copied_toast, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(this, "Log copied to clipboard", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showResetLogConfirmation() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.log_reset_confirm_title)
|
||||
.setMessage(R.string.log_reset_confirm_msg)
|
||||
.setPositiveButton(R.string.reset_log, (dialog, which) -> resetLogFile())
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void resetLogFile() {
|
||||
File logFile = new File(getFilesDir(), "watchdog_heartbeat_log.txt");
|
||||
try (PrintWriter pw = new PrintWriter(logFile)) {
|
||||
pw.print(""); // Truncate file to 0 bytes
|
||||
connectionLog.setText("");
|
||||
addToLog(getString(R.string.log_reset_user));
|
||||
|
||||
// Clear rapid growth warning
|
||||
SharedPreferences internalPrefs = getSharedPreferences("IIAB_Internal", Context.MODE_PRIVATE);
|
||||
internalPrefs.edit().putBoolean(IIABWatchdog.PREF_RAPID_GROWTH, false).apply();
|
||||
if (logWarning != null) logWarning.setVisibility(View.GONE);
|
||||
|
||||
updateLogSizeUI();
|
||||
Toast.makeText(this, R.string.log_cleared_toast, Toast.LENGTH_SHORT).show();
|
||||
} catch (IOException e) {
|
||||
Toast.makeText(this, getString(R.string.failed_reset_log, e.getMessage()), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleWatchdogClick() {
|
||||
boolean isEnabled = prefs.getWatchdogEnable();
|
||||
if (isEnabled) {
|
||||
|
|
@ -542,14 +379,14 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
Intent intent = new Intent(this, WatchdogService.class);
|
||||
if (stop) {
|
||||
stopService(intent);
|
||||
addToLog(getString(R.string.watchdog_stopped));
|
||||
addToLog("Watchdog Stopping...");
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(intent.setAction(WatchdogService.ACTION_START));
|
||||
} else {
|
||||
startService(intent.setAction(WatchdogService.ACTION_START));
|
||||
}
|
||||
addToLog(getString(R.string.watchdog_started));
|
||||
addToLog("Watchdog Starting...");
|
||||
}
|
||||
updateUI();
|
||||
}
|
||||
|
|
@ -563,6 +400,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -571,7 +409,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
if (km != null && km.isDeviceSecure()) isSecure = true;
|
||||
|
||||
if (biometricManager.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS || isSecure) {
|
||||
addToLog(getString(R.string.user_initiated_conn));
|
||||
addToLog("User initiated connection");
|
||||
toggleService(false);
|
||||
} else {
|
||||
showEnrollmentDialog();
|
||||
|
|
@ -581,13 +419,13 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
|
||||
private void showEnrollmentDialog() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.security_required_title)
|
||||
.setMessage(R.string.security_required_msg)
|
||||
.setPositiveButton(R.string.go_to_settings, (dialog, which) -> {
|
||||
.setTitle("Security Required")
|
||||
.setMessage("You must set up a PIN, Pattern, or Fingerprint on your device before enabling the secure environment.")
|
||||
.setPositiveButton("Go to Settings", (dialog, which) -> {
|
||||
Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
|
||||
startActivity(intent);
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
|
|
@ -598,7 +436,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
@Override
|
||||
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
|
||||
super.onAuthenticationSucceeded(result);
|
||||
addToLog(getString(R.string.auth_success_disconnect));
|
||||
addToLog("Authentication Success. Disconnecting...");
|
||||
toggleService(true);
|
||||
}
|
||||
});
|
||||
|
|
@ -606,8 +444,8 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL;
|
||||
|
||||
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(getString(R.string.auth_required_title))
|
||||
.setSubtitle(getString(R.string.auth_required_subtitle))
|
||||
.setTitle("Authentication required")
|
||||
.setSubtitle("Authenticate to disable the secure environment")
|
||||
.setAllowedAuthenticators(authenticators)
|
||||
.build();
|
||||
|
||||
|
|
@ -627,8 +465,8 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL;
|
||||
|
||||
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(getString(R.string.unlock_watchdog_title))
|
||||
.setSubtitle(getString(R.string.unlock_watchdog_subtitle))
|
||||
.setTitle("Unlock Master Watchdog")
|
||||
.setSubtitle("Authentication required to stop Termux protection")
|
||||
.setAllowedAuthenticators(authenticators)
|
||||
.build();
|
||||
|
||||
|
|
@ -642,10 +480,10 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
Intent intent = new Intent(this, TProxyService.class);
|
||||
if (isEnable) {
|
||||
startService(intent.setAction(TProxyService.ACTION_DISCONNECT));
|
||||
addToLog(getString(R.string.vpn_stopping));
|
||||
addToLog("VPN Stopping...");
|
||||
} else {
|
||||
startService(intent.setAction(TProxyService.ACTION_CONNECT));
|
||||
addToLog(getString(R.string.vpn_starting));
|
||||
addToLog("VPN Starting...");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -710,16 +548,12 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
String logEntry = "[" + currentTime + "] " + message + "\n";
|
||||
if (connectionLog != null) {
|
||||
connectionLog.append(logEntry);
|
||||
scrollToBottom();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void scrollToBottom() {
|
||||
if (connectionLog.getLayout() != null) {
|
||||
final int scrollAmount = connectionLog.getLayout().getLineTop(connectionLog.getLineCount()) - connectionLog.getHeight();
|
||||
// Automatic scrolling to bottom
|
||||
final int scrollAmount = connectionLog.getLayout() != null ?
|
||||
connectionLog.getLayout().getLineTop(connectionLog.getLineCount()) - connectionLog.getHeight() : 0;
|
||||
if (scrollAmount > 0)
|
||||
connectionLog.scrollTo(0, scrollAmount);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
package org.iiab.controller;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class TermuxCallbackReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (IIABWatchdog.ACTION_TERMUX_OUTPUT.equals(intent.getAction())) {
|
||||
Bundle resultExtras = intent.getExtras();
|
||||
if (resultExtras != null) {
|
||||
int exitCode = resultExtras.getInt("exitCode", -1);
|
||||
String stdout = resultExtras.getString("stdout", "");
|
||||
String stderr = resultExtras.getString("stderr", "");
|
||||
|
||||
String logMsg;
|
||||
if (exitCode == 0) {
|
||||
logMsg = "[Termux] Stimulus OK (exit 0)";
|
||||
} else {
|
||||
logMsg = "[Termux] Pulse Error (exit " + exitCode + ")";
|
||||
if (!stderr.isEmpty()) {
|
||||
logMsg += ": " + stderr;
|
||||
}
|
||||
}
|
||||
|
||||
// Write to BlackBox log
|
||||
IIABWatchdog.writeToBlackBox(context, logMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/watchdog_description"
|
||||
android:text="Protects Termux from Doze mode and keeps Wi-Fi active."
|
||||
android:textSize="13sp"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
android:id="@+id/config_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/configuration_label"
|
||||
android:text="▶ Configuration"
|
||||
android:textStyle="bold"
|
||||
android:padding="12dp"
|
||||
android:background="?attr/sectionHeaderBackground"
|
||||
|
|
@ -154,7 +154,7 @@
|
|||
android:id="@+id/adv_config_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/advanced_settings_label"
|
||||
android:text="▶ Advanced Settings"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:padding="8dp"
|
||||
android:textSize="13sp"
|
||||
|
|
@ -207,7 +207,7 @@
|
|||
android:id="@+id/log_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/connection_log_label"
|
||||
android:text="▶ Connection Log"
|
||||
android:textStyle="bold"
|
||||
android:padding="10dp"
|
||||
android:background="?attr/sectionHeaderBackground"
|
||||
|
|
@ -215,27 +215,6 @@
|
|||
android:clickable="true"
|
||||
android:focusable="true"/>
|
||||
|
||||
<!-- Log Warnings (Growth rate warning) -->
|
||||
<TextView
|
||||
android:id="@+id/log_warning_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FF9800"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="italic"
|
||||
android:padding="4dp"
|
||||
android:visibility="gone"
|
||||
android:text="@string/log_warning_rapid_growth" />
|
||||
|
||||
<!-- Loading Indicator for Logs -->
|
||||
<ProgressBar
|
||||
android:id="@+id/log_progress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/connection_log"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -249,21 +228,9 @@
|
|||
android:fadeScrollbars="false"
|
||||
android:scrollbarSize="10dp"
|
||||
android:scrollbarThumbVertical="@drawable/scrollbar_thumb"
|
||||
android:text="@string/system_ready"
|
||||
android:text="System ready...\n"
|
||||
android:textSize="11sp"/>
|
||||
|
||||
<!-- Log Size Indicator -->
|
||||
<TextView
|
||||
android:id="@+id/log_size_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="10sp"
|
||||
android:paddingEnd="8dp"
|
||||
android:visibility="gone"
|
||||
android:text="Size: 0KB / 10MB" />
|
||||
|
||||
<!-- Log Actions Bar -->
|
||||
<LinearLayout
|
||||
android:id="@+id/log_actions"
|
||||
|
|
@ -278,7 +245,7 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/reset_log"
|
||||
android:text="Reset Log"
|
||||
android:textSize="12sp"
|
||||
android:backgroundTint="#D32F2F"
|
||||
android:textColor="#FFFFFF"
|
||||
|
|
@ -290,7 +257,7 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/copy_all"
|
||||
android:text="Copy All"
|
||||
android:textSize="12sp"
|
||||
android:backgroundTint="#388E3C"
|
||||
android:textColor="#FFFFFF"
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<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>
|
||||
<string name="socks_port">Puerto Socks:</string>
|
||||
<string name="socks_user">Usuario Socks:</string>
|
||||
<string name="socks_pass">Contraseña Socks:</string>
|
||||
<string name="dns_ipv4">DNS IPv4:</string>
|
||||
<string name="dns_ipv6">DNS IPv6:</string>
|
||||
<string name="udp_in_tcp">Relé UDP sobre TCP</string>
|
||||
<string name="remote_dns">DNS Remoto</string>
|
||||
<string name="ipv4">IPv4</string>
|
||||
<string name="ipv6">IPv6</string>
|
||||
<string name="global">Global</string>
|
||||
<string name="apps">Aplicaciones</string>
|
||||
<string name="save">Guardar</string>
|
||||
<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="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>
|
||||
|
||||
<!-- New strings for translatability -->
|
||||
<string name="watchdog_description">Protege Termux del modo Doze y mantiene el Wi-Fi activo.</string>
|
||||
<string name="reset_log">Reiniciar Log</string>
|
||||
<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="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>
|
||||
<string name="loading_history">--- Cargando Historial ---</string>
|
||||
<string name="error_reading_history">Error al leer el historial: %s</string>
|
||||
<string name="end_of_history">--- Fin del Historial ---</string>
|
||||
<string name="recovery_pulse_received">Pulso de recuperación recibido del sistema. Forzando VPN...</string>
|
||||
<string name="battery_opt_title">Optimización de Batería</string>
|
||||
<string name="battery_opt_msg">Para que el Watchdog funcione de manera confiable, desactive las optimizaciones de batería para esta aplicación.</string>
|
||||
<string name="go_to_settings">Ir a Ajustes</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="saved_toast">Guardado</string>
|
||||
<string name="settings_saved">Ajustes Guardados</string>
|
||||
<string name="log_reset_log">Log reiniciado</string>
|
||||
<string name="log_reset_user">Log reiniciado por el usuario</string>
|
||||
<string name="log_copied_toast">Log copiado al portapapeles</string>
|
||||
<string name="watchdog_stopped">Watchdog Detenido</string>
|
||||
<string name="watchdog_started">Watchdog Iniciado</string>
|
||||
<string name="vpn_stopping">Deteniendo VPN...</string>
|
||||
<string name="vpn_starting">Iniciando VPN...</string>
|
||||
<string name="log_cleared_toast">Log borrado</string>
|
||||
<string name="failed_reset_log">Error al reiniciar log: %s</string>
|
||||
<string name="unlock_watchdog_title">Desbloquear Watchdog Maestro</string>
|
||||
<string name="unlock_watchdog_subtitle">Se requiere autenticación para detener la protección de Termux</string>
|
||||
<string name="auth_success_disconnect">Autenticación exitosa. Desconectando...</string>
|
||||
<string name="auth_required_title">Autenticación requerida</string>
|
||||
<string name="auth_required_subtitle">Autentíquese para desactivar el entorno seguro</string>
|
||||
<string name="security_required_title">Seguridad Requerida</string>
|
||||
<string name="security_required_msg">Debe configurar un PIN, Patrón o Huella digital en su dispositivo antes de activar el entorno seguro.</string>
|
||||
<string name="user_initiated_conn">Conexión iniciada por el usuario</string>
|
||||
<string name="vpn_permission_granted">Permiso de VPN concedido. Conectando...</string>
|
||||
|
||||
<!-- IIABWatchdog strings -->
|
||||
<string name="pulse_stimulating">Pulso: Estimulando Termux...</string>
|
||||
<string name="critical_os_blocked">CRÍTICO: El SO bloqueó el estímulo a Termux (SecurityException).</string>
|
||||
<string name="ping_ok">PING 8085: OK</string>
|
||||
<string name="ping_fail">PING 8085: ERROR (%s)</string>
|
||||
<string name="session_started">SESIÓN DE LATIDO INICIADA</string>
|
||||
<string name="session_stopped">SESIÓN DE LATIDO DETENIDA</string>
|
||||
|
||||
<!-- TermuxCallbackReceiver strings -->
|
||||
<string name="termux_stimulus_ok">[Termux] Estímulo OK (exit 0)</string>
|
||||
<string name="termux_pulse_error">[Termux] Error de pulso (exit %1$d): %2$s</string>
|
||||
|
||||
<string name="log_size_format">Tamaño: %1$s / 10MB</string>
|
||||
</resources>
|
||||
|
|
@ -20,60 +20,4 @@
|
|||
<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="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">Advanced 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>
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Reference in New Issue