Commit 6240a1b4 authored by Mygod's avatar Mygod

Merge branch 'master' into dns-server

parents 732c46ee 78f021cd
......@@ -32,7 +32,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.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"
}
}
......
......@@ -30,7 +30,7 @@ import java.nio.ByteBuffer
import java.nio.ByteOrder
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 stat = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN)
override fun acceptInternal(socket: LocalSocket) {
......
......@@ -23,6 +23,7 @@ package com.github.shadowsocks.database
import android.net.Uri
import android.util.Base64
import android.util.Log
import android.util.LongSparseArray
import androidx.core.net.toUri
import androidx.room.*
import com.github.shadowsocks.plugin.PluginConfiguration
......@@ -249,7 +250,7 @@ class Profile : Serializable {
}
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_port", remotePort)
put("password", password)
......@@ -274,7 +275,7 @@ class Profile : Serializable {
}
})
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) }
}
......
......@@ -56,7 +56,7 @@ class ProfileConfigActivity : AppCompatActivity() {
menuInflater.inflate(R.menu.profile_config_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem?) = child.onOptionsItemSelected(item)
override fun onOptionsItemSelected(item: MenuItem) = child.onOptionsItemSelected(item)
override fun onBackPressed() {
if (DataStore.dirty) AlertDialog.Builder(this)
......
......@@ -179,7 +179,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
} 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 -> {
val activity = requireActivity()
AlertDialog.Builder(activity)
......
......@@ -28,6 +28,7 @@ import android.nfc.NdefRecord
import android.nfc.NfcAdapter
import android.os.Bundle
import android.text.format.Formatter
import android.util.LongSparseArray
import android.view.*
import android.widget.ImageView
import android.widget.LinearLayout
......@@ -98,7 +99,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
return image
}
override fun onAttach(context: Context?) {
override fun onAttach(context: Context) {
super.onAttach(context)
val adapter = NfcAdapter.getDefaultAdapter(context)
adapter?.setNdefPushMessage(NdefMessage(arrayOf(
......@@ -301,7 +302,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
val profilesAdapter by lazy { ProfilesAdapter() }
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>()!! }
......@@ -443,7 +444,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
REQUEST_EXPORT_PROFILES -> {
val profiles = ProfileManager.getAllProfiles()
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 {
it.write(JSONArray(profiles.map { it.toJson(lookup) }.toTypedArray()).toString(2))
}
......@@ -458,7 +459,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
override fun onTrafficUpdated(profileId: Long, stats: TrafficStats) {
if (profileId != 0L) { // ignore aggregate stats
statsCache[profileId] = stats
statsCache.put(profileId, stats)
profilesAdapter.refreshId(profileId)
}
}
......
......@@ -25,7 +25,7 @@ android {
dependencies {
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"
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test:runner:$androidTestVersion"
......
......@@ -52,7 +52,7 @@ android {
dependencies {
implementation project(':core')
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'
......
......@@ -21,26 +21,26 @@
package com.github.shadowsocks.tv
import androidx.core.os.bundleOf
import androidx.leanback.preference.LeanbackPreferenceDialogFragment
import androidx.leanback.preference.LeanbackSettingsFragment
import androidx.leanback.preference.LeanbackPreferenceDialogFragmentCompat
import androidx.leanback.preference.LeanbackSettingsFragmentCompat
import androidx.preference.*
import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.tv.preference.LeanbackSingleListPreferenceDialogFragment
import com.github.shadowsocks.utils.Key
class MainFragment : LeanbackSettingsFragment() {
class MainFragment : LeanbackSettingsFragmentCompat() {
override fun onPreferenceStartInitialScreen() = startPreferenceFragment(MainPreferenceFragment())
override fun onPreferenceStartScreen(caller: PreferenceFragment?, pref: PreferenceScreen?): Boolean {
override fun onPreferenceStartScreen(caller: PreferenceFragmentCompat?, pref: PreferenceScreen?): Boolean {
onPreferenceStartInitialScreen()
return true
}
override fun onPreferenceStartFragment(caller: PreferenceFragment?, pref: Preference?) = false
override fun onPreferenceDisplayDialog(caller: PreferenceFragment, pref: Preference?): Boolean {
override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat?, pref: Preference?) = false
override fun onPreferenceDisplayDialog(caller: PreferenceFragmentCompat, pref: Preference?): Boolean {
if (pref?.key == Key.id) {
if ((childFragmentManager.findFragmentById(R.id.settings_preference_fragment_container)
as MainPreferenceFragment).state == BaseService.STOPPED) {
startPreferenceFragment(ProfilesDialogFragment().apply {
arguments = bundleOf(Pair(LeanbackPreferenceDialogFragment.ARG_KEY, Key.id))
arguments = bundleOf(Pair(LeanbackPreferenceDialogFragmentCompat.ARG_KEY, Key.id))
setTargetFragment(caller, 0)
})
}
......@@ -48,7 +48,7 @@ class MainFragment : LeanbackSettingsFragment() {
}
if (pref is ListPreference && pref !is MultiSelectListPreference) {
startPreferenceFragment(LeanbackSingleListPreferenceDialogFragment().apply {
arguments = bundleOf(Pair(LeanbackPreferenceDialogFragment.ARG_KEY, pref.key))
arguments = bundleOf(Pair(LeanbackPreferenceDialogFragmentCompat.ARG_KEY, pref.key))
setTargetFragment(caller, 0)
})
return true
......
......@@ -25,22 +25,40 @@ import android.text.format.Formatter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import android.widget.TextView
import androidx.leanback.preference.LeanbackListPreferenceDialogFragment
import androidx.leanback.preference.LeanbackListPreferenceDialogFragmentCompat
import androidx.recyclerview.widget.RecyclerView
import com.github.shadowsocks.Core
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.plugin.PluginConfiguration
import com.github.shadowsocks.preference.DataStore
class ProfilesDialogFragment : LeanbackListPreferenceDialogFragment() {
inner class ProfilesAdapter : RecyclerView.Adapter<ViewHolder>(), ViewHolder.OnItemClickListener {
private val profiles = ProfileManager.getAllProfiles()!!
class ProfilesDialogFragment : LeanbackListPreferenceDialogFragmentCompat() {
private inner class ProfileViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
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
.from(parent.context).inflate(R.layout.leanback_list_preference_item_single_2, parent, false), this)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ProfileViewHolder(
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]
holder.widgetView.isChecked = profile.id == DataStore.profileId
holder.titleView.text = profile.formattedName
......@@ -54,24 +72,15 @@ class ProfilesDialogFragment : LeanbackListPreferenceDialogFragment() {
}
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()
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View {
private val adapter = ProfilesAdapter()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return super.onCreateView(inflater, container, savedInstanceState)!!.also {
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
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.leanback.preference.LeanbackListPreferenceDialogFragment
import androidx.leanback.preference.LeanbackListPreferenceDialogFragmentCompat
import androidx.recyclerview.widget.RecyclerView
/**
* Fix: scroll to selected item.
*/
open class LeanbackSingleListPreferenceDialogFragment : LeanbackListPreferenceDialogFragment() {
open class LeanbackSingleListPreferenceDialogFragment : LeanbackListPreferenceDialogFragmentCompat() {
companion object {
private val mEntryValues = LeanbackListPreferenceDialogFragment::class.java
private val mEntryValues = LeanbackListPreferenceDialogFragmentCompat::class.java
.getDeclaredField("mEntryValues").apply { isAccessible = true }
private val mInitialSelection = LeanbackListPreferenceDialogFragment::class.java
private val mInitialSelection = LeanbackListPreferenceDialogFragmentCompat::class.java
.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 index = (mEntryValues.get(this) as? Array<CharSequence?>)?.indexOfFirst { it == selected }
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