Commit 72995830 authored by Mygod's avatar Mygod Committed by Max Lv

Move search view up into toolbar

parent 12527be0
...@@ -47,8 +47,7 @@ ...@@ -47,8 +47,7 @@
<string name="proxied_apps_summary">Configure VPN mode for selected apps</string> <string name="proxied_apps_summary">Configure VPN mode for selected apps</string>
<string name="on">On</string> <string name="on">On</string>
<string name="off">Off</string> <string name="off">Off</string>
<string name="mode">Mode</string> <string name="proxied_apps_mode">Mode</string>
<string name="quick_search">Quick search</string>
<string name="bypass_apps">Bypass</string> <string name="bypass_apps">Bypass</string>
<string name="bypass_apps_summary">Enable this option to bypass selected apps</string> <string name="bypass_apps_summary">Enable this option to bypass selected apps</string>
<string name="auto_connect">Auto Connect</string> <string name="auto_connect">Auto Connect</string>
......
...@@ -32,13 +32,12 @@ import android.content.pm.PackageInfo ...@@ -32,13 +32,12 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.* import android.view.*
import android.widget.* import android.widget.Filter
import android.widget.Filterable
import android.widget.SearchView
import androidx.annotation.UiThread import androidx.annotation.UiThread
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
...@@ -51,6 +50,8 @@ import com.github.shadowsocks.utils.Key ...@@ -51,6 +50,8 @@ 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.Multimap
import com.google.common.collect.MultimapBuilder import com.google.common.collect.MultimapBuilder
import kotlinx.android.synthetic.main.layout_apps.*
import kotlinx.android.synthetic.main.layout_apps_item.view.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
class AppManager : AppCompatActivity() { class AppManager : AppCompatActivity() {
...@@ -93,30 +94,22 @@ class AppManager : AppCompatActivity() { ...@@ -93,30 +94,22 @@ class AppManager : AppCompatActivity() {
} }
private inner class AppViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener { private inner class AppViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
private val icon = view.findViewById<ImageView>(R.id.itemicon)
private val check = view.findViewById<Switch>(R.id.itemcheck)
private val tvTitle = view.findViewById<TextView>(R.id.title)
private val tvDesc = view.findViewById<TextView>(R.id.desc)
private lateinit var item: ProxiedApp private lateinit var item: ProxiedApp
init { init {
view.setOnClickListener(this) view.setOnClickListener(this)
} }
@SuppressLint("SetTextI18n")
fun bind(app: ProxiedApp) { fun bind(app: ProxiedApp) {
this.item = app item = app
itemView.itemicon.setImageDrawable(app.icon)
icon.setImageDrawable(app.icon) itemView.title.text = app.name
tvTitle.text = app.name itemView.desc.text = "${app.packageName} (${app.uid})"
tvDesc.text = "${app.packageName} (${app.uid})" itemView.itemcheck.isChecked = isProxiedApp(app)
check.isChecked = isProxiedApp(app)
} }
fun handlePayload(payloads: List<String>) { fun handlePayload(payloads: List<String>) {
if (payloads.contains("switch")) { if (payloads.contains("switch")) itemView.itemcheck.isChecked = isProxiedApp(item)
check.isChecked = isProxiedApp(item)
}
} }
override fun onClick(v: View?) { override fun onClick(v: View?) {
...@@ -169,14 +162,10 @@ class AppManager : AppCompatActivity() { ...@@ -169,14 +162,10 @@ class AppManager : AppCompatActivity() {
override fun getFilter(): Filter { override fun getFilter(): Filter {
return object : Filter() { return object : Filter() {
override fun performFiltering(constraint: CharSequence): FilterResults { override fun performFiltering(constraint: CharSequence): FilterResults {
val filteredApps = if (constraint.isEmpty()) { val filteredApps = if (constraint.isEmpty()) apps else apps.filter {
apps it.name.contains(constraint, true) ||
} else { it.packageName.contains(constraint, true) ||
apps.filter { it.uid.toString().contains(constraint)
it.name.contains(constraint, true) ||
it.packageName.contains(constraint, true) ||
it.uid.toString().contains(constraint)
}
} }
return FilterResults().also { return FilterResults().also {
...@@ -196,11 +185,6 @@ class AppManager : AppCompatActivity() { ...@@ -196,11 +185,6 @@ 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 proxiedUidMap: Multimap<Int, String>
private lateinit var toolbar: Toolbar
private lateinit var bypassGroup: RadioGroup
private lateinit var appListView: RecyclerView
private lateinit var loadingView: View
private lateinit var editQuery: EditText
private lateinit var appsAdapter: AppsAdapter 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
...@@ -231,25 +215,24 @@ class AppManager : AppCompatActivity() { ...@@ -231,25 +215,24 @@ class AppManager : AppCompatActivity() {
private fun loadApps() { private fun loadApps() {
loader?.cancel() loader?.cancel()
loader = GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED) { loader = GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED) {
loadingView.crossFadeFrom(appListView) loading.crossFadeFrom(list)
val adapter = appListView.adapter as AppsAdapter val adapter = list.adapter as AppsAdapter
withContext(Dispatchers.IO) { adapter.reload() } withContext(Dispatchers.IO) { adapter.reload() }
val queryText = editQuery.text val queryText = search.query
if (queryText.isEmpty()) { if (queryText.isNullOrEmpty()) {
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
} else { } else {
adapter.filter.filter(queryText) adapter.filter.filter(queryText)
} }
appListView.crossFadeFrom(loadingView) list.crossFadeFrom(loading)
} }
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.layout_apps) setContentView(R.layout.layout_apps)
toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
supportActionBar!!.setDisplayHomeAsUpEnabled(true) supportActionBar!!.setDisplayHomeAsUpEnabled(true)
...@@ -258,7 +241,6 @@ class AppManager : AppCompatActivity() { ...@@ -258,7 +241,6 @@ class AppManager : AppCompatActivity() {
DataStore.dirty = true DataStore.dirty = true
} }
bypassGroup = findViewById(R.id.bypass_group)
bypassGroup.check(if (DataStore.bypass) R.id.btn_bypass else R.id.btn_on) bypassGroup.check(if (DataStore.bypass) R.id.btn_bypass else R.id.btn_on)
bypassGroup.setOnCheckedChangeListener { _, checkedId -> bypassGroup.setOnCheckedChangeListener { _, checkedId ->
DataStore.dirty = true DataStore.dirty = true
...@@ -273,24 +255,14 @@ class AppManager : AppCompatActivity() { ...@@ -273,24 +255,14 @@ class AppManager : AppCompatActivity() {
} }
initProxiedApps() initProxiedApps()
loadingView = findViewById(R.id.loading) list.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
appListView = findViewById(R.id.list) list.itemAnimator = DefaultItemAnimator()
appListView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
appListView.itemAnimator = DefaultItemAnimator()
appsAdapter = AppsAdapter() appsAdapter = AppsAdapter()
appListView.adapter = appsAdapter list.adapter = appsAdapter
editQuery = findViewById(R.id.edit_query)
editQuery.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { search.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
appsAdapter.filter.filter(s) override fun onQueryTextSubmit(query: String?) = false
} override fun onQueryTextChange(newText: String?) = true.also { appsAdapter.filter.filter(newText) }
}) })
instance = this instance = this
...@@ -312,14 +284,14 @@ class AppManager : AppCompatActivity() { ...@@ -312,14 +284,14 @@ class AppManager : AppCompatActivity() {
ProfileManager.updateProfile(it) ProfileManager.updateProfile(it)
} }
if (DataStore.directBootAware) DirectBoot.update() if (DataStore.directBootAware) DirectBoot.update()
Snackbar.make(appListView, R.string.action_apply_all, Snackbar.LENGTH_LONG).show() Snackbar.make(list, R.string.action_apply_all, Snackbar.LENGTH_LONG).show()
} else Snackbar.make(appListView, R.string.action_export_err, Snackbar.LENGTH_LONG).show() } else Snackbar.make(list, R.string.action_export_err, Snackbar.LENGTH_LONG).show()
return true return true
} }
R.id.action_export_clipboard -> { R.id.action_export_clipboard -> {
clipboard.primaryClip = ClipData.newPlainText(Key.individual, clipboard.primaryClip = ClipData.newPlainText(Key.individual,
"${DataStore.bypass}\n${DataStore.individual}") "${DataStore.bypass}\n${DataStore.individual}")
Snackbar.make(appListView, R.string.action_export_msg, Snackbar.LENGTH_LONG).show() Snackbar.make(list, R.string.action_export_msg, Snackbar.LENGTH_LONG).show()
return true return true
} }
R.id.action_import_clipboard -> { R.id.action_import_clipboard -> {
...@@ -332,13 +304,13 @@ class AppManager : AppCompatActivity() { ...@@ -332,13 +304,13 @@ class AppManager : AppCompatActivity() {
bypassGroup.check(if (enabled.toBoolean()) R.id.btn_bypass else R.id.btn_on) bypassGroup.check(if (enabled.toBoolean()) R.id.btn_bypass else R.id.btn_on)
DataStore.individual = apps DataStore.individual = apps
DataStore.dirty = true DataStore.dirty = true
Snackbar.make(appListView, R.string.action_import_msg, Snackbar.LENGTH_LONG).show() Snackbar.make(list, R.string.action_import_msg, Snackbar.LENGTH_LONG).show()
initProxiedApps(apps) initProxiedApps(apps)
loadApps() loadApps()
return true return true
} catch (_: IllegalArgumentException) { } } catch (_: IllegalArgumentException) { }
} }
Snackbar.make(appListView, R.string.action_import_err, Snackbar.LENGTH_LONG).show() Snackbar.make(list, R.string.action_import_err, Snackbar.LENGTH_LONG).show()
} }
} }
return false return false
......
...@@ -7,10 +7,27 @@ ...@@ -7,10 +7,27 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:duplicateParentState="false"> android:duplicateParentState="false">
<include layout="@layout/toolbar_light_dark"/> <androidx.appcompat.widget.Toolbar
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:touchscreenBlocksFocus="false"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.DayNight"
android:id="@+id/toolbar">
<SearchView
android:id="@+id/search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:iconifiedByDefault="false"
android:queryHint="@android:string/search_go"/>
</androidx.appcompat.widget.Toolbar>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/linearLayout2"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:colorBackground" android:background="?android:colorBackground"
...@@ -22,13 +39,13 @@ ...@@ -22,13 +39,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:minWidth="40dp" android:minWidth="40dp"
android:text="@string/mode" android:text="@string/proxied_apps_mode"
app:layout_constraintBottom_toBottomOf="@+id/bypass_group" app:layout_constraintBottom_toBottomOf="@+id/bypassGroup"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/bypass_group" /> app:layout_constraintTop_toTopOf="@+id/bypassGroup" />
<RadioGroup <RadioGroup
android:id="@+id/bypass_group" android:id="@+id/bypassGroup"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
...@@ -62,20 +79,6 @@ ...@@ -62,20 +79,6 @@
android:tooltipText="@string/bypass_apps_summary" /> android:tooltipText="@string/bypass_apps_summary" />
</RadioGroup> </RadioGroup>
<EditText
android:id="@+id/edit_query"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="68dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:hint="@string/quick_search"
android:inputType="textFilter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bypass_group" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
......
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