Skip to content

Commit 43b02fb

Browse files
committed
feat: Tools screen (connection test + network info)
- Add ToolsActivity with a URL/latency probe (HTTP status + round-trip time) and a network-info dump (active transport + interface IPv4 addresses). - Wire it into the navigation menu; add ACCESS_NETWORK_STATE. - Translate all Tools strings for zh-rCN/zh-rTW/in/ru.
1 parent 97558f6 commit 43b02fb

9 files changed

Lines changed: 257 additions & 6 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
66
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
77
<uses-permission android:name="android.permission.INTERNET" />
8+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
89
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
910
<uses-permission android:name="android.permission.WAKE_LOCK" />
1011

@@ -41,6 +42,11 @@
4142
android:exported="false"
4243
android:parentActivityName=".ui.MainActivity" />
4344

45+
<activity
46+
android:name=".ui.ToolsActivity"
47+
android:exported="false"
48+
android:parentActivityName=".ui.MainActivity" />
49+
4450
<activity
4551
android:name=".service.VpnRequestActivity"
4652
android:exported="false"

app/src/main/java/top/uwu/mikubox/ui/MenuBottomSheet.kt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import android.view.View
77
import android.view.ViewGroup
88
import android.widget.ImageView
99
import android.widget.TextView
10-
import android.widget.Toast
1110
import androidx.core.view.isVisible
1211
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
1312
import top.uwu.mikubox.R
@@ -42,7 +41,9 @@ class MenuBottomSheet : BottomSheetDialogFragment() {
4241
view.findViewById<View>(R.id.menu_logcat).setOnClickListener {
4342
open(LogcatActivity::class.java)
4443
}
45-
view.findViewById<View>(R.id.menu_tools).setOnClickListener { comingSoon() }
44+
view.findViewById<View>(R.id.menu_tools).setOnClickListener {
45+
open(ToolsActivity::class.java)
46+
}
4647
view.findViewById<View>(R.id.menu_about).setOnClickListener {
4748
open(AboutActivity::class.java)
4849
}
@@ -53,10 +54,6 @@ class MenuBottomSheet : BottomSheetDialogFragment() {
5354
dismiss()
5455
}
5556

56-
private fun comingSoon() {
57-
Toast.makeText(requireContext(), R.string.toast_coming_soon, Toast.LENGTH_SHORT).show()
58-
}
59-
6057
private fun bindRow(root: View, rowId: Int, iconRes: Int, titleRes: Int) {
6158
val row = root.findViewById<View>(rowId)
6259
row.findViewById<ImageView>(R.id.row_icon).setImageResource(iconRes)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package top.uwu.mikubox.ui
2+
3+
import android.net.ConnectivityManager
4+
import android.net.NetworkCapabilities
5+
import android.os.Bundle
6+
import android.os.SystemClock
7+
import androidx.appcompat.app.AppCompatActivity
8+
import androidx.core.content.getSystemService
9+
import androidx.lifecycle.lifecycleScope
10+
import kotlinx.coroutines.Dispatchers
11+
import kotlinx.coroutines.launch
12+
import kotlinx.coroutines.withContext
13+
import top.uwu.mikubox.R
14+
import top.uwu.mikubox.databinding.ActivityToolsBinding
15+
import java.net.HttpURLConnection
16+
import java.net.Inet4Address
17+
import java.net.NetworkInterface
18+
import java.net.URL
19+
20+
/** Core-independent diagnostics: a URL/latency test and a network-info dump. */
21+
class ToolsActivity : AppCompatActivity() {
22+
23+
private lateinit var binding: ActivityToolsBinding
24+
25+
override fun onCreate(savedInstanceState: Bundle?) {
26+
super.onCreate(savedInstanceState)
27+
binding = ActivityToolsBinding.inflate(layoutInflater)
28+
setContentView(binding.root)
29+
30+
binding.toolbar.setNavigationOnClickListener { finish() }
31+
binding.btnTest.setOnClickListener { runTest() }
32+
binding.tvNetworkInfo.text = networkInfo()
33+
}
34+
35+
private fun runTest() {
36+
val url = binding.etTestUrl.text?.toString()?.trim().orEmpty()
37+
if (url.isEmpty()) return
38+
binding.btnTest.isEnabled = false
39+
binding.tvTestResult.text = getString(R.string.tools_testing)
40+
lifecycleScope.launch {
41+
val result = withContext(Dispatchers.IO) { probe(url) }
42+
binding.tvTestResult.text = result
43+
binding.btnTest.isEnabled = true
44+
}
45+
}
46+
47+
private fun probe(url: String): String = runCatching {
48+
val conn = (URL(url).openConnection() as HttpURLConnection).apply {
49+
connectTimeout = 5000
50+
readTimeout = 5000
51+
requestMethod = "GET"
52+
instanceFollowRedirects = false
53+
}
54+
val start = SystemClock.elapsedRealtime()
55+
conn.connect()
56+
val code = conn.responseCode
57+
val elapsed = SystemClock.elapsedRealtime() - start
58+
conn.disconnect()
59+
getString(R.string.tools_test_ok, code, elapsed)
60+
}.getOrElse { getString(R.string.tools_test_failed, it.message ?: it.javaClass.simpleName) }
61+
62+
private fun networkInfo(): String {
63+
val sb = StringBuilder()
64+
val cm = getSystemService<ConnectivityManager>()
65+
val caps = cm?.activeNetwork?.let { cm.getNetworkCapabilities(it) }
66+
val transport = when {
67+
caps == null -> getString(R.string.tools_net_none)
68+
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> "Wi-Fi"
69+
caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> "Cellular"
70+
caps.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> "Ethernet"
71+
caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN) -> "VPN"
72+
else -> getString(R.string.tools_net_other)
73+
}
74+
sb.append(getString(R.string.tools_net_transport, transport)).append('\n')
75+
76+
runCatching {
77+
NetworkInterface.getNetworkInterfaces().toList()
78+
.filter { it.isUp && !it.isLoopback }
79+
.forEach { nic ->
80+
val addrs = nic.inetAddresses.toList()
81+
.filterIsInstance<Inet4Address>()
82+
.joinToString(", ") { it.hostAddress ?: "" }
83+
if (addrs.isNotBlank()) sb.append(nic.name).append(": ").append(addrs).append('\n')
84+
}
85+
}
86+
return sb.toString().trimEnd()
87+
}
88+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
android:layout_width="match_parent"
5+
android:layout_height="match_parent"
6+
android:background="?attr/colorSurface"
7+
android:fitsSystemWindows="true">
8+
9+
<com.google.android.material.appbar.AppBarLayout
10+
android:layout_width="match_parent"
11+
android:layout_height="wrap_content"
12+
app:elevation="0dp">
13+
14+
<com.google.android.material.appbar.MaterialToolbar
15+
android:id="@+id/toolbar"
16+
android:layout_width="match_parent"
17+
android:layout_height="?attr/actionBarSize"
18+
app:navigationIcon="@drawable/ic_arrow_back"
19+
app:title="@string/menu_tools"
20+
app:titleTextColor="?attr/colorOnSurface" />
21+
</com.google.android.material.appbar.AppBarLayout>
22+
23+
<androidx.core.widget.NestedScrollView
24+
android:layout_width="match_parent"
25+
android:layout_height="match_parent"
26+
android:clipToPadding="false"
27+
android:paddingBottom="16dp"
28+
app:layout_behavior="@string/appbar_scrolling_view_behavior">
29+
30+
<LinearLayout
31+
android:layout_width="match_parent"
32+
android:layout_height="wrap_content"
33+
android:orientation="vertical"
34+
android:paddingHorizontal="16dp">
35+
36+
<TextView
37+
style="@style/SettingsSectionHeader"
38+
android:text="@string/tools_connection_test" />
39+
40+
<com.google.android.material.card.MaterialCardView style="@style/SettingsCard">
41+
42+
<LinearLayout
43+
android:layout_width="match_parent"
44+
android:layout_height="wrap_content"
45+
android:orientation="vertical"
46+
android:padding="16dp">
47+
48+
<com.google.android.material.textfield.TextInputLayout
49+
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
50+
android:layout_width="match_parent"
51+
android:layout_height="wrap_content">
52+
53+
<com.google.android.material.textfield.TextInputEditText
54+
android:id="@+id/et_test_url"
55+
android:layout_width="match_parent"
56+
android:layout_height="wrap_content"
57+
android:hint="@string/tools_test_url"
58+
android:inputType="textUri"
59+
android:maxLines="1"
60+
android:text="@string/tools_test_url_default" />
61+
</com.google.android.material.textfield.TextInputLayout>
62+
63+
<TextView
64+
android:id="@+id/tv_test_result"
65+
android:layout_width="match_parent"
66+
android:layout_height="wrap_content"
67+
android:layout_marginTop="12dp"
68+
android:textColor="?attr/colorOnSurfaceVariant"
69+
android:textSize="14sp" />
70+
71+
<com.google.android.material.button.MaterialButton
72+
android:id="@+id/btn_test"
73+
android:layout_width="match_parent"
74+
android:layout_height="wrap_content"
75+
android:layout_marginTop="12dp"
76+
android:text="@string/tools_run_test" />
77+
</LinearLayout>
78+
</com.google.android.material.card.MaterialCardView>
79+
80+
<TextView
81+
style="@style/SettingsSectionHeader"
82+
android:text="@string/tools_network_info" />
83+
84+
<com.google.android.material.card.MaterialCardView style="@style/SettingsCard">
85+
86+
<TextView
87+
android:id="@+id/tv_network_info"
88+
android:layout_width="match_parent"
89+
android:layout_height="wrap_content"
90+
android:fontFamily="monospace"
91+
android:padding="20dp"
92+
android:textColor="?attr/colorOnSurface"
93+
android:textSize="13sp"
94+
android:textIsSelectable="true" />
95+
</com.google.android.material.card.MaterialCardView>
96+
</LinearLayout>
97+
</androidx.core.widget.NestedScrollView>
98+
99+
</androidx.coordinatorlayout.widget.CoordinatorLayout>

app/src/main/res/values-in/strings.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@
7878
<string name="settings_boot_summary">Sambungkan ulang profil terpilih setelah reboot</string>
7979
<string name="settings_mtu">MTU</string>
8080

81+
<!-- Tools -->
82+
<string name="tools_connection_test">Tes koneksi</string>
83+
<string name="tools_test_url">URL tes</string>
84+
<string name="tools_run_test">Jalankan tes</string>
85+
<string name="tools_testing">Menguji…</string>
86+
<string name="tools_test_ok">HTTP %1$d · %2$d ms</string>
87+
<string name="tools_test_failed">Gagal: %1$s</string>
88+
<string name="tools_network_info">Info jaringan</string>
89+
<string name="tools_net_transport">Jenis koneksi: %1$s</string>
90+
<string name="tools_net_none">Tidak ada koneksi</string>
91+
<string name="tools_net_other">Lainnya</string>
92+
8193
<!-- About / Miku intro -->
8294
<string name="menu_about">Tentang</string>
8395
<string name="about_title">Tentang</string>

app/src/main/res/values-ru/strings.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@
8080
<string name="settings_boot_summary">Переподключать выбранный профиль после перезагрузки</string>
8181
<string name="settings_mtu">MTU</string>
8282

83+
<!-- Tools -->
84+
<string name="tools_connection_test">Проверка соединения</string>
85+
<string name="tools_test_url">URL для проверки</string>
86+
<string name="tools_run_test">Запустить проверку</string>
87+
<string name="tools_testing">Проверка…</string>
88+
<string name="tools_test_ok">HTTP %1$d · %2$d мс</string>
89+
<string name="tools_test_failed">Ошибка: %1$s</string>
90+
<string name="tools_network_info">Сведения о сети</string>
91+
<string name="tools_net_transport">Тип соединения: %1$s</string>
92+
<string name="tools_net_none">Нет соединения</string>
93+
<string name="tools_net_other">Другое</string>
94+
8395
<!-- About / Miku intro -->
8496
<string name="menu_about">О программе</string>
8597
<string name="about_title">О программе</string>

app/src/main/res/values-zh-rCN/strings.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@
7878
<string name="settings_boot_summary">重启后重新连接所选配置文件</string>
7979
<string name="settings_mtu">MTU</string>
8080

81+
<!-- Tools -->
82+
<string name="tools_connection_test">连接测试</string>
83+
<string name="tools_test_url">测试网址</string>
84+
<string name="tools_run_test">开始测试</string>
85+
<string name="tools_testing">测试中…</string>
86+
<string name="tools_test_ok">HTTP %1$d · %2$d ms</string>
87+
<string name="tools_test_failed">失败:%1$s</string>
88+
<string name="tools_network_info">网络信息</string>
89+
<string name="tools_net_transport">连接类型:%1$s</string>
90+
<string name="tools_net_none">无连接</string>
91+
<string name="tools_net_other">其他</string>
92+
8193
<!-- About / Miku intro -->
8294
<string name="menu_about">关于</string>
8395
<string name="about_title">关于</string>

app/src/main/res/values-zh-rTW/strings.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@
7878
<string name="settings_boot_summary">重開機後重新連線所選設定檔</string>
7979
<string name="settings_mtu">MTU</string>
8080

81+
<!-- Tools -->
82+
<string name="tools_connection_test">連線測試</string>
83+
<string name="tools_test_url">測試網址</string>
84+
<string name="tools_run_test">開始測試</string>
85+
<string name="tools_testing">測試中…</string>
86+
<string name="tools_test_ok">HTTP %1$d · %2$d ms</string>
87+
<string name="tools_test_failed">失敗:%1$s</string>
88+
<string name="tools_network_info">網路資訊</string>
89+
<string name="tools_net_transport">連線類型:%1$s</string>
90+
<string name="tools_net_none">無連線</string>
91+
<string name="tools_net_other">其他</string>
92+
8193
<!-- About / Miku intro -->
8294
<string name="menu_about">關於</string>
8395
<string name="about_title">關於</string>

app/src/main/res/values/strings.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,19 @@
8080
<string name="settings_boot_summary">Reconnect the selected profile after reboot</string>
8181
<string name="settings_mtu">MTU</string>
8282

83+
<!-- Tools -->
84+
<string name="tools_connection_test">Connection test</string>
85+
<string name="tools_test_url">Test URL</string>
86+
<string name="tools_test_url_default" translatable="false">https://www.gstatic.com/generate_204</string>
87+
<string name="tools_run_test">Run test</string>
88+
<string name="tools_testing">Testing…</string>
89+
<string name="tools_test_ok">HTTP %1$d · %2$d ms</string>
90+
<string name="tools_test_failed">Failed: %1$s</string>
91+
<string name="tools_network_info">Network info</string>
92+
<string name="tools_net_transport">Transport: %1$s</string>
93+
<string name="tools_net_none">No connection</string>
94+
<string name="tools_net_other">Other</string>
95+
8396
<!-- About / Miku intro -->
8497
<string name="menu_about">About</string>
8598
<string name="about_title">About</string>

0 commit comments

Comments
 (0)