@@ -51,7 +51,6 @@ class ConnectionsFragment : Fragment() {
5151 onItemClick = { conn -> showConnectionDetail(conn) },
5252 onIpLookup = { ip -> lookupIp(ip) }
5353 )
54-
5554 binding.rvConnections.layoutManager = LinearLayoutManager (requireContext())
5655 binding.rvConnections.adapter = adapter
5756
@@ -62,6 +61,8 @@ class ConnectionsFragment : Fragment() {
6261 binding.chipListen.setOnClickListener { setFilter(" listen" ) }
6362 binding.chipCloseWait.setOnClickListener { setFilter(" close_wait" ) }
6463 binding.chipTimeWait.setOnClickListener { setFilter(" time_wait" ) }
64+ binding.chipIpExposed.setOnClickListener { setFilter(" ip_exposed" ) }
65+ binding.chipExposedListener.setOnClickListener { setFilter(" exposed_listener" ) }
6566
6667 binding.etSearch.addTextChangedListener(object : TextWatcher {
6768 override fun beforeTextChanged (s : CharSequence? , start : Int , count : Int , after : Int ) {}
@@ -93,38 +94,57 @@ class ConnectionsFragment : Fragment() {
9394 " listen" -> filtered.filter { it.displayState == " LISTEN" }
9495 " close_wait" -> filtered.filter { it.displayState == " CLOSE_WAIT" }
9596 " time_wait" -> filtered.filter { it.displayState == " TIME_WAIT" }
97+ " ip_exposed" -> filtered.filter { it.isRealIpExposed }
98+ " exposed_listener" -> filtered.filter { it.isExposedListener }
9699 else -> filtered
97100 }
98101
99102 if (searchText.isNotBlank()) {
100103 val query = searchText.lowercase()
101104 filtered = filtered.filter { conn ->
102105 conn.localIp.lowercase().contains(query) ||
103- conn.remoteIp.lowercase().contains(query) ||
104- conn.localPort.toString().contains(query) ||
105- conn.remotePort.toString().contains(query) ||
106- conn.appName.lowercase().contains(query) ||
107- conn.protocol.lowercase().contains(query)
106+ conn.remoteIp.lowercase().contains(query) ||
107+ conn.localPort.toString().contains(query) ||
108+ conn.remotePort.toString().contains(query) ||
109+ conn.appName.lowercase().contains(query) ||
110+ conn.protocol.lowercase().contains(query)
108111 }
109112 }
110113
111114 adapter.submitList(filtered)
112- binding.tvConnectionCount.text = " 共 " + filtered.size + " 个连接"
115+ binding.tvConnectionCount.text = when (currentFilter) {
116+ " ip_exposed" -> " \u26a0 \u771f\u5b9e IP\u66b4\u9732 : ${filtered.size} \u4e2a\u8fde\u63a5 "
117+ " exposed_listener" -> " \u26a0 \u7aef\u53e3\u66b4\u9732 : ${filtered.size} \u4e2a\u76d1\u542c "
118+ else -> " \u5171 ${filtered.size} \u4e2a\u8fde\u63a5 "
119+ }
113120 }
114121
115122 private fun showConnectionDetail (conn : ConnectionInfo ) {
116123 val ctx = context ? : return
117124
118125 val sb = StringBuilder ()
119- sb.appendLine(" 协议 : " + conn.protocol)
120- sb.appendLine(" 状态 : " + conn.displayState)
126+ sb.appendLine(" \u534f\u8bae : " + conn.protocol)
127+ sb.appendLine(" \u72b6\u6001 : " + conn.displayState)
121128 sb.appendLine(" " )
122- sb.appendLine(" 本地地址 : " + conn.localIp + " :" + conn.localPort)
123- sb.appendLine(" 远程地址 : " + conn.remoteIp + " :" + conn.remotePort)
129+ sb.appendLine(" \u672c\u5730\u5730\u5740 : " + conn.localIp + " :" + conn.localPort)
130+ sb.appendLine(" \u8fdc\u7a0b\u5730\u5740 : " + conn.remoteIp + " :" + conn.remotePort)
124131 sb.appendLine(" " )
125- sb.appendLine(" 应用 : " + conn.appName)
132+ sb.appendLine(" \u5e94\u7528 : " + conn.appName)
126133 sb.appendLine(" UID: " + conn.uid)
127134
135+ if (conn.isRealIpExposed) {
136+ sb.appendLine(" " )
137+ sb.appendLine(" \u26a0 \u8b66\u544a : \u8be5\u8fde\u63a5\u66b4\u9732\u4e86\u60a8\u7684\u771f\u5b9e IP\u5730\u5740\uff01 " )
138+ sb.appendLine(" \u672c\u5730\u5730\u5740 ${conn.localIp} \u662f\u516c\u7f51 IP\uff0c " )
139+ sb.appendLine(" \u8bf4\u660e\u6b64\u8fde\u63a5\u672a\u901a\u8fc7 VPN/\u4ee3\u7406\uff0c\u76f4\u63a5\u66b4\u9732\u771f\u5b9e\u8eab\u4efd\u3002 " )
140+ }
141+ if (conn.isExposedListener) {
142+ sb.appendLine(" " )
143+ sb.appendLine(" \u26a0 \u8b66\u544a : \u8be5\u7aef\u53e3\u76d1\u542c\u5728\u6240\u6709\u7f51\u5361\uff01 " )
144+ sb.appendLine(" \u540c\u4e00\u5c40\u57df\u7f51\u5185\u5176\u4ed6\u8bbe\u5907\u53ef\u4ee5\u8fde\u63a5\u6b64\u7aef\u53e3\u3002 " )
145+ sb.appendLine(" \u5728\u516c\u5171 WiFi\u4e0b\u5b58\u5728\u5b89\u5168\u98ce\u9669\u3002 " )
146+ }
147+
128148 val scrollView = ScrollView (ctx)
129149 val textView = TextView (ctx).apply {
130150 text = sb.toString()
@@ -135,38 +155,34 @@ class ConnectionsFragment : Fragment() {
135155 scrollView.addView(textView)
136156
137157 AlertDialog .Builder (ctx)
138- .setTitle(" 连接详情 " )
158+ .setTitle(" \u8fde\u63a5\u8be6\u60c5 " )
139159 .setView(scrollView)
140- .setPositiveButton(" 确定 " , null )
141- .setNeutralButton(" 查询远程IP " ) { _, _ ->
160+ .setPositiveButton(" \u786e\u5b9a " , null )
161+ .setNeutralButton(" \u67e5\u8be2\u8fdc\u7a0b IP " ) { _, _ ->
142162 lookupIp(conn.remoteIp)
143163 }
144- .setNegativeButton(" 复制 " ) { _, _ ->
164+ .setNegativeButton(" \u590d\u5236 " ) { _, _ ->
145165 val clipboard = ctx.getSystemService(
146166 android.content.Context .CLIPBOARD_SERVICE
147167 ) as android.content.ClipboardManager
148168 val clip = android.content.ClipData .newPlainText(" connection" , sb.toString())
149169 clipboard.setPrimaryClip(clip)
150- Toast .makeText(ctx, " 已复制 " , Toast .LENGTH_SHORT ).show()
170+ Toast .makeText(ctx, " \u5df2\u590d\u5236 " , Toast .LENGTH_SHORT ).show()
151171 }
152172 .show()
153173 }
154174
155175 private fun lookupIp (ip : String ) {
156176 val ctx = context ? : return
157-
158177 if (IpLocator .isLocalIp(ip)) {
159- Toast .makeText(ctx, " 这是本机/局域网地址,无需查询 " , Toast .LENGTH_SHORT ).show()
178+ Toast .makeText(ctx, " \u8fd9\u662f\u672c\u673a / \u5c40\u57df\u7f51\u5730\u5740\uff0c\u65e0\u9700\u67e5\u8be2 " , Toast .LENGTH_SHORT ).show()
160179 return
161180 }
162-
163- Toast .makeText(ctx, " 正在查询 " + ip + " ..." , Toast .LENGTH_SHORT ).show()
164-
181+ Toast .makeText(ctx, " \u6b63\u5728\u67e5\u8be2 $ip ..." , Toast .LENGTH_SHORT ).show()
165182 viewLifecycleOwner.lifecycleScope.launch {
166183 val info = withContext(Dispatchers .IO ) {
167184 IpLocator .lookup(ip)
168185 }
169-
170186 if (_binding != null ) {
171187 val scrollView = ScrollView (ctx)
172188 val textView = TextView (ctx).apply {
@@ -176,18 +192,17 @@ class ConnectionsFragment : Fragment() {
176192 setTextIsSelectable(true )
177193 }
178194 scrollView.addView(textView)
179-
180195 AlertDialog .Builder (ctx)
181- .setTitle(" IP 归属地查询 " )
196+ .setTitle(" IP \u5f52\u5c5e\u5730\u67e5\u8be2 " )
182197 .setView(scrollView)
183- .setPositiveButton(" 确定 " , null )
184- .setNeutralButton(" 复制 " ) { _, _ ->
198+ .setPositiveButton(" \u786e\u5b9a " , null )
199+ .setNeutralButton(" \u590d\u5236 " ) { _, _ ->
185200 val clipboard = ctx.getSystemService(
186201 android.content.Context .CLIPBOARD_SERVICE
187202 ) as android.content.ClipboardManager
188203 val clip = android.content.ClipData .newPlainText(" ip_info" , info.format())
189204 clipboard.setPrimaryClip(clip)
190- Toast .makeText(ctx, " 已复制 " , Toast .LENGTH_SHORT ).show()
205+ Toast .makeText(ctx, " \u5df2\u590d\u5236 " , Toast .LENGTH_SHORT ).show()
191206 }
192207 .show()
193208 }
0 commit comments