Commit 6240a1b4 authored by Mygod's avatar Mygod

Merge branch 'master' into dns-server

parents 732c46ee 78f021cd
...@@ -32,7 +32,7 @@ buildscript { ...@@ -32,7 +32,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:3.3.0' classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.0'
classpath 'com.google.gms:google-services:4.2.0' classpath 'com.google.gms:google-services:4.2.0'
classpath 'io.fabric.tools:gradle:1.27.0' classpath 'io.fabric.tools:gradle:1.27.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
} }
} }
......
...@@ -30,7 +30,7 @@ import java.nio.ByteBuffer ...@@ -30,7 +30,7 @@ import java.nio.ByteBuffer
import java.nio.ByteOrder import java.nio.ByteOrder
class TrafficMonitor(statFile: File) : AutoCloseable { class TrafficMonitor(statFile: File) : AutoCloseable {
private val thread = object : LocalSocketListener("TrafficMonitor", statFile) { private val thread = object : LocalSocketListener("TrafficMonitor-" + statFile.name, statFile) {
private val buffer = ByteArray(16) private val buffer = ByteArray(16)
private val stat = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN) private val stat = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN)
override fun acceptInternal(socket: LocalSocket) { override fun acceptInternal(socket: LocalSocket) {
......
...@@ -23,6 +23,7 @@ package com.github.shadowsocks.database ...@@ -23,6 +23,7 @@ package com.github.shadowsocks.database
import android.net.Uri import android.net.Uri
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
import android.util.LongSparseArray
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.room.* import androidx.room.*
import com.github.shadowsocks.plugin.PluginConfiguration import com.github.shadowsocks.plugin.PluginConfiguration
...@@ -249,7 +250,7 @@ class Profile : Serializable { ...@@ -249,7 +250,7 @@ class Profile : Serializable {
} }
override fun toString() = toUri().toString() override fun toString() = toUri().toString()
fun toJson(profiles: Map<Long, Profile>? = null): JSONObject = JSONObject().apply { fun toJson(profiles: LongSparseArray<Profile>? = null): JSONObject = JSONObject().apply {
put("server", host) put("server", host)
put("server_port", remotePort) put("server_port", remotePort)
put("password", password) put("password", password)
...@@ -274,7 +275,7 @@ class Profile : Serializable { ...@@ -274,7 +275,7 @@ class Profile : Serializable {
} }
}) })
put("udpdns", udpdns) put("udpdns", udpdns)
val fallback = profiles[udpFallback] val fallback = profiles.get(udpFallback ?: return@apply)
if (fallback != null && fallback.plugin.isNullOrEmpty()) fallback.toJson().also { put("udp_fallback", it) } if (fallback != null && fallback.plugin.isNullOrEmpty()) fallback.toJson().also { put("udp_fallback", it) }
} }
......
...@@ -56,7 +56,7 @@ class ProfileConfigActivity : AppCompatActivity() { ...@@ -56,7 +56,7 @@ class ProfileConfigActivity : AppCompatActivity() {
menuInflater.inflate(R.menu.profile_config_menu, menu) menuInflater.inflate(R.menu.profile_config_menu, menu)
return true return true
} }
override fun onOptionsItemSelected(item: MenuItem?) = child.onOptionsItemSelected(item) override fun onOptionsItemSelected(item: MenuItem) = child.onOptionsItemSelected(item)
override fun onBackPressed() { override fun onBackPressed() {
if (DataStore.dirty) AlertDialog.Builder(this) if (DataStore.dirty) AlertDialog.Builder(this)
......
...@@ -179,7 +179,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), ...@@ -179,7 +179,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
} else super.onActivityResult(requestCode, resultCode, data) } else super.onActivityResult(requestCode, resultCode, data)
} }
override fun onOptionsItemSelected(item: MenuItem?) = when (item?.itemId) { override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_delete -> { R.id.action_delete -> {
val activity = requireActivity() val activity = requireActivity()
AlertDialog.Builder(activity) AlertDialog.Builder(activity)
......
...@@ -28,6 +28,7 @@ import android.nfc.NdefRecord ...@@ -28,6 +28,7 @@ import android.nfc.NdefRecord
import android.nfc.NfcAdapter import android.nfc.NfcAdapter
import android.os.Bundle import android.os.Bundle
import android.text.format.Formatter import android.text.format.Formatter
import android.util.LongSparseArray
import android.view.* import android.view.*
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
...@@ -98,7 +99,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { ...@@ -98,7 +99,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
return image return image
} }
override fun onAttach(context: Context?) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
val adapter = NfcAdapter.getDefaultAdapter(context) val adapter = NfcAdapter.getDefaultAdapter(context)
adapter?.setNdefPushMessage(NdefMessage(arrayOf( adapter?.setNdefPushMessage(NdefMessage(arrayOf(
...@@ -301,7 +302,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { ...@@ -301,7 +302,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
val profilesAdapter by lazy { ProfilesAdapter() } val profilesAdapter by lazy { ProfilesAdapter() }
private lateinit var undoManager: UndoSnackbarManager<Profile> private lateinit var undoManager: UndoSnackbarManager<Profile>
private val statsCache = mutableMapOf<Long, TrafficStats>() private val statsCache = LongSparseArray<TrafficStats>()
private val clipboard by lazy { requireContext().getSystemService<ClipboardManager>()!! } private val clipboard by lazy { requireContext().getSystemService<ClipboardManager>()!! }
...@@ -443,7 +444,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { ...@@ -443,7 +444,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
REQUEST_EXPORT_PROFILES -> { REQUEST_EXPORT_PROFILES -> {
val profiles = ProfileManager.getAllProfiles() val profiles = ProfileManager.getAllProfiles()
if (profiles != null) try { if (profiles != null) try {
val lookup = profiles.associateBy { it.id } val lookup = LongSparseArray<Profile>(profiles.size).apply { profiles.forEach { put(it.id, it) } }
requireContext().contentResolver.openOutputStream(data?.data!!)!!.bufferedWriter().use { requireContext().contentResolver.openOutputStream(data?.data!!)!!.bufferedWriter().use {
it.write(JSONArray(profiles.map { it.toJson(lookup) }.toTypedArray()).toString(2)) it.write(JSONArray(profiles.map { it.toJson(lookup) }.toTypedArray()).toString(2))
} }
...@@ -458,7 +459,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener { ...@@ -458,7 +459,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
override fun onTrafficUpdated(profileId: Long, stats: TrafficStats) { override fun onTrafficUpdated(profileId: Long, stats: TrafficStats) {
if (profileId != 0L) { // ignore aggregate stats if (profileId != 0L) { // ignore aggregate stats
statsCache[profileId] = stats statsCache.put(profileId, stats)
profilesAdapter.refreshId(profileId) profilesAdapter.refreshId(profileId)
} }
} }
......
...@@ -25,7 +25,7 @@ android { ...@@ -25,7 +25,7 @@ android {
dependencies { dependencies {
api "androidx.core:core-ktx:1.0.1" api "androidx.core:core-ktx:1.0.1"
api "com.google.android.material:material:1.1.0-alpha02" api "com.google.android.material:material:1.1.0-alpha03"
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion" api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
testImplementation "junit:junit:$junitVersion" testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test:runner:$androidTestVersion" androidTestImplementation "androidx.test:runner:$androidTestVersion"
......
...@@ -52,7 +52,7 @@ android { ...@@ -52,7 +52,7 @@ android {
dependencies { dependencies {
implementation project(':core') implementation project(':core')
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "androidx.leanback:leanback-preference:1.0.0" implementation "androidx.leanback:leanback-preference:1.1.0-alpha01"
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
......
...@@ -21,26 +21,26 @@ ...@@ -21,26 +21,26 @@
package com.github.shadowsocks.tv package com.github.shadowsocks.tv
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.leanback.preference.LeanbackPreferenceDialogFragment import androidx.leanback.preference.LeanbackPreferenceDialogFragmentCompat
import androidx.leanback.preference.LeanbackSettingsFragment import androidx.leanback.preference.LeanbackSettingsFragmentCompat
import androidx.preference.* import androidx.preference.*
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.tv.preference.LeanbackSingleListPreferenceDialogFragment import com.github.shadowsocks.tv.preference.LeanbackSingleListPreferenceDialogFragment
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
class MainFragment : LeanbackSettingsFragment() { class MainFragment : LeanbackSettingsFragmentCompat() {
override fun onPreferenceStartInitialScreen() = startPreferenceFragment(MainPreferenceFragment()) override fun onPreferenceStartInitialScreen() = startPreferenceFragment(MainPreferenceFragment())
override fun onPreferenceStartScreen(caller: PreferenceFragment?, pref: PreferenceScreen?): Boolean { override fun onPreferenceStartScreen(caller: PreferenceFragmentCompat?, pref: PreferenceScreen?): Boolean {
onPreferenceStartInitialScreen() onPreferenceStartInitialScreen()
return true return true
} }
override fun onPreferenceStartFragment(caller: PreferenceFragment?, pref: Preference?) = false override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat?, pref: Preference?) = false
override fun onPreferenceDisplayDialog(caller: PreferenceFragment, pref: Preference?): Boolean { override fun onPreferenceDisplayDialog(caller: PreferenceFragmentCompat, pref: Preference?): Boolean {
if (pref?.key == Key.id) { if (pref?.key == Key.id) {
if ((childFragmentManager.findFragmentById(R.id.settings_preference_fragment_container) if ((childFragmentManager.findFragmentById(R.id.settings_preference_fragment_container)
as MainPreferenceFragment).state == BaseService.STOPPED) { as MainPreferenceFragment).state == BaseService.STOPPED) {
startPreferenceFragment(ProfilesDialogFragment().apply { startPreferenceFragment(ProfilesDialogFragment().apply {
arguments = bundleOf(Pair(LeanbackPreferenceDialogFragment.ARG_KEY, Key.id)) arguments = bundleOf(Pair(LeanbackPreferenceDialogFragmentCompat.ARG_KEY, Key.id))
setTargetFragment(caller, 0) setTargetFragment(caller, 0)
}) })
} }
...@@ -48,7 +48,7 @@ class MainFragment : LeanbackSettingsFragment() { ...@@ -48,7 +48,7 @@ class MainFragment : LeanbackSettingsFragment() {
} }
if (pref is ListPreference && pref !is MultiSelectListPreference) { if (pref is ListPreference && pref !is MultiSelectListPreference) {
startPreferenceFragment(LeanbackSingleListPreferenceDialogFragment().apply { startPreferenceFragment(LeanbackSingleListPreferenceDialogFragment().apply {
arguments = bundleOf(Pair(LeanbackPreferenceDialogFragment.ARG_KEY, pref.key)) arguments = bundleOf(Pair(LeanbackPreferenceDialogFragmentCompat.ARG_KEY, pref.key))
setTargetFragment(caller, 0) setTargetFragment(caller, 0)
}) })
return true return true
......
...@@ -25,22 +25,40 @@ import android.text.format.Formatter ...@@ -25,22 +25,40 @@ import android.text.format.Formatter
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.CompoundButton
import android.widget.TextView import android.widget.TextView
import androidx.leanback.preference.LeanbackListPreferenceDialogFragment import androidx.leanback.preference.LeanbackListPreferenceDialogFragmentCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.plugin.PluginConfiguration import com.github.shadowsocks.plugin.PluginConfiguration
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
class ProfilesDialogFragment : LeanbackListPreferenceDialogFragment() { class ProfilesDialogFragment : LeanbackListPreferenceDialogFragmentCompat() {
inner class ProfilesAdapter : RecyclerView.Adapter<ViewHolder>(), ViewHolder.OnItemClickListener { private inner class ProfileViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
private val profiles = ProfileManager.getAllProfiles()!! val widgetView = view.findViewById<CompoundButton>(R.id.button)
val titleView = view.findViewById<TextView>(android.R.id.title)
init {
view.findViewById<ViewGroup>(R.id.container).setOnClickListener(this)
}
override fun onClick(v: View) {
val index = adapterPosition
if (index == RecyclerView.NO_POSITION) return
Core.switchProfile(adapter.profiles[index].id)
(targetFragment as MainPreferenceFragment).startService()
fragmentManager?.popBackStack()
adapter.notifyDataSetChanged()
}
}
private inner class ProfilesAdapter : RecyclerView.Adapter<ProfileViewHolder>() {
val profiles = ProfileManager.getAllProfiles()!!
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(LayoutInflater override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ProfileViewHolder(
.from(parent.context).inflate(R.layout.leanback_list_preference_item_single_2, parent, false), this) LayoutInflater.from(parent.context).inflate(R.layout.leanback_list_preference_item_single_2,
parent, false))
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ProfileViewHolder, position: Int) {
val profile = profiles[position] val profile = profiles[position]
holder.widgetView.isChecked = profile.id == DataStore.profileId holder.widgetView.isChecked = profile.id == DataStore.profileId
holder.titleView.text = profile.formattedName holder.titleView.text = profile.formattedName
...@@ -54,24 +72,15 @@ class ProfilesDialogFragment : LeanbackListPreferenceDialogFragment() { ...@@ -54,24 +72,15 @@ class ProfilesDialogFragment : LeanbackListPreferenceDialogFragment() {
} }
override fun getItemCount() = profiles.size override fun getItemCount() = profiles.size
override fun onItemClick(viewHolder: ViewHolder) {
val index = viewHolder.adapterPosition
if (index == RecyclerView.NO_POSITION) return
Core.switchProfile(profiles[index].id)
(targetFragment as MainPreferenceFragment).startService()
fragmentManager?.popBackStack()
notifyDataSetChanged()
}
val selectedIndex = profiles.indexOfFirst { it.id == DataStore.profileId }
} }
override fun onCreateAdapter() = ProfilesAdapter() private val adapter = ProfilesAdapter()
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return super.onCreateView(inflater, container, savedInstanceState)!!.also { return super.onCreateView(inflater, container, savedInstanceState)!!.also {
val list = it.findViewById<RecyclerView>(android.R.id.list) val list = it.findViewById<RecyclerView>(android.R.id.list)
list.layoutManager!!.scrollToPosition((list.adapter as ProfilesAdapter).selectedIndex) list.adapter = adapter
list.layoutManager!!.scrollToPosition(adapter.profiles.indexOfFirst { it.id == DataStore.profileId })
} }
} }
} }
...@@ -24,21 +24,21 @@ import android.os.Bundle ...@@ -24,21 +24,21 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.leanback.preference.LeanbackListPreferenceDialogFragment import androidx.leanback.preference.LeanbackListPreferenceDialogFragmentCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
/** /**
* Fix: scroll to selected item. * Fix: scroll to selected item.
*/ */
open class LeanbackSingleListPreferenceDialogFragment : LeanbackListPreferenceDialogFragment() { open class LeanbackSingleListPreferenceDialogFragment : LeanbackListPreferenceDialogFragmentCompat() {
companion object { companion object {
private val mEntryValues = LeanbackListPreferenceDialogFragment::class.java private val mEntryValues = LeanbackListPreferenceDialogFragmentCompat::class.java
.getDeclaredField("mEntryValues").apply { isAccessible = true } .getDeclaredField("mEntryValues").apply { isAccessible = true }
private val mInitialSelection = LeanbackListPreferenceDialogFragment::class.java private val mInitialSelection = LeanbackListPreferenceDialogFragmentCompat::class.java
.getDeclaredField("mInitialSelection").apply { isAccessible = true } .getDeclaredField("mInitialSelection").apply { isAccessible = true }
} }
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val selected = mInitialSelection.get(this) as? String val selected = mInitialSelection.get(this) as? String
val index = (mEntryValues.get(this) as? Array<CharSequence?>)?.indexOfFirst { it == selected } val index = (mEntryValues.get(this) as? Array<CharSequence?>)?.indexOfFirst { it == selected }
return super.onCreateView(inflater, container, savedInstanceState)!!.also { return super.onCreateView(inflater, container, savedInstanceState)!!.also {
......
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