diff --git a/build.gradle b/build.gradle index ccc6a36..9b82d35 100644 --- a/build.gradle +++ b/build.gradle @@ -14,8 +14,8 @@ buildscript { junitVersion = '4.13' androidTestVersion = '1.2.0' androidEspressoVersion = '3.2.0' - versionCode = 5000968 - versionName = '5.1.21-nightly' + versionCode = 5001008 + versionName = '5.1.24-nightly' resConfigs = ['ar', 'es', 'fa', 'fr', 'ja', 'ko', 'ru', 'tr', 'zh-rCN', 'zh-rTW'] } diff --git a/gitupdate.bat b/gitupdate.bat index 2e7030a..20a5de0 100644 --- a/gitupdate.bat +++ b/gitupdate.bat @@ -3,6 +3,6 @@ git pull origin master git add -A git commit -m "update" git push origin master -git tag -a v5.1.22 -m "release v5.1.22" +git tag -a v5.1.24 -m "release v5.1.24" git push origin --tags pause \ No newline at end of file diff --git a/libv2ray/libv2ray.aar b/libv2ray/libv2ray.aar deleted file mode 100644 index 79b35c3..0000000 Binary files a/libv2ray/libv2ray.aar and /dev/null differ diff --git a/tv/.gitignore b/tv/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/tv/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/tv/build.gradle b/tv/build.gradle deleted file mode 100644 index 1453c67..0000000 --- a/tv/build.gradle +++ /dev/null @@ -1,71 +0,0 @@ -import com.android.build.OutputFile -import java.util.regex.Matcher -import java.util.regex.Pattern - -apply plugin: 'com.android.application' -apply plugin: 'com.google.android.gms.oss-licenses-plugin' -apply plugin: 'com.google.firebase.crashlytics' -apply plugin: 'com.google.gms.google-services' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' - -def getCurrentFlavor() { - String task = getGradle().getStartParameter().getTaskRequests().toString() - Matcher matcher = Pattern.compile("(assemble|generate)\\w*(Release|Debug)").matcher(task) - if (matcher.find()) return matcher.group(2).toLowerCase() else { - println "Warning: No match found for $task" - return "debug" - } -} - -android { - compileSdkVersion rootProject.compileSdkVersion - defaultConfig { - applicationId "com.github.shadowsocks.tv" - minSdkVersion rootProject.minSdkVersion - targetSdkVersion rootProject.sdkVersion - versionCode rootProject.versionCode - versionName rootProject.versionName - resConfigs rootProject.resConfigs - } - buildTypes { - debug { - pseudoLocalesEnabled true - } - release { - shrinkResources true - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - compileOptions { - coreLibraryDesugaringEnabled true - sourceCompatibility javaVersion - targetCompatibility javaVersion - } - kotlinOptions.jvmTarget = javaVersion - packagingOptions.exclude '**/*.kotlin_*' - splits { - abi { - enable true - universalApk true - } - } - sourceSets.main.jniLibs.srcDirs += - new File(project(':core').buildDir, "intermediates/bundles/${getCurrentFlavor()}/jni") -} - -dependencies { - coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$desugarLibsVersion" - implementation project(':core') - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.leanback:leanback-preference:1.1.0-alpha03' -} - -ext.abiCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, x86: 3, x86_64: 4] -if (getCurrentFlavor() == 'release') android.applicationVariants.all { variant -> - variant.outputs.each { output -> - def offset = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI)) - if (offset != null) output.versionCodeOverride = variant.versionCode + offset - } -} diff --git a/tv/google-services.json b/tv/google-services.json deleted file mode 100644 index 8c8c768..0000000 --- a/tv/google-services.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "project_info": { - "project_number": "261400168171", - "firebase_url": "https://admob-app-id-3330146721.firebaseio.com", - "project_id": "admob-app-id-3330146721", - "storage_bucket": "admob-app-id-3330146721.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:261400168171:android:dbdd6331c434162f", - "android_client_info": { - "package_name": "com.github.shadowsocks" - } - }, - "oauth_client": [ - { - "client_id": "261400168171-sfik8o3pj7e243583olorh7s5974vab1.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "com.github.shadowsocks", - "certificate_hash": "58a90f84cfe99d4280aec677c9a1292fae131677" - } - }, - { - "client_id": "261400168171-g7aelv5bu012ojr7dod7lq09c9anjimh.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyCee3fAad7nb3YsxeUO9mqqHFfAvsSCbVs" - } - ], - "services": { - "analytics_service": { - "status": 1 - }, - "appinvite_service": { - "status": 1, - "other_platform_oauth_client": [] - }, - "ads_service": { - "status": 2 - } - }, - "admob_app_id": "ca-app-pub-9097031975646651~3330146721" - }, - { - "client_info": { - "mobilesdk_app_id": "1:261400168171:android:0dbac07695d93817", - "android_client_info": { - "package_name": "com.github.shadowsocks.tv" - } - }, - "oauth_client": [ - { - "client_id": "261400168171-g7aelv5bu012ojr7dod7lq09c9anjimh.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyCee3fAad7nb3YsxeUO9mqqHFfAvsSCbVs" - } - ], - "services": { - "analytics_service": { - "status": 1 - }, - "appinvite_service": { - "status": 1, - "other_platform_oauth_client": [] - }, - "ads_service": { - "status": 2 - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/tv/src/main/AndroidManifest.xml b/tv/src/main/AndroidManifest.xml deleted file mode 100644 index 45d8c5f..0000000 --- a/tv/src/main/AndroidManifest.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/tv/src/main/java/com/github/shadowsocks/tv/App.kt b/tv/src/main/java/com/github/shadowsocks/tv/App.kt deleted file mode 100644 index 10d42e8..0000000 --- a/tv/src/main/java/com/github/shadowsocks/tv/App.kt +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * * - * Copyright (C) 2018 by Max Lv * - * Copyright (C) 2018 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -package com.github.shadowsocks.tv - -import android.app.Application -import android.content.res.Configuration -import com.github.shadowsocks.Core - -class App : Application() { - override fun onCreate() { - super.onCreate() - Core.init(this, MainActivity::class) - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - Core.updateNotificationChannels() - } -} diff --git a/tv/src/main/java/com/github/shadowsocks/tv/MainActivity.kt b/tv/src/main/java/com/github/shadowsocks/tv/MainActivity.kt deleted file mode 100644 index 196dc0b..0000000 --- a/tv/src/main/java/com/github/shadowsocks/tv/MainActivity.kt +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * * - * Copyright (C) 2018 by Max Lv * - * Copyright (C) 2018 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -package com.github.shadowsocks.tv - -import android.os.Bundle -import androidx.fragment.app.FragmentActivity -import com.github.shadowsocks.utils.SingleInstanceActivity - -class MainActivity : FragmentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - SingleInstanceActivity.register(this) ?: return - setContentView(R.layout.activity_main) - } -} diff --git a/tv/src/main/java/com/github/shadowsocks/tv/MainFragment.kt b/tv/src/main/java/com/github/shadowsocks/tv/MainFragment.kt deleted file mode 100644 index df10c72..0000000 --- a/tv/src/main/java/com/github/shadowsocks/tv/MainFragment.kt +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* - * * - * Copyright (C) 2018 by Max Lv * - * Copyright (C) 2018 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -package com.github.shadowsocks.tv - -import android.os.Bundle -import android.view.View -import android.view.ViewGroup -import androidx.core.os.bundleOf -import androidx.core.view.updateLayoutParams -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 : LeanbackSettingsFragmentCompat() { - override fun onPreferenceStartInitialScreen() = startPreferenceFragment(MainPreferenceFragment()) - override fun onPreferenceStartScreen(caller: PreferenceFragmentCompat?, pref: PreferenceScreen?): Boolean { - onPreferenceStartInitialScreen() - return true - } - 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.State.Stopped) { - startPreferenceFragment(ProfilesDialogFragment().apply { - arguments = bundleOf(Pair(LeanbackPreferenceDialogFragmentCompat.ARG_KEY, Key.id)) - setTargetFragment(caller, 0) - }) - } - return true - } - if (pref is ListPreference && pref !is MultiSelectListPreference) { - startPreferenceFragment(LeanbackSingleListPreferenceDialogFragment().apply { - arguments = bundleOf(Pair(LeanbackPreferenceDialogFragmentCompat.ARG_KEY, pref.key)) - setTargetFragment(caller, 0) - }) - return true - } - return super.onPreferenceDisplayDialog(caller, pref) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - view.findViewById(R.id.settings_preference_fragment_container).updateLayoutParams { - width = ViewGroup.LayoutParams.MATCH_PARENT - } - } -} diff --git a/tv/src/main/java/com/github/shadowsocks/tv/MainPreferenceFragment.kt b/tv/src/main/java/com/github/shadowsocks/tv/MainPreferenceFragment.kt deleted file mode 100644 index d2b990a..0000000 --- a/tv/src/main/java/com/github/shadowsocks/tv/MainPreferenceFragment.kt +++ /dev/null @@ -1,345 +0,0 @@ -/******************************************************************************* - * * - * Copyright (C) 2018 by Max Lv * - * Copyright (C) 2018 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -package com.github.shadowsocks.tv - -import android.app.Activity -import android.app.backup.BackupManager -import android.content.ActivityNotFoundException -import android.content.Intent -import android.net.VpnService -import android.os.Bundle -import android.os.Handler -import android.os.RemoteException -import android.text.format.Formatter -import android.util.Log -import android.widget.Toast -import androidx.fragment.app.viewModels -import androidx.leanback.preference.LeanbackPreferenceFragmentCompat -import androidx.lifecycle.observe -import androidx.preference.* -import com.crashlytics.android.Crashlytics -import com.github.shadowsocks.BootReceiver -import com.github.shadowsocks.Core -import com.github.shadowsocks.aidl.IShadowsocksService -import com.github.shadowsocks.aidl.ShadowsocksConnection -import com.github.shadowsocks.aidl.TrafficStats -import com.github.shadowsocks.bg.BaseService -import com.github.shadowsocks.database.ProfileManager -import com.github.shadowsocks.net.HttpsTest -import com.github.shadowsocks.net.TcpFastOpen -import com.github.shadowsocks.preference.DataStore -import com.github.shadowsocks.preference.EditTextPreferenceModifiers -import com.github.shadowsocks.preference.HostsSummaryProvider -import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener -import com.github.shadowsocks.utils.Key -import com.github.shadowsocks.utils.datas -import com.github.shadowsocks.utils.printLog -import com.github.shadowsocks.utils.readableMessage -import com.google.android.gms.oss.licenses.OssLicensesMenuActivity - -class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksConnection.Callback, - OnPreferenceDataStoreChangeListener { - companion object { - private const val REQUEST_CONNECT = 1 - private const val REQUEST_REPLACE_PROFILES = 2 - private const val REQUEST_EXPORT_PROFILES = 3 - private const val REQUEST_HOSTS = 4 - private const val TAG = "MainPreferenceFragment" - } - - private lateinit var fab: ListPreference - private lateinit var stats: Preference - private lateinit var controlImport: Preference - private lateinit var hosts: EditTextPreference - private lateinit var serviceMode: Preference - private lateinit var tfo: SwitchPreference - private lateinit var shareOverLan: Preference - private lateinit var portProxy: EditTextPreference - private lateinit var portLocalDns: EditTextPreference - private lateinit var portTransproxy: EditTextPreference - private val onServiceModeChange = Preference.OnPreferenceChangeListener { _, newValue -> - val (enabledLocalDns, enabledTransproxy) = when (newValue as String?) { - Key.modeProxy -> Pair(false, false) - Key.modeVpn -> Pair(true, false) - Key.modeTransproxy -> Pair(true, true) - else -> throw IllegalArgumentException("newValue: $newValue") - } - hosts.isEnabled = enabledLocalDns - portLocalDns.isEnabled = enabledLocalDns - portTransproxy.isEnabled = enabledTransproxy - true - } - private val tester by viewModels() - - // service - var state = BaseService.State.Idle - private set - override fun stateChanged(state: BaseService.State, profileName: String?, msg: String?) = changeState(state, msg) - override fun trafficUpdated(profileId: Long, stats: TrafficStats) { - if (profileId == 0L) context?.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: BaseService.State, msg: String? = null) { - val context = context ?: return - fab.isEnabled = state.canStop || state == BaseService.State.Stopped - fab.setTitle(when (state) { - BaseService.State.Connecting -> R.string.connecting - BaseService.State.Connected -> R.string.stop - BaseService.State.Stopping -> R.string.stopping - else -> R.string.connect - }) - stats.setTitle(R.string.connection_test_pending) - if ((state == BaseService.State.Connected).also { stats.isVisible = it }) tester.status.observe(this) { - it.retrieve(stats::setTitle) { msg -> Toast.makeText(context, msg, Toast.LENGTH_LONG).show() } - } else { - trafficUpdated(0, TrafficStats()) - tester.status.removeObservers(this) - if (state != BaseService.State.Idle) tester.invalidate() - } - if (msg != null) Toast.makeText(context, getString(R.string.vpn_error, msg), Toast.LENGTH_SHORT).show() - this.state = state - val stopped = state == BaseService.State.Stopped - controlImport.isEnabled = stopped - tfo.isEnabled = stopped - serviceMode.isEnabled = stopped - shareOverLan.isEnabled = stopped - portProxy.isEnabled = stopped - if (stopped) onServiceModeChange.onPreferenceChange(null, DataStore.serviceMode) else { - portLocalDns.isEnabled = false - portTransproxy.isEnabled = false - } - } - - private val handler = Handler() - private val connection = ShadowsocksConnection(handler, true) - override fun onServiceConnected(service: IShadowsocksService) = changeState(try { - BaseService.State.values()[service.state] - } catch (_: RemoteException) { - BaseService.State.Idle - }) - override fun onServiceDisconnected() = changeState(BaseService.State.Idle) - override fun onBinderDied() { - connection.disconnect(requireContext()) - connection.connect(requireContext(), this) - } - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - preferenceManager.preferenceDataStore = DataStore.publicStore - DataStore.initGlobal() - addPreferencesFromResource(R.xml.pref_main) - fab = findPreference(Key.id)!! - populateProfiles() - stats = findPreference(Key.controlStats)!! - controlImport = findPreference(Key.controlImport)!! - - findPreference(Key.persistAcrossReboot)!!.setOnPreferenceChangeListener { _, value -> - BootReceiver.enabled = value as Boolean - true - } - - tfo = findPreference(Key.tfo)!! - tfo.isChecked = DataStore.tcpFastOpen - tfo.setOnPreferenceChangeListener { _, value -> - if (value as Boolean && !TcpFastOpen.sendEnabled) { - val result = TcpFastOpen.enable()?.trim() - if (TcpFastOpen.sendEnabled) true else { - Toast.makeText(requireContext(), if (result.isNullOrEmpty()) - getText(R.string.tcp_fastopen_failure) else result, Toast.LENGTH_SHORT).show() - false - } - } else true - } - if (!TcpFastOpen.supported) { - tfo.isEnabled = false - tfo.summary = getString(R.string.tcp_fastopen_summary_unsupported, System.getProperty("os.version")) - } - - hosts = findPreference(Key.hosts)!! - hosts.summaryProvider = HostsSummaryProvider - serviceMode = findPreference(Key.serviceMode)!! - shareOverLan = findPreference(Key.shareOverLan)!! - portProxy = findPreference(Key.portProxy)!! - portProxy.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) - portLocalDns = findPreference(Key.portLocalDns)!! - portLocalDns.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) - portTransproxy = findPreference(Key.portTransproxy)!! - portTransproxy.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) - serviceMode.onPreferenceChangeListener = onServiceModeChange - findPreference(Key.about)!!.summary = getString(R.string.about_title, BuildConfig.VERSION_NAME) - - changeState(BaseService.State.Idle) // reset everything to init state - connection.connect(requireContext(), this) - DataStore.publicStore.registerChangeListener(this) - } - - override fun onStart() { - super.onStart() - connection.bandwidthTimeout = 500 - } - - override fun onResume() { - super.onResume() - fab.value = DataStore.profileId.toString() - } - - private fun populateProfiles() { - ProfileManager.ensureNotEmpty() - val profiles = ProfileManager.getActiveProfiles()!! - fab.value = null - fab.entries = profiles.map { it.formattedName }.toTypedArray() - fab.entryValues = profiles.map { it.id.toString() }.toTypedArray() - } - - fun startService() { - when { - state != BaseService.State.Stopped -> return - DataStore.serviceMode == Key.modeVpn -> { - val intent = VpnService.prepare(requireContext()) - if (intent != null) startActivityForResult(intent, REQUEST_CONNECT) - else onActivityResult(REQUEST_CONNECT, Activity.RESULT_OK, null) - } - else -> Core.startService() - } - } - - override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String) { - when (key) { - Key.serviceMode -> handler.post { - connection.disconnect(requireContext()) - connection.connect(requireContext(), this) - } - } - } - - override fun onStop() { - connection.bandwidthTimeout = 0 - super.onStop() - } - - override fun onPreferenceTreeClick(preference: Preference?) = when (preference?.key) { - Key.id -> { - if (state == BaseService.State.Connected) Core.stopService() - true - } - Key.controlStats -> { - tester.testConnection() - true - } - Key.controlImport -> { - startFilesForResult(Intent(Intent.ACTION_GET_CONTENT).apply { - type = "application/*" - putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) - putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("application/*", "text/*")) - }, REQUEST_REPLACE_PROFILES) - true - } - Key.controlExport -> { - startFilesForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply { - type = "application/json" - putExtra(Intent.EXTRA_TITLE, "profiles.json") // optional title that can be edited - }, REQUEST_EXPORT_PROFILES) - true - } - Key.about -> { - Toast.makeText(requireContext(), "https://shadowsocks.org/android", Toast.LENGTH_SHORT).show() - true - } - Key.aboutOss -> { - startActivity(Intent(context, OssLicensesMenuActivity::class.java)) - true - } - else -> super.onPreferenceTreeClick(preference) - } - - override fun onDisplayPreferenceDialog(preference: Preference?) { - if (preference != hosts || startFilesForResult(Intent(Intent.ACTION_GET_CONTENT).setType("*/*"), REQUEST_HOSTS)) - super.onDisplayPreferenceDialog(preference) - } - - private fun startFilesForResult(intent: Intent, requestCode: Int): Boolean { - try { - startActivityForResult(intent.addCategory(Intent.CATEGORY_OPENABLE), requestCode) - return false - } catch (_: ActivityNotFoundException) { } catch (_: SecurityException) { } - Toast.makeText(requireContext(), R.string.file_manager_missing, Toast.LENGTH_SHORT).show() - return true - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - when (requestCode) { - REQUEST_CONNECT -> if (resultCode == Activity.RESULT_OK) Core.startService() else { - 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_REPLACE_PROFILES -> { - if (resultCode != Activity.RESULT_OK) return - val context = requireContext() - try { - ProfileManager.createProfilesFromJson(data!!.datas.asSequence().map { - context.contentResolver.openInputStream(it) - }.filterNotNull(), true) - } catch (e: Exception) { - printLog(e) - Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show() - } - populateProfiles() - } - REQUEST_EXPORT_PROFILES -> { - if (resultCode != Activity.RESULT_OK) return - val profiles = ProfileManager.serializeToJson() - val context = requireContext() - if (profiles != null) try { - context.contentResolver.openOutputStream(data?.data!!)!!.bufferedWriter().use { - it.write(profiles.toString(2)) - } - } catch (e: Exception) { - printLog(e) - Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show() - } - } - REQUEST_HOSTS -> { - if (resultCode != Activity.RESULT_OK) return - val context = requireContext() - try { - // we read and persist all its content here to avoid content URL permission issues - hosts.text = context.contentResolver.openInputStream(data!!.data!!)!!.bufferedReader().readText() - } catch (e: Exception) { - Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show() - } - } - else -> super.onActivityResult(requestCode, resultCode, data) - } - } - - override fun onDestroy() { - super.onDestroy() - DataStore.publicStore.unregisterChangeListener(this) - val context = requireContext() - connection.disconnect(context) - BackupManager(context).dataChanged() - } -} diff --git a/tv/src/main/java/com/github/shadowsocks/tv/ProfilesDialogFragment.kt b/tv/src/main/java/com/github/shadowsocks/tv/ProfilesDialogFragment.kt deleted file mode 100644 index 3e7087e..0000000 --- a/tv/src/main/java/com/github/shadowsocks/tv/ProfilesDialogFragment.kt +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************* - * * - * Copyright (C) 2018 by Max Lv * - * Copyright (C) 2018 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -package com.github.shadowsocks.tv - -import android.os.Bundle -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.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 : LeanbackListPreferenceDialogFragmentCompat() { - private inner class ProfileViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener { - val widgetView = view.findViewById(R.id.button) - val titleView = view.findViewById(android.R.id.title) - init { - view.findViewById(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() - parentFragmentManager.popBackStack() - adapter.notifyDataSetChanged() - } - } - private inner class ProfilesAdapter : RecyclerView.Adapter() { - val profiles = ProfileManager.getActiveProfiles()!! - - 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: ProfileViewHolder, position: Int) { - val profile = profiles[position] - holder.widgetView.isChecked = profile.id == DataStore.profileId - holder.titleView.text = profile.formattedName - holder.itemView.findViewById(android.R.id.summary).text = ArrayList().apply { - if (!profile.name.isNullOrEmpty()) this += profile.formattedAddress - val id = PluginConfiguration(profile.plugin ?: "").selected - if (id.isNotEmpty()) this += getString(R.string.profile_plugin, id) - if (profile.tx > 0 || profile.rx > 0) this += getString(R.string.traffic, - Formatter.formatFileSize(activity, profile.tx), Formatter.formatFileSize(activity, profile.rx)) - }.joinToString("\n") - } - - override fun getItemCount() = profiles.size - } - - private val adapter = ProfilesAdapter() - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - return super.onCreateView(inflater, container, savedInstanceState)!!.apply { - val list = findViewById(android.R.id.list) - list.adapter = adapter - list.layoutManager!!.scrollToPosition(adapter.profiles.indexOfFirst { it.id == DataStore.profileId }) - } - } -} diff --git a/tv/src/main/java/com/github/shadowsocks/tv/preference/LeanbackSingleListPreferenceDialogFragment.kt b/tv/src/main/java/com/github/shadowsocks/tv/preference/LeanbackSingleListPreferenceDialogFragment.kt deleted file mode 100644 index 7600e29..0000000 --- a/tv/src/main/java/com/github/shadowsocks/tv/preference/LeanbackSingleListPreferenceDialogFragment.kt +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************************* - * * - * Copyright (C) 2018 by Max Lv * - * Copyright (C) 2018 by Mygod Studio * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -package com.github.shadowsocks.tv.preference - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.leanback.preference.LeanbackListPreferenceDialogFragmentCompat -import androidx.recyclerview.widget.RecyclerView - -/** - * Fix: scroll to selected item. - */ -open class LeanbackSingleListPreferenceDialogFragment : LeanbackListPreferenceDialogFragmentCompat() { - companion object { - private val mEntryValues = LeanbackListPreferenceDialogFragmentCompat::class.java - .getDeclaredField("mEntryValues").apply { isAccessible = true } - private val mInitialSelection = LeanbackListPreferenceDialogFragmentCompat::class.java - .getDeclaredField("mInitialSelection").apply { isAccessible = true } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val selected = mInitialSelection.get(this) as? String - val index = (mEntryValues.get(this) as? Array)?.indexOfFirst { it == selected } - return super.onCreateView(inflater, container, savedInstanceState)!!.also { - if (index != null) it.findViewById(android.R.id.list).layoutManager!!.scrollToPosition(index) - } - } -} diff --git a/tv/src/main/res/layout/activity_main.xml b/tv/src/main/res/layout/activity_main.xml deleted file mode 100644 index 30e2f0b..0000000 --- a/tv/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,10 +0,0 @@ - - diff --git a/tv/src/main/res/layout/leanback_list_preference_item_single_2.xml b/tv/src/main/res/layout/leanback_list_preference_item_single_2.xml deleted file mode 100644 index 65a75a9..0000000 --- a/tv/src/main/res/layout/leanback_list_preference_item_single_2.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/tv/src/main/res/values/styles.xml b/tv/src/main/res/values/styles.xml deleted file mode 100644 index 6bad520..0000000 --- a/tv/src/main/res/values/styles.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/tv/src/main/res/xml/pref_main.xml b/tv/src/main/res/xml/pref_main.xml deleted file mode 100644 index f7c6250..0000000 --- a/tv/src/main/res/xml/pref_main.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - -