Commit 8108b9f7 authored by CzBiX's avatar CzBiX Committed by Max Lv

Sync app switch with same uid

parent 8e7336bf
...@@ -63,10 +63,13 @@ dependencies { ...@@ -63,10 +63,13 @@ dependencies {
implementation 'com.twofortyfouram:android-plugin-api-for-locale:1.0.4' implementation 'com.twofortyfouram:android-plugin-api-for-locale:1.0.4'
implementation 'net.glxn.qrgen:android:2.0' implementation 'net.glxn.qrgen:android:2.0'
implementation 'xyz.belvi.mobilevision:barcodescanner:2.0.3' implementation 'xyz.belvi.mobilevision:barcodescanner:2.0.3'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.guava:guava:27.0.1-android'
testImplementation "junit:junit:$junitVersion" testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test:runner:$androidTestVersion" androidTestImplementation "androidx.test:runner:$androidTestVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidEspressoVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidEspressoVersion"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
} }
repositories { repositories {
mavenCentral() mavenCentral()
......
...@@ -50,6 +50,8 @@ import com.github.shadowsocks.preference.DataStore ...@@ -50,6 +50,8 @@ import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.DirectBoot import com.github.shadowsocks.utils.DirectBoot
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.common.collect.Multimap
import com.google.common.collect.MultimapBuilder
import kotlinx.coroutines.* import kotlinx.coroutines.*
class AppManager : AppCompatActivity() { class AppManager : AppCompatActivity() {
...@@ -97,7 +99,6 @@ class AppManager : AppCompatActivity() { ...@@ -97,7 +99,6 @@ class AppManager : AppCompatActivity() {
private val tvTitle = view.findViewById<TextView>(R.id.title) private val tvTitle = view.findViewById<TextView>(R.id.title)
private val tvDesc = view.findViewById<TextView>(R.id.desc) private val tvDesc = view.findViewById<TextView>(R.id.desc)
private lateinit var item: ProxiedApp private lateinit var item: ProxiedApp
private val proxied get() = proxiedApps.contains(item.packageName)
init { init {
view.setOnClickListener(this) view.setOnClickListener(this)
...@@ -106,36 +107,62 @@ class AppManager : AppCompatActivity() { ...@@ -106,36 +107,62 @@ class AppManager : AppCompatActivity() {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun bind(app: ProxiedApp) { fun bind(app: ProxiedApp) {
this.item = app this.item = app
icon.setImageDrawable(app.icon) icon.setImageDrawable(app.icon)
tvTitle.text = app.name tvTitle.text = app.name
tvDesc.text = "${app.uid}(${app.packageName})" tvDesc.text = "${app.uid}(${app.packageName})"
check.isChecked = proxied check.isChecked = isProxiedApp(app)
}
fun handlePayload(payloads: List<String>) {
if (payloads.contains("switch")) {
check.isChecked = isProxiedApp(item)
}
} }
override fun onClick(v: View?) { override fun onClick(v: View?) {
if (proxied) { if (isProxiedApp(item)) {
proxiedApps.remove(item.packageName) val list = proxiedUidMap.removeAll(item.uid)
check.isChecked = false proxiedApps.removeAll(list)
} else { } else {
proxiedApps.add(item.packageName) proxiedApps.add(item.packageName)
check.isChecked = true proxiedUidMap.put(item.uid, item.packageName)
} }
DataStore.individual = proxiedApps.joinToString("\n") DataStore.individual = proxiedApps.joinToString("\n")
DataStore.dirty = true DataStore.dirty = true
appsAdapter.notifyItemRangeChanged(0, appsAdapter.itemCount, "switch")
} }
} }
private inner class AppsAdapter : RecyclerView.Adapter<AppViewHolder>(), Filterable { private inner class AppsAdapter : RecyclerView.Adapter<AppViewHolder>(), Filterable {
private var apps = listOf<ProxiedApp>()
private var filteredApps = apps private var filteredApps = apps
suspend fun reload() { suspend fun reload() {
apps = getApps(packageManager) val list = getApps(packageManager)
.sortedWith(compareBy({ !proxiedApps.contains(it.packageName) }, { it.name.toString() }))
val map = MultimapBuilder.treeKeys().arrayListValues().build<Int, String>()
list.filter { app ->
app.packageName in proxiedApps
}.forEach { app ->
map.put(app.uid, app.packageName)
}
proxiedUidMap = map
apps = list.sortedWith(compareBy({ !isProxiedApp(it) }, { it.name.toString() }))
filteredApps = apps filteredApps = apps
} }
override fun onBindViewHolder(holder: AppViewHolder, position: Int) = holder.bind(filteredApps[position]) override fun onBindViewHolder(holder: AppViewHolder, position: Int) = holder.bind(filteredApps[position])
override fun onBindViewHolder(holder: AppViewHolder, position: Int, payloads: List<Any>) {
if (payloads.isNotEmpty()) {
@Suppress("UNCHECKED_CAST")
holder.handlePayload(payloads as List<String>)
return
}
onBindViewHolder(holder, position)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder = override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder =
AppViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_apps_item, parent, false)) AppViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_apps_item, parent, false))
override fun getItemCount(): Int = filteredApps.size override fun getItemCount(): Int = filteredApps.size
...@@ -169,13 +196,16 @@ class AppManager : AppCompatActivity() { ...@@ -169,13 +196,16 @@ class AppManager : AppCompatActivity() {
} }
private lateinit var proxiedApps: HashSet<String> private lateinit var proxiedApps: HashSet<String>
private lateinit var proxiedUidMap: Multimap<Int, String>
private lateinit var toolbar: Toolbar private lateinit var toolbar: Toolbar
private lateinit var bypassSwitch: RadioButton private lateinit var bypassSwitch: RadioButton
private lateinit var appListView: RecyclerView private lateinit var appListView: RecyclerView
private lateinit var loadingView: View private lateinit var loadingView: View
private lateinit var editQuery: EditText private lateinit var editQuery: EditText
private lateinit var appsAdapter: AppsAdapter
private val clipboard by lazy { getSystemService<ClipboardManager>()!! } private val clipboard by lazy { getSystemService<ClipboardManager>()!! }
private var loader: Job? = null private var loader: Job? = null
private var apps = listOf<ProxiedApp>()
private val shortAnimTime by lazy { resources.getInteger(android.R.integer.config_shortAnimTime).toLong() } private val shortAnimTime by lazy { resources.getInteger(android.R.integer.config_shortAnimTime).toLong() }
private fun View.crossFadeFrom(other: View) { private fun View.crossFadeFrom(other: View) {
...@@ -195,6 +225,11 @@ class AppManager : AppCompatActivity() { ...@@ -195,6 +225,11 @@ class AppManager : AppCompatActivity() {
private fun initProxiedApps(str: String = DataStore.individual) { private fun initProxiedApps(str: String = DataStore.individual) {
proxiedApps = str.split('\n').toHashSet() proxiedApps = str.split('\n').toHashSet()
} }
private fun isProxiedApp(app: ProxiedApp): Boolean {
return proxiedUidMap.containsKey(app.uid)
}
@UiThread @UiThread
private fun loadApps() { private fun loadApps() {
loader?.cancel() loader?.cancel()
...@@ -257,7 +292,7 @@ class AppManager : AppCompatActivity() { ...@@ -257,7 +292,7 @@ class AppManager : AppCompatActivity() {
appListView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false) appListView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
appListView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL)) appListView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
appListView.itemAnimator = DefaultItemAnimator() appListView.itemAnimator = DefaultItemAnimator()
val appsAdapter = AppsAdapter() appsAdapter = AppsAdapter()
appListView.adapter = appsAdapter appListView.adapter = appsAdapter
editQuery = findViewById(R.id.edit_query) editQuery = findViewById(R.id.edit_query)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment