Skip to content

Commit f014131

Browse files
authored
Merge pull request shadowsocks#2123 from shadowsocks/plugin-1.2.0
Plugin library 1.2.0
2 parents 1c1a8ed + f4621f3 commit f014131

File tree

10 files changed

+257
-124
lines changed

10 files changed

+257
-124
lines changed

core/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
apply plugin: 'com.android.library'
22
apply plugin: 'kotlin-android'
3+
apply plugin: 'kotlin-android-extensions'
34
apply plugin: 'kotlin-kapt'
45

56
android {
@@ -38,6 +39,10 @@ android {
3839
}
3940
}
4041

42+
androidExtensions {
43+
experimental = true
44+
}
45+
4146
def lifecycleVersion = '2.0.0'
4247
def roomVersion = '2.0.0'
4348
def workVersion = '1.0.0-rc01'

core/src/main/java/com/github/shadowsocks/database/Profile.kt

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package com.github.shadowsocks.database
2222

2323
import android.net.Uri
24+
import android.os.Parcelable
2425
import android.util.Base64
2526
import android.util.Log
2627
import android.util.LongSparseArray
@@ -32,6 +33,7 @@ import com.github.shadowsocks.preference.DataStore
3233
import com.github.shadowsocks.utils.Key
3334
import com.github.shadowsocks.utils.asIterable
3435
import com.github.shadowsocks.utils.parsePort
36+
import kotlinx.android.parcel.Parcelize
3537
import org.json.JSONArray
3638
import org.json.JSONObject
3739
import org.json.JSONTokener
@@ -41,7 +43,31 @@ import java.net.URISyntaxException
4143
import java.util.*
4244

4345
@Entity
44-
class Profile : Serializable {
46+
@Parcelize
47+
data class Profile(
48+
@PrimaryKey(autoGenerate = true)
49+
var id: Long = 0,
50+
var name: String? = "",
51+
var host: String = "198.199.101.152",
52+
var remotePort: Int = 8388,
53+
var password: String = "u1rRWTssNv0p",
54+
var method: String = "aes-256-cfb",
55+
var route: String = "all",
56+
var remoteDns: String = "8.8.8.8",
57+
var proxyApps: Boolean = false,
58+
var bypass: Boolean = false,
59+
var udpdns: Boolean = false,
60+
var ipv6: Boolean = true,
61+
var individual: String = "",
62+
var tx: Long = 0,
63+
var rx: Long = 0,
64+
var userOrder: Long = 0,
65+
var plugin: String? = null,
66+
var udpFallback: Long? = null,
67+
68+
@Ignore // not persisted in db, only used by direct boot
69+
var dirty: Boolean = false
70+
) : Parcelable, Serializable {
4571
companion object {
4672
private const val TAG = "ShadowParser"
4773
private const val serialVersionUID = 0L
@@ -200,29 +226,6 @@ class Profile : Serializable {
200226
fun deleteAll(): Int
201227
}
202228

203-
@PrimaryKey(autoGenerate = true)
204-
var id: Long = 0
205-
var name: String? = ""
206-
var host: String = "198.199.101.152"
207-
var remotePort: Int = 8388
208-
var password: String = "u1rRWTssNv0p"
209-
var method: String = "aes-256-cfb"
210-
var route: String = "all"
211-
var remoteDns: String = "8.8.8.8"
212-
var proxyApps: Boolean = false
213-
var bypass: Boolean = false
214-
var udpdns: Boolean = false
215-
var ipv6: Boolean = true
216-
var individual: String = ""
217-
var tx: Long = 0
218-
var rx: Long = 0
219-
var userOrder: Long = 0
220-
var plugin: String? = null
221-
var udpFallback: Long? = null
222-
223-
@Ignore // not persisted in db, only used by direct boot
224-
var dirty: Boolean = false
225-
226229
val formattedAddress get() = (if (host.contains(":")) "[%s]:%d" else "%s:%d").format(host, remotePort)
227230
val formattedName get() = if (name.isNullOrEmpty()) formattedAddress else name!!
228231

mobile/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import java.util.regex.Pattern
55
apply plugin: 'com.android.application'
66
apply plugin: 'io.fabric'
77
apply plugin: 'kotlin-android'
8+
apply plugin: 'kotlin-android-extensions'
89

910
def getCurrentFlavor() {
1011
String task = getGradle().getStartParameter().getTaskRequests().toString()
@@ -49,6 +50,10 @@ android {
4950
new File(project(':core').buildDir, "intermediates/bundles/${getCurrentFlavor()}/jni")
5051
}
5152

53+
androidExtensions {
54+
experimental = true
55+
}
56+
5257
dependencies {
5358
implementation project(':core')
5459
implementation "androidx.browser:browser:1.0.0"

mobile/src/main/java/com/github/shadowsocks/MainActivity.kt

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ package com.github.shadowsocks
2323
import android.app.Activity
2424
import android.app.backup.BackupManager
2525
import android.content.ActivityNotFoundException
26+
import android.content.DialogInterface
2627
import android.content.Intent
2728
import android.net.VpnService
2829
import android.nfc.NdefMessage
2930
import android.nfc.NfcAdapter
3031
import android.os.Bundle
3132
import android.os.DeadObjectException
3233
import android.os.Handler
34+
import android.os.Parcelable
3335
import android.util.Log
3436
import android.view.KeyCharacterMap
3537
import android.view.KeyEvent
@@ -51,13 +53,16 @@ import com.github.shadowsocks.aidl.TrafficStats
5153
import com.github.shadowsocks.bg.BaseService
5254
import com.github.shadowsocks.database.Profile
5355
import com.github.shadowsocks.database.ProfileManager
56+
import com.github.shadowsocks.plugin.AlertDialogFragment
57+
import com.github.shadowsocks.plugin.Empty
5458
import com.github.shadowsocks.preference.DataStore
5559
import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
5660
import com.github.shadowsocks.utils.Key
5761
import com.github.shadowsocks.widget.ServiceButton
5862
import com.github.shadowsocks.widget.StatsBar
5963
import com.google.android.material.navigation.NavigationView
6064
import com.google.android.material.snackbar.Snackbar
65+
import kotlinx.android.parcel.Parcelize
6166

6267
class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPreferenceDataStoreChangeListener,
6368
NavigationView.OnNavigationItemSelectedListener {
@@ -68,6 +73,17 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
6873
var stateListener: ((Int) -> Unit)? = null
6974
}
7075

76+
@Parcelize
77+
data class ProfilesArg(val profiles: List<Profile>) : Parcelable
78+
class ImportProfilesDialogFragment : AlertDialogFragment<ProfilesArg, Empty>() {
79+
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
80+
setTitle(R.string.add_profile_dialog)
81+
setPositiveButton(R.string.yes) { _, _ -> arg.profiles.forEach { ProfileManager.createProfile(it) } }
82+
setNegativeButton(R.string.no, null)
83+
setMessage(arg.profiles.joinToString("\n"))
84+
}
85+
}
86+
7187
// UI
7288
private lateinit var fab: ServiceButton
7389
private lateinit var stats: StatsBar
@@ -188,17 +204,8 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
188204
}
189205
if (sharedStr.isNullOrEmpty()) return
190206
val profiles = Profile.findAllUrls(sharedStr, Core.currentProfile?.first).toList()
191-
if (profiles.isEmpty()) {
192-
snackbar().setText(R.string.profile_invalid_input).show()
193-
return
194-
}
195-
AlertDialog.Builder(this)
196-
.setTitle(R.string.add_profile_dialog)
197-
.setPositiveButton(R.string.yes) { _, _ -> profiles.forEach { ProfileManager.createProfile(it) } }
198-
.setNegativeButton(R.string.no, null)
199-
.setMessage(profiles.joinToString("\n"))
200-
.create()
201-
.show()
207+
if (profiles.isEmpty()) snackbar().setText(R.string.profile_invalid_input).show()
208+
else ImportProfilesDialogFragment().withArg(ProfilesArg(profiles)).show(supportFragmentManager, null)
202209
}
203210

204211
override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String?) {

mobile/src/main/java/com/github/shadowsocks/ProfileConfigActivity.kt

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@
2121
package com.github.shadowsocks
2222

2323
import android.app.Activity
24+
import android.content.DialogInterface
2425
import android.content.Intent
2526
import android.os.Bundle
2627
import android.view.Menu
2728
import android.view.MenuItem
2829
import androidx.appcompat.app.AlertDialog
2930
import androidx.appcompat.app.AppCompatActivity
31+
import com.github.shadowsocks.plugin.AlertDialogFragment
32+
import com.github.shadowsocks.plugin.Empty
3033
import com.github.shadowsocks.plugin.PluginContract
3134
import com.github.shadowsocks.preference.DataStore
3235

@@ -35,6 +38,15 @@ class ProfileConfigActivity : AppCompatActivity() {
3538
const val REQUEST_CODE_PLUGIN_HELP = 1
3639
}
3740

41+
class UnsavedChangesDialogFragment : AlertDialogFragment<Empty, Empty>() {
42+
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
43+
setTitle(R.string.unsaved_changes_prompt)
44+
setPositiveButton(R.string.yes, listener)
45+
setNegativeButton(R.string.no, listener)
46+
setNeutralButton(android.R.string.cancel, null)
47+
}
48+
}
49+
3850
private val child by lazy { supportFragmentManager.findFragmentById(R.id.content) as ProfileConfigFragment }
3951

4052
override fun onCreate(savedInstanceState: Bundle?) {
@@ -59,13 +71,8 @@ class ProfileConfigActivity : AppCompatActivity() {
5971
override fun onOptionsItemSelected(item: MenuItem) = child.onOptionsItemSelected(item)
6072

6173
override fun onBackPressed() {
62-
if (DataStore.dirty) AlertDialog.Builder(this)
63-
.setTitle(R.string.unsaved_changes_prompt)
64-
.setPositiveButton(R.string.yes) { _, _ -> child.saveAndExit() }
65-
.setNegativeButton(R.string.no) { _, _ -> finish() }
66-
.setNeutralButton(android.R.string.cancel, null)
67-
.create()
68-
.show() else super.onBackPressed()
74+
if (DataStore.dirty) UnsavedChangesDialogFragment().show(child, ProfileConfigFragment.REQUEST_UNSAVED_CHANGES)
75+
else super.onBackPressed()
6976
}
7077

7178
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ package com.github.shadowsocks
2222

2323
import android.app.Activity
2424
import android.content.BroadcastReceiver
25+
import android.content.DialogInterface
2526
import android.content.Intent
2627
import android.os.Bundle
28+
import android.os.Parcelable
2729
import android.view.MenuItem
2830
import androidx.appcompat.app.AlertDialog
2931
import androidx.core.os.bundleOf
@@ -33,10 +35,7 @@ import androidx.preference.SwitchPreference
3335
import com.github.shadowsocks.Core.app
3436
import com.github.shadowsocks.database.Profile
3537
import com.github.shadowsocks.database.ProfileManager
36-
import com.github.shadowsocks.plugin.PluginConfiguration
37-
import com.github.shadowsocks.plugin.PluginContract
38-
import com.github.shadowsocks.plugin.PluginManager
39-
import com.github.shadowsocks.plugin.PluginOptions
38+
import com.github.shadowsocks.plugin.*
4039
import com.github.shadowsocks.preference.DataStore
4140
import com.github.shadowsocks.preference.IconListPreference
4241
import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
@@ -47,11 +46,26 @@ import com.github.shadowsocks.utils.Key
4746
import com.google.android.material.snackbar.Snackbar
4847
import com.takisoft.preferencex.EditTextPreference
4948
import com.takisoft.preferencex.PreferenceFragmentCompat
49+
import kotlinx.android.parcel.Parcelize
5050

5151
class ProfileConfigFragment : PreferenceFragmentCompat(),
5252
Preference.OnPreferenceChangeListener, OnPreferenceDataStoreChangeListener {
5353
companion object {
5454
private const val REQUEST_CODE_PLUGIN_CONFIGURE = 1
55+
const val REQUEST_UNSAVED_CHANGES = 2
56+
}
57+
58+
@Parcelize
59+
data class ProfileIdArg(val profileId: Long) : Parcelable
60+
class DeleteConfirmationDialogFragment : AlertDialogFragment<ProfileIdArg, Empty>() {
61+
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
62+
setTitle(R.string.delete_confirm_prompt)
63+
setPositiveButton(R.string.yes) { _, _ ->
64+
ProfileManager.delProfile(arg.profileId)
65+
requireActivity().finish()
66+
}
67+
setNegativeButton(R.string.no, null)
68+
}
5569
}
5670

5771
private var profileId = -1L
@@ -116,7 +130,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
116130
bundleOf(Pair("key", Key.pluginConfigure),
117131
Pair(PluginConfigurationDialogFragment.PLUGIN_ID_FRAGMENT_TAG, pluginConfiguration.selected)))
118132

119-
fun saveAndExit() {
133+
private fun saveAndExit() {
120134
val profile = ProfileManager.getProfile(profileId) ?: Profile()
121135
profile.id = profileId
122136
profile.deserialize()
@@ -161,28 +175,26 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
161175
}
162176

163177
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
164-
if (requestCode == REQUEST_CODE_PLUGIN_CONFIGURE) when (resultCode) {
165-
Activity.RESULT_OK -> {
166-
val options = data?.getStringExtra(PluginContract.EXTRA_OPTIONS)
167-
pluginConfigure.text = options
168-
onPreferenceChange(null, options)
178+
when (requestCode) {
179+
REQUEST_CODE_PLUGIN_CONFIGURE -> when (resultCode) {
180+
Activity.RESULT_OK -> {
181+
val options = data?.getStringExtra(PluginContract.EXTRA_OPTIONS)
182+
pluginConfigure.text = options
183+
onPreferenceChange(null, options)
184+
}
185+
PluginContract.RESULT_FALLBACK -> showPluginEditor()
186+
}
187+
REQUEST_UNSAVED_CHANGES -> when (resultCode) {
188+
DialogInterface.BUTTON_POSITIVE -> saveAndExit()
189+
DialogInterface.BUTTON_NEGATIVE -> requireActivity().finish()
169190
}
170-
PluginContract.RESULT_FALLBACK -> showPluginEditor()
171-
} else super.onActivityResult(requestCode, resultCode, data)
191+
else -> super.onActivityResult(requestCode, resultCode, data)
192+
}
172193
}
173194

174195
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
175196
R.id.action_delete -> {
176-
val activity = requireActivity()
177-
AlertDialog.Builder(activity)
178-
.setTitle(R.string.delete_confirm_prompt)
179-
.setPositiveButton(R.string.yes) { _, _ ->
180-
ProfileManager.delProfile(profileId)
181-
activity.finish()
182-
}
183-
.setNegativeButton(R.string.no, null)
184-
.create()
185-
.show()
197+
DeleteConfirmationDialogFragment().withArg(ProfileIdArg(profileId)).show(this)
186198
true
187199
}
188200
R.id.action_apply -> {

0 commit comments

Comments
 (0)