From 41b0983582d6de11e951f5b3627602ccfd7f597f Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 20 Jan 2026 10:46:46 +0800 Subject: [PATCH] Refactor logcat handling to use LogcatViewModel --- .../java/com/v2ray/ang/ui/LogcatActivity.kt | 107 +++++------------- .../com/v2ray/ang/ui/LogcatRecyclerAdapter.kt | 18 +-- .../v2ray/ang/viewmodel/LogcatViewModel.kt | 63 +++++++++++ 3 files changed, 103 insertions(+), 85 deletions(-) create mode 100644 V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/LogcatViewModel.kt diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt index b8fbaeee..df600d68 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatActivity.kt @@ -2,37 +2,34 @@ package com.v2ray.ang.ui import android.annotation.SuppressLint import android.os.Bundle -import android.util.Log import android.view.Menu import android.view.MenuItem import androidx.appcompat.widget.SearchView +import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.swiperefreshlayout.widget.SwipeRefreshLayout -import com.v2ray.ang.AppConfig -import com.v2ray.ang.AppConfig.ANG_PACKAGE import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityLogcatBinding +import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toastSuccess import com.v2ray.ang.util.Utils +import com.v2ray.ang.viewmodel.LogcatViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.io.IOException - class LogcatActivity : BaseActivity(), SwipeRefreshLayout.OnRefreshListener { private val binding by lazy { ActivityLogcatBinding.inflate(layoutInflater) } - - private var logsetsAll: MutableList = mutableListOf() - var logsets: MutableList = mutableListOf() - private val adapter by lazy { LogcatRecyclerAdapter(this) } + private val viewModel: LogcatViewModel by viewModels() + private lateinit var adapter: LogcatRecyclerAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - //setContentView(binding.root) setContentViewWithToolbar(binding.root, showHomeAsUp = true, title = getString(R.string.title_logcat)) + adapter = LogcatRecyclerAdapter(viewModel, ::onLogLongClick) + binding.recyclerView.setHasFixedSize(true) binding.recyclerView.layoutManager = LinearLayoutManager(this) addCustomDividerToRecyclerView(binding.recyclerView, this, R.drawable.custom_divider) @@ -40,58 +37,12 @@ class LogcatActivity : BaseActivity(), SwipeRefreshLayout.OnRefreshListener { binding.refreshLayout.setOnRefreshListener(this) - logsets.add(getString(R.string.pull_down_to_refresh)) + toast(getString(R.string.pull_down_to_refresh)) } - private fun getLogcat() { - - try { - binding.refreshLayout.isRefreshing = true - - lifecycleScope.launch(Dispatchers.Default) { - val lst = LinkedHashSet() - lst.add("logcat") - lst.add("-d") - lst.add("-v") - lst.add("time") - lst.add("-s") - lst.add("GoLog,${ANG_PACKAGE},AndroidRuntime,System.err") - val process = withContext(Dispatchers.IO) { - Runtime.getRuntime().exec(lst.toTypedArray()) - } - - val allText = process.inputStream.bufferedReader().use { it.readLines() }.reversed() - launch(Dispatchers.Main) { - logsetsAll = allText.toMutableList() - logsets = allText.toMutableList() - refreshData() - binding.refreshLayout.isRefreshing = false - } - } - } catch (e: IOException) { - Log.e(AppConfig.TAG, "Failed to get logcat", e) - } - } - - private fun clearLogcat() { - try { - lifecycleScope.launch(Dispatchers.Default) { - val lst = LinkedHashSet() - lst.add("logcat") - lst.add("-c") - withContext(Dispatchers.IO) { - val process = Runtime.getRuntime().exec(lst.toTypedArray()) - process.waitFor() - } - launch(Dispatchers.Main) { - logsetsAll.clear() - logsets.clear() - refreshData() - } - } - } catch (e: IOException) { - Log.e(AppConfig.TAG, "Failed to clear logcat", e) - } + private fun onLogLongClick(log: String): Boolean { + Utils.setClipboard(this, log) + return true } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -104,12 +55,14 @@ class LogcatActivity : BaseActivity(), SwipeRefreshLayout.OnRefreshListener { override fun onQueryTextSubmit(query: String?): Boolean = false override fun onQueryTextChange(newText: String?): Boolean { - filterLogs(newText) + viewModel.filter(newText) + refreshData() return false } }) searchView.setOnCloseListener { - filterLogs("") + viewModel.filter("") + refreshData() false } } @@ -119,33 +72,33 @@ class LogcatActivity : BaseActivity(), SwipeRefreshLayout.OnRefreshListener { override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { R.id.copy_all -> { - Utils.setClipboard(this, logsets.joinToString("\n")) + val all = viewModel.getAll().joinToString("\n") + Utils.setClipboard(this, all) toastSuccess(R.string.toast_success) true } R.id.clear_all -> { - clearLogcat() + lifecycleScope.launch(Dispatchers.IO) { + viewModel.clearLogcat() + withContext(Dispatchers.Main) { + refreshData() + } + } true } else -> super.onOptionsItemSelected(item) } - private fun filterLogs(content: String?): Boolean { - val key = content?.trim() - logsets = if (key.isNullOrEmpty()) { - logsetsAll.toMutableList() - } else { - logsetsAll.filter { it.contains(key) }.toMutableList() - } - - refreshData() - return true - } - override fun onRefresh() { - getLogcat() + lifecycleScope.launch(Dispatchers.IO) { + viewModel.loadLogcat() + withContext(Dispatchers.Main) { + binding.refreshLayout.isRefreshing = false + refreshData() + } + } } @SuppressLint("NotifyDataSetChanged") diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt index d1522cf4..106bcf96 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/LogcatRecyclerAdapter.kt @@ -6,17 +6,20 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.v2ray.ang.AppConfig import com.v2ray.ang.databinding.ItemRecyclerLogcatBinding -import com.v2ray.ang.util.Utils +import com.v2ray.ang.viewmodel.LogcatViewModel -class LogcatRecyclerAdapter(val activity: LogcatActivity) : RecyclerView.Adapter() { - private var mActivity: LogcatActivity = activity +class LogcatRecyclerAdapter( + private val viewModel: LogcatViewModel, + private val onLongClick: ((String) -> Boolean)? = null +) : RecyclerView.Adapter() { - - override fun getItemCount() = mActivity.logsets.size + override fun getItemCount() = viewModel.getAll().size override fun onBindViewHolder(holder: MainViewHolder, position: Int) { try { - val log = mActivity.logsets[position] + val logs = viewModel.getAll() + val log = logs[position] + if (log.isEmpty()) { holder.itemSubSettingBinding.logTag.text = "" holder.itemSubSettingBinding.logContent.text = "" @@ -27,8 +30,7 @@ class LogcatRecyclerAdapter(val activity: LogcatActivity) : RecyclerView.Adapter } holder.itemView.setOnLongClickListener { - Utils.setClipboard(mActivity, log) - true + onLongClick?.invoke(log) ?: false } } catch (e: Exception) { Log.e(AppConfig.TAG, "Error binding log view data", e) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/LogcatViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/LogcatViewModel.kt new file mode 100644 index 00000000..e1c5c939 --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/LogcatViewModel.kt @@ -0,0 +1,63 @@ +package com.v2ray.ang.viewmodel + +import androidx.lifecycle.ViewModel +import com.v2ray.ang.AppConfig +import com.v2ray.ang.AppConfig.ANG_PACKAGE +import java.io.IOException +import java.util.LinkedHashSet + +class LogcatViewModel : ViewModel() { + private val logsetsAll: MutableList = mutableListOf() + private var filteredLogs: List = emptyList() + private var currentFilter: String = "" + + fun getAll(): List = filteredLogs + + fun loadLogcat() { + try { + val lst = LinkedHashSet() + lst.add("logcat") + lst.add("-d") + lst.add("-v") + lst.add("time") + lst.add("-s") + lst.add("GoLog,${ANG_PACKAGE},AndroidRuntime,System.err") + val process = Runtime.getRuntime().exec(lst.toTypedArray()) + val allText = process.inputStream.bufferedReader().use { it.readLines() }.reversed() + + logsetsAll.clear() + logsetsAll.addAll(allText) + applyFilter() + } catch (e: IOException) { + android.util.Log.e(AppConfig.TAG, "Failed to get logcat", e) + } + } + + fun clearLogcat() { + try { + val lst = LinkedHashSet() + lst.add("logcat") + lst.add("-c") + val process = Runtime.getRuntime().exec(lst.toTypedArray()) + process.waitFor() + + logsetsAll.clear() + filteredLogs = emptyList() + } catch (e: IOException) { + android.util.Log.e(AppConfig.TAG, "Failed to clear logcat", e) + } + } + + fun filter(content: String?) { + currentFilter = content?.trim() ?: "" + applyFilter() + } + + private fun applyFilter() { + filteredLogs = if (currentFilter.isEmpty()) { + logsetsAll.toList() + } else { + logsetsAll.filter { it.contains(currentFilter) } + } + } +}