[controller] agregar traducciones mecánicas; interconectar status; completar el dashboard
This commit is contained in:
parent
4856fcc7c5
commit
1f3eb13444
|
|
@ -9,8 +9,8 @@ android {
|
|||
applicationId "org.iiab.controller"
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 34
|
||||
versionCode 28
|
||||
versionName "v0.1.32beta"
|
||||
versionCode 31
|
||||
versionName "v0.2.1beta"
|
||||
setProperty("archivesBaseName", "$applicationId-$versionName")
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@
|
|||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 28,
|
||||
"versionName": "v0.1.32beta",
|
||||
"outputFile": "org.iiab.controller-v0.1.32beta-release.apk"
|
||||
"versionCode": 29,
|
||||
"versionName": "v0.1.33beta",
|
||||
"outputFile": "org.iiab.controller-v0.1.33beta-release.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File",
|
||||
|
|
@ -22,14 +22,14 @@
|
|||
"minApi": 28,
|
||||
"maxApi": 30,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/1/org.iiab.controller-v0.1.32beta-release.dm"
|
||||
"baselineProfiles/1/org.iiab.controller-v0.1.33beta-release.dm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"minApi": 31,
|
||||
"maxApi": 2147483647,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/0/org.iiab.controller-v0.1.32beta-release.dm"
|
||||
"baselineProfiles/0/org.iiab.controller-v0.1.33beta-release.dm"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
<!-- Android 11+ Package Visibility -->
|
||||
<queries>
|
||||
<package android:name="com.termux" />
|
||||
<package android:name="com.termux.api" />
|
||||
</queries>
|
||||
|
||||
<application android:label="@string/app_name"
|
||||
|
|
@ -62,7 +63,8 @@
|
|||
|
||||
<activity android:name=".MainActivity" android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:exported="true">
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden|screenLayout">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
|
@ -93,4 +95,7 @@
|
|||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
|
||||
android:minSdkVersion="34" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
============================================================================
|
||||
Name : AppListActivity.java
|
||||
Author : hev <r@hev.cc>
|
||||
Contributors: IIAB Project
|
||||
Copyright : Copyright (c) 2025 xyz
|
||||
Copyright (c) 2026 IIAB Project
|
||||
Description : App List Activity
|
||||
============================================================================
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : BatteryUtils.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Manage battery permissions
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.app.Activity;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : BiometricHelper.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Biometrics helper
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.content.Context;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,498 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : DashboardFragment.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Initial dasboard status activity
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.File;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class DashboardFragment extends Fragment {
|
||||
|
||||
private TextView txtDeviceName;
|
||||
private TextView txtWifiIp, txtHotspotIp, txtUptime, txtBattery, badgeStatus, txtStorage, txtRam, txtSwap, txtTermuxState;
|
||||
private ProgressBar progStorage, progRam, progSwap;
|
||||
private View ledTermuxState;
|
||||
private LinearLayout modulesContainer;
|
||||
|
||||
private final Handler refreshHandler = new Handler(Looper.getMainLooper());
|
||||
private Runnable refreshRunnable;
|
||||
|
||||
// List of modules to scan (Endpoint, Display Name)
|
||||
private final Object[][] TARGET_MODULES = {
|
||||
{"books", R.string.dash_books},
|
||||
{"kiwix", R.string.dash_kiwix},
|
||||
{"kolibri", R.string.dash_kolibri},
|
||||
{"maps", R.string.dash_maps},
|
||||
{"matomo", R.string.dash_matomo},
|
||||
{"dashboard", R.string.dash_system}
|
||||
};
|
||||
|
||||
public enum SystemState {
|
||||
ONLINE, OFFLINE, DEBIAN_ONLY, INSTALLER, TERMUX_ONLY, NONE
|
||||
}
|
||||
|
||||
private SystemState currentSystemState = SystemState.NONE;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_dashboard, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// Bindings
|
||||
txtDeviceName = view.findViewById(R.id.dash_text_device_name);
|
||||
txtWifiIp = view.findViewById(R.id.dash_text_wifi_ip);
|
||||
txtHotspotIp = view.findViewById(R.id.dash_text_hotspot_ip);
|
||||
txtUptime = view.findViewById(R.id.dash_text_uptime);
|
||||
txtBattery = view.findViewById(R.id.dash_text_battery);
|
||||
badgeStatus = view.findViewById(R.id.dash_badge_status);
|
||||
|
||||
txtStorage = view.findViewById(R.id.dash_text_storage);
|
||||
txtRam = view.findViewById(R.id.dash_text_ram);
|
||||
txtSwap = view.findViewById(R.id.dash_text_swap);
|
||||
progStorage = view.findViewById(R.id.dash_progress_storage);
|
||||
progRam = view.findViewById(R.id.dash_progress_ram);
|
||||
progSwap = view.findViewById(R.id.dash_progress_swap);
|
||||
|
||||
ledTermuxState = view.findViewById(R.id.led_termux_state);
|
||||
txtTermuxState = view.findViewById(R.id.text_termux_state);
|
||||
modulesContainer = view.findViewById(R.id.modules_container);
|
||||
|
||||
// Generate module views dynamically
|
||||
createModuleViews();
|
||||
|
||||
// Configure refresh timer (every 5 seconds)
|
||||
refreshRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateSystemStats();
|
||||
checkServerAndModules();
|
||||
refreshHandler.postDelayed(this, 5000);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
refreshHandler.post(refreshRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
refreshHandler.removeCallbacks(refreshRunnable);
|
||||
}
|
||||
|
||||
private void updateSystemStats() {
|
||||
txtDeviceName.setText(getDeviceName());
|
||||
|
||||
// --- 0. CALCULATE SERVER UPTIME ---
|
||||
long uptimeMillis = android.os.SystemClock.elapsedRealtime();
|
||||
long minutes = (uptimeMillis / (1000 * 60)) % 60;
|
||||
long hours = (uptimeMillis / (1000 * 60 * 60)) % 24;
|
||||
long days = (uptimeMillis / (1000 * 60 * 60 * 24));
|
||||
|
||||
// Format: "Uptime: 2d 14h 05m" (Omit days if 0)
|
||||
String timeStr = (days > 0) ?
|
||||
String.format(Locale.US, "%dd %02dh %02dm", days, hours, minutes) :
|
||||
String.format(Locale.US, "%02dh %02dm", hours, minutes);
|
||||
|
||||
txtUptime.setText(Html.fromHtml(getString(R.string.dash_uptime_format, timeStr), Html.FROM_HTML_MODE_LEGACY));
|
||||
|
||||
txtWifiIp.setText(Html.fromHtml(getString(R.string.dash_wifi_format, getWifiIp()), Html.FROM_HTML_MODE_LEGACY));
|
||||
txtHotspotIp.setText(Html.fromHtml(getString(R.string.dash_hotspot_format, getHotspotIp()), Html.FROM_HTML_MODE_LEGACY));
|
||||
|
||||
int batteryLevel = getBatteryPercentage();
|
||||
if (batteryLevel >= 0) {
|
||||
txtBattery.setText(Html.fromHtml(getString(R.string.dash_battery_format, batteryLevel), Html.FROM_HTML_MODE_LEGACY));
|
||||
} else {
|
||||
txtBattery.setText(Html.fromHtml(getString(R.string.dash_battery_no_value), Html.FROM_HTML_MODE_LEGACY));
|
||||
}
|
||||
|
||||
// --- 1. GET REAL RAM AND SWAP FROM LINUX ---
|
||||
long memTotal = 0, memAvailable = 0, swapTotal = 0, swapFree = 0;
|
||||
try (BufferedReader br = new BufferedReader(new FileReader("/proc/meminfo"))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.startsWith("MemTotal:")) memTotal = parseMemLine(line);
|
||||
else if (line.startsWith("MemAvailable:")) memAvailable = parseMemLine(line);
|
||||
// If phone is old and doesn't have "MemAvailable", use "MemFree"
|
||||
else if (memAvailable == 0 && line.startsWith("MemFree:")) memAvailable = parseMemLine(line);
|
||||
else if (line.startsWith("SwapTotal:")) swapTotal = parseMemLine(line);
|
||||
else if (line.startsWith("SwapFree:")) swapFree = parseMemLine(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Convert the values from kB to GB (1 GB = 1048576 kB)
|
||||
double memTotalGb = memTotal / 1048576.0;
|
||||
double memUsedGb = (memTotal - memAvailable) / 1048576.0;
|
||||
int memProgress = memTotal > 0 ? (int) (((memTotal - memAvailable) * 100) / memTotal) : 0;
|
||||
|
||||
double swapTotalGb = swapTotal / 1048576.0;
|
||||
double swapUsedGb = (swapTotal - swapFree) / 1048576.0;
|
||||
int swapProgress = swapTotal > 0 ? (int) (((swapTotal - swapFree) * 100) / swapTotal) : 0;
|
||||
|
||||
// --- UPDATE UI (TEXT AND BARS) ---
|
||||
txtRam.setText(String.format(Locale.US, "%.2f GB / %.2f GB", memUsedGb, memTotalGb));
|
||||
progRam.setProgress(memProgress);
|
||||
|
||||
if (swapTotal > 0) {
|
||||
txtSwap.setText(String.format(Locale.US, "%.2f GB / %.2f GB", swapUsedGb, swapTotalGb));
|
||||
progSwap.setProgress(swapProgress);
|
||||
} else {
|
||||
// If the device does not use Swap
|
||||
txtSwap.setText("-- / --");
|
||||
progSwap.setProgress(0);
|
||||
}
|
||||
|
||||
// 2. Get Internal Storage
|
||||
File path = android.os.Environment.getDataDirectory();
|
||||
long totalSpace = path.getTotalSpace() / (1024 * 1024 * 1024); // To GB
|
||||
long freeSpace = path.getFreeSpace() / (1024 * 1024 * 1024);
|
||||
long usedSpace = totalSpace - freeSpace;
|
||||
|
||||
txtStorage.setText(usedSpace + " GB / " + totalSpace + " GB");
|
||||
progStorage.setProgress(totalSpace > 0 ? (int) ((usedSpace * 100) / totalSpace) : 0);
|
||||
}
|
||||
|
||||
private void createModuleViews() {
|
||||
modulesContainer.removeAllViews();
|
||||
|
||||
// Set 3 lines
|
||||
for (int row = 0; row < 2; row++) {
|
||||
LinearLayout rowLayout = new LinearLayout(requireContext());
|
||||
rowLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
rowLayout.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
rowLayout.setBaselineAligned(false);
|
||||
rowLayout.setWeightSum(3f);
|
||||
rowLayout.setPadding(0, 0, 0, 16);
|
||||
|
||||
// Create 3 columns per row
|
||||
for (int col = 0; col < 3; col++) {
|
||||
int index = (row * 3) + col;
|
||||
if (index >= TARGET_MODULES.length) break;
|
||||
|
||||
// Grid
|
||||
LinearLayout cell = new LinearLayout(requireContext());
|
||||
cell.setOrientation(LinearLayout.HORIZONTAL);
|
||||
cell.setBackgroundResource(R.drawable.rounded_button);
|
||||
cell.setBackgroundTintList(android.content.res.ColorStateList.valueOf(
|
||||
androidx.core.content.ContextCompat.getColor(requireContext(), R.color.dash_module_bg)));
|
||||
cell.setPadding(16, 24, 16, 24);
|
||||
cell.setGravity(android.view.Gravity.CENTER);
|
||||
|
||||
LinearLayout.LayoutParams cellParams = new LinearLayout.LayoutParams(
|
||||
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f);
|
||||
|
||||
// Leave small margins between the cards so they don't stick together.
|
||||
int margin = 8;
|
||||
if (col == 0) cellParams.setMargins(0, 0, margin, 0); // Left
|
||||
else if (col == 1) cellParams.setMargins(margin/2, 0, margin/2, 0); // Center
|
||||
else cellParams.setMargins(margin, 0, 0, 0); // Right
|
||||
|
||||
cell.setLayoutParams(cellParams);
|
||||
|
||||
// Small LED
|
||||
View led = new View(requireContext());
|
||||
led.setLayoutParams(new LinearLayout.LayoutParams(20, 20));
|
||||
led.setBackgroundResource(R.drawable.led_off);
|
||||
led.setId(View.generateViewId());
|
||||
|
||||
// Module name
|
||||
TextView name = new TextView(requireContext());
|
||||
LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
textParams.setMargins(12, 0, 0, 0);
|
||||
name.setLayoutParams(textParams);
|
||||
name.setText(getString((Integer) TARGET_MODULES[index][1]));
|
||||
name.setTextColor(androidx.core.content.ContextCompat.getColor(requireContext(), R.color.dash_module_text));
|
||||
name.setTextSize(11f);
|
||||
name.setSingleLine(true);
|
||||
|
||||
cell.addView(led);
|
||||
cell.addView(name);
|
||||
cell.setTag(TARGET_MODULES[index][0]);
|
||||
|
||||
rowLayout.addView(cell);
|
||||
}
|
||||
modulesContainer.addView(rowLayout);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkServerAndModules() {
|
||||
new Thread(() -> {
|
||||
// 1. Ping the network once
|
||||
boolean isMainServerAlive = pingUrl("http://localhost:8085/home");
|
||||
|
||||
// 2. Ask the State Machine for the definitive truth
|
||||
currentSystemState = evaluateSystemState(isMainServerAlive);
|
||||
|
||||
// 3. Update the UI on the main thread
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
|
||||
// Configure the Top Traffic Light (Server Status)
|
||||
if (currentSystemState == SystemState.ONLINE) {
|
||||
badgeStatus.setText(R.string.dash_online);
|
||||
badgeStatus.setBackgroundTintList(android.content.res.ColorStateList.valueOf(
|
||||
androidx.core.content.ContextCompat.getColor(requireContext(), R.color.dash_status_online)));
|
||||
} else {
|
||||
badgeStatus.setText(R.string.dash_offline);
|
||||
badgeStatus.setBackgroundTintList(android.content.res.ColorStateList.valueOf(
|
||||
androidx.core.content.ContextCompat.getColor(requireContext(), R.color.dash_text_secondary)));
|
||||
}
|
||||
|
||||
// Configure the Bottom LED and Suggestion Message
|
||||
switch (currentSystemState) {
|
||||
case ONLINE:
|
||||
ledTermuxState.setBackgroundResource(R.drawable.led_on_green);
|
||||
txtTermuxState.setText(getString(R.string.dash_state_online));
|
||||
txtTermuxState.setTextColor(androidx.core.content.ContextCompat.getColor(requireContext(), R.color.dash_text_primary));
|
||||
break;
|
||||
case OFFLINE:
|
||||
ledTermuxState.setBackgroundResource(R.drawable.led_off);
|
||||
txtTermuxState.setText(getString(R.string.dash_state_offline));
|
||||
txtTermuxState.setTextColor(androidx.core.content.ContextCompat.getColor(requireContext(), R.color.dash_text_secondary));
|
||||
break;
|
||||
case DEBIAN_ONLY:
|
||||
ledTermuxState.setBackgroundResource(R.drawable.led_off);
|
||||
txtTermuxState.setText(getString(R.string.dash_state_debian_only));
|
||||
txtTermuxState.setTextColor(androidx.core.content.ContextCompat.getColor(requireContext(), R.color.dash_text_primary));
|
||||
break;
|
||||
case INSTALLER:
|
||||
ledTermuxState.setBackgroundResource(R.drawable.led_off);
|
||||
txtTermuxState.setText(getString(R.string.dash_state_installer));
|
||||
txtTermuxState.setTextColor(androidx.core.content.ContextCompat.getColor(requireContext(), R.color.dash_text_primary));
|
||||
break;
|
||||
case TERMUX_ONLY:
|
||||
ledTermuxState.setBackgroundResource(R.drawable.led_off);
|
||||
txtTermuxState.setText(getString(R.string.dash_state_termux_only));
|
||||
txtTermuxState.setTextColor(androidx.core.content.ContextCompat.getColor(requireContext(), R.color.dash_warning));
|
||||
break;
|
||||
case NONE:
|
||||
ledTermuxState.setBackgroundResource(R.drawable.led_off);
|
||||
txtTermuxState.setText(getString(R.string.dash_state_none));
|
||||
txtTermuxState.setTextColor(androidx.core.content.ContextCompat.getColor(requireContext(), R.color.dash_warning));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// 4. Scan individual modules (Only if the system is ONLINE)
|
||||
for (int r = 0; r < modulesContainer.getChildCount(); r++) {
|
||||
LinearLayout row = (LinearLayout) modulesContainer.getChildAt(r);
|
||||
|
||||
for (int c = 0; c < row.getChildCount(); c++) {
|
||||
LinearLayout card = (LinearLayout) row.getChildAt(c);
|
||||
String endpoint = (String) card.getTag();
|
||||
if (endpoint == null) continue;
|
||||
|
||||
View led = card.getChildAt(0);
|
||||
|
||||
// Module ON = (System is ONLINE) AND (URL responds)
|
||||
boolean isModuleAlive = (currentSystemState == SystemState.ONLINE) && pingUrl("http://localhost:8085/" + endpoint);
|
||||
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
led.setBackgroundResource(isModuleAlive ? R.drawable.led_on_green : R.drawable.led_off);
|
||||
});
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private boolean pingUrl(String urlStr) {
|
||||
try {
|
||||
URL url = new URL(urlStr);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setUseCaches(false);
|
||||
conn.setConnectTimeout(1500);
|
||||
conn.setReadTimeout(1500);
|
||||
conn.setRequestMethod("GET");
|
||||
return (conn.getResponseCode() >= 200 && conn.getResponseCode() < 400);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts the numbers (in kB) from the lines of /proc/meminfo
|
||||
private long parseMemLine(String line) {
|
||||
try {
|
||||
String[] parts = line.split("\\s+");
|
||||
return Long.parseLong(parts[1]);
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// --- METHODS FOR OBTAINING IPs ---
|
||||
private String getWifiIp() {
|
||||
return getIpByInterface("wlan0");
|
||||
}
|
||||
|
||||
private String getHotspotIp() {
|
||||
String[] hotspotInterfaces = {"ap0", "wlan1", "swlan0"};
|
||||
for (String iface : hotspotInterfaces) {
|
||||
String ip = getIpByInterface(iface);
|
||||
if (!ip.equals("--")) return ip;
|
||||
}
|
||||
return "--";
|
||||
}
|
||||
|
||||
private String getIpByInterface(String interfaceName) {
|
||||
try {
|
||||
List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
|
||||
for (NetworkInterface intf : interfaces) {
|
||||
if (intf.getName().equalsIgnoreCase(interfaceName)) {
|
||||
List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
|
||||
for (InetAddress addr : addrs) {
|
||||
if (!addr.isLoopbackAddress() && addr instanceof Inet4Address) {
|
||||
return addr.getHostAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "--";
|
||||
}
|
||||
|
||||
private int getBatteryPercentage() {
|
||||
try {
|
||||
IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
|
||||
Intent batteryStatus = requireContext().registerReceiver(null, iFilter);
|
||||
if (batteryStatus != null) {
|
||||
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
|
||||
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
|
||||
return (int) ((level / (float) scale) * 100);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// --- METHODS FOR OBTAINING THE DEVICE NAME ---
|
||||
private String getDeviceName() {
|
||||
String manufacturer = android.os.Build.MANUFACTURER;
|
||||
String model = android.os.Build.MODEL;
|
||||
|
||||
if (model.toLowerCase().startsWith(manufacturer.toLowerCase())) {
|
||||
return capitalize(model);
|
||||
} else {
|
||||
return capitalize(manufacturer) + " " + model;
|
||||
}
|
||||
}
|
||||
|
||||
private String capitalize(String s) {
|
||||
if (s == null || s.length() == 0) return "";
|
||||
char first = s.charAt(0);
|
||||
if (Character.isUpperCase(first)) {
|
||||
return s;
|
||||
} else {
|
||||
return Character.toUpperCase(first) + s.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
// The 5 possible system states
|
||||
// --- MASTER STATE EVALUATOR ---
|
||||
private SystemState evaluateSystemState(boolean isNginxAlive) {
|
||||
// 1. Does the Nginx server respond? (The network doesn't lie)
|
||||
if (isNginxAlive) {
|
||||
return SystemState.ONLINE;
|
||||
}
|
||||
|
||||
// 2. Does Termux physically exist on the Android device?
|
||||
boolean isTermuxInstalled = false;
|
||||
try {
|
||||
requireContext().getPackageManager().getPackageInfo("com.termux", 0);
|
||||
isTermuxInstalled = true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
isTermuxInstalled = false;
|
||||
}
|
||||
|
||||
File stateDir = new File(Environment.getExternalStorageDirectory(), ".iiab_state");
|
||||
|
||||
// Ghost Handling: If Termux is uninstalled, but garbage remains, delete it.
|
||||
if (!isTermuxInstalled) {
|
||||
if (stateDir.exists()) {
|
||||
deleteRecursive(stateDir);
|
||||
}
|
||||
return SystemState.NONE;
|
||||
}
|
||||
|
||||
// 3. Is IIAB fully compiled/restored and ready?
|
||||
File flagIiabReady = new File(stateDir, "flag_iiab_ready");
|
||||
if (flagIiabReady.exists()) {
|
||||
return SystemState.OFFLINE; // The real offline state
|
||||
}
|
||||
|
||||
// 4. Is the base Debian OS installed, but NO IIAB yet? (The Virgin Debian Trap)
|
||||
File flagSystem = new File(stateDir, "flag_system_installed");
|
||||
if (flagSystem.exists()) {
|
||||
return SystemState.DEBIAN_ONLY;
|
||||
}
|
||||
|
||||
// 5. Is only the installer ready?
|
||||
File flagInstaller = new File(stateDir, "flag_installer_present");
|
||||
if (flagInstaller.exists()) {
|
||||
return SystemState.INSTALLER;
|
||||
}
|
||||
|
||||
// 6. Only the raw base app is present.
|
||||
return SystemState.TERMUX_ONLY;
|
||||
}
|
||||
|
||||
// Helper method to recursively delete the .iiab_state folder if Termux was uninstalled
|
||||
private void deleteRecursive(File fileOrDirectory) {
|
||||
if (fileOrDirectory.isDirectory()) {
|
||||
File[] children = fileOrDirectory.listFiles();
|
||||
if (children != null) {
|
||||
for (File child : children) {
|
||||
deleteRecursive(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
fileOrDirectory.delete();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : DashboardManager.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Initial dasboard status helper
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.app.Activity;
|
||||
|
|
@ -19,7 +27,11 @@ public class DashboardManager {
|
|||
private final View standaloneEspwButton;
|
||||
private final View standaloneEspwDescription;
|
||||
|
||||
// We pass a Callback so the Dashboard can tell MainActivity to start/stop the VPN
|
||||
// Memory variables to avoid freezing the screen
|
||||
private boolean lastTunnelState = false;
|
||||
private boolean lastDegradedState = false;
|
||||
private boolean isFirstRun = true;
|
||||
|
||||
public interface DashboardActionCallback {
|
||||
void onToggleEspwRequested();
|
||||
}
|
||||
|
|
@ -44,7 +56,7 @@ public class DashboardManager {
|
|||
}
|
||||
|
||||
private void setupListeners(DashboardActionCallback callback) {
|
||||
// Single tap opens Settings directly (No wrench icons needed!)
|
||||
// Single tap opens Settings directly
|
||||
dashWifi.setOnClickListener(v -> activity.startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS)));
|
||||
|
||||
dashHotspot.setOnClickListener(v -> {
|
||||
|
|
@ -71,6 +83,15 @@ public class DashboardManager {
|
|||
|
||||
// The Magic Morphing Animation!
|
||||
public void setTunnelState(boolean isTunnelActive, boolean isDegraded) {
|
||||
// ANTI-FREEZE SHIELD!
|
||||
// If the state is exactly the same as 3 seconds ago, abort to avoid blocking the UI
|
||||
if (!isFirstRun && lastTunnelState == isTunnelActive && lastDegradedState == isDegraded) {
|
||||
return;
|
||||
}
|
||||
isFirstRun = false;
|
||||
lastTunnelState = isTunnelActive;
|
||||
lastDegradedState = isDegraded;
|
||||
|
||||
// Tells Android to smoothly animate any layout changes we make next
|
||||
TransitionManager.beginDelayedTransition((ViewGroup) dashboardContainer.getParent(), new AutoTransition().setDuration(300));
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : DeployFragment.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Installation / deployment view
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
public class DeployFragment extends Fragment {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_deploy, container, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : IIABWatchdog.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Watchdog activity
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : LogManager.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Watchdog log manager
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.content.Context;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
============================================================================
|
||||
Name : MainActivity.java
|
||||
Author : hev <r@hev.cc>
|
||||
Contributors: IIAB Project
|
||||
Copyright : Copyright (c) 2025 hev
|
||||
Copyright (c) 2026 IIAB Project
|
||||
Copyright : Copyright (c) 2023 xyz
|
||||
Description : Main Activity
|
||||
============================================================================
|
||||
|
|
@ -27,6 +30,7 @@ import android.content.ComponentName;
|
|||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.graphics.Color;
|
||||
|
|
@ -55,6 +59,9 @@ import androidx.biometric.BiometricManager;
|
|||
import androidx.biometric.BiometricPrompt;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
|
|
@ -76,50 +83,25 @@ import java.net.InetSocketAddress;
|
|||
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;
|
||||
private EditText edittext_socks_port;
|
||||
private EditText edittext_socks_user;
|
||||
private EditText edittext_socks_pass;
|
||||
private EditText edittext_dns_ipv4;
|
||||
private EditText edittext_dns_ipv6;
|
||||
private CheckBox checkbox_udp_in_tcp;
|
||||
private CheckBox checkbox_remote_dns;
|
||||
private CheckBox checkbox_global;
|
||||
private CheckBox checkbox_maintenance;
|
||||
private TextView textview_maintenance_warning;
|
||||
private CheckBox checkbox_ipv4;
|
||||
private CheckBox checkbox_ipv6;
|
||||
private Button button_apps;
|
||||
private Button button_save;
|
||||
private Button button_control;
|
||||
private Button button_browse_content;
|
||||
private Button watchdogControl;
|
||||
private TextView connectionLog;
|
||||
private LinearLayout logActions;
|
||||
private LinearLayout configLayout;
|
||||
private TextView configLabel;
|
||||
private LinearLayout advancedConfig;
|
||||
private TextView advConfigLabel;
|
||||
private TextView logLabel;
|
||||
private TextView logWarning;
|
||||
private TextView logSizeText;
|
||||
public Preferences prefs;
|
||||
private ImageButton themeToggle;
|
||||
private ImageButton btnSettings;
|
||||
private TextView versionFooter;
|
||||
private ProgressBar logProgress;
|
||||
private android.widget.ImageView headerIcon;
|
||||
|
||||
// Cassette Deck UI
|
||||
private LinearLayout deckContainer;
|
||||
private ProgressButton btnServerControl;
|
||||
private ObjectAnimator fusionAnimator;
|
||||
private android.animation.ObjectAnimator exploreAnimator;
|
||||
private boolean isServerAlive = false;
|
||||
private boolean isNegotiating = false;
|
||||
private boolean isProxyDegraded = false;
|
||||
private Boolean targetServerState = null;
|
||||
private String serverTransitionText = "";
|
||||
// Tabs UI
|
||||
private TabLayout tabLayout;
|
||||
private ViewPager2 viewPager;
|
||||
private TextView versionFooter;
|
||||
public boolean isServerAlive = false;
|
||||
public boolean isNegotiating = false;
|
||||
public boolean isProxyDegraded = false;
|
||||
public Boolean targetServerState = null;
|
||||
public String serverTransitionText = "";
|
||||
public UsageFragment usageFragment;
|
||||
|
||||
public void setUsageFragment(UsageFragment fragment) {
|
||||
this.usageFragment = fragment;
|
||||
}
|
||||
private final Handler timeoutHandler = new Handler(android.os.Looper.getMainLooper());
|
||||
private Runnable timeoutRunnable;
|
||||
private boolean isWifiActive = false;
|
||||
|
|
@ -127,13 +109,11 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
private String currentTargetUrl = null;
|
||||
private long pulseStartTime = 0;
|
||||
|
||||
private DashboardManager dashboardManager;
|
||||
|
||||
private ActivityResultLauncher<Intent> vpnPermissionLauncher;
|
||||
private ActivityResultLauncher<String[]> requestPermissionsLauncher;
|
||||
private ActivityResultLauncher<Intent> batteryOptLauncher;
|
||||
|
||||
private boolean isReadingLogs = false;
|
||||
public boolean isReadingLogs = false;
|
||||
private final Handler sizeUpdateHandler = new Handler();
|
||||
private Runnable sizeUpdateRunnable;
|
||||
|
||||
|
|
@ -150,10 +130,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
if (IIABWatchdog.ACTION_LOG_MESSAGE.equals(action)) {
|
||||
String message = intent.getStringExtra(IIABWatchdog.EXTRA_MESSAGE);
|
||||
addToLog(message);
|
||||
updateLogSizeUI();
|
||||
if (usageFragment != null) usageFragment.updateLogSizeUI();
|
||||
}
|
||||
else if (WatchdogService.ACTION_STATE_STARTED.equals(action)) {
|
||||
// Calculate where we are in the 1200ms cycle (600ms down + 600ms up)
|
||||
long elapsed = System.currentTimeMillis() - pulseStartTime;
|
||||
long fullCycle = 1200;
|
||||
|
||||
|
|
@ -169,13 +148,13 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
|
||||
// Wait exactly until the wave hits 1.0f alpha, then lock it!
|
||||
new Handler(android.os.Looper.getMainLooper()).postDelayed(() -> {
|
||||
finalizeEntryPulse();
|
||||
if (usageFragment != null) usageFragment.finalizeEntryPulse();
|
||||
}, timeToNextCycleEnd);
|
||||
}
|
||||
else if (WatchdogService.ACTION_STATE_STOPPED.equals(action)) {
|
||||
// Service is down! Give it a 1.5 second visual margin, then stop the exit pulse.
|
||||
new Handler(android.os.Looper.getMainLooper()).postDelayed(() -> {
|
||||
finalizeExitPulse();
|
||||
if (usageFragment != null) usageFragment.finalizeExitPulse();
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
|
|
@ -186,8 +165,8 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Intercept launch and redirect to Setup Wizard if first time
|
||||
SharedPreferences internalPrefs = getSharedPreferences("IIAB_Internal", Context.MODE_PRIVATE);
|
||||
if (!internalPrefs.getBoolean("setup_complete", false)) {
|
||||
SharedPreferences internalPrefs = getSharedPreferences(getString(R.string.pref_file_internal), Context.MODE_PRIVATE);
|
||||
if (!internalPrefs.getBoolean(getString(R.string.pref_key_setup_complete), false)) {
|
||||
startActivity(new Intent(this, SetupActivity.class));
|
||||
finish();
|
||||
return;
|
||||
|
|
@ -196,6 +175,24 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
prefs = new Preferences(this);
|
||||
setContentView(R.layout.main);
|
||||
|
||||
// --- START TABS & VIEWPAGER ---
|
||||
tabLayout = findViewById(R.id.tab_layout);
|
||||
viewPager = findViewById(R.id.view_pager);
|
||||
|
||||
MainPagerAdapter pagerAdapter = new MainPagerAdapter(this);
|
||||
viewPager.setAdapter(pagerAdapter);
|
||||
|
||||
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
|
||||
switch (position) {
|
||||
case 0: tab.setText(R.string.tab_status); break;
|
||||
case 1: tab.setText(R.string.tab_usage); break;
|
||||
case 2: tab.setText(R.string.tab_deploy); break;
|
||||
}
|
||||
}).attach();
|
||||
versionFooter = findViewById(R.id.version_text);
|
||||
setVersionFooter();
|
||||
viewPager.setCurrentItem(1, false);
|
||||
|
||||
// 1. Initialize Result Launchers
|
||||
vpnPermissionLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
|
|
@ -229,118 +226,14 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
}
|
||||
);
|
||||
|
||||
// UI Bindings
|
||||
edittext_socks_addr = findViewById(R.id.socks_addr);
|
||||
edittext_socks_udp_addr = findViewById(R.id.socks_udp_addr);
|
||||
edittext_socks_port = findViewById(R.id.socks_port);
|
||||
edittext_socks_user = findViewById(R.id.socks_user);
|
||||
edittext_socks_pass = findViewById(R.id.socks_pass);
|
||||
edittext_dns_ipv4 = findViewById(R.id.dns_ipv4);
|
||||
edittext_dns_ipv6 = findViewById(R.id.dns_ipv6);
|
||||
checkbox_ipv4 = findViewById(R.id.ipv4);
|
||||
checkbox_ipv6 = findViewById(R.id.ipv6);
|
||||
checkbox_global = findViewById(R.id.global);
|
||||
checkbox_udp_in_tcp = findViewById(R.id.udp_in_tcp);
|
||||
checkbox_remote_dns = findViewById(R.id.remote_dns);
|
||||
checkbox_maintenance = findViewById(R.id.checkbox_maintenance);
|
||||
checkbox_maintenance.setOnClickListener(this);
|
||||
textview_maintenance_warning = findViewById(R.id.maintenance_warning);
|
||||
button_apps = findViewById(R.id.apps);
|
||||
button_save = findViewById(R.id.save);
|
||||
button_control = findViewById(R.id.control);
|
||||
button_browse_content = findViewById(R.id.btnBrowseContent);
|
||||
watchdogControl = findViewById(R.id.watchdog_control);
|
||||
|
||||
logActions = findViewById(R.id.log_actions);
|
||||
Button btnClearLog = findViewById(R.id.btn_clear_log);
|
||||
Button btnCopyLog = findViewById(R.id.btn_copy_log);
|
||||
connectionLog = findViewById(R.id.connection_log);
|
||||
logProgress = findViewById(R.id.log_progress);
|
||||
logWarning = findViewById(R.id.log_warning_text);
|
||||
logSizeText = findViewById(R.id.log_size_text);
|
||||
themeToggle = findViewById(R.id.theme_toggle);
|
||||
btnSettings = findViewById(R.id.btn_settings);
|
||||
versionFooter = findViewById(R.id.version_text);
|
||||
configLayout = findViewById(R.id.config_layout);
|
||||
configLabel = findViewById(R.id.config_label);
|
||||
advancedConfig = findViewById(R.id.advanced_config);
|
||||
advConfigLabel = findViewById(R.id.adv_config_label);
|
||||
logLabel = findViewById(R.id.log_label);
|
||||
|
||||
deckContainer = findViewById(R.id.deck_container);
|
||||
btnServerControl = findViewById(R.id.btn_server_control);
|
||||
headerIcon = findViewById(R.id.header_icon);
|
||||
ImageButton btnShareQr = findViewById(R.id.btn_share_qr);
|
||||
|
||||
dashboardManager = new DashboardManager(this, findViewById(android.R.id.content), () -> {
|
||||
handleControlClick();
|
||||
});
|
||||
|
||||
// Listeners
|
||||
watchdogControl.setOnClickListener(v -> {
|
||||
boolean willBeEnabled = !prefs.getWatchdogEnable();
|
||||
setWatchdogState(willBeEnabled);
|
||||
});
|
||||
btnClearLog.setOnClickListener(this);
|
||||
btnCopyLog.setOnClickListener(this);
|
||||
themeToggle.setOnClickListener(v -> toggleTheme());
|
||||
btnSettings.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, SetupActivity.class)));
|
||||
configLabel.setOnClickListener(v -> handleConfigToggle());
|
||||
advConfigLabel.setOnClickListener(v -> toggleVisibility(advancedConfig, advConfigLabel, getString(R.string.advanced_settings_label)));
|
||||
logLabel.setOnClickListener(v -> handleLogToggle());
|
||||
checkbox_udp_in_tcp.setOnClickListener(this);
|
||||
checkbox_remote_dns.setOnClickListener(this);
|
||||
checkbox_global.setOnClickListener(this);
|
||||
button_apps.setOnClickListener(this);
|
||||
button_save.setOnClickListener(this);
|
||||
|
||||
btnServerControl.setOnClickListener(v -> {
|
||||
// Ignore clicks if we are already waiting for a state change
|
||||
if (targetServerState != null) return;
|
||||
|
||||
// Freeze the transition text and define the TARGET state
|
||||
serverTransitionText = !isServerAlive ? getString(R.string.server_booting) : getString(R.string.server_shutting_down);
|
||||
targetServerState = !isServerAlive;
|
||||
|
||||
// Lock the UI and start infinite animation
|
||||
updateUIColorsAndVisibility();
|
||||
btnServerControl.startProgress();
|
||||
|
||||
// Set a hard timeout (45 seconds) as a safety net
|
||||
timeoutRunnable = () -> {
|
||||
if (targetServerState != null) {
|
||||
targetServerState = null; // Abort transition
|
||||
btnServerControl.stopProgress();
|
||||
updateUIColorsAndVisibility();
|
||||
addToLog(getString(R.string.server_timeout_warning));
|
||||
}
|
||||
};
|
||||
timeoutHandler.postDelayed(timeoutRunnable, getResources().getInteger(R.integer.server_cool_off_duration_ms));
|
||||
|
||||
// Execute the corresponding script command
|
||||
if (!isServerAlive) {
|
||||
startTermuxEnvironmentVisible("--start");
|
||||
} else {
|
||||
startTermuxEnvironmentVisible("--stop");
|
||||
|
||||
// Turn off Watchdog gracefully when stopping the server manually
|
||||
if (prefs.getWatchdogEnable()) {
|
||||
setWatchdogState(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Logic to open the WebView (PortalActivity)
|
||||
button_browse_content.setOnClickListener(v -> {
|
||||
if (!isServerAlive) {
|
||||
Snackbar.make(v, R.string.qr_error_no_server, Snackbar.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
if (currentTargetUrl != null) {
|
||||
Intent intent = new Intent(MainActivity.this, PortalActivity.class);
|
||||
intent.putExtra("TARGET_URL", currentTargetUrl);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
// --- QR Share Button Logic ---
|
||||
btnShareQr.setOnClickListener(v -> {
|
||||
|
|
@ -359,18 +252,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
startActivity(new Intent(MainActivity.this, QrActivity.class));
|
||||
});
|
||||
|
||||
connectionLog.setMovementMethod(new ScrollingMovementMethod());
|
||||
connectionLog.setTextIsSelectable(true);
|
||||
connectionLog.setOnTouchListener((v, event) -> {
|
||||
v.getParent().requestDisallowInterceptTouchEvent(true);
|
||||
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
|
||||
v.getParent().requestDisallowInterceptTouchEvent(false);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
applySavedTheme();
|
||||
setVersionFooter();
|
||||
updateUI();
|
||||
|
||||
addToLog(getString(R.string.app_started));
|
||||
|
|
@ -378,7 +260,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
sizeUpdateRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateLogSizeUI();
|
||||
if (usageFragment != null && usageFragment.isAdded()) usageFragment.updateLogSizeUI();
|
||||
sizeUpdateHandler.postDelayed(this, 10000);
|
||||
}
|
||||
};
|
||||
|
|
@ -447,7 +329,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
private void runNegotiationSequence() {
|
||||
isNegotiating = true;
|
||||
runOnUiThread(() -> {
|
||||
startExplorePulse(); // The orange button starts to beat.
|
||||
if (usageFragment != null) usageFragment.startExplorePulse(); // The orange button starts to beat.
|
||||
updateUIColorsAndVisibility(); // We forced an immediate visual update
|
||||
});
|
||||
|
||||
|
|
@ -504,66 +386,21 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
}
|
||||
}
|
||||
|
||||
private void handleLogToggle() {
|
||||
boolean isOpening = connectionLog.getVisibility() == View.GONE;
|
||||
if (isOpening) {
|
||||
if (isReadingLogs) return;
|
||||
isReadingLogs = true;
|
||||
if (logProgress != null) logProgress.setVisibility(View.VISIBLE);
|
||||
|
||||
// We delegate the reading to the Manager
|
||||
LogManager.readLogsAsync(this, (logContent, isRapidGrowth) -> {
|
||||
if (connectionLog != null) {
|
||||
connectionLog.setText(logContent);
|
||||
scrollToBottom();
|
||||
}
|
||||
if (logProgress != null) logProgress.setVisibility(View.GONE);
|
||||
if (logWarning != null) logWarning.setVisibility(isRapidGrowth ? View.VISIBLE : View.GONE);
|
||||
updateLogSizeUI();
|
||||
isReadingLogs = false;
|
||||
});
|
||||
|
||||
startLogSizeUpdates();
|
||||
} else {
|
||||
stopLogSizeUpdates();
|
||||
}
|
||||
toggleVisibility(connectionLog, logLabel, getString(R.string.connection_log_label));
|
||||
logActions.setVisibility(connectionLog.getVisibility());
|
||||
if (logSizeText != null) logSizeText.setVisibility(connectionLog.getVisibility());
|
||||
}
|
||||
|
||||
private void startLogSizeUpdates() {
|
||||
public void startLogSizeUpdates() {
|
||||
sizeUpdateHandler.removeCallbacks(sizeUpdateRunnable);
|
||||
sizeUpdateHandler.post(sizeUpdateRunnable);
|
||||
}
|
||||
|
||||
private void stopLogSizeUpdates() {
|
||||
public void stopLogSizeUpdates() {
|
||||
sizeUpdateHandler.removeCallbacks(sizeUpdateRunnable);
|
||||
}
|
||||
|
||||
private void updateLogSizeUI() {
|
||||
if (logSizeText == null) return;
|
||||
// The LogManager class does the calculation
|
||||
String sizeStr = LogManager.getFormattedSize(this);
|
||||
logSizeText.setText(getString(R.string.log_size_format, sizeStr));
|
||||
}
|
||||
|
||||
private void connectVpn() {
|
||||
Intent intent = new Intent(this, TProxyService.class);
|
||||
startService(intent.setAction(TProxyService.ACTION_CONNECT));
|
||||
addToLog(getString(R.string.vpn_permission_granted));
|
||||
}
|
||||
|
||||
private void setVersionFooter() {
|
||||
try {
|
||||
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
|
||||
String version = pInfo.versionName;
|
||||
versionFooter.setText(version);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
versionFooter.setText(R.string.default_version);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
|
@ -574,6 +411,8 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
// Check permissions status
|
||||
updateHeaderIconsOpacity();
|
||||
// Check battery status whenever returning to the app
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
|
|
@ -590,7 +429,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
startService(vpnIntent.setAction(TProxyService.ACTION_CONNECT));
|
||||
setIntent(null);
|
||||
}
|
||||
if (connectionLog != null && connectionLog.getVisibility() == View.VISIBLE) {
|
||||
if (usageFragment != null && usageFragment.isLogVisible()) {
|
||||
startLogSizeUpdates();
|
||||
}
|
||||
serverCheckHandler.removeCallbacks(serverCheckRunnable);
|
||||
|
|
@ -626,11 +465,6 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
else themeToggle.setImageResource(R.drawable.ic_theme_system);
|
||||
}
|
||||
|
||||
private void toggleVisibility(View view, TextView label, String text) {
|
||||
boolean isGone = view.getVisibility() == View.GONE;
|
||||
view.setVisibility(isGone ? View.VISIBLE : View.GONE);
|
||||
label.setText(String.format(getString(isGone ? R.string.label_separator_down : R.string.label_separator_up), text));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
|
|
@ -652,55 +486,10 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (view == checkbox_global || view == checkbox_remote_dns || view == checkbox_maintenance) {
|
||||
savePrefs();
|
||||
updateUI();
|
||||
} else if (view == button_apps) {
|
||||
startActivity(new Intent(this, AppListActivity.class));
|
||||
} else if (view.getId() == R.id.save) {
|
||||
savePrefs();
|
||||
Toast.makeText(this, R.string.saved_toast, Toast.LENGTH_SHORT).show();
|
||||
addToLog(getString(R.string.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();
|
||||
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();
|
||||
}
|
||||
}
|
||||
// Delegated
|
||||
}
|
||||
|
||||
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() {
|
||||
LogManager.clearLogs(this, new LogManager.LogClearCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
connectionLog.setText("");
|
||||
addToLog(getString(R.string.log_reset_user));
|
||||
if (logWarning != null) logWarning.setVisibility(View.GONE);
|
||||
updateLogSizeUI();
|
||||
Toast.makeText(MainActivity.this, R.string.log_cleared_toast, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
Toast.makeText(MainActivity.this, getString(R.string.failed_reset_log, message), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleWatchdogClick() {
|
||||
public void handleWatchdogClick() {
|
||||
setWatchdogState(!prefs.getWatchdogEnable());
|
||||
}
|
||||
|
||||
|
|
@ -709,9 +498,10 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
Intent intent = new Intent(this, WatchdogService.class);
|
||||
|
||||
if (enable) {
|
||||
forceTermuxToForeground();
|
||||
intent.setAction(WatchdogService.ACTION_START);
|
||||
addToLog(getString(R.string.watchdog_started));
|
||||
if (isServerAlive) startFusionPulse();
|
||||
if (isServerAlive && usageFragment != null) usageFragment.startFusionPulse();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(intent);
|
||||
|
|
@ -720,7 +510,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
}
|
||||
} else {
|
||||
addToLog(getString(R.string.watchdog_stopped));
|
||||
startExitPulse();
|
||||
if (usageFragment != null) usageFragment.startExitPulse();
|
||||
stopService(intent);
|
||||
}
|
||||
|
||||
|
|
@ -728,7 +518,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
updateUIColorsAndVisibility();
|
||||
}
|
||||
|
||||
private void handleControlClick() {
|
||||
public void handleControlClick() {
|
||||
if (!isServerAlive) {
|
||||
Snackbar.make(findViewById(android.R.id.content), R.string.qr_error_no_server, Snackbar.LENGTH_LONG).show();
|
||||
return;
|
||||
|
|
@ -751,19 +541,47 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
}
|
||||
}
|
||||
|
||||
// --- Secure Advanced Settings Menu ---
|
||||
private void handleConfigToggle() {
|
||||
if (configLayout.getVisibility() == View.GONE) {
|
||||
if (BiometricHelper.isDeviceSecure(this)) {
|
||||
BiometricHelper.prompt(this,
|
||||
getString(R.string.auth_required_title),
|
||||
getString(R.string.auth_required_subtitle),
|
||||
() -> toggleVisibility(configLayout, configLabel, getString(R.string.advanced_settings_label)));
|
||||
} else {
|
||||
BiometricHelper.showEnrollmentDialog(this);
|
||||
public void handleBrowseContentClick(View v) {
|
||||
if (!isServerAlive) {
|
||||
Snackbar.make(v, R.string.qr_error_no_server, Snackbar.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
if (currentTargetUrl != null) {
|
||||
Intent intent = new Intent(this, PortalActivity.class);
|
||||
intent.putExtra("TARGET_URL", currentTargetUrl);
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
public void handleServerLaunchClick(View v) {
|
||||
// Set a hard timeout as a safety net
|
||||
timeoutRunnable = () -> {
|
||||
if (targetServerState != null) {
|
||||
targetServerState = null; // Abort transition
|
||||
if (usageFragment != null) runOnUiThread(() -> usageFragment.stopBtnProgress());
|
||||
updateUIColorsAndVisibility();
|
||||
addToLog(getString(R.string.server_timeout_warning));
|
||||
}
|
||||
};
|
||||
timeoutHandler.postDelayed(timeoutRunnable, getResources().getInteger(R.integer.server_cool_off_duration_ms));
|
||||
|
||||
// Execute the corresponding script command
|
||||
if (!isServerAlive) {
|
||||
startTermuxEnvironmentVisible("--start");
|
||||
|
||||
// Fallback for Oppo/Xiaomi
|
||||
new Handler(android.os.Looper.getMainLooper()).postDelayed(() -> {
|
||||
if (targetServerState != null && !isServerAlive) {
|
||||
Snackbar.make(v, R.string.termux_stuck_warning, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}, getResources().getInteger(R.integer.server_snackbar_delay_ms));
|
||||
|
||||
} else {
|
||||
toggleVisibility(configLayout, configLabel, getString(R.string.advanced_settings_label));
|
||||
startTermuxEnvironmentVisible("--stop");
|
||||
|
||||
// Turn off Watchdog gracefully when stopping the server manually
|
||||
if (prefs.getWatchdogEnable()) {
|
||||
setWatchdogState(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -781,45 +599,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
}
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
boolean vpnActive = prefs.getEnable();
|
||||
boolean watchdogActive = prefs.getWatchdogEnable();
|
||||
|
||||
if (dashboardManager != null) dashboardManager.setTunnelState(vpnActive, isProxyDegraded);
|
||||
|
||||
if (vpnActive) {
|
||||
button_control.setText(R.string.control_disable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_on));
|
||||
} else {
|
||||
button_control.setText(R.string.control_enable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_off));
|
||||
}
|
||||
if (watchdogActive) {
|
||||
watchdogControl.setText(R.string.watchdog_disable);
|
||||
} else {
|
||||
watchdogControl.setText(R.string.watchdog_enable);
|
||||
}
|
||||
edittext_socks_addr.setText(prefs.getSocksAddress());
|
||||
edittext_socks_udp_addr.setText(prefs.getSocksUdpAddress());
|
||||
edittext_socks_port.setText(String.valueOf(prefs.getSocksPort()));
|
||||
edittext_socks_user.setText(prefs.getSocksUsername());
|
||||
edittext_socks_pass.setText(prefs.getSocksPassword());
|
||||
edittext_dns_ipv4.setText(prefs.getDnsIpv4());
|
||||
edittext_dns_ipv6.setText(prefs.getDnsIpv6());
|
||||
checkbox_ipv4.setChecked(prefs.getIpv4());
|
||||
checkbox_ipv6.setChecked(prefs.getIpv6());
|
||||
checkbox_global.setChecked(prefs.getGlobal());
|
||||
checkbox_udp_in_tcp.setChecked(prefs.getUdpInTcp());
|
||||
checkbox_remote_dns.setChecked(prefs.getRemoteDns());
|
||||
checkbox_maintenance.setChecked(prefs.getMaintenanceMode());
|
||||
boolean editable = !vpnActive;
|
||||
edittext_socks_addr.setEnabled(editable);
|
||||
edittext_socks_port.setEnabled(editable);
|
||||
button_save.setEnabled(editable);
|
||||
|
||||
checkbox_maintenance.setEnabled(editable);
|
||||
if (textview_maintenance_warning != null) {
|
||||
textview_maintenance_warning.setVisibility(vpnActive ? View.VISIBLE : View.GONE);
|
||||
public void updateUI() {
|
||||
if (usageFragment != null) {
|
||||
usageFragment.updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -845,7 +627,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
if (targetServerState != null && isServerAlive == targetServerState) {
|
||||
targetServerState = null; // Transition complete!
|
||||
timeoutHandler.removeCallbacks(timeoutRunnable); // Cancel safety net
|
||||
runOnUiThread(() -> btnServerControl.stopProgress()); // Unlock button
|
||||
if (usageFragment != null) runOnUiThread(() -> usageFragment.stopBtnProgress());
|
||||
}
|
||||
|
||||
if (vpnOn && boxAlive) {
|
||||
|
|
@ -860,163 +642,10 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
}).start();
|
||||
}
|
||||
|
||||
private void updateUIColorsAndVisibility() {
|
||||
boolean isVpnActive = prefs.getEnable();
|
||||
boolean isWatchdogOn = prefs.getWatchdogEnable();
|
||||
|
||||
// Draw island (Tunnel LED colors)
|
||||
if (dashboardManager != null) {
|
||||
dashboardManager.setTunnelState(isVpnActive, isProxyDegraded);
|
||||
public void updateUIColorsAndVisibility() {
|
||||
if (usageFragment != null) {
|
||||
usageFragment.updateUIColorsAndVisibility();
|
||||
}
|
||||
|
||||
// Draw main VPN button (ESPW)
|
||||
if (!isServerAlive) {
|
||||
// Lock and dim the VPN button if there is no server to connect to
|
||||
if (isVpnActive) {
|
||||
button_control.setText(R.string.control_disable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_on_dim));
|
||||
} else {
|
||||
button_control.setText(R.string.control_enable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_off_dim));
|
||||
}
|
||||
} else {
|
||||
// Unlock if server is alive
|
||||
button_control.setEnabled(true);
|
||||
if (isVpnActive) {
|
||||
button_control.setText(R.string.control_disable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_on));
|
||||
} else {
|
||||
button_control.setText(R.string.control_enable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_off));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Explore Content button
|
||||
// Ensure it is ALWAYS visible, never GONE
|
||||
button_browse_content.setVisibility(View.VISIBLE);
|
||||
|
||||
if (!isServerAlive) {
|
||||
// State 1: Stopped (Greyed out)
|
||||
stopExplorePulse();
|
||||
button_browse_content.setEnabled(true);
|
||||
button_browse_content.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_explore_disabled));
|
||||
button_browse_content.setAlpha(1.0f);
|
||||
button_browse_content.setTextColor(Color.parseColor("#888888"));
|
||||
} else if (isNegotiating) {
|
||||
// State 3: Negotiating
|
||||
button_browse_content.setEnabled(true);
|
||||
button_browse_content.setTextColor(Color.WHITE);
|
||||
} else {
|
||||
// State: Alive
|
||||
stopExplorePulse();
|
||||
button_browse_content.setEnabled(true);
|
||||
button_browse_content.setTextColor(Color.WHITE);
|
||||
button_browse_content.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_explore_ready));
|
||||
|
||||
if (isVpnActive && !isProxyDegraded) {
|
||||
button_browse_content.setAlpha(1.0f); // 100% Perfect state
|
||||
startExplorePulse();
|
||||
} else {
|
||||
button_browse_content.setAlpha(0.6f); // Watered down fallback state
|
||||
}
|
||||
}
|
||||
|
||||
// FUSION LOGIC (Watchdog & Server Control)
|
||||
if (targetServerState != null) {
|
||||
// STATE: COOL-OFF (Locked)
|
||||
btnServerControl.setAlpha(0.6f);
|
||||
btnServerControl.setText(serverTransitionText);
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_explore_disabled));
|
||||
} else {
|
||||
// STATE: NORMAL (Unlocked)
|
||||
btnServerControl.setAlpha(1.0f);
|
||||
if (isServerAlive) {
|
||||
btnServerControl.setText(R.string.stop_server);
|
||||
if (isWatchdogOn) {
|
||||
deckContainer.setBackgroundColor(Color.parseColor("#44FF9800"));
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on));
|
||||
} else {
|
||||
if (fusionAnimator == null || !fusionAnimator.isRunning()) deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_danger));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_off));
|
||||
}
|
||||
} else {
|
||||
deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
btnServerControl.setText(R.string.launch_server);
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_success));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, isWatchdogOn ? R.color.btn_watchdog_on : R.color.btn_watchdog_off));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startFusionPulse() {
|
||||
deckContainer.setBackgroundColor(Color.parseColor("#44FF9800"));
|
||||
if (fusionAnimator != null && fusionAnimator.isRunning()) fusionAnimator.cancel();
|
||||
|
||||
pulseStartTime = System.currentTimeMillis();
|
||||
|
||||
// Pulses infinitely until the Service broadcast stops it
|
||||
fusionAnimator = ObjectAnimator.ofFloat(deckContainer, "alpha", 1f, 0.4f);
|
||||
fusionAnimator.setDuration(600);
|
||||
fusionAnimator.setRepeatCount(ObjectAnimator.INFINITE);
|
||||
fusionAnimator.setRepeatMode(ObjectAnimator.REVERSE);
|
||||
fusionAnimator.start();
|
||||
}
|
||||
|
||||
private void startExitPulse() {
|
||||
if (fusionAnimator != null && fusionAnimator.isRunning()) fusionAnimator.cancel();
|
||||
|
||||
// A slower, deliberate pulse infinitely until the Service + 1.5s delay stops it
|
||||
fusionAnimator = ObjectAnimator.ofFloat(deckContainer, "alpha", deckContainer.getAlpha(), 0.3f);
|
||||
fusionAnimator.setDuration(800);
|
||||
fusionAnimator.setRepeatCount(ObjectAnimator.INFINITE);
|
||||
fusionAnimator.setRepeatMode(ObjectAnimator.REVERSE);
|
||||
fusionAnimator.start();
|
||||
}
|
||||
|
||||
private void startExplorePulse() {
|
||||
button_browse_content.setAlpha(1.0f); // 100% Bright Orange
|
||||
button_browse_content.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_explore_ready));
|
||||
|
||||
if (exploreAnimator == null) {
|
||||
android.animation.PropertyValuesHolder scaleX = android.animation.PropertyValuesHolder.ofFloat(View.SCALE_X, 1.0f, 1.03f);
|
||||
android.animation.PropertyValuesHolder scaleY = android.animation.PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.0f, 1.03f);
|
||||
exploreAnimator = android.animation.ObjectAnimator.ofPropertyValuesHolder(button_browse_content, scaleX, scaleY);
|
||||
exploreAnimator.setDuration(800); // 800ms per heartbeat
|
||||
exploreAnimator.setRepeatCount(android.animation.ObjectAnimator.INFINITE);
|
||||
exploreAnimator.setRepeatMode(android.animation.ObjectAnimator.REVERSE);
|
||||
}
|
||||
if (!exploreAnimator.isRunning()) exploreAnimator.start();
|
||||
}
|
||||
|
||||
private void stopExplorePulse() {
|
||||
if (exploreAnimator != null && exploreAnimator.isRunning()) {
|
||||
exploreAnimator.cancel();
|
||||
}
|
||||
// Restore to normal size
|
||||
button_browse_content.setScaleX(1.0f);
|
||||
button_browse_content.setScaleY(1.0f);
|
||||
|
||||
// Diluted orange (ready, but waiting for the tunnel)
|
||||
button_browse_content.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_explore_ready));
|
||||
button_browse_content.setAlpha(0.6f);
|
||||
}
|
||||
|
||||
private void finalizeEntryPulse() {
|
||||
if (fusionAnimator != null) fusionAnimator.cancel();
|
||||
deckContainer.setAlpha(1f); // Lock solid instantly
|
||||
}
|
||||
|
||||
private void finalizeExitPulse() {
|
||||
if (fusionAnimator != null) fusionAnimator.cancel();
|
||||
deckContainer.animate()
|
||||
.alpha(1f)
|
||||
.setDuration(300)
|
||||
.withEndAction(() -> {
|
||||
deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
private void startTermuxEnvironmentVisible(String actionFlag) {
|
||||
|
|
@ -1075,43 +704,100 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
this.isWifiActive = isWifiOn;
|
||||
this.isHotspotActive = isHotspotOn;
|
||||
|
||||
// Let the Dashboard handle the LEDs!
|
||||
if (dashboardManager != null) dashboardManager.updateConnectivityLeds(isWifiOn, isHotspotOn);
|
||||
if (usageFragment != null) {
|
||||
runOnUiThread(() -> usageFragment.updateConnectivityLeds(this.isWifiActive, this.isHotspotActive));
|
||||
}
|
||||
}
|
||||
|
||||
private void savePrefs() {
|
||||
prefs.setSocksAddress("127.0.0.1");
|
||||
prefs.setSocksPort(1080);
|
||||
prefs.setSocksUdpAddress("");
|
||||
prefs.setSocksUsername("");
|
||||
prefs.setSocksPassword("");
|
||||
prefs.setIpv4(true);
|
||||
prefs.setIpv6(true);
|
||||
prefs.setUdpInTcp(false);
|
||||
prefs.setRemoteDns(true);
|
||||
prefs.setGlobal(true);
|
||||
|
||||
prefs.setDnsIpv4(edittext_dns_ipv4.getText().toString());
|
||||
prefs.setDnsIpv6(edittext_dns_ipv6.getText().toString());
|
||||
prefs.setMaintenanceMode(checkbox_maintenance.isChecked());
|
||||
public void savePrefs() {
|
||||
if (usageFragment != null) {
|
||||
usageFragment.savePrefsFromUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void addToLog(String message) {
|
||||
runOnUiThread(() -> {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
|
||||
String currentTime = sdf.format(new Date());
|
||||
String logEntry = "[" + currentTime + "] " + message + "\n";
|
||||
if (connectionLog != null) {
|
||||
connectionLog.append(logEntry);
|
||||
scrollToBottom();
|
||||
public void addToLog(String message) {
|
||||
if (usageFragment != null) {
|
||||
usageFragment.addToLog(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void scrollToBottom() {
|
||||
if (connectionLog.getLayout() != null) {
|
||||
int scroll = connectionLog.getLayout().getLineTop(connectionLog.getLineCount()) - connectionLog.getHeight();
|
||||
if (scroll > 0) connectionLog.scrollTo(0, scroll);
|
||||
private void setVersionFooter() {
|
||||
try {
|
||||
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
|
||||
String version = pInfo.versionName;
|
||||
|
||||
String footerText = getString(R.string.version_footer_format, version);
|
||||
|
||||
versionFooter.setText(footerText);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
versionFooter.setText(getString(R.string.version_footer_fallback));
|
||||
}
|
||||
}
|
||||
private void forceTermuxToForeground() {
|
||||
try {
|
||||
Intent intent = getPackageManager().getLaunchIntentForPackage("com.termux");
|
||||
if (intent != null) {
|
||||
// Bring existing activity to the foreground
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
|
||||
startActivity(intent);
|
||||
addToLog(getString(R.string.force_termux_foreground));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
addToLog(getString(R.string.termux_invocation_error, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
// --- PERMISSION CHECKERS FOR UI OPACITY ---
|
||||
|
||||
private void updateHeaderIconsOpacity() {
|
||||
boolean hasAllControllerPerms = hasNotifPermission() && hasTermuxPermission() && hasBatteryPermission() && hasStoragePermission();
|
||||
boolean hasTermuxStorage = hasTermuxStoragePermission();
|
||||
|
||||
// If any vital permission is missing, dim the icons to 40% opacity (0.4f)
|
||||
boolean allPerfect = hasAllControllerPerms && hasTermuxStorage;
|
||||
float targetAlpha = allPerfect ? 1.0f : 0.4f;
|
||||
|
||||
if (btnSettings != null) btnSettings.setAlpha(targetAlpha);
|
||||
if (headerIcon != null) headerIcon.setAlpha(targetAlpha);
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
private boolean hasTermuxStoragePermission() {
|
||||
try {
|
||||
int result = getPackageManager().checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE, "com.termux");
|
||||
if (result == PackageManager.PERMISSION_GRANTED) return true;
|
||||
|
||||
// Fallback: If Android denies the package query, check if the directory actually exists
|
||||
File stateDir = new File(android.os.Environment.getExternalStorageDirectory(), ".iiab_state");
|
||||
return stateDir.exists();
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasStoragePermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
return Environment.isExternalStorageManager();
|
||||
} else {
|
||||
return ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : MainPagerAdapter.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Main Pager Adapter
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
|
||||
public class MainPagerAdapter extends FragmentStateAdapter {
|
||||
|
||||
public MainPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
|
||||
super(fragmentActivity);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return new DashboardFragment();
|
||||
case 1:
|
||||
return new UsageFragment();
|
||||
case 2:
|
||||
return new DeployFragment();
|
||||
default:
|
||||
return new DashboardFragment();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : PortalActivity.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Webview portal activity
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
============================================================================
|
||||
Name : Preferences.java
|
||||
Author : hev <r@hev.cc>
|
||||
Contributors: IIAB Project
|
||||
Copyright : Copyright (c) 2023 xyz
|
||||
Copyright (c) 2026 IIAB Project
|
||||
Description : Preferences
|
||||
============================================================================
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : ProgressButton.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Button animation helper
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : QrActivity.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : QR share content helper
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
============================================================================
|
||||
Name : ServiceReceiver.java
|
||||
Author : hev <r@hev.cc>
|
||||
Contributors: IIAB Project
|
||||
Copyright : Copyright (c) 2023 xyz
|
||||
Copyright (c) 2026 IIAB Project
|
||||
Description : ServiceReceiver
|
||||
============================================================================
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : SetupActivity.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Setup permission table helper
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.Manifest;
|
||||
|
|
@ -9,6 +17,7 @@ import android.net.Uri;
|
|||
import android.net.VpnService;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
import android.view.View;
|
||||
|
|
@ -28,13 +37,15 @@ public class SetupActivity extends AppCompatActivity {
|
|||
|
||||
private static final String TERMUX_PERMISSION = "com.termux.permission.RUN_COMMAND";
|
||||
|
||||
private SwitchCompat switchNotif, switchTermux, switchVpn, switchBattery;
|
||||
private SwitchCompat switchNotif, switchTermux, switchStorage, switchVpn, switchBattery;
|
||||
private Button btnContinue;
|
||||
private Button btnManageAll;
|
||||
private Button btnTermuxOverlay;
|
||||
private Button btnTermuxStorage;
|
||||
private Button btnManageTermux;
|
||||
|
||||
private ActivityResultLauncher<String> requestPermissionLauncher;
|
||||
private ActivityResultLauncher<Intent> storageLauncher;
|
||||
private ActivityResultLauncher<Intent> vpnLauncher;
|
||||
private ActivityResultLauncher<Intent> batteryLauncher;
|
||||
|
||||
|
|
@ -48,11 +59,13 @@ public class SetupActivity extends AppCompatActivity {
|
|||
|
||||
switchNotif = findViewById(R.id.switch_perm_notifications);
|
||||
switchTermux = findViewById(R.id.switch_perm_termux);
|
||||
switchStorage = findViewById(R.id.switch_perm_storage);
|
||||
switchVpn = findViewById(R.id.switch_perm_vpn);
|
||||
switchBattery = findViewById(R.id.switch_perm_battery);
|
||||
btnContinue = findViewById(R.id.btn_setup_continue);
|
||||
btnManageAll = findViewById(R.id.btn_manage_all);
|
||||
btnTermuxOverlay = findViewById(R.id.btn_termux_overlay);
|
||||
btnTermuxStorage = findViewById(R.id.btn_termux_storage);
|
||||
btnManageTermux = findViewById(R.id.btn_manage_termux);
|
||||
|
||||
// Hide Notification switch if Android < 13
|
||||
|
|
@ -65,9 +78,11 @@ public class SetupActivity extends AppCompatActivity {
|
|||
checkAllPermissions();
|
||||
|
||||
btnContinue.setOnClickListener(v -> {
|
||||
// Tell bash that permissions are handled
|
||||
writeTermuxPermissionFlags();
|
||||
// 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();
|
||||
SharedPreferences prefs = getSharedPreferences(getString(R.string.pref_file_internal), Context.MODE_PRIVATE);
|
||||
prefs.edit().putBoolean(getString(R.string.pref_key_setup_complete), true).apply();
|
||||
|
||||
finish();
|
||||
});
|
||||
|
|
@ -79,6 +94,11 @@ public class SetupActivity extends AppCompatActivity {
|
|||
isGranted -> checkAllPermissions()
|
||||
);
|
||||
|
||||
storageLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> checkAllPermissions()
|
||||
);
|
||||
|
||||
vpnLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> checkAllPermissions()
|
||||
|
|
@ -115,6 +135,30 @@ public class SetupActivity extends AppCompatActivity {
|
|||
switchTermux.setChecked(false);
|
||||
});
|
||||
|
||||
switchStorage.setOnClickListener(v -> {
|
||||
if (hasStoragePermission()) {
|
||||
handleRevokeAttempt(v);
|
||||
return;
|
||||
}
|
||||
if (switchStorage.isChecked()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
try {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
|
||||
intent.addCategory("android.intent.category.DEFAULT");
|
||||
intent.setData(Uri.parse(String.format("package:%s", getApplicationContext().getPackageName())));
|
||||
storageLauncher.launch(intent);
|
||||
} catch (Exception e) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
|
||||
storageLauncher.launch(intent);
|
||||
}
|
||||
} else {
|
||||
requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
}
|
||||
switchStorage.setChecked(false); // Force visual state back until system confirms
|
||||
});
|
||||
|
||||
switchVpn.setOnClickListener(v -> {
|
||||
if (hasVpnPermission()) {
|
||||
handleRevokeAttempt(v);
|
||||
|
|
@ -158,6 +202,18 @@ public class SetupActivity extends AppCompatActivity {
|
|||
}
|
||||
});
|
||||
|
||||
// Direct access to Termux settings to grant Files/Storage permission
|
||||
btnTermuxStorage.setOnClickListener(v -> {
|
||||
try {
|
||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
intent.setData(Uri.parse("package:com.termux"));
|
||||
startActivity(intent);
|
||||
// Toast.makeText(this, "Please go to Permissions and allow Storage/Files", Toast.LENGTH_LONG).show();
|
||||
} catch (Exception e) {
|
||||
Snackbar.make(v, R.string.termux_not_installed_error, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
|
||||
// Direct access to Controller settings (Reuses the method from Phase 1)
|
||||
btnManageTermux.setOnClickListener(v -> {
|
||||
try {
|
||||
|
|
@ -202,15 +258,17 @@ public class SetupActivity extends AppCompatActivity {
|
|||
private void checkAllPermissions() {
|
||||
boolean notif = hasNotifPermission();
|
||||
boolean termux = hasTermuxPermission();
|
||||
boolean storage = hasStoragePermission();
|
||||
boolean vpn = hasVpnPermission();
|
||||
boolean battery = hasBatteryPermission();
|
||||
|
||||
switchNotif.setChecked(notif);
|
||||
switchTermux.setChecked(termux);
|
||||
switchStorage.setChecked(storage);
|
||||
switchVpn.setChecked(vpn);
|
||||
switchBattery.setChecked(battery);
|
||||
|
||||
boolean allGranted = termux && vpn && battery;
|
||||
boolean allGranted = termux && storage && vpn && battery;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
allGranted = allGranted && notif;
|
||||
}
|
||||
|
|
@ -242,4 +300,25 @@ public class SetupActivity extends AppCompatActivity {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean hasStoragePermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
return Environment.isExternalStorageManager();
|
||||
} else {
|
||||
return ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeTermuxPermissionFlags() {
|
||||
java.io.File stateDir = new java.io.File(android.os.Environment.getExternalStorageDirectory(), ".iiab_state");
|
||||
if (!stateDir.exists()) {
|
||||
stateDir.mkdirs();
|
||||
}
|
||||
try {
|
||||
new java.io.File(stateDir, "flag_perm_battery").createNewFile();
|
||||
new java.io.File(stateDir, "flag_perm_overlay").createNewFile();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
============================================================================
|
||||
Name : TProxyService.java
|
||||
Author : hev <r@hev.cc>
|
||||
Contributors: IIAB Project
|
||||
Copyright : Copyright (c) 2024 xyz
|
||||
Copyright (c) 2026 IIAB Project
|
||||
Description : TProxy Service with integrated Watchdog
|
||||
============================================================================
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : TermuxCallbackReceiver.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Termux callback helper
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : UsageFragment.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Usage Fragment Activity
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.method.ScrollingMovementMethod;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class UsageFragment extends Fragment implements View.OnClickListener {
|
||||
|
||||
private MainActivity mainActivity;
|
||||
// INTERFACE VARS
|
||||
private EditText edittext_socks_addr, edittext_socks_udp_addr, edittext_socks_port, edittext_socks_user, edittext_socks_pass, edittext_dns_ipv4, edittext_dns_ipv6;
|
||||
private CheckBox checkbox_udp_in_tcp, checkbox_remote_dns, checkbox_global, checkbox_maintenance, checkbox_ipv4, checkbox_ipv6;
|
||||
private TextView textview_maintenance_warning, configLabel, advConfigLabel, logLabel, logWarning, logSizeText, connectionLog;
|
||||
private Button button_apps, button_save, button_control, button_browse_content, watchdogControl, btnClearLog, btnCopyLog;
|
||||
private LinearLayout logActions, configLayout, advancedConfig, deckContainer;
|
||||
private ProgressBar logProgress;
|
||||
private ProgressButton btnServerControl;
|
||||
|
||||
private ObjectAnimator fusionAnimator;
|
||||
private ObjectAnimator exploreAnimator;
|
||||
private DashboardManager dashboardManager;
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof MainActivity) {
|
||||
mainActivity = (MainActivity) context;
|
||||
mainActivity.setUsageFragment(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_usage, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// UI Bindings
|
||||
edittext_socks_addr = view.findViewById(R.id.socks_addr);
|
||||
edittext_socks_udp_addr = view.findViewById(R.id.socks_udp_addr);
|
||||
edittext_socks_port = view.findViewById(R.id.socks_port);
|
||||
edittext_socks_user = view.findViewById(R.id.socks_user);
|
||||
edittext_socks_pass = view.findViewById(R.id.socks_pass);
|
||||
edittext_dns_ipv4 = view.findViewById(R.id.dns_ipv4);
|
||||
edittext_dns_ipv6 = view.findViewById(R.id.dns_ipv6);
|
||||
checkbox_ipv4 = view.findViewById(R.id.ipv4);
|
||||
checkbox_ipv6 = view.findViewById(R.id.ipv6);
|
||||
checkbox_global = view.findViewById(R.id.global);
|
||||
checkbox_udp_in_tcp = view.findViewById(R.id.udp_in_tcp);
|
||||
checkbox_remote_dns = view.findViewById(R.id.remote_dns);
|
||||
checkbox_maintenance = view.findViewById(R.id.checkbox_maintenance);
|
||||
textview_maintenance_warning = view.findViewById(R.id.maintenance_warning);
|
||||
button_apps = view.findViewById(R.id.apps);
|
||||
button_save = view.findViewById(R.id.save);
|
||||
button_control = view.findViewById(R.id.control);
|
||||
button_browse_content = view.findViewById(R.id.btnBrowseContent);
|
||||
watchdogControl = view.findViewById(R.id.watchdog_control);
|
||||
|
||||
logActions = view.findViewById(R.id.log_actions);
|
||||
btnClearLog = view.findViewById(R.id.btn_clear_log);
|
||||
btnCopyLog = view.findViewById(R.id.btn_copy_log);
|
||||
connectionLog = view.findViewById(R.id.connection_log);
|
||||
logProgress = view.findViewById(R.id.log_progress);
|
||||
logWarning = view.findViewById(R.id.log_warning_text);
|
||||
logSizeText = view.findViewById(R.id.log_size_text);
|
||||
configLayout = view.findViewById(R.id.config_layout);
|
||||
configLabel = view.findViewById(R.id.config_label);
|
||||
advancedConfig = view.findViewById(R.id.advanced_config);
|
||||
advConfigLabel = view.findViewById(R.id.adv_config_label);
|
||||
logLabel = view.findViewById(R.id.log_label);
|
||||
|
||||
deckContainer = view.findViewById(R.id.deck_container);
|
||||
btnServerControl = view.findViewById(R.id.btn_server_control);
|
||||
|
||||
dashboardManager = new DashboardManager(requireActivity(), view, () -> {
|
||||
mainActivity.handleControlClick();
|
||||
});
|
||||
|
||||
// Listeners
|
||||
watchdogControl.setOnClickListener(v -> mainActivity.handleWatchdogClick());
|
||||
button_control.setOnClickListener(v -> mainActivity.handleControlClick());
|
||||
button_browse_content.setOnClickListener(v -> mainActivity.handleBrowseContentClick(v));
|
||||
btnClearLog.setOnClickListener(this);
|
||||
btnCopyLog.setOnClickListener(this);
|
||||
configLabel.setOnClickListener(v -> handleConfigToggle());
|
||||
advConfigLabel.setOnClickListener(v -> toggleVisibility(advancedConfig, advConfigLabel, getString(R.string.advanced_settings_label)));
|
||||
logLabel.setOnClickListener(v -> handleLogToggle());
|
||||
checkbox_udp_in_tcp.setOnClickListener(this);
|
||||
checkbox_remote_dns.setOnClickListener(this);
|
||||
checkbox_global.setOnClickListener(this);
|
||||
checkbox_maintenance.setOnClickListener(this);
|
||||
button_apps.setOnClickListener(this);
|
||||
button_save.setOnClickListener(this);
|
||||
|
||||
btnServerControl.setOnClickListener(v -> {
|
||||
if (mainActivity.targetServerState != null) return;
|
||||
|
||||
mainActivity.serverTransitionText = !mainActivity.isServerAlive ? getString(R.string.server_booting) : getString(R.string.server_shutting_down);
|
||||
mainActivity.targetServerState = !mainActivity.isServerAlive;
|
||||
|
||||
updateUIColorsAndVisibility();
|
||||
btnServerControl.startProgress();
|
||||
|
||||
mainActivity.handleServerLaunchClick(v);
|
||||
});
|
||||
|
||||
connectionLog.setMovementMethod(new ScrollingMovementMethod());
|
||||
connectionLog.setTextIsSelectable(true);
|
||||
connectionLog.setOnTouchListener((v, event) -> {
|
||||
v.getParent().requestDisallowInterceptTouchEvent(true);
|
||||
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
|
||||
v.getParent().requestDisallowInterceptTouchEvent(false);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
updateUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v == checkbox_global || v == checkbox_remote_dns || v == checkbox_maintenance) {
|
||||
mainActivity.savePrefs();
|
||||
updateUI();
|
||||
} else if (v == button_apps) {
|
||||
startActivity(new Intent(requireContext(), AppListActivity.class));
|
||||
} else if (v.getId() == R.id.save) {
|
||||
mainActivity.savePrefs();
|
||||
Toast.makeText(requireContext(), R.string.saved_toast, Toast.LENGTH_SHORT).show();
|
||||
addToLog(getString(R.string.settings_saved));
|
||||
} else if (v.getId() == R.id.btn_clear_log) {
|
||||
showResetLogConfirmation();
|
||||
} else if (v.getId() == R.id.btn_copy_log) {
|
||||
ClipboardManager clipboard = (ClipboardManager) requireContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("IIAB Log", connectionLog.getText().toString());
|
||||
if (clipboard != null) {
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast.makeText(requireContext(), R.string.log_copied_toast, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateUI() {
|
||||
if (button_control == null) return;
|
||||
|
||||
boolean vpnActive = mainActivity.prefs.getEnable();
|
||||
boolean watchdogActive = mainActivity.prefs.getWatchdogEnable();
|
||||
|
||||
if (dashboardManager != null) dashboardManager.setTunnelState(vpnActive, mainActivity.isProxyDegraded);
|
||||
|
||||
if (vpnActive) {
|
||||
button_control.setText(R.string.control_disable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_vpn_on));
|
||||
} else {
|
||||
button_control.setText(R.string.control_enable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_vpn_off));
|
||||
}
|
||||
if (watchdogActive) {
|
||||
watchdogControl.setText(R.string.watchdog_disable);
|
||||
} else {
|
||||
watchdogControl.setText(R.string.watchdog_enable);
|
||||
}
|
||||
edittext_socks_addr.setText(mainActivity.prefs.getSocksAddress());
|
||||
edittext_socks_udp_addr.setText(mainActivity.prefs.getSocksUdpAddress());
|
||||
edittext_socks_port.setText(String.valueOf(mainActivity.prefs.getSocksPort()));
|
||||
edittext_socks_user.setText(mainActivity.prefs.getSocksUsername());
|
||||
edittext_socks_pass.setText(mainActivity.prefs.getSocksPassword());
|
||||
edittext_dns_ipv4.setText(mainActivity.prefs.getDnsIpv4());
|
||||
edittext_dns_ipv6.setText(mainActivity.prefs.getDnsIpv6());
|
||||
checkbox_ipv4.setChecked(mainActivity.prefs.getIpv4());
|
||||
checkbox_ipv6.setChecked(mainActivity.prefs.getIpv6());
|
||||
checkbox_global.setChecked(mainActivity.prefs.getGlobal());
|
||||
checkbox_udp_in_tcp.setChecked(mainActivity.prefs.getUdpInTcp());
|
||||
checkbox_remote_dns.setChecked(mainActivity.prefs.getRemoteDns());
|
||||
checkbox_maintenance.setChecked(mainActivity.prefs.getMaintenanceMode());
|
||||
boolean editable = !vpnActive;
|
||||
edittext_socks_addr.setEnabled(editable);
|
||||
edittext_socks_port.setEnabled(editable);
|
||||
button_save.setEnabled(editable);
|
||||
|
||||
checkbox_maintenance.setEnabled(editable);
|
||||
if (textview_maintenance_warning != null) {
|
||||
textview_maintenance_warning.setVisibility(vpnActive ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateUIColorsAndVisibility() {
|
||||
if (!isAdded() || getContext() == null) {
|
||||
return;
|
||||
}
|
||||
if (button_control == null) return;
|
||||
|
||||
boolean isVpnActive = mainActivity.prefs.getEnable();
|
||||
boolean isWatchdogOn = mainActivity.prefs.getWatchdogEnable();
|
||||
|
||||
if (dashboardManager != null) {
|
||||
dashboardManager.setTunnelState(isVpnActive, mainActivity.isProxyDegraded);
|
||||
}
|
||||
|
||||
// Main VPN Button
|
||||
if (!mainActivity.isServerAlive) {
|
||||
if (isVpnActive) {
|
||||
button_control.setText(R.string.control_disable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_vpn_on_dim));
|
||||
} else {
|
||||
button_control.setText(R.string.control_enable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_vpn_off_dim));
|
||||
}
|
||||
} else {
|
||||
button_control.setEnabled(true);
|
||||
if (isVpnActive) {
|
||||
button_control.setText(R.string.control_disable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_vpn_on));
|
||||
} else {
|
||||
button_control.setText(R.string.control_enable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_vpn_off));
|
||||
}
|
||||
}
|
||||
|
||||
// Explore Button
|
||||
button_browse_content.setVisibility(View.VISIBLE);
|
||||
if (!mainActivity.isServerAlive) {
|
||||
stopExplorePulse();
|
||||
button_browse_content.setEnabled(true);
|
||||
button_browse_content.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_explore_disabled));
|
||||
button_browse_content.setAlpha(1.0f);
|
||||
button_browse_content.setTextColor(Color.parseColor("#888888"));
|
||||
} else if (mainActivity.isNegotiating) {
|
||||
button_browse_content.setEnabled(true);
|
||||
button_browse_content.setTextColor(Color.WHITE);
|
||||
} else {
|
||||
stopExplorePulse();
|
||||
button_browse_content.setEnabled(true);
|
||||
button_browse_content.setTextColor(Color.WHITE);
|
||||
button_browse_content.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_explore_ready));
|
||||
|
||||
if (isVpnActive && !mainActivity.isProxyDegraded) {
|
||||
button_browse_content.setAlpha(1.0f);
|
||||
startExplorePulse();
|
||||
} else {
|
||||
button_browse_content.setAlpha(0.6f);
|
||||
}
|
||||
}
|
||||
|
||||
// Server Control Logic
|
||||
if (mainActivity.targetServerState != null) {
|
||||
btnServerControl.setAlpha(0.6f);
|
||||
btnServerControl.setText(mainActivity.serverTransitionText);
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_explore_disabled));
|
||||
} else {
|
||||
btnServerControl.setAlpha(1.0f);
|
||||
if (mainActivity.isServerAlive) {
|
||||
btnServerControl.setText(R.string.stop_server);
|
||||
if (isWatchdogOn) {
|
||||
deckContainer.setBackgroundColor(Color.parseColor("#44FF9800"));
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_watchdog_on));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_watchdog_on));
|
||||
} else {
|
||||
if (fusionAnimator == null || !fusionAnimator.isRunning()) deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_danger));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_watchdog_off));
|
||||
}
|
||||
} else {
|
||||
deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
btnServerControl.setText(R.string.launch_server);
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_success));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), isWatchdogOn ? R.color.btn_watchdog_on : R.color.btn_watchdog_off));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopBtnProgress() {
|
||||
btnServerControl.stopProgress();
|
||||
}
|
||||
|
||||
public void startFusionPulse() {
|
||||
deckContainer.setBackgroundColor(Color.parseColor("#44FF9800"));
|
||||
if (fusionAnimator != null && fusionAnimator.isRunning()) fusionAnimator.cancel();
|
||||
fusionAnimator = ObjectAnimator.ofFloat(deckContainer, "alpha", 1f, 0.4f);
|
||||
fusionAnimator.setDuration(600);
|
||||
fusionAnimator.setRepeatCount(ObjectAnimator.INFINITE);
|
||||
fusionAnimator.setRepeatMode(ObjectAnimator.REVERSE);
|
||||
fusionAnimator.start();
|
||||
}
|
||||
|
||||
public void startExitPulse() {
|
||||
if (fusionAnimator != null && fusionAnimator.isRunning()) fusionAnimator.cancel();
|
||||
fusionAnimator = ObjectAnimator.ofFloat(deckContainer, "alpha", deckContainer.getAlpha(), 0.3f);
|
||||
fusionAnimator.setDuration(800);
|
||||
fusionAnimator.setRepeatCount(ObjectAnimator.INFINITE);
|
||||
fusionAnimator.setRepeatMode(ObjectAnimator.REVERSE);
|
||||
fusionAnimator.start();
|
||||
}
|
||||
|
||||
public void startExplorePulse() {
|
||||
button_browse_content.setAlpha(1.0f);
|
||||
button_browse_content.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_explore_ready));
|
||||
if (exploreAnimator == null) {
|
||||
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1.0f, 1.03f);
|
||||
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.0f, 1.03f);
|
||||
exploreAnimator = ObjectAnimator.ofPropertyValuesHolder(button_browse_content, scaleX, scaleY);
|
||||
exploreAnimator.setDuration(800);
|
||||
exploreAnimator.setRepeatCount(ObjectAnimator.INFINITE);
|
||||
exploreAnimator.setRepeatMode(ObjectAnimator.REVERSE);
|
||||
}
|
||||
if (!exploreAnimator.isRunning()) exploreAnimator.start();
|
||||
}
|
||||
|
||||
public void stopExplorePulse() {
|
||||
if (exploreAnimator != null && exploreAnimator.isRunning()) exploreAnimator.cancel();
|
||||
button_browse_content.setScaleX(1.0f);
|
||||
button_browse_content.setScaleY(1.0f);
|
||||
button_browse_content.setBackgroundTintList(ContextCompat.getColorStateList(requireContext(), R.color.btn_explore_ready));
|
||||
button_browse_content.setAlpha(0.6f);
|
||||
}
|
||||
|
||||
public void finalizeEntryPulse() {
|
||||
if (fusionAnimator != null) fusionAnimator.cancel();
|
||||
deckContainer.setAlpha(1f);
|
||||
}
|
||||
|
||||
public void finalizeExitPulse() {
|
||||
if (fusionAnimator != null) fusionAnimator.cancel();
|
||||
deckContainer.animate().alpha(1f).setDuration(300).withEndAction(() -> deckContainer.setBackgroundColor(Color.TRANSPARENT)).start();
|
||||
}
|
||||
|
||||
public void addToLog(String message) {
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
|
||||
String currentTime = sdf.format(new Date());
|
||||
String logEntry = "[" + currentTime + "] " + message + "\n";
|
||||
if (connectionLog != null) {
|
||||
connectionLog.append(logEntry);
|
||||
scrollToBottom();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void scrollToBottom() {
|
||||
if (connectionLog != null && connectionLog.getLayout() != null) {
|
||||
int scroll = connectionLog.getLayout().getLineTop(connectionLog.getLineCount()) - connectionLog.getHeight();
|
||||
if (scroll > 0) connectionLog.scrollTo(0, scroll);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLogSizeUI() {
|
||||
if (logSizeText == null) return;
|
||||
String sizeStr = LogManager.getFormattedSize(requireContext());
|
||||
logSizeText.setText(getString(R.string.log_size_format, sizeStr));
|
||||
}
|
||||
|
||||
public void updateConnectivityLeds(boolean wifiOn, boolean hotspotOn) {
|
||||
if (dashboardManager != null) {
|
||||
dashboardManager.updateConnectivityLeds(wifiOn, hotspotOn);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLogVisible() {
|
||||
return connectionLog != null && connectionLog.getVisibility() == View.VISIBLE;
|
||||
}
|
||||
|
||||
private void handleLogToggle() {
|
||||
boolean isOpening = connectionLog.getVisibility() == View.GONE;
|
||||
if (isOpening) {
|
||||
if (mainActivity.isReadingLogs) return;
|
||||
mainActivity.isReadingLogs = true;
|
||||
if (logProgress != null) logProgress.setVisibility(View.VISIBLE);
|
||||
|
||||
LogManager.readLogsAsync(requireContext(), (logContent, isRapidGrowth) -> {
|
||||
if (connectionLog != null) {
|
||||
connectionLog.setText(logContent);
|
||||
scrollToBottom();
|
||||
}
|
||||
if (logProgress != null) logProgress.setVisibility(View.GONE);
|
||||
if (logWarning != null) logWarning.setVisibility(isRapidGrowth ? View.VISIBLE : View.GONE);
|
||||
updateLogSizeUI();
|
||||
mainActivity.isReadingLogs = false;
|
||||
});
|
||||
mainActivity.startLogSizeUpdates();
|
||||
} else {
|
||||
mainActivity.stopLogSizeUpdates();
|
||||
}
|
||||
toggleVisibility(connectionLog, logLabel, getString(R.string.connection_log_label));
|
||||
logActions.setVisibility(connectionLog.getVisibility());
|
||||
if (logSizeText != null) logSizeText.setVisibility(connectionLog.getVisibility());
|
||||
}
|
||||
|
||||
private void handleConfigToggle() {
|
||||
if (configLayout.getVisibility() == View.GONE) {
|
||||
if (BiometricHelper.isDeviceSecure(requireContext())) {
|
||||
BiometricHelper.prompt((androidx.appcompat.app.AppCompatActivity) requireActivity(),
|
||||
getString(R.string.auth_required_title),
|
||||
getString(R.string.auth_required_subtitle),
|
||||
() -> toggleVisibility(configLayout, configLabel, getString(R.string.advanced_settings_label)));
|
||||
} else {
|
||||
BiometricHelper.showEnrollmentDialog(requireContext());
|
||||
}
|
||||
} else {
|
||||
toggleVisibility(configLayout, configLabel, getString(R.string.advanced_settings_label));
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleVisibility(View view, TextView label, String text) {
|
||||
boolean isGone = view.getVisibility() == View.GONE;
|
||||
view.setVisibility(isGone ? View.VISIBLE : View.GONE);
|
||||
label.setText(String.format(getString(isGone ? R.string.label_separator_down : R.string.label_separator_up), text));
|
||||
}
|
||||
|
||||
private void showResetLogConfirmation() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.log_reset_confirm_title)
|
||||
.setMessage(R.string.log_reset_confirm_msg)
|
||||
.setPositiveButton(R.string.reset_log, (dialog, which) -> {
|
||||
LogManager.clearLogs(requireContext(), new LogManager.LogClearCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
connectionLog.setText("");
|
||||
addToLog(getString(R.string.log_reset_user));
|
||||
if (logWarning != null) logWarning.setVisibility(View.GONE);
|
||||
updateLogSizeUI();
|
||||
Toast.makeText(requireContext(), R.string.log_cleared_toast, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
Toast.makeText(requireContext(), getString(R.string.failed_reset_log, message), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null).show();
|
||||
}
|
||||
|
||||
public void savePrefsFromUI() {
|
||||
mainActivity.prefs.setSocksAddress("127.0.0.1");
|
||||
mainActivity.prefs.setSocksPort(1080);
|
||||
mainActivity.prefs.setSocksUdpAddress("");
|
||||
mainActivity.prefs.setSocksUsername("");
|
||||
mainActivity.prefs.setSocksPassword("");
|
||||
mainActivity.prefs.setIpv4(true);
|
||||
mainActivity.prefs.setIpv6(true);
|
||||
mainActivity.prefs.setUdpInTcp(false);
|
||||
mainActivity.prefs.setRemoteDns(true);
|
||||
mainActivity.prefs.setGlobal(true);
|
||||
|
||||
mainActivity.prefs.setDnsIpv4(edittext_dns_ipv4.getText().toString());
|
||||
mainActivity.prefs.setDnsIpv6(edittext_dns_ipv6.getText().toString());
|
||||
mainActivity.prefs.setMaintenanceMode(checkbox_maintenance.isChecked());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : VpnRecoveryReceiver
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Button Tunnel helper
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.app.Notification;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* ============================================================================
|
||||
* Name : WatchdogService.java
|
||||
* Author : IIAB Project
|
||||
* Copyright : Copyright (c) 2026 IIAB Project
|
||||
* Description : Watchdog service helper
|
||||
* ============================================================================
|
||||
*/
|
||||
package org.iiab.controller;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp" android:height="24dp"
|
||||
android:viewportWidth="24" android:viewportHeight="24"
|
||||
android:tint="?attr/colorPrimary">
|
||||
<path android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2a3,3 0 0,0 -3,3v2L6.5,7a1.5,1.5 0 0,0 -1.5,1.5v1h14v-1A1.5,1.5 0 0,0 17.5,7L15,7V5A3,3 0 0,0 12,2M12,4a1,1 0 0,1 1,1v2h-2V5A1,1 0 0,1 12,4M4,11v9a2,2 0 0,0 2,2h12a2,2 0 0,0 2,-2v-9H4M11,13h2v7h-2v-7M7.5,13h1.5v5H7.5v-5M15,13h1.5v5H15v-5z"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<corners android:radius="6dp" />
|
||||
<solid android:color="@color/dash_bar_bg" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape>
|
||||
<corners android:radius="6dp" />
|
||||
<solid android:color="#FFFFFF" /> </shape>
|
||||
</clip>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
|
|
@ -5,8 +5,6 @@
|
|||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/windowBackground">
|
||||
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_setup_continue"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -73,6 +71,17 @@
|
|||
android:layout_marginBottom="8dp"
|
||||
android:theme="@style/PurpleSwitchTheme"/>
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/switch_perm_storage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/setup_perm_storage"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="16sp"
|
||||
android:paddingVertical="12dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:theme="@style/PurpleSwitchTheme"/>
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/switch_perm_vpn"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -129,6 +138,16 @@
|
|||
android:gravity="start|center_vertical"
|
||||
android:textColor="@color/lightGray66" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_termux_storage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/setup_termux_storage_btn"
|
||||
android:textAllCaps="false"
|
||||
style="?android:attr/borderlessButtonStyle"
|
||||
android:gravity="start|center_vertical"
|
||||
android:textColor="@color/lightGray66" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_manage_termux"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,291 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/dash_bg_main">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dash_text_device_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dash_device"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/dash_text_primary"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="@color/dash_bg_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dash_text_wifi_ip"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textColor="@color/dash_text_primary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dash_text_hotspot_ip"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textColor="@color/dash_text_primary" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dash_text_uptime"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textColor="@color/dash_text_primary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dash_text_battery"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textColor="@color/dash_text_primary" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/dash_divider"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="8dp">
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/dash_main_storage"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/dash_text_primary" />
|
||||
<TextView
|
||||
android:id="@+id/dash_text_storage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="-- GB / -- GB"
|
||||
android:textColor="@color/dash_text_secondary"
|
||||
android:textSize="12sp"/>
|
||||
</LinearLayout>
|
||||
<ProgressBar
|
||||
android:id="@+id/dash_progress_storage"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="12dp"
|
||||
android:progressDrawable="@drawable/rounded_progress_bar"
|
||||
android:progressTint="@color/dash_bar_storage"
|
||||
android:progressBackgroundTint="@color/dash_bar_bg"
|
||||
android:progress="0"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginEnd="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dash_ram_memory"
|
||||
android:textColor="@color/dash_text_primary"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dash_text_ram"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="-- / --"
|
||||
android:textColor="@color/dash_text_secondary"
|
||||
android:textSize="12sp"
|
||||
android:gravity="end"
|
||||
android:layout_marginTop="2dp" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/dash_progress_ram"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:progressDrawable="@drawable/rounded_progress_bar"
|
||||
android:progressTint="@color/dash_bar_ram"
|
||||
android:progressBackgroundTint="@color/dash_bar_bg"
|
||||
android:progress="0"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dash_swap_virtual"
|
||||
android:textColor="@color/dash_text_primary"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dash_text_swap"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="-- / --"
|
||||
android:textColor="@color/dash_text_secondary"
|
||||
android:textSize="12sp"
|
||||
android:gravity="end"
|
||||
android:layout_marginTop="2dp" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/dash_progress_swap"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:progressDrawable="@drawable/rounded_progress_bar"
|
||||
android:progressTint="@color/dash_bar_swap"
|
||||
android:progressBackgroundTint="@color/dash_bar_bg"
|
||||
android:progress="0"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dash_iiab_system"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/dash_text_primary"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="@color/dash_bg_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/dash_server_status"
|
||||
android:textColor="@color/dash_text_primary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dash_badge_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dash_offline"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="#555555"
|
||||
android:textColor="@color/dash_text_primary"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingVertical="4dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/dash_divider"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dash_card_termux_state"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/led_termux_state"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:background="@drawable/led_off"
|
||||
android:layout_marginEnd="12dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_termux_state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dash_termux_searching"
|
||||
android:textColor="@color/dash_text_primary" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dash_installed_modules"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/dash_text_primary"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/modules_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
</LinearLayout>
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="24dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:padding="24dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:src="@android:drawable/ic_menu_manage"
|
||||
android:tint="?attr/colorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/deploy_wip_title"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/deploy_wip_desc"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_marginTop="8dp"/>
|
||||
</LinearLayout>
|
||||
|
|
@ -0,0 +1,402 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<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="@string/wifi"
|
||||
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="@string/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="@string/tunnel"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/control"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="90dp"
|
||||
android:text="@string/control_enable"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#FFFFFF"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="@color/btn_vpn_off"
|
||||
android:textAllCaps="false"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/control_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/vpn_description"
|
||||
android:textSize="13sp"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnBrowseContent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="90dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:text="@string/browse_content"
|
||||
android:textSize="21sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#FFFFFF"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="@color/btn_explore_disabled"
|
||||
android:textAllCaps="false"
|
||||
android:elevation="4dp"
|
||||
android:enabled="false" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/config_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/advanced_settings_label"
|
||||
android:textStyle="bold"
|
||||
android:padding="12dp"
|
||||
android:background="?attr/sectionHeaderBackground"
|
||||
android:textColor="#FFFFFF"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:layout_marginTop="20dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/config_layout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:padding="12dp"
|
||||
android:background="?attr/sectionBackground">
|
||||
|
||||
<Button
|
||||
android:id="@+id/apps"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/apps"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="#673AB7"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textAllCaps="false"/>
|
||||
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/dns_ipv4" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
|
||||
<EditText android:id="@+id/dns_ipv4" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary"/>
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/dns_ipv6" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
|
||||
<EditText android:id="@+id/dns_ipv6" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_maintenance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/maintenance_mode"
|
||||
android:checked="true" />
|
||||
<TextView
|
||||
android:id="@+id/maintenance_warning"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/maintenance_warning_msg"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="italic"
|
||||
android:textColor="#FF9800"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button android:id="@+id/save"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/save"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="#555555"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textAllCaps="false"/>
|
||||
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_addr" android:textColor="?android:attr/textColorSecondary" android:textSize="12sp" android:visibility="gone"/>
|
||||
<EditText android:id="@+id/socks_addr" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary" android:visibility="gone"/>
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_port" android:textColor="?android:attr/textColorSecondary" android:textSize="12sp" android:visibility="gone"/>
|
||||
<EditText android:id="@+id/socks_port" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:inputType="number" android:textColor="?android:attr/textColorPrimary" android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/adv_config_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/advanced_settings_label"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:padding="8dp"
|
||||
android:textSize="13sp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/advanced_config"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_udp_addr" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
|
||||
<EditText android:id="@+id/socks_udp_addr" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary"/>
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_user" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
|
||||
<EditText android:id="@+id/socks_user" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary"/>
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_pass" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
|
||||
<EditText android:id="@+id/socks_pass" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:inputType="textPassword" android:textColor="?android:attr/textColorPrimary"/>
|
||||
<CheckBox android:id="@+id/udp_in_tcp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/udp_in_tcp" android:textColor="?android:attr/textColorSecondary"/>
|
||||
<CheckBox android:id="@+id/ipv4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ipv4" android:textColor="?android:attr/textColorSecondary"/>
|
||||
<CheckBox android:id="@+id/ipv6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ipv6" android:textColor="?android:attr/textColorSecondary"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<CheckBox android:id="@+id/remote_dns" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/remote_dns" android:textColor="?android:attr/textColorSecondary"/>
|
||||
<CheckBox android:id="@+id/global" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/global" android:textColor="?android:attr/textColorSecondary"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/divider_color" android:layout_marginBottom="16dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/deck_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="#00000000"
|
||||
android:padding="3dp"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false"
|
||||
android:weightSum="2">
|
||||
|
||||
<org.iiab.controller.ProgressButton
|
||||
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"
|
||||
app:progressButtonHeight="6dp"
|
||||
app:progressButtonDuration="@integer/server_cool_off_duration_ms"
|
||||
app:progressButtonColor="#FF9800" />
|
||||
|
||||
<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"
|
||||
android:text="@string/watchdog_description"
|
||||
android:textSize="13sp"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/divider_color" android:layout_marginVertical="20dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/log_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/connection_log_label"
|
||||
android:textStyle="bold"
|
||||
android:padding="10dp"
|
||||
android:background="?attr/sectionHeaderBackground"
|
||||
android:textColor="#FFFFFF"
|
||||
android:clickable="true"
|
||||
android:focusable="true"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/log_warning_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/text_warning"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="italic"
|
||||
android:padding="4dp"
|
||||
android:visibility="gone"
|
||||
android:text="@string/log_warning_rapid_growth" />
|
||||
|
||||
<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"
|
||||
android:layout_height="250dp"
|
||||
android:background="#000000"
|
||||
android:textColor="#00FF00"
|
||||
android:fontFamily="monospace"
|
||||
android:padding="8dp"
|
||||
android:visibility="gone"
|
||||
android:scrollbars="vertical"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbarSize="10dp"
|
||||
android:scrollbarThumbVertical="@drawable/scrollbar_thumb"
|
||||
android:text="@string/system_ready"
|
||||
android:textSize="11sp"/>
|
||||
|
||||
<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" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/log_actions"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="4dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_clear_log"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/reset_log"
|
||||
android:textSize="12sp"
|
||||
android:backgroundTint="@color/btn_danger"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textAllCaps="false"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_copy_log"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/copy_all"
|
||||
android:textSize="12sp"
|
||||
android:backgroundTint="@color/btn_success"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textAllCaps="false"
|
||||
android:layout_marginStart="4dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="32dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
|
@ -6,7 +6,6 @@
|
|||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/windowBackground">
|
||||
|
||||
<!-- Custom Header/Toolbar -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/actionBarSize"
|
||||
|
|
@ -53,7 +52,6 @@
|
|||
android:scaleType="fitCenter"
|
||||
android:tint="#FFFFFF" />
|
||||
|
||||
<!-- Triple Toggle Theme ImageButton -->
|
||||
<ImageButton
|
||||
android:id="@+id/theme_toggle"
|
||||
android:layout_width="48dp"
|
||||
|
|
@ -66,443 +64,32 @@
|
|||
android:tint="#FFFFFF" />
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:tabMode="fixed"
|
||||
app:tabGravity="fill"
|
||||
app:tabIndicatorColor="#FFFFFF"
|
||||
app:tabIndicatorHeight="3dp"
|
||||
app:tabSelectedTextColor="#FFFFFF"
|
||||
app:tabTextColor="#888888"
|
||||
app:tabTextAppearance="@style/CustomTabTextStyle"
|
||||
android:background="#1A1A1A"/>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/view_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<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
|
||||
android:id="@+id/control"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="90dp"
|
||||
android:text="@string/control_enable"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#FFFFFF"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="@color/btn_vpn_off"
|
||||
android:textAllCaps="false"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/control_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/vpn_description"
|
||||
android:textSize="13sp"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="12dp"/>
|
||||
|
||||
<!-- Local WebView -->
|
||||
<Button
|
||||
android:id="@+id/btnBrowseContent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="90dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:text="@string/browse_content"
|
||||
android:textSize="21sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#FFFFFF"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="@color/btn_explore_disabled"
|
||||
android:textAllCaps="false"
|
||||
android:elevation="4dp"
|
||||
android:enabled="false" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/config_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/advanced_settings_label"
|
||||
android:textStyle="bold"
|
||||
android:padding="12dp"
|
||||
android:background="?attr/sectionHeaderBackground"
|
||||
android:textColor="#FFFFFF"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:layout_marginTop="20dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/config_layout"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:padding="12dp"
|
||||
android:background="?attr/sectionBackground">
|
||||
|
||||
<Button
|
||||
android:id="@+id/apps"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/apps"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="#673AB7"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textAllCaps="false"/>
|
||||
|
||||
<TextView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dns_ipv4"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="11sp"/>
|
||||
<EditText android:id="@+id/dns_ipv4"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
<TextView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dns_ipv6"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="11sp"/>
|
||||
<EditText android:id="@+id/dns_ipv6"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox_maintenance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="Maintenance Mode"
|
||||
android:checked="true" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/maintenance_warning"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Disable Safe Pocket Web in order to modify"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="italic"
|
||||
android:textColor="#FF9800"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button android:id="@+id/save"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/save"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="#555555"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textAllCaps="false"/>
|
||||
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_addr" android:textColor="?android:attr/textColorSecondary" android:textSize="12sp" android:visibility="gone"/>
|
||||
<EditText android:id="@+id/socks_addr" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary" android:visibility="gone"/>
|
||||
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_port" android:textColor="?android:attr/textColorSecondary" android:textSize="12sp" android:visibility="gone"/>
|
||||
<EditText android:id="@+id/socks_port" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:inputType="number" android:textColor="?android:attr/textColorPrimary" android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/adv_config_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/advanced_settings_label"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:padding="8dp"
|
||||
android:textSize="13sp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/advanced_config"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_udp_addr" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
|
||||
<EditText android:id="@+id/socks_udp_addr" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_user" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
|
||||
<EditText android:id="@+id/socks_user" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/socks_pass" android:textColor="?android:attr/textColorSecondary" android:textSize="11sp"/>
|
||||
<EditText android:id="@+id/socks_pass" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:inputType="textPassword" android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
<CheckBox android:id="@+id/udp_in_tcp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/udp_in_tcp" android:textColor="?android:attr/textColorSecondary"/>
|
||||
<CheckBox android:id="@+id/ipv4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ipv4" android:textColor="?android:attr/textColorSecondary"/>
|
||||
<CheckBox android:id="@+id/ipv6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ipv6" android:textColor="?android:attr/textColorSecondary"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="gone">
|
||||
<CheckBox android:id="@+id/remote_dns" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/remote_dns" android:textColor="?android:attr/textColorSecondary"/>
|
||||
<CheckBox android:id="@+id/global" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/global" android:textColor="?android:attr/textColorSecondary"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- HR above Watchdog -->
|
||||
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/divider_color" android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Watchdog Control Section -->
|
||||
<LinearLayout
|
||||
android:id="@+id/deck_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="#00000000"
|
||||
android:padding="3dp"
|
||||
android:orientation="horizontal"
|
||||
android:baselineAligned="false"
|
||||
android:weightSum="2">
|
||||
|
||||
<org.iiab.controller.ProgressButton
|
||||
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"
|
||||
|
||||
app:progressButtonHeight="6dp"
|
||||
app:progressButtonDuration="@integer/server_cool_off_duration_ms"
|
||||
app:progressButtonColor="#FF9800" />
|
||||
|
||||
<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"
|
||||
android:text="@string/watchdog_description"
|
||||
android:textSize="13sp"
|
||||
android:gravity="center"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/divider_color" android:layout_marginVertical="20dp"/>
|
||||
|
||||
<!-- Log Section (Collapsible) -->
|
||||
<TextView
|
||||
android:id="@+id/log_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/connection_log_label"
|
||||
android:textStyle="bold"
|
||||
android:padding="10dp"
|
||||
android:background="?attr/sectionHeaderBackground"
|
||||
android:textColor="#FFFFFF"
|
||||
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="@color/text_warning"
|
||||
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"
|
||||
android:layout_height="250dp"
|
||||
android:background="#000000"
|
||||
android:textColor="#00FF00"
|
||||
android:fontFamily="monospace"
|
||||
android:padding="8dp"
|
||||
android:visibility="gone"
|
||||
android:scrollbars="vertical"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbarSize="10dp"
|
||||
android:scrollbarThumbVertical="@drawable/scrollbar_thumb"
|
||||
android:text="@string/system_ready"
|
||||
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"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="4dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_clear_log"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/reset_log"
|
||||
android:textSize="12sp"
|
||||
android:backgroundTint="@color/btn_danger"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textAllCaps="false"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_copy_log"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/copy_all"
|
||||
android:textSize="12sp"
|
||||
android:backgroundTint="@color/btn_success"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textAllCaps="false"
|
||||
android:layout_marginStart="4dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Version Footer -->
|
||||
<View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/divider_color" android:layout_marginTop="32dp" android:layout_marginBottom="8dp"/>
|
||||
android:layout_weight="1" />
|
||||
<TextView
|
||||
android:id="@+id/version_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="v0.1.17alpha"
|
||||
android:textColor="@color/text_muted"
|
||||
android:textSize="10sp"
|
||||
android:paddingBottom="16dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
android:text="v0.1.x"
|
||||
android:textColor="#888888"
|
||||
android:textSize="11sp"
|
||||
android:padding="8dp"
|
||||
android:background="#1A1A1A" />
|
||||
</LinearLayout>
|
||||
|
|
@ -98,6 +98,8 @@
|
|||
<string name="termux_perm_denied">Permiso de Termux denegado</string>
|
||||
<string name="notif_perm_granted">Permiso de notificaciones concedido</string>
|
||||
<string name="notif_perm_denied">Permiso de notificaciones denegado</string>
|
||||
<string name="force_termux_foreground">Forzar a Termux a pasar a primer plano...</string>
|
||||
<string name="termux_stuck_warning">¿Termux no abre? Habilite Watchdog Maestro para forzar que obtenga el foco.</string>
|
||||
|
||||
<!-- Logs -->
|
||||
<string name="log_reset_confirm_title">¿Reiniciar historial de log?</string>
|
||||
|
|
@ -152,4 +154,67 @@
|
|||
<string name="recovery_channel_name">Recuperación VPN</string>
|
||||
<string name="recovery_notif_title">Safe Pocket Web Interrumpido</string>
|
||||
<string name="recovery_notif_text">Toque para restaurar el entorno seguro inmediatamente.</string>
|
||||
|
||||
<!-- Tabs design UI -->
|
||||
<string name="tab_status">Estado</string>
|
||||
<string name="tab_usage">Uso</string>
|
||||
<string name="tab_deploy">Instalación</string>
|
||||
|
||||
<string name="dash_uptime">Tiempo de funcionamiento: %1$s</string>
|
||||
<string name="dash_ip">IP: %1$s</string>
|
||||
|
||||
<!-- Landing -->
|
||||
<string name="dash_title">IIAB-oA Controller</string>
|
||||
<string name="dash_subtitle_localhost">localhost</string>
|
||||
<string name="dash_online">En línea</string>
|
||||
<string name="dash_offline">Desconectado</string>
|
||||
<string name="dash_device">Cargando el dispositivo...</string>
|
||||
<string name="dash_iiab_system">Sistema IIAB-oA</string>
|
||||
<string name="dash_server_status">Estado del Servidor:</string>
|
||||
<string name="dash_termux_searching">Buscando instalación...</string>
|
||||
|
||||
<string name="dash_main_storage">Almacenamiento Principal</string>
|
||||
<string name="dash_ram_memory">Memoria RAM</string>
|
||||
<string name="dash_swap_virtual">Swap (Virtual)</string>
|
||||
|
||||
<string name="dash_system_state">Estado del Sistema</string>
|
||||
<string name="dash_state_installed">Instalación detectada</string>
|
||||
<string name="dash_state_raw">Termux Raw (Instalación requerida)</string>
|
||||
|
||||
<string name="dash_installed_modules">Módulos Instalados</string>
|
||||
|
||||
<string name="deploy_wip_title">WIP - En construcción</string>
|
||||
|
||||
<string name="deploy_wip_desc">El módulo Termux y el instalador de entorno estarán disponibles aquí pronto.</string>
|
||||
|
||||
<!-- Usage UI -->
|
||||
<string name="wifi">Wi-Fi</string>
|
||||
<string name="hotspot">Hotspot</string>
|
||||
<string name="tunnel">Túnel</string>
|
||||
<string name="maintenance_mode">Modo Mantenimiento</string>
|
||||
<string name="maintenance_warning_msg">Desactive Safe Pocket Web para poder modificar</string>
|
||||
|
||||
<!-- Misc/Internal -->
|
||||
<string name="pref_file_internal">IIAB_Internal</string>
|
||||
<string name="pref_key_setup_complete">setup_complete</string>
|
||||
<string name="version_footer_format">IIAB-oA · 2026 · Controller %1$s</string>
|
||||
<string name="version_footer_fallback">IIAB-oA · 2026 · Controller v0.1.xbeta</string>
|
||||
<string name="termux_invocation_error">Error al invocar Termux: %1$s</string>
|
||||
<string name="uptime_no_value">Tiempo de actividad: --</string>
|
||||
<string name="hotspot_fdash">Hotspot: --</string>
|
||||
<string name="wi_fi_fdash">Wi-Fi: --</string>
|
||||
<string name="battery_custom">"Battery: "</string>
|
||||
<string name="battery_no_value">Battery: --%</string>
|
||||
|
||||
<string name="dash_wifi_format"><b>Wi-Fi:</b> %1$s</string>
|
||||
<string name="dash_hotspot_format"><b>Hotspot:</b> %1$s</string>
|
||||
<string name="dash_uptime_format"><b>Tiempo de actividad:</b> %1$s</string>
|
||||
<string name="dash_battery_format"><b>Bateria:</b> %1$d%%</string>
|
||||
<string name="dash_battery_no_value"><b>Bateria:</b> --%%</string>
|
||||
<string name="dash_books">Libros</string>
|
||||
<string name="dash_kiwix">Kiwix</string>
|
||||
<string name="dash_kolibri">Kolibri</string>
|
||||
<string name="dash_maps">Mapas</string>
|
||||
<string name="dash_matomo">Matomo</string>
|
||||
<string name="dash_system">Sistema</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,220 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- General App -->
|
||||
<string name="app_name">IIAB-oA Controller</string>
|
||||
<string name="default_version">v0.1.x</string>
|
||||
<string name="save">Enregistrer</string>
|
||||
<string name="cancel">Annuler</string>
|
||||
<string name="saved_toast">Enregistré</string>
|
||||
<string name="settings_saved">Paramètres enregistrés</string>
|
||||
<string name="fix_action">CORRIGER</string>
|
||||
<string name="configuration_label">Configuration</string>
|
||||
<string name="advanced_settings_label">Paramètres du tunnel</string>
|
||||
<string name="connection_log_label">Journal de connexion</string>
|
||||
<string name="settings_label">PARAMÈTRES</string>
|
||||
|
||||
<!-- SetupActivity -->
|
||||
<string name="setup_title">Configuration initiale</string>
|
||||
<string name="setup_welcome">Bienvenue dans l\'assistant de configuration de %1$s.\n\nPour fonctionner correctement, nous avons besoin des autorisations suivantes :</string>
|
||||
<string name="setup_perm_notifications">Notifications Push</string>
|
||||
<string name="setup_perm_termux">Exécution de Termux</string>
|
||||
<string name="setup_perm_vpn">Safe Pocket Web (VPN)</string>
|
||||
<string name="setup_perm_battery">Désactiver l\'optimisation de la batterie</string>
|
||||
<string name="setup_continue">Continuer</string>
|
||||
<string name="revoke_permission_warning">Pour révoquer des autorisations, vous devez le faire depuis les paramètres système.</string>
|
||||
<string name="termux_not_installed_error">Termux n\'est pas installé ou l\'appareil n\'est pas pris en charge.</string>
|
||||
<string name="termux_not_installed">Termux n\'est pas installé.</string>
|
||||
|
||||
<!-- VPN / Socks -->
|
||||
<string name="control_enable">Activer Safe Pocket Web</string>
|
||||
<string name="control_disable">Désactiver Safe Pocket Web</string>
|
||||
<string name="vpn_description">Activez des URL conviviales. Bloquez les menaces.</string>
|
||||
<string name="socks_addr">Adresse Socks :</string>
|
||||
<string name="socks_udp_addr">Adresse UDP Socks :</string>
|
||||
<string name="socks_port">Port Socks :</string>
|
||||
<string name="socks_user">Nom d\'utilisateur Socks :</string>
|
||||
<string name="socks_pass">Mot de passe Socks :</string>
|
||||
<string name="dns_ipv4">DNS IPv4 :</string>
|
||||
<string name="dns_ipv6">DNS IPv6 :</string>
|
||||
<string name="udp_in_tcp">Relais UDP sur TCP</string>
|
||||
<string name="remote_dns">DNS distant</string>
|
||||
<string name="ipv4">IPv4</string>
|
||||
<string name="ipv6">IPv6</string>
|
||||
<string name="global">Global</string>
|
||||
<string name="apps">Applications</string>
|
||||
<string name="vpn_stopping">VPN en cours d\'arrêt...</string>
|
||||
<string name="vpn_starting">VPN en cours de démarrage...</string>
|
||||
<string name="user_initiated_conn">Connexion initiée par l\'utilisateur</string>
|
||||
<string name="vpn_permission_granted">Autorisation VPN accordée. Connexion...</string>
|
||||
<string name="tproxy_channel_name">socks5</string>
|
||||
|
||||
<!-- WatchdogService / IIABWatchdog -->
|
||||
<string name="watchdog_enable">Activer\nle Watchdog maître</string>
|
||||
<string name="watchdog_disable">Désactiver\nle Watchdog maître</string>
|
||||
<string name="watchdog_description">Protège Termux du mode Doze et maintient le Wi-Fi actif.</string>
|
||||
<string name="watchdog_stopped">Watchdog arrêté</string>
|
||||
<string name="watchdog_started">Watchdog démarré</string>
|
||||
<string name="watchdog_channel_name">Service IIAB Watchdog</string>
|
||||
<string name="watchdog_channel_desc">Garantit que les services restent actifs lorsque l\'écran est éteint.</string>
|
||||
<string name="watchdog_notif_title">IIAB Watchdog actif</string>
|
||||
<string name="watchdog_notif_text">Protection de l\'environnement Termux...</string>
|
||||
<string name="syncing_watchdog">Synchronisation de l\'état du Watchdog. Activé : %b</string>
|
||||
<string name="watchdog_thread_started">Watchdog Thread : Boucle démarrée</string>
|
||||
<string name="watchdog_thread_interrupted">Watchdog Thread : Interrompu, arrêt en cours...</string>
|
||||
<string name="watchdog_thread_error">Watchdog Thread : Erreur dans la boucle</string>
|
||||
<string name="watchdog_thread_ended">Watchdog Thread : Boucle terminée</string>
|
||||
<string name="cpu_wakelock_acquired">CPU WakeLock acquis sous protection VPN</string>
|
||||
<string name="wifi_lock_acquired">Wi-Fi Lock acquis sous protection VPN</string>
|
||||
<string name="error_acquiring_locks">Erreur lors de l\'acquisition des verrous</string>
|
||||
<string name="cpu_wakelock_released">CPU WakeLock libéré</string>
|
||||
<string name="wifi_lock_released">Wi-Fi Lock libéré</string>
|
||||
|
||||
<!-- Pulse / Heartbeat -->
|
||||
<string name="pulse_stimulating">Pulse : Stimulation de Termux...</string>
|
||||
<string name="critical_os_blocked">CRITIQUE : Le système d\'exploitation a bloqué la stimulation de Termux (SecurityException).</string>
|
||||
<string name="ping_ok">PING 8085 : OK</string>
|
||||
<string name="ping_fail">PING 8085 : ÉCHEC (%s)</string>
|
||||
<string name="session_started">SESSION DE HEARTBEAT DÉMARRÉE</string>
|
||||
<string name="session_stopped">SESSION DE HEARTBEAT ARRÊTÉE</string>
|
||||
<string name="permission_denied_log">Autorisation refusée : Assurez-vous que le manifeste contient RUN_COMMAND et que l\'application n\'est pas restreinte.</string>
|
||||
<string name="unexpected_error_termux">Erreur inattendue lors de l\'envoi de l\'intention vers Termux</string>
|
||||
<string name="pulse_error_log">Erreur de Pulse : %s</string>
|
||||
<string name="maintenance_write_failed">Échec de l\'écriture de maintenance</string>
|
||||
<string name="failed_write_blackbox">Échec de l\'écriture dans BlackBox</string>
|
||||
<string name="recovery_pulse_received">Pulse de récupération reçu du système. VPN en cours d\'application...</string>
|
||||
|
||||
<!-- TermuxCallbackReceiver / Operations -->
|
||||
<string name="termux_stimulus_ok">[Termux] Stimulus OK (exit 0)</string>
|
||||
<string name="termux_pulse_error">[Termux] Erreur de Pulse (exit %1$d) : %2$s</string>
|
||||
<string name="server_timeout_warning">Avertissement : Le délai de transition de l\'état du serveur a expiré.</string>
|
||||
<string name="server_booting">Démarrage...</string>
|
||||
<string name="server_shutting_down">Arrêt en cours...</string>
|
||||
<string name="failed_termux_intent">CRITIQUE : Échec de l\'intention Termux : %s</string>
|
||||
<string name="sent_to_termux">Envoyé à Termux : %s</string>
|
||||
<string name="maintenance_mode_enabled">Mode de maintenance activé : Termux a un accès direct à Internet</string>
|
||||
<string name="stop_server">🛑 Arrêter le serveur</string>
|
||||
<string name="launch_server">🚀 Lancer le serveur</string>
|
||||
<string name="termux_perm_granted">Autorisation Termux accordée</string>
|
||||
<string name="termux_perm_denied">Autorisation Termux refusée</string>
|
||||
<string name="notif_perm_granted">Autorisation de notification accordée</string>
|
||||
<string name="notif_perm_denied">Autorisation de notification refusée</string>
|
||||
<string name="force_termux_foreground">Forcer Termux au premier plan...</string>
|
||||
<string name="termux_stuck_warning">Termux ne s\'ouvre pas ? Activez le Watchdog maître pour le forcer à prendre le focus.</string>
|
||||
|
||||
<!-- Logs -->
|
||||
<string name="log_reset_confirm_title">Réinitialiser l\'historique des journaux ?</string>
|
||||
<string name="log_reset_confirm_msg">Cela supprimera définitivement tous les journaux de connexion enregistrés. Cette action ne peut pas être annulée.</string>
|
||||
<string name="log_warning_rapid_growth">Le fichier de journalisation croît trop rapidement, vous devriez peut-être vérifier si quelque chose échoue.</string>
|
||||
<string name="reset_log">Réinitialiser le journal</string>
|
||||
<string name="copy_all">Tout copier</string>
|
||||
<string name="log_reset_log">Journal réinitialisé</string>
|
||||
<string name="log_reset_user">Journal réinitialisé par l\'utilisateur</string>
|
||||
<string name="log_copied_toast">Journal copié dans le presse-papier</string>
|
||||
<string name="log_cleared_toast">Journal effacé</string>
|
||||
<string name="failed_reset_log">Échec de la réinitialisation du journal : %s</string>
|
||||
<string name="log_size_format">Taille : %1$s / 10 Mo</string>
|
||||
<string name="log_size_bytes">%d o</string>
|
||||
<string name="log_size_kb">%.1f Ko</string>
|
||||
<string name="log_size_mb">%.2f Mo</string>
|
||||
<string name="no_blackbox_found">--- Aucun fichier BlackBox trouvé ---</string>
|
||||
<string name="loading_history">--- Chargement de l\'historique ---</string>
|
||||
<string name="error_reading_history">Erreur lors de la lecture de l\'historique : %s</string>
|
||||
<string name="end_of_history">--- Fin de l\'historique ---</string>
|
||||
|
||||
<!-- Battery Optimizations -->
|
||||
<string name="battery_opt_title">Optimisation de la batterie</string>
|
||||
<string name="battery_opt_msg">Pour que le Watchdog fonctionne de manière fiable, veuillez désactiver les optimisations de batterie pour cette application.</string>
|
||||
<string name="go_to_settings">Aller aux paramètres</string>
|
||||
<string name="battery_opt_oppo_extra">\n\nOPPO/Realme détecté : Veuillez vous assurer d\'activer « Autoriser l\'activité en arrière-plan » dans les paramètres de cette application.</string>
|
||||
<string name="battery_opt_xiaomi_extra">\n\nXiaomi détecté : Veuillez régler l\'économie de batterie sur « Aucune restriction » dans les paramètres.</string>
|
||||
<string name="battery_opt_denied">Pour que l\'application fonctionne à 100 %, veuillez désactiver l\'optimisation de la batterie.</string>
|
||||
|
||||
<!-- UI / Misc -->
|
||||
<string name="browse_content">🚀 Explorer le contenu</string>
|
||||
<string name="system_ready">Système prêt...\n</string>
|
||||
<string name="app_started">Application démarrée</string>
|
||||
<string name="label_separator_down">▼ %s</string>
|
||||
<string name="label_separator_up">▶ %s</string>
|
||||
<string name="qr_error_no_server">Lancez le serveur pour partager du contenu sur le réseau.</string>
|
||||
<string name="qr_error_no_network">Activez le Wi-Fi ou le point d\'accès pour partager du contenu sur le réseau.</string>
|
||||
<string name="qr_title_wifi">Réseau Wi-Fi</string>
|
||||
<string name="qr_title_hotspot">Réseau point d\'accès</string>
|
||||
<string name="qr_flip_network">Changer de réseau</string>
|
||||
|
||||
<!-- Authentication / Security -->
|
||||
<string name="unlock_watchdog_title">Déverrouiller le Watchdog maître</string>
|
||||
<string name="unlock_watchdog_subtitle">Authentification requise pour arrêter la protection Termux</string>
|
||||
<string name="auth_success_disconnect">Authentification réussie. Déconnexion...</string>
|
||||
<string name="auth_required_title">Authentification requise</string>
|
||||
<string name="auth_required_subtitle">Authentifiez-vous pour désactiver l\'environnement sécurisé</string>
|
||||
<string name="security_required_title">Sécurité requise</string>
|
||||
<string name="security_required_msg">Vous devez définir un code PIN, un schéma ou une empreinte digitale sur votre appareil avant d\'activer l\'environnement sécurisé.</string>
|
||||
|
||||
<!-- VPN Recovery Service -->
|
||||
<string name="recovery_channel_name">Récupération VPN</string>
|
||||
<string name="recovery_notif_title">Safe Pocket Web interrompu</string>
|
||||
<string name="recovery_notif_text">Appuyez pour restaurer immédiatement l\'environnement sécurisé.</string>
|
||||
|
||||
<!-- Tabs design UI -->
|
||||
<string name="tab_status">Statut</string>
|
||||
<string name="tab_usage">Utilisation</string>
|
||||
<string name="tab_deploy">Installation</string>
|
||||
|
||||
<string name="dash_uptime">Temps de fonctionnement : %1$s</string>
|
||||
<string name="dash_ip">IP : %1$s</string>
|
||||
|
||||
<!-- Landing -->
|
||||
<string name="dash_title">IIAB-oA Controller</string>
|
||||
<string name="dash_subtitle_localhost">localhost</string>
|
||||
<string name="dash_online">En ligne</string>
|
||||
<string name="dash_offline">Hors ligne</string>
|
||||
<string name="dash_device">Chargement de l\'appareil...</string>
|
||||
<string name="dash_iiab_system">Système IIAB-oA</string>
|
||||
<string name="dash_server_status">Statut du serveur :</string>
|
||||
<string name="dash_termux_searching">Recherche d\'installation...</string>
|
||||
|
||||
<string name="dash_main_storage">Stockage principal</string>
|
||||
<string name="dash_ram_memory">Mémoire RAM</string>
|
||||
<string name="dash_swap_virtual">Swap (virtuel)</string>
|
||||
|
||||
<string name="dash_system_state">État du système</string>
|
||||
<string name="dash_state_installed">Installation détectée</string>
|
||||
<string name="dash_state_raw">Termux brut (installation requise)</string>
|
||||
|
||||
<string name="dash_installed_modules">Modules installés</string>
|
||||
|
||||
<string name="deploy_wip_title">WIP - En construction</string>
|
||||
|
||||
<string name="deploy_wip_desc">Le module Termux et l\'installateur d\'environnement seront disponibles ici prochainement.</string>
|
||||
|
||||
<!-- Usage UI -->
|
||||
<string name="wifi">Wi-Fi</string>
|
||||
<string name="hotspot">Point d\'accès</string>
|
||||
<string name="tunnel">Tunnel</string>
|
||||
<string name="maintenance_mode">Mode de maintenance</string>
|
||||
<string name="maintenance_warning_msg">Désactivez Safe Pocket Web pour pouvoir modifier</string>
|
||||
|
||||
<!-- Misc/Internal -->
|
||||
<string name="pref_file_internal">IIAB_Internal</string>
|
||||
<string name="pref_key_setup_complete">setup_complete</string>
|
||||
<string name="version_footer_format">IIAB-oA · 2026 · Controller %1$s</string>
|
||||
<string name="version_footer_fallback">IIAB-oA · 2026 · Controller v0.1.xbeta</string>
|
||||
<string name="termux_invocation_error">Erreur lors de l\'invocation de Termux : %1$s</string>
|
||||
<string name="uptime_no_value">Temps d\'activité : --</string>
|
||||
<string name="hotspot_fdash">Point d\'accès : --</string>
|
||||
<string name="wi_fi_fdash">Wi-Fi : --</string>
|
||||
<string name="battery_custom">"Battery: "</string>
|
||||
<string name="battery_no_value">Battery: --%</string>
|
||||
|
||||
<string name="dash_wifi_format"><b>Wi-Fi:</b> %1$s</string>
|
||||
<string name="dash_hotspot_format"><b>Hotspot:</b> %1$s</string>
|
||||
<string name="dash_uptime_format"><b>Uptime:</b> %1$s</string>
|
||||
<string name="dash_battery_format"><b>Battery:</b> %1$d%%</string>
|
||||
<string name="dash_battery_no_value"><b>Battery:</b> --%%</string>
|
||||
<string name="dash_books">Books</string>
|
||||
<string name="dash_kiwix">Kiwix</string>
|
||||
<string name="dash_kolibri">Kolibri</string>
|
||||
<string name="dash_maps">Maps</string>
|
||||
<string name="dash_matomo">Matomo</string>
|
||||
<string name="dash_system">System</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- General App -->
|
||||
<string name="app_name">IIAB-oA Controller</string>
|
||||
<string name="default_version">v0.1.x</string>
|
||||
<string name="save">सहेजें</string>
|
||||
<string name="cancel">रद्द करें</string>
|
||||
<string name="saved_toast">सहेजा गया</string>
|
||||
<string name="settings_saved">सेटिंग्स सहेजी गईं</string>
|
||||
<string name="fix_action">ठीक करें</string>
|
||||
<string name="configuration_label">कॉन्फ़िगरेशन</string>
|
||||
<string name="advanced_settings_label">टनल सेटिंग्स</string>
|
||||
<string name="connection_log_label">कनेक्शन लॉग</string>
|
||||
<string name="settings_label">सेटिंग्स</string>
|
||||
|
||||
<!-- SetupActivity -->
|
||||
<string name="setup_title">प्रारंभिक सेटअप</string>
|
||||
<string name="setup_welcome">%1$s सेटअप विज़ार्ड में आपका स्वागत है।\n\nठीक से काम करने के लिए, हमें निम्नलिखित अनुमतियों की आवश्यकता है:</string>
|
||||
<string name="setup_perm_notifications">पुश सूचनाएं</string>
|
||||
<string name="setup_perm_termux">Termux निष्पादन</string>
|
||||
<string name="setup_perm_vpn">Safe Pocket Web (VPN)</string>
|
||||
<string name="setup_perm_battery">बैटरी अनुकूलन अक्षम करें</string>
|
||||
<string name="setup_continue">जारी रखें</string>
|
||||
<string name="revoke_permission_warning">अनुमतियां रद्द करने के लिए, आपको सिस्टम सेटिंग्स में जाना होगा।</string>
|
||||
<string name="termux_not_installed_error">Termux इंस्टॉल नहीं है या डिवाइस समर्थित नहीं है।</string>
|
||||
<string name="termux_not_installed">Termux इंस्टॉल नहीं है।</string>
|
||||
|
||||
<!-- VPN / Socks -->
|
||||
<string name="control_enable">Safe Pocket Web सक्षम करें</string>
|
||||
<string name="control_disable">Safe Pocket Web अक्षम करें</string>
|
||||
<string name="vpn_description">अनुकूल URL सक्षम करें। खतरों को ब्लॉक करें।</string>
|
||||
<string name="socks_addr">Socks पता:</string>
|
||||
<string name="socks_udp_addr">Socks UDP पता:</string>
|
||||
<string name="socks_port">Socks पोर्ट:</string>
|
||||
<string name="socks_user">Socks उपयोगकर्ता नाम:</string>
|
||||
<string name="socks_pass">Socks पासवर्ड:</string>
|
||||
<string name="dns_ipv4">DNS IPv4:</string>
|
||||
<string name="dns_ipv6">DNS IPv6:</string>
|
||||
<string name="udp_in_tcp">TCP पर UDP रिले</string>
|
||||
<string name="remote_dns">रिमोट DNS</string>
|
||||
<string name="ipv4">IPv4</string>
|
||||
<string name="ipv6">IPv6</string>
|
||||
<string name="global">ग्लोबल</string>
|
||||
<string name="apps">ऐप्स</string>
|
||||
<string name="vpn_stopping">VPN बंद हो रहा है...</string>
|
||||
<string name="vpn_starting">VPN शुरू हो रहा है...</string>
|
||||
<string name="user_initiated_conn">उपयोगकर्ता द्वारा शुरू किया गया कनेक्शन</string>
|
||||
<string name="vpn_permission_granted">VPN अनुमति दी गई। कनेक्ट हो रहा है...</string>
|
||||
<string name="tproxy_channel_name">socks5</string>
|
||||
|
||||
<!-- WatchdogService / IIABWatchdog -->
|
||||
<string name="watchdog_enable">मास्टर वॉचडॉग\nसक्षम करें</string>
|
||||
<string name="watchdog_disable">मास्टर वॉचडॉग\nअक्षम करें</string>
|
||||
<string name="watchdog_description">Termux को Doze मोड से बचाता है और Wi-Fi को सक्रिय रखता है।</string>
|
||||
<string name="watchdog_stopped">वॉचडॉग बंद</string>
|
||||
<string name="watchdog_started">वॉचडॉग शुरू</string>
|
||||
<string name="watchdog_channel_name">IIAB वॉचडॉग सेवा</string>
|
||||
<string name="watchdog_channel_desc">यह सुनिश्चित करता है कि स्क्रीन बंद होने पर भी सेवाएं सक्रिय रहें।</string>
|
||||
<string name="watchdog_notif_title">IIAB वॉचडॉग सक्रिय</string>
|
||||
<string name="watchdog_notif_text">Termux वातावरण की सुरक्षा...</string>
|
||||
<string name="syncing_watchdog">वॉचडॉग स्थिति सिंक हो रही है। सक्षम: %b</string>
|
||||
<string name="watchdog_thread_started">वॉचडॉग थ्रेड: लूप शुरू हुआ</string>
|
||||
<string name="watchdog_thread_interrupted">वॉचडॉग थ्रेड: बाधित, बंद हो रहा है...</string>
|
||||
<string name="watchdog_thread_error">वॉचडॉग थ्रेड: लूप में त्रुटि</string>
|
||||
<string name="watchdog_thread_ended">वॉचडॉग थ्रेड: लूप समाप्त</string>
|
||||
<string name="cpu_wakelock_acquired">VPN सुरक्षा के तहत CPU WakeLock प्राप्त</string>
|
||||
<string name="wifi_lock_acquired">VPN सुरक्षा के तहत Wi-Fi Lock प्राप्त</string>
|
||||
<string name="error_acquiring_locks">लॉक प्राप्त करने में त्रुटि</string>
|
||||
<string name="cpu_wakelock_released">CPU WakeLock जारी</string>
|
||||
<string name="wifi_lock_released">Wi-Fi Lock जारी</string>
|
||||
|
||||
<!-- Pulse / Heartbeat -->
|
||||
<string name="pulse_stimulating">पल्स: Termux को उत्तेजित करना...</string>
|
||||
<string name="critical_os_blocked">क्रिटिकल: OS ने Termux उत्तेजना को ब्लॉक कर दिया (SecurityException)।</string>
|
||||
<string name="ping_ok">PING 8085: OK</string>
|
||||
<string name="ping_fail">PING 8085: विफल (%s)</string>
|
||||
<string name="session_started">हार्टबीट सत्र शुरू हुआ</string>
|
||||
<string name="session_stopped">हार्टबीट सत्र बंद हुआ</string>
|
||||
<string name="permission_denied_log">अनुमति अस्वीकार: सुनिश्चित करें कि मैनिफ़ेस्ट में RUN_COMMAND है और ऐप प्रतिबंधित नहीं है।</string>
|
||||
<string name="unexpected_error_termux">Termux पर इंटेंट भेजने में अनपेक्षित त्रुटि</string>
|
||||
<string name="pulse_error_log">पल्स त्रुटि: %s</string>
|
||||
<string name="maintenance_write_failed">रखरखाव लेखन विफल</string>
|
||||
<string name="failed_write_blackbox">BlackBox में लिखने में विफल</string>
|
||||
<string name="recovery_pulse_received">सिस्टम से रिकवरी पल्स प्राप्त। VPN लागू किया जा रहा है...</string>
|
||||
|
||||
<!-- TermuxCallbackReceiver / Operations -->
|
||||
<string name="termux_stimulus_ok">[Termux] स्टिमुलस OK (exit 0)</string>
|
||||
<string name="termux_pulse_error">[Termux] पल्स त्रुटि (exit %1$d): %2$s</string>
|
||||
<string name="server_timeout_warning">चेतावनी: सर्वर स्थिति परिवर्तन का समय समाप्त हो गया।</string>
|
||||
<string name="server_booting">बूट हो रहा है...</string>
|
||||
<string name="server_shutting_down">बंद हो रहा है...</string>
|
||||
<string name="failed_termux_intent">क्रिटिकल: Termux इंटेंट विफल: %s</string>
|
||||
<string name="sent_to_termux">Termux को भेजा गया: %s</string>
|
||||
<string name="maintenance_mode_enabled">रखरखाव मोड सक्षम: Termux के पास सीधा इंटरनेट एक्सेस है</string>
|
||||
<string name="stop_server">🛑 सर्वर बंद करें</string>
|
||||
<string name="launch_server">🚀 सर्वर लॉन्च करें</string>
|
||||
<string name="termux_perm_granted">Termux अनुमति दी गई</string>
|
||||
<string name="termux_perm_denied">Termux अनुमति अस्वीकार</string>
|
||||
<string name="notif_perm_granted">सूचना अनुमति दी गई</string>
|
||||
<string name="notif_perm_denied">सूचना अनुमति अस्वीकार</string>
|
||||
<string name="force_termux_foreground">Termux को फॉरग्राउंड में मजबूर किया जा रहा है...</string>
|
||||
<string name="termux_stuck_warning">Termux नहीं खुल रहा है? फोकस पाने के लिए मास्टर वॉचडॉग सक्षम करें।</string>
|
||||
|
||||
<!-- Logs -->
|
||||
<string name="log_reset_confirm_title">लॉग इतिहास रीसेट करें?</string>
|
||||
<string name="log_reset_confirm_msg">यह सभी संग्रहीत कनेक्शन लॉग को स्थायी रूप से हटा देगा। यह कार्रवाई पूर्ववत नहीं की जा सकती।</string>
|
||||
<string name="log_warning_rapid_growth">लॉग फ़ाइल बहुत तेज़ी से बढ़ रही है, आपको जांचना चाहिए कि क्या कुछ विफल हो रहा है</string>
|
||||
<string name="reset_log">लॉग रीसेट करें</string>
|
||||
<string name="copy_all">सभी कॉपी करें</string>
|
||||
<string name="log_reset_log">लॉग रीसेट किया गया</string>
|
||||
<string name="log_reset_user">उपयोगकर्ता द्वारा लॉग रीसेट</string>
|
||||
<string name="log_copied_toast">लॉग क्लिपबोर्ड पर कॉपी किया गया</string>
|
||||
<string name="log_cleared_toast">लॉग साफ़ किया गया</string>
|
||||
<string name="failed_reset_log">लॉग रीसेट करने में विफल: %s</string>
|
||||
<string name="log_size_format">आकार: %1$s / 10MB</string>
|
||||
<string name="log_size_bytes">%d B</string>
|
||||
<string name="log_size_kb">%.1f KB</string>
|
||||
<string name="log_size_mb">%.2f MB</string>
|
||||
<string name="no_blackbox_found">--- कोई BlackBox फ़ाइल नहीं मिली ---</string>
|
||||
<string name="loading_history">--- इतिहास लोड हो रहा है ---</string>
|
||||
<string name="error_reading_history">इतिहास पढ़ने में त्रुटि: %s</string>
|
||||
<string name="end_of_history">--- इतिहास का अंत ---</string>
|
||||
|
||||
<!-- Battery Optimizations -->
|
||||
<string name="battery_opt_title">बैटरी अनुकूलन</string>
|
||||
<string name="battery_opt_msg">वॉचडॉग के विश्वसनीय रूप से काम करने के लिए, कृपया इस ऐप के लिए बैटरी अनुकूलन को अक्षम करें।</string>
|
||||
<string name="go_to_settings">सेटिंग्स पर जाएं</string>
|
||||
<string name="battery_opt_oppo_extra">\n\nOPPO/Realme पहचाना गया: कृपया सुनिश्चित करें कि आप इस ऐप की सेटिंग्स में \'Allow background activity\' सक्षम करें।</string>
|
||||
<string name="battery_opt_xiaomi_extra">\n\nXiaomi पहचाना गया: कृपया सेटिंग्स में बैटरी सेवर को \'No restrictions\' पर सेट करें।</string>
|
||||
<string name="battery_opt_denied">ऐप को 100% काम करने के लिए, कृपया बैटरी अनुकूलन को अक्षम करें।</string>
|
||||
|
||||
<!-- UI / Misc -->
|
||||
<string name="browse_content">🚀 सामग्री देखें</string>
|
||||
<string name="system_ready">सिस्टम तैयार...\n</string>
|
||||
<string name="app_started">एप्लिकेशन शुरू हुआ</string>
|
||||
<string name="label_separator_down">▼ %s</string>
|
||||
<string name="label_separator_up">▶ %s</string>
|
||||
<string name="qr_error_no_server">नेटवर्क पर सामग्री साझा करने के लिए सर्वर लॉन्च करें।</string>
|
||||
<string name="qr_error_no_network">नेटवर्क पर सामग्री साझा करने के लिए Wi-Fi या हॉटस्पॉट सक्षम करें।</string>
|
||||
<string name="qr_title_wifi">Wi-Fi नेटवर्क</string>
|
||||
<string name="qr_title_hotspot">हॉटस्पॉट नेटवर्क</string>
|
||||
<string name="qr_flip_network">नेटवर्क स्विच करें</string>
|
||||
|
||||
<!-- Authentication / Security -->
|
||||
<string name="unlock_watchdog_title">मास्टर वॉचडॉग अनलॉक करें</string>
|
||||
<string name="unlock_watchdog_subtitle">Termux सुरक्षा को रोकने के लिए प्रमाणीकरण आवश्यक है</string>
|
||||
<string name="auth_success_disconnect">प्रमाणीकरण सफल। डिस्कनेक्ट हो रहा है...</string>
|
||||
<string name="auth_required_title">प्रमाणीकरण आवश्यक है</string>
|
||||
<string name="auth_required_subtitle">सुरक्षित वातावरण को अक्षम करने के लिए प्रमाणित करें</string>
|
||||
<string name="security_required_title">सुरक्षा आवश्यक</string>
|
||||
<string name="security_required_msg">सुरक्षित वातावरण को सक्षम करने से पहले आपको अपने डिवाइस पर PIN, पैटर्न या फ़िंगरप्रिंट सेट करना होगा।</string>
|
||||
|
||||
<!-- VPN Recovery Service -->
|
||||
<string name="recovery_channel_name">VPN रिकवरी</string>
|
||||
<string name="recovery_notif_title">Safe Pocket Web बाधित</string>
|
||||
<string name="recovery_notif_text">सुरक्षित वातावरण को तुरंत बहाल करने के लिए टैप करें।</string>
|
||||
|
||||
<!-- Tabs design UI -->
|
||||
<string name="tab_status">स्थिति</string>
|
||||
<string name="tab_usage">उपयोग</string>
|
||||
<string name="tab_deploy">इंस्टालेशन</string>
|
||||
|
||||
<string name="dash_uptime">अपटाइम: %1$s</string>
|
||||
<string name="dash_ip">IP: %1$s</string>
|
||||
|
||||
<!-- Landing -->
|
||||
<string name="dash_title">IIAB-oA Controller</string>
|
||||
<string name="dash_subtitle_localhost">localhost</string>
|
||||
<string name="dash_online">ऑनलाइन</string>
|
||||
<string name="dash_offline">ऑफ़लाइन</string>
|
||||
<string name="dash_device">डिवाइस चार्ज हो रहा है...</string>
|
||||
<string name="dash_iiab_system">सिस्टम IIAB-oA</string>
|
||||
<string name="dash_server_status">सर्वर स्थिति:</string>
|
||||
<string name="dash_termux_searching">इंस्टालेशन खोज रहा है...</string>
|
||||
|
||||
<string name="dash_main_storage">मुख्य स्टोरेज</string>
|
||||
<string name="dash_ram_memory">RAM मेमोरी</string>
|
||||
<string name="dash_swap_virtual">स्वैप (वर्चुअल)</string>
|
||||
|
||||
<string name="dash_system_state">सिस्टम स्थिति</string>
|
||||
<string name="dash_state_installed">इंस्टालेशन मिला</string>
|
||||
<string name="dash_state_raw">Termux Raw (इंस्टालेशन आवश्यक)</string>
|
||||
|
||||
<string name="dash_installed_modules">इंस्टॉल किए गए मॉड्यूल</string>
|
||||
|
||||
<string name="deploy_wip_title">WIP - निर्माणाधीन</string>
|
||||
|
||||
<string name="deploy_wip_desc">Termux मॉड्यूल और वातावरण इंस्टॉलर जल्द ही यहां उपलब्ध होंगे।</string>
|
||||
|
||||
<!-- Usage UI -->
|
||||
<string name="wifi">Wi-Fi</string>
|
||||
<string name="hotspot">हॉटस्पॉट</string>
|
||||
<string name="tunnel">टनल</string>
|
||||
<string name="maintenance_mode">रखरखाव मोड</string>
|
||||
<string name="maintenance_warning_msg">संशोधन करने के लिए Safe Pocket Web अक्षम करें</string>
|
||||
|
||||
<!-- Misc/Internal -->
|
||||
<string name="pref_file_internal">IIAB_Internal</string>
|
||||
<string name="pref_key_setup_complete">setup_complete</string>
|
||||
<string name="version_footer_format">IIAB-oA · 2026 · Controller %1$s</string>
|
||||
<string name="version_footer_fallback">IIAB-oA · 2026 · Controller v0.1.xbeta</string>
|
||||
<string name="termux_invocation_error">Termux को बुलाने में त्रुटि: %1$s</string>
|
||||
<string name="uptime_no_value">अपटाइम: --</string>
|
||||
<string name="hotspot_fdash">हॉटस्पॉट: --</string>
|
||||
<string name="wi_fi_fdash">Wi-Fi: --</string>
|
||||
<string name="battery_custom">"Battery: "</string>
|
||||
<string name="battery_no_value">Battery: --%</string>
|
||||
|
||||
<string name="dash_wifi_format"><b>Wi-Fi:</b> %1$s</string>
|
||||
<string name="dash_hotspot_format"><b>Hotspot:</b> %1$s</string>
|
||||
<string name="dash_uptime_format"><b>Uptime:</b> %1$s</string>
|
||||
<string name="dash_battery_format"><b>Battery:</b> %1$d%%</string>
|
||||
<string name="dash_battery_no_value"><b>Battery:</b> --%%</string>
|
||||
<string name="dash_books">Books</string>
|
||||
<string name="dash_kiwix">Kiwix</string>
|
||||
<string name="dash_kolibri">Kolibri</string>
|
||||
<string name="dash_maps">Maps</string>
|
||||
<string name="dash_matomo">Matomo</string>
|
||||
<string name="dash_system">System</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="dash_bg_main">#121212</color>
|
||||
<color name="dash_bg_card">#242424</color>
|
||||
<color name="dash_text_primary">#FAFAFA</color>
|
||||
<color name="dash_text_secondary">#BDBDBD</color>
|
||||
<color name="dash_module_bg">#242424</color>
|
||||
<color name="dash_module_text">#FFFFFF</color>
|
||||
<color name="dash_divider">#333333</color>
|
||||
<color name="dash_warning">#FFB300</color>
|
||||
<color name="dash_status_online">#4CAF50</color>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- General App -->
|
||||
<string name="app_name">IIAB-oA Controller</string>
|
||||
<string name="default_version">v0.1.x</string>
|
||||
<string name="save">Salvar</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="saved_toast">Salvo</string>
|
||||
<string name="settings_saved">Configurações salvas</string>
|
||||
<string name="fix_action">CORRIGIR</string>
|
||||
<string name="configuration_label">Configuração</string>
|
||||
<string name="advanced_settings_label">Configurações do Túnel</string>
|
||||
<string name="connection_log_label">Log de Conexão</string>
|
||||
<string name="settings_label">CONFIGURAÇÕES</string>
|
||||
|
||||
<!-- SetupActivity -->
|
||||
<string name="setup_title">Configuração Inicial</string>
|
||||
<string name="setup_welcome">Bem-vindo ao assistente de configuração do %1$s.\n\nPara funcionar corretamente, precisamos das seguintes permissões:</string>
|
||||
<string name="setup_perm_notifications">Notificações Push</string>
|
||||
<string name="setup_perm_termux">Execução do Termux</string>
|
||||
<string name="setup_perm_vpn">Safe Pocket Web (VPN)</string>
|
||||
<string name="setup_perm_battery">Desativar Otimização de Bateria</string>
|
||||
<string name="setup_continue">Continuar</string>
|
||||
<string name="revoke_permission_warning">Para revogar permissões, você deve fazê-lo nas configurações do sistema.</string>
|
||||
<string name="termux_not_installed_error">O Termux não está instalado ou o dispositivo não é compatível.</string>
|
||||
<string name="termux_not_installed">O Termux não está instalado.</string>
|
||||
|
||||
<!-- VPN / Socks -->
|
||||
<string name="control_enable">Ativar Safe Pocket Web</string>
|
||||
<string name="control_disable">Desativar Safe Pocket Web</string>
|
||||
<string name="vpn_description">Ative URLs amigáveis. Bloqueie as ameaças.</string>
|
||||
<string name="socks_addr">Endereço Socks:</string>
|
||||
<string name="socks_udp_addr">Endereço UDP Socks:</string>
|
||||
<string name="socks_port">Porta Socks:</string>
|
||||
<string name="socks_user">Usuário Socks:</string>
|
||||
<string name="socks_pass">Senha 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">Aplicativos</string>
|
||||
<string name="vpn_stopping">Parando VPN...</string>
|
||||
<string name="vpn_starting">Iniciando VPN...</string>
|
||||
<string name="user_initiated_conn">Conexão iniciada pelo usuário</string>
|
||||
<string name="vpn_permission_granted">Permissão de VPN concedida. Conectando...</string>
|
||||
<string name="tproxy_channel_name">socks5</string>
|
||||
|
||||
<!-- WatchdogService / IIABWatchdog -->
|
||||
<string name="watchdog_enable">Ativar\nWatchdog Mestre</string>
|
||||
<string name="watchdog_disable">Desativar\nWatchdog Mestre</string>
|
||||
<string name="watchdog_description">Protege o Termux do modo Doze e mantém o Wi-Fi ativo.</string>
|
||||
<string name="watchdog_stopped">Watchdog Parado</string>
|
||||
<string name="watchdog_started">Watchdog Iniciado</string>
|
||||
<string name="watchdog_channel_name">Serviço IIAB Watchdog</string>
|
||||
<string name="watchdog_channel_desc">Garante que os serviços permaneçam ativos quando a tela estiver desligada.</string>
|
||||
<string name="watchdog_notif_title">IIAB Watchdog Ativo</string>
|
||||
<string name="watchdog_notif_text">Protegendo o ambiente Termux...</string>
|
||||
<string name="syncing_watchdog">Sincronizando estado do Watchdog. Ativado: %b</string>
|
||||
<string name="watchdog_thread_started">Watchdog Thread: Loop iniciado</string>
|
||||
<string name="watchdog_thread_interrupted">Watchdog Thread: Interrompido, parando...</string>
|
||||
<string name="watchdog_thread_error">Watchdog Thread: Erro no loop</string>
|
||||
<string name="watchdog_thread_ended">Watchdog Thread: Loop encerrado</string>
|
||||
<string name="cpu_wakelock_acquired">CPU WakeLock adquirido sob proteção VPN</string>
|
||||
<string name="wifi_lock_acquired">Wi-Fi Lock adquirido sob proteção VPN</string>
|
||||
<string name="error_acquiring_locks">Erro ao adquirir bloqueios</string>
|
||||
<string name="cpu_wakelock_released">CPU WakeLock liberado</string>
|
||||
<string name="wifi_lock_released">Wi-Fi Lock liberado</string>
|
||||
|
||||
<!-- Pulse / Heartbeat -->
|
||||
<string name="pulse_stimulating">Pulso: Estimulando o Termux...</string>
|
||||
<string name="critical_os_blocked">CRÍTICO: O SO bloqueou o estímulo do Termux (SecurityException).</string>
|
||||
<string name="ping_ok">PING 8085: OK</string>
|
||||
<string name="ping_fail">PING 8085: FALHA (%s)</string>
|
||||
<string name="session_started">SESSÃO DE BATIMENTO CARDÍACO INICIADA</string>
|
||||
<string name="session_stopped">SESSÃO DE BATIMENTO CARDÍACO PARADA</string>
|
||||
<string name="permission_denied_log">Permissão Negada: Certifique-se de que o manifesto tem RUN_COMMAND e o aplicativo não está restrito.</string>
|
||||
<string name="unexpected_error_termux">Erro inesperado ao enviar intent para o Termux</string>
|
||||
<string name="pulse_error_log">Erro de Pulso: %s</string>
|
||||
<string name="maintenance_write_failed">Falha na escrita de manutenção</string>
|
||||
<string name="failed_write_blackbox">Falha ao escrever no BlackBox</string>
|
||||
<string name="recovery_pulse_received">Pulso de recuperação recebido do sistema. Forçando VPN...</string>
|
||||
|
||||
<!-- TermuxCallbackReceiver / Operations -->
|
||||
<string name="termux_stimulus_ok">[Termux] Estímulo OK (exit 0)</string>
|
||||
<string name="termux_pulse_error">[Termux] Erro de Pulso (exit %1$d): %2$s</string>
|
||||
<string name="server_timeout_warning">Aviso: Tempo limite de transição de estado do servidor excedido.</string>
|
||||
<string name="server_booting">Inicializando...</string>
|
||||
<string name="server_shutting_down">Desligando...</string>
|
||||
<string name="failed_termux_intent">CRÍTICO: Falha no Intent do Termux: %s</string>
|
||||
<string name="sent_to_termux">Enviado para o Termux: %s</string>
|
||||
<string name="maintenance_mode_enabled">Modo de manutenção ativado: Termux tem acesso direto à Internet</string>
|
||||
<string name="stop_server">🛑 Parar Servidor</string>
|
||||
<string name="launch_server">🚀 Iniciar Servidor</string>
|
||||
<string name="termux_perm_granted">Permissão do Termux concedida</string>
|
||||
<string name="termux_perm_denied">Permissão do Termux negada</string>
|
||||
<string name="notif_perm_granted">Permissão de notificação concedida</string>
|
||||
<string name="notif_perm_denied">Permissão de notificação negada</string>
|
||||
<string name="force_termux_foreground">Forçando o Termux para o primeiro plano...</string>
|
||||
<string name="termux_stuck_warning">Termux não abre? Ative o Watchdog Mestre para forçá-lo a ganhar foco.</string>
|
||||
|
||||
<!-- Logs -->
|
||||
<string name="log_reset_confirm_title">Redefinir Histórico de Log?</string>
|
||||
<string name="log_reset_confirm_msg">Isso apagará permanentemente todos os logs de conexão armazenados. Esta ação não pode ser desfeita.</string>
|
||||
<string name="log_warning_rapid_growth">O arquivo de log está crescendo muito rapidamente, verifique se algo está falhando.</string>
|
||||
<string name="reset_log">Redefinir Log</string>
|
||||
<string name="copy_all">Copiar Tudo</string>
|
||||
<string name="log_reset_log">Log redefinido</string>
|
||||
<string name="log_reset_user">Log redefinido pelo usuário</string>
|
||||
<string name="log_copied_toast">Log copiado para a área de transferência</string>
|
||||
<string name="log_cleared_toast">Log limpo</string>
|
||||
<string name="failed_reset_log">Falha ao redefinir log: %s</string>
|
||||
<string name="log_size_format">Tamanho: %1$s / 10MB</string>
|
||||
<string name="log_size_bytes">%d B</string>
|
||||
<string name="log_size_kb">%.1f KB</string>
|
||||
<string name="log_size_mb">%.2f MB</string>
|
||||
<string name="no_blackbox_found">--- Nenhum arquivo BlackBox encontrado ---</string>
|
||||
<string name="loading_history">--- Carregando Histórico ---</string>
|
||||
<string name="error_reading_history">Erro ao ler histórico: %s</string>
|
||||
<string name="end_of_history">--- Fim do Histórico ---</string>
|
||||
|
||||
<!-- Battery Optimizations -->
|
||||
<string name="battery_opt_title">Otimização de Bateria</string>
|
||||
<string name="battery_opt_msg">Para que o Watchdog funcione de forma confiável, desative as otimizações de bateria para este aplicativo.</string>
|
||||
<string name="go_to_settings">Ir para Configurações</string>
|
||||
<string name="battery_opt_oppo_extra">\n\nOPPO/Realme detectado: Certifique-se de ativar \'Permitir atividade em segundo plano\' nas configurações deste aplicativo.</string>
|
||||
<string name="battery_opt_xiaomi_extra">\n\nXiaomi detectado: Defina a economia de bateria para \'Sem restrições\' nas configurações.</string>
|
||||
<string name="battery_opt_denied">Para que o app funcione 100%, desative a otimização de bateria.</string>
|
||||
|
||||
<!-- UI / Misc -->
|
||||
<string name="browse_content">🚀 Explorar Conteúdo</string>
|
||||
<string name="system_ready">Sistema pronto...\n</string>
|
||||
<string name="app_started">Aplicativo Iniciado</string>
|
||||
<string name="label_separator_down">▼ %s</string>
|
||||
<string name="label_separator_up">▶ %s</string>
|
||||
<string name="qr_error_no_server">Inicie o servidor para compartilhar conteúdo pela rede.</string>
|
||||
<string name="qr_error_no_network">Ative o Wi-Fi ou Hotspot para compartilhar conteúdo pela rede.</string>
|
||||
<string name="qr_title_wifi">Rede Wi-Fi</string>
|
||||
<string name="qr_title_hotspot">Rede Hotspot</string>
|
||||
<string name="qr_flip_network">Trocar Rede</string>
|
||||
|
||||
<!-- Authentication / Security -->
|
||||
<string name="unlock_watchdog_title">Desbloquear Watchdog Mestre</string>
|
||||
<string name="unlock_watchdog_subtitle">Autenticação necessária para parar a proteção do Termux</string>
|
||||
<string name="auth_success_disconnect">Autenticação bem-sucedida. Desconectando...</string>
|
||||
<string name="auth_required_title">Autenticação necessária</string>
|
||||
<string name="auth_required_subtitle">Autentique-se para desativar o ambiente seguro</string>
|
||||
<string name="security_required_title">Segurança Necessária</string>
|
||||
<string name="security_required_msg">Você deve definir um PIN, Padrão ou Impressão Digital no seu dispositivo antes de ativar o ambiente seguro.</string>
|
||||
|
||||
<!-- VPN Recovery Service -->
|
||||
<string name="recovery_channel_name">Recuperação de VPN</string>
|
||||
<string name="recovery_notif_title">Safe Pocket Web Interrompido</string>
|
||||
<string name="recovery_notif_text">Toque para restaurar o ambiente seguro imediatamente.</string>
|
||||
|
||||
<!-- Tabs design UI -->
|
||||
<string name="tab_status">Status</string>
|
||||
<string name="tab_usage">Uso</string>
|
||||
<string name="tab_deploy">Instalação</string>
|
||||
|
||||
<string name="dash_uptime">Tempo de atividade: %1$s</string>
|
||||
<string name="dash_ip">IP: %1$s</string>
|
||||
|
||||
<!-- Landing -->
|
||||
<string name="dash_title">IIAB-oA Controller</string>
|
||||
<string name="dash_subtitle_localhost">localhost</string>
|
||||
<string name="dash_online">Online</string>
|
||||
<string name="dash_offline">Offline</string>
|
||||
<string name="dash_device">Carregando o dispositivo...</string>
|
||||
<string name="dash_iiab_system">Sistema IIAB-oA</string>
|
||||
<string name="dash_server_status">Status do Servidor:</string>
|
||||
<string name="dash_termux_searching">Buscando instalação...</string>
|
||||
|
||||
<string name="dash_main_storage">Armazenamento Principal</string>
|
||||
<string name="dash_ram_memory">Memória RAM</string>
|
||||
<string name="dash_swap_virtual">Swap (Virtual)</string>
|
||||
|
||||
<string name="dash_system_state">Estado do Sistema</string>
|
||||
<string name="dash_state_installed">Instalação Detectada</string>
|
||||
<string name="dash_state_raw">Termux Bruto (Instalação Necessária)</string>
|
||||
|
||||
<string name="dash_installed_modules">Módulos Instalados</string>
|
||||
|
||||
<string name="deploy_wip_title">WIP - Em Construção</string>
|
||||
|
||||
<string name="deploy_wip_desc">O módulo Termux e o instalador de ambiente estarão disponíveis aqui em breve.</string>
|
||||
|
||||
<!-- Usage UI -->
|
||||
<string name="wifi">Wi-Fi</string>
|
||||
<string name="hotspot">Hotspot</string>
|
||||
<string name="tunnel">Túnel</string>
|
||||
<string name="maintenance_mode">Modo de Manutenção</string>
|
||||
<string name="maintenance_warning_msg">Desative o Safe Pocket Web para poder modificar</string>
|
||||
|
||||
<!-- Misc/Internal -->
|
||||
<string name="pref_file_internal">IIAB_Internal</string>
|
||||
<string name="pref_key_setup_complete">setup_complete</string>
|
||||
<string name="version_footer_format">IIAB-oA · 2026 · Controller %1$s</string>
|
||||
<string name="version_footer_fallback">IIAB-oA · 2026 · Controller v0.1.xbeta</string>
|
||||
<string name="termux_invocation_error">Erro ao invocar Termux: %1$s</string>
|
||||
<string name="uptime_no_value">Tempo de atividade: --</string>
|
||||
<string name="hotspot_fdash">Hotspot: --</string>
|
||||
<string name="wi_fi_fdash">Wi-Fi: --</string>
|
||||
<string name="battery_custom">"Battery: "</string>
|
||||
<string name="battery_no_value">Battery: --%</string>
|
||||
|
||||
<string name="dash_wifi_format"><b>Wi-Fi:</b> %1$s</string>
|
||||
<string name="dash_hotspot_format"><b>Hotspot:</b> %1$s</string>
|
||||
<string name="dash_uptime_format"><b>Uptime:</b> %1$s</string>
|
||||
<string name="dash_battery_format"><b>Battery:</b> %1$d%%</string>
|
||||
<string name="dash_battery_no_value"><b>Battery:</b> --%%</string>
|
||||
<string name="dash_books">Books</string>
|
||||
<string name="dash_kiwix">Kiwix</string>
|
||||
<string name="dash_kolibri">Kolibri</string>
|
||||
<string name="dash_maps">Maps</string>
|
||||
<string name="dash_matomo">Matomo</string>
|
||||
<string name="dash_system">System</string>
|
||||
</resources>
|
||||
|
|
@ -98,6 +98,8 @@
|
|||
<string name="termux_perm_denied">Разрешение Termux отклонено</string>
|
||||
<string name="notif_perm_granted">Разрешение на уведомления предоставлено</string>
|
||||
<string name="notif_perm_denied">Разрешение на уведомления отклонено</string>
|
||||
<string name="force_termux_foreground">Принудительно перевести Termux на передний план...</string>
|
||||
<string name="termux_stuck_warning">Termux не открывается? Включите Мастер Watchdog, чтобы принудительно вывести его на передний план.</string>
|
||||
|
||||
<!-- Logs -->
|
||||
<string name="log_reset_confirm_title">Сбросить историю журнала?</string>
|
||||
|
|
@ -152,4 +154,67 @@
|
|||
<string name="recovery_channel_name">Восстановление VPN</string>
|
||||
<string name="recovery_notif_title">Safe Pocket Web прерван</string>
|
||||
<string name="recovery_notif_text">Нажмите, чтобы немедленно восстановить безопасное окружение.</string>
|
||||
|
||||
<!-- Tabs design UI -->
|
||||
<string name="tab_status">Статус</string>
|
||||
<string name="tab_usage">Использование</string>
|
||||
<string name="tab_deploy">Установка</string>
|
||||
|
||||
<string name="dash_uptime">Время работы: %1$s</string>
|
||||
<string name="dash_ip">IP: %1$s</string>
|
||||
|
||||
<!-- Landing -->
|
||||
<string name="dash_title">IIAB-oA Controller</string>
|
||||
<string name="dash_subtitle_localhost">localhost</string>
|
||||
<string name="dash_online">В сети</string>
|
||||
<string name="dash_offline">Оффлайн</string>
|
||||
<string name="dash_device">Зарядка устройства...</string>
|
||||
<string name="dash_iiab_system">Система IIAB-oA</string>
|
||||
<string name="dash_server_status">Статус сервера:</string>
|
||||
<string name="dash_termux_searching">Поиск установки...</string>
|
||||
|
||||
<string name="dash_main_storage">Основная память</string>
|
||||
<string name="dash_ram_memory">ОЗУ</string>
|
||||
<string name="dash_swap_virtual">Swap (Виртуальная)</string>
|
||||
|
||||
<string name="dash_system_state">Состояние системы</string>
|
||||
<string name="dash_state_installed">Установка обнаружена</string>
|
||||
<string name="dash_state_raw">Termux Raw (Требуется установка)</string>
|
||||
|
||||
<string name="dash_installed_modules">Установленные модули</string>
|
||||
|
||||
<string name="deploy_wip_title">WIP - В разработке</string>
|
||||
|
||||
<string name="deploy_wip_desc">Модуль Termux и установщик окружения скоро будут доступны здесь.</string>
|
||||
|
||||
<!-- Usage UI -->
|
||||
<string name="wifi">Wi-Fi</string>
|
||||
<string name="hotspot">Точка доступа</string>
|
||||
<string name="tunnel">Туннель</string>
|
||||
<string name="maintenance_mode">Режим обслуживания</string>
|
||||
<string name="maintenance_warning_msg">Отключите Safe Pocket Web для внесения изменений</string>
|
||||
|
||||
<!-- Misc/Internal -->
|
||||
<string name="pref_file_internal">IIAB_Internal</string>
|
||||
<string name="pref_key_setup_complete">setup_complete</string>
|
||||
<string name="version_footer_format">IIAB-oA · 2026 · Controller %1$s</string>
|
||||
<string name="version_footer_fallback">IIAB-oA · 2026 · Controller v0.1.xbeta</string>
|
||||
<string name="termux_invocation_error">Ошибка вызова Termux: %1$s</string>
|
||||
<string name="uptime_no_value">Время работы: --</string>
|
||||
<string name="hotspot_fdash">Точка доступа: --</string>
|
||||
<string name="wi_fi_fdash">Wi-Fi: --</string>
|
||||
<string name="battery_custom">"Battery: "</string>
|
||||
<string name="battery_no_value">Battery: --%</string>
|
||||
|
||||
<string name="dash_wifi_format"><b>Wi-Fi:</b> %1$s</string>
|
||||
<string name="dash_hotspot_format"><b>Hotspot:</b> %1$s</string>
|
||||
<string name="dash_uptime_format"><b>Uptime:</b> %1$s</string>
|
||||
<string name="dash_battery_format"><b>Battery:</b> %1$d%%</string>
|
||||
<string name="dash_battery_no_value"><b>Battery:</b> --%%</string>
|
||||
<string name="dash_books">Books</string>
|
||||
<string name="dash_kiwix">Kiwix</string>
|
||||
<string name="dash_kolibri">Kolibri</string>
|
||||
<string name="dash_maps">Maps</string>
|
||||
<string name="dash_matomo">Matomo</string>
|
||||
<string name="dash_system">System</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -28,4 +28,26 @@
|
|||
<color name="divider_color">#444444</color>
|
||||
<color name="btn_danger">#D32F2F</color>
|
||||
<color name="btn_success">#388E3C</color>
|
||||
|
||||
<color name="bar_storage">#4DB6AC</color>
|
||||
<color name="bar_ram">#FFB300</color>
|
||||
<color name="bar_swap">#7986CB</color>
|
||||
<color name="bar_background">#333333</color>
|
||||
|
||||
<!-- Landing -->
|
||||
<color name="dash_bar_storage">#4DB6AC</color>
|
||||
<color name="dash_bar_ram">#FFB300</color>
|
||||
<color name="dash_bar_swap">#7986CB</color>
|
||||
<color name="dash_badge_online">#2E7D32</color>
|
||||
<color name="dash_bar_bg">#333333</color>
|
||||
|
||||
<color name="dash_bg_main">#F4F5F7</color>
|
||||
<color name="dash_bg_card">#FFFFFF</color>
|
||||
<color name="dash_text_primary">#202124</color>
|
||||
<color name="dash_text_secondary">#5F6368</color>
|
||||
<color name="dash_module_bg">#E8EAED</color>
|
||||
<color name="dash_module_text">#202124</color>
|
||||
<color name="dash_divider">#E0E0E0</color>
|
||||
<color name="dash_warning">#E65100</color>
|
||||
<color name="dash_status_online">#2E7D32</color>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="server_cool_off_duration_ms">60000</integer>
|
||||
<integer name="server_snackbar_delay_ms">20000</integer>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
<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_storage">Local Storage Access</string>
|
||||
<string name="setup_perm_battery">Disable Battery Optimization</string>
|
||||
<string name="setup_termux_storage_btn">Files and media (Storage)</string>
|
||||
<string name="setup_continue">Continue</string>
|
||||
<string name="revoke_permission_warning">To revoke permissions, you must do it from system settings.</string>
|
||||
<string name="termux_not_installed_error">Termux is not installed or device not supported.</string>
|
||||
|
|
@ -98,6 +100,8 @@
|
|||
<string name="termux_perm_denied">Termux permission denied</string>
|
||||
<string name="notif_perm_granted">Notification permission granted</string>
|
||||
<string name="notif_perm_denied">Notification permission denied</string>
|
||||
<string name="force_termux_foreground">Forcing Termux to the foreground...</string>
|
||||
<string name="termux_stuck_warning">Termux not opening? Enable Master Watchdog to force it to gain focus.</string>
|
||||
|
||||
<!-- Logs -->
|
||||
<string name="log_reset_confirm_title">Reset Log History?</string>
|
||||
|
|
@ -152,4 +156,70 @@
|
|||
<string name="recovery_channel_name">VPN Recovery</string>
|
||||
<string name="recovery_notif_title">Safe Pocket Web Interrupted</string>
|
||||
<string name="recovery_notif_text">Tap to restore secure environment immediately.</string>
|
||||
|
||||
<!-- Tabs design UI -->
|
||||
<string name="tab_status">Status</string>
|
||||
<string name="tab_usage">Usage</string>
|
||||
<string name="tab_deploy">Installation</string>
|
||||
|
||||
<!-- Landing -->
|
||||
<string name="dash_uptime">Uptime: %1$s</string>
|
||||
<string name="dash_ip">IP: %1$s</string>
|
||||
<string name="dash_title">IIAB-oA Controller</string>
|
||||
<string name="dash_subtitle_localhost">localhost</string>
|
||||
<string name="dash_device">Loading device...</string>
|
||||
<string name="dash_wifi_format"><b>Wi-Fi:</b> %1$s</string>
|
||||
<string name="dash_hotspot_format"><b>Hotspot:</b> %1$s</string>
|
||||
<string name="dash_uptime_format"><b>Uptime:</b> %1$s</string>
|
||||
<string name="dash_battery_format"><b>Battery:</b> %1$d%%</string>
|
||||
<string name="dash_battery_no_value"><b>Battery:</b> --%%</string>
|
||||
|
||||
<string name="dash_main_storage">Main Storage</string>
|
||||
<string name="dash_ram_memory">RAM Memory</string>
|
||||
<string name="dash_swap_virtual">Swap (Virtual)</string>
|
||||
|
||||
<string name="dash_iiab_system">IIAB-oA System</string>
|
||||
<string name="dash_server_status">Server Status:</string>
|
||||
<string name="dash_online">Online</string>
|
||||
<string name="dash_offline">Offline</string>
|
||||
<string name="dash_system_state">System State</string>
|
||||
<string name="dash_termux_searching">Searching for installation...</string>
|
||||
|
||||
<string name="dash_state_online">IIAB-oA seems online, check for available services.</string>
|
||||
<string name="dash_state_offline">IIAB-oA seems offline, try launching it.</string>
|
||||
<string name="dash_state_installer">Installer found, open the installation tab for more info.</string>
|
||||
<string name="dash_state_debian_only">Base OS installed. Proceed to install IIAB.</string>
|
||||
<string name="dash_state_termux_only">Termux found, go to Installation tab to manage it.</string>
|
||||
<string name="dash_state_none">No component identified, not even Termux.</string>
|
||||
|
||||
<string name="dash_installed_modules">Installed Modules</string>
|
||||
<string name="dash_books">Books</string>
|
||||
<string name="dash_kiwix">Kiwix</string>
|
||||
<string name="dash_kolibri">Kolibri</string>
|
||||
<string name="dash_maps">Maps</string>
|
||||
<string name="dash_matomo">Matomo</string>
|
||||
<string name="dash_system">System</string>
|
||||
|
||||
<string name="deploy_wip_title">WIP - Under Construction</string>
|
||||
<string name="deploy_wip_desc">The Termux module and environment installer will be available here soon.</string>
|
||||
|
||||
<!-- Usage UI -->
|
||||
<string name="wifi">Wi-Fi</string>
|
||||
<string name="hotspot">Hotspot</string>
|
||||
<string name="tunnel">Tunnel</string>
|
||||
<string name="maintenance_mode">Maintenance Mode</string>
|
||||
<string name="maintenance_warning_msg">Disable Safe Pocket Web in order to modify</string>
|
||||
|
||||
<!-- Misc/Internal -->
|
||||
<string name="pref_file_internal">IIAB_Internal</string>
|
||||
<string name="pref_key_setup_complete">setup_complete</string>
|
||||
<string name="version_footer_format">IIAB-oA · 2026 · Controller %1$s</string>
|
||||
<string name="version_footer_fallback">IIAB-oA · 2026 · Controller v0.1.xbeta</string>
|
||||
<string name="termux_invocation_error">Error invoking Termux: %1$s</string>
|
||||
<string name="uptime_no_value">Uptime: --</string>
|
||||
<string name="hotspot_fdash">Hotspot: --</string>
|
||||
<string name="wi_fi_fdash">Wi-Fi: --</string>
|
||||
<string name="battery_custom">"Battery: "</string>
|
||||
<string name="battery_no_value">Battery: --%</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -20,4 +20,9 @@
|
|||
<style name="PurpleSwitchTheme" parent="">
|
||||
<item name="colorControlActivated">#8A2BE2</item>
|
||||
</style>
|
||||
<style name="CustomTabTextStyle" parent="TextAppearance.Design.Tab">
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textSize">13sp</item>
|
||||
<item name="textAllCaps">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Reference in New Issue