Commit 78f021cd authored by Mygod's avatar Mygod

Update dependencies

parent 6f355ed1
......@@ -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"
}
}
......
......@@ -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)
......
......@@ -99,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(
......
......@@ -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"
......
......@@ -54,7 +54,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
......
......@@ -32,8 +32,7 @@ import android.text.format.Formatter
import android.util.Log
import android.util.LongSparseArray
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import androidx.leanback.preference.LeanbackPreferenceFragment
import androidx.leanback.preference.LeanbackPreferenceFragmentCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.lifecycle.get
......@@ -55,10 +54,12 @@ import com.github.shadowsocks.net.HttpsTest
import com.github.shadowsocks.net.TcpFastOpen
import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
import com.github.shadowsocks.utils.*
import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.datas
import com.github.shadowsocks.utils.printLog
import org.json.JSONArray
class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnection.Callback,
class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksConnection.Callback,
OnPreferenceDataStoreChangeListener {
companion object {
private const val REQUEST_CONNECT = 1
......@@ -94,11 +95,13 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
private set
override fun stateChanged(state: Int, profileName: String?, msg: String?) = changeState(state, msg)
override fun trafficUpdated(profileId: Long, stats: TrafficStats) {
if (profileId == 0L) this@MainPreferenceFragment.stats.summary = getString(R.string.stat_summary,
getString(R.string.speed, Formatter.formatFileSize(activity, stats.txRate)),
getString(R.string.speed, Formatter.formatFileSize(activity, stats.rxRate)),
Formatter.formatFileSize(activity, stats.txTotal),
Formatter.formatFileSize(activity, stats.rxTotal))
if (profileId == 0L) requireContext().let { context ->
this.stats.summary = getString(R.string.stat_summary,
getString(R.string.speed, Formatter.formatFileSize(context, stats.txRate)),
getString(R.string.speed, Formatter.formatFileSize(context, stats.rxRate)),
Formatter.formatFileSize(context, stats.txTotal),
Formatter.formatFileSize(context, stats.rxTotal))
}
}
private fun changeState(state: Int, msg: String? = null) {
......@@ -111,15 +114,14 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
})
stats.setTitle(R.string.connection_test_pending)
stats.isVisible = state == BaseService.CONNECTED
val owner = activity as FragmentActivity // TODO: change to this when refactored to androidx
if (state != BaseService.CONNECTED) {
trafficUpdated(0, TrafficStats())
tester.status.removeObservers(owner)
tester.status.removeObservers(this)
if (state != BaseService.IDLE) tester.invalidate()
} else tester.status.observe(owner, Observer {
it.retrieve(stats::setTitle) { Toast.makeText(activity, it, Toast.LENGTH_LONG).show() }
} else tester.status.observe(this, Observer {
it.retrieve(stats::setTitle) { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
})
if (msg != null) Toast.makeText(activity, getString(R.string.vpn_error, msg), Toast.LENGTH_SHORT).show()
if (msg != null) Toast.makeText(requireContext(), getString(R.string.vpn_error, msg), Toast.LENGTH_SHORT).show()
this.state = state
if (state == BaseService.STOPPED) {
controlImport.isEnabled = true
......@@ -148,9 +150,9 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
})
override fun onServiceDisconnected() = changeState(BaseService.IDLE)
override fun onBinderDied() {
connection.disconnect(activity)
connection.disconnect(requireContext())
Executable.killAll()
connection.connect(activity, this)
connection.connect(requireContext(), this)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
......@@ -176,7 +178,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
if (value as Boolean && !TcpFastOpen.sendEnabled) {
val result = TcpFastOpen.enable()?.trim()
if (TcpFastOpen.sendEnabled) true else {
Toast.makeText(activity, if (result.isNullOrEmpty())
Toast.makeText(requireContext(), if (result.isNullOrEmpty())
getText(R.string.tcp_fastopen_failure) else result, Toast.LENGTH_SHORT).show()
false
}
......@@ -196,14 +198,14 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
findPreference(Key.about).apply {
summary = getString(R.string.about_title, BuildConfig.VERSION_NAME)
setOnPreferenceClickListener {
Toast.makeText(activity, "https://shadowsocks.org/android", Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), "https://shadowsocks.org/android", Toast.LENGTH_SHORT).show()
true
}
}
tester = ViewModelProviders.of(activity as FragmentActivity).get()
tester = ViewModelProviders.of(this).get()
changeState(BaseService.IDLE) // reset everything to init state
connection.connect(activity, this)
connection.connect(requireContext(), this)
DataStore.publicStore.registerChangeListener(this)
}
......@@ -229,7 +231,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
when {
state != BaseService.STOPPED -> return
DataStore.serviceMode == Key.modeVpn -> {
val intent = VpnService.prepare(activity)
val intent = VpnService.prepare(requireContext())
if (intent != null) startActivityForResult(intent, REQUEST_CONNECT)
else onActivityResult(REQUEST_CONNECT, Activity.RESULT_OK, null)
}
......@@ -240,8 +242,8 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String?) {
when (key) {
Key.serviceMode -> handler.post {
connection.disconnect(activity)
connection.connect(activity, this)
connection.disconnect(requireContext())
connection.connect(requireContext(), this)
}
}
}
......@@ -284,13 +286,13 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
startActivityForResult(intent, requestCode)
return
} catch (_: ActivityNotFoundException) { } catch (_: SecurityException) { }
Toast.makeText(activity, R.string.file_manager_missing, Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), R.string.file_manager_missing, Toast.LENGTH_SHORT).show()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_CONNECT -> if (resultCode == Activity.RESULT_OK) Core.startService() else {
Toast.makeText(activity, R.string.vpn_permission_denied, Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), R.string.vpn_permission_denied, Toast.LENGTH_SHORT).show()
Crashlytics.log(Log.ERROR, TAG, "Failed to start VpnService from onActivityResult: $data")
}
REQUEST_IMPORT_PROFILES -> {
......@@ -298,8 +300,9 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
val profiles = ProfileManager.getAllProfiles()?.associateBy { it.formattedAddress }
val feature = profiles?.values?.singleOrNull { it.id == DataStore.profileId }
val lazyClear = lazy { ProfileManager.clear() }
val context = requireContext()
for (uri in data!!.datas) try {
Profile.parseJson(activity.contentResolver.openInputStream(uri)!!.bufferedReader().readText(),
Profile.parseJson(context.contentResolver.openInputStream(uri)!!.bufferedReader().readText(),
feature) {
lazyClear.value
// if two profiles has the same address, treat them as the same profile and copy stats over
......@@ -311,21 +314,22 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
}
} catch (e: Exception) {
printLog(e)
Toast.makeText(activity, e.localizedMessage, Toast.LENGTH_SHORT).show()
Toast.makeText(context, e.localizedMessage, Toast.LENGTH_SHORT).show()
}
populateProfiles()
}
REQUEST_EXPORT_PROFILES -> {
if (resultCode != Activity.RESULT_OK) return
val profiles = ProfileManager.getAllProfiles()
val context = requireContext()
if (profiles != null) try {
val lookup = LongSparseArray<Profile>(profiles.size).apply { profiles.forEach { put(it.id, it) } }
activity.contentResolver.openOutputStream(data?.data!!)!!.bufferedWriter().use {
context.contentResolver.openOutputStream(data?.data!!)!!.bufferedWriter().use {
it.write(JSONArray(profiles.map { it.toJson(lookup) }.toTypedArray()).toString(2))
}
} catch (e: Exception) {
printLog(e)
Toast.makeText(activity, e.localizedMessage, Toast.LENGTH_SHORT).show()
Toast.makeText(context, e.localizedMessage, Toast.LENGTH_SHORT).show()
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
......@@ -335,7 +339,8 @@ class MainPreferenceFragment : LeanbackPreferenceFragment(), ShadowsocksConnecti
override fun onDestroy() {
super.onDestroy()
DataStore.publicStore.unregisterChangeListener(this)
connection.disconnect(activity)
BackupManager(activity).dataChanged()
val context = requireContext()
connection.disconnect(context)
BackupManager(context).dataChanged()
}
}
......@@ -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