Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
com.ccwangluo.accelerator
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sheteng
com.ccwangluo.accelerator
Commits
f0141317
Unverified
Commit
f0141317
authored
Feb 21, 2019
by
Max Lv
Committed by
GitHub
Feb 21, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2123 from shadowsocks/plugin-1.2.0
Plugin library 1.2.0
parents
1c1a8ed9
f4621f3f
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
257 additions
and
124 deletions
+257
-124
core/build.gradle
core/build.gradle
+5
-0
core/src/main/java/com/github/shadowsocks/database/Profile.kt
.../src/main/java/com/github/shadowsocks/database/Profile.kt
+27
-24
mobile/build.gradle
mobile/build.gradle
+5
-0
mobile/src/main/java/com/github/shadowsocks/MainActivity.kt
mobile/src/main/java/com/github/shadowsocks/MainActivity.kt
+18
-11
mobile/src/main/java/com/github/shadowsocks/ProfileConfigActivity.kt
...main/java/com/github/shadowsocks/ProfileConfigActivity.kt
+14
-7
mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt
...main/java/com/github/shadowsocks/ProfileConfigFragment.kt
+34
-22
mobile/src/main/java/com/github/shadowsocks/acl/CustomRulesFragment.kt
...in/java/com/github/shadowsocks/acl/CustomRulesFragment.kt
+74
-58
plugin/CHANGES.md
plugin/CHANGES.md
+3
-0
plugin/build.gradle
plugin/build.gradle
+7
-2
plugin/src/main/java/com/github/shadowsocks/plugin/AlertDialogFragment.kt
...java/com/github/shadowsocks/plugin/AlertDialogFragment.kt
+70
-0
No files found.
core/build.gradle
View file @
f0141317
apply
plugin:
'com.android.library'
apply
plugin:
'com.android.library'
apply
plugin:
'kotlin-android'
apply
plugin:
'kotlin-android'
apply
plugin:
'kotlin-android-extensions'
apply
plugin:
'kotlin-kapt'
apply
plugin:
'kotlin-kapt'
android
{
android
{
...
@@ -38,6 +39,10 @@ android {
...
@@ -38,6 +39,10 @@ android {
}
}
}
}
androidExtensions
{
experimental
=
true
}
def
lifecycleVersion
=
'2.0.0'
def
lifecycleVersion
=
'2.0.0'
def
roomVersion
=
'2.0.0'
def
roomVersion
=
'2.0.0'
def
workVersion
=
'1.0.0-rc01'
def
workVersion
=
'1.0.0-rc01'
...
...
core/src/main/java/com/github/shadowsocks/database/Profile.kt
View file @
f0141317
...
@@ -21,6 +21,7 @@
...
@@ -21,6 +21,7 @@
package
com.github.shadowsocks.database
package
com.github.shadowsocks.database
import
android.net.Uri
import
android.net.Uri
import
android.os.Parcelable
import
android.util.Base64
import
android.util.Base64
import
android.util.Log
import
android.util.Log
import
android.util.LongSparseArray
import
android.util.LongSparseArray
...
@@ -32,6 +33,7 @@ import com.github.shadowsocks.preference.DataStore
...
@@ -32,6 +33,7 @@ import com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.utils.Key
import
com.github.shadowsocks.utils.Key
import
com.github.shadowsocks.utils.asIterable
import
com.github.shadowsocks.utils.asIterable
import
com.github.shadowsocks.utils.parsePort
import
com.github.shadowsocks.utils.parsePort
import
kotlinx.android.parcel.Parcelize
import
org.json.JSONArray
import
org.json.JSONArray
import
org.json.JSONObject
import
org.json.JSONObject
import
org.json.JSONTokener
import
org.json.JSONTokener
...
@@ -41,7 +43,31 @@ import java.net.URISyntaxException
...
@@ -41,7 +43,31 @@ import java.net.URISyntaxException
import
java.util.*
import
java.util.*
@Entity
@Entity
class
Profile
:
Serializable
{
@Parcelize
data class
Profile
(
@PrimaryKey
(
autoGenerate
=
true
)
var
id
:
Long
=
0
,
var
name
:
String
?
=
""
,
var
host
:
String
=
"198.199.101.152"
,
var
remotePort
:
Int
=
8388
,
var
password
:
String
=
"u1rRWTssNv0p"
,
var
method
:
String
=
"aes-256-cfb"
,
var
route
:
String
=
"all"
,
var
remoteDns
:
String
=
"8.8.8.8"
,
var
proxyApps
:
Boolean
=
false
,
var
bypass
:
Boolean
=
false
,
var
udpdns
:
Boolean
=
false
,
var
ipv6
:
Boolean
=
true
,
var
individual
:
String
=
""
,
var
tx
:
Long
=
0
,
var
rx
:
Long
=
0
,
var
userOrder
:
Long
=
0
,
var
plugin
:
String
?
=
null
,
var
udpFallback
:
Long
?
=
null
,
@Ignore
// not persisted in db, only used by direct boot
var
dirty
:
Boolean
=
false
)
:
Parcelable
,
Serializable
{
companion
object
{
companion
object
{
private
const
val
TAG
=
"ShadowParser"
private
const
val
TAG
=
"ShadowParser"
private
const
val
serialVersionUID
=
0L
private
const
val
serialVersionUID
=
0L
...
@@ -200,29 +226,6 @@ class Profile : Serializable {
...
@@ -200,29 +226,6 @@ class Profile : Serializable {
fun
deleteAll
():
Int
fun
deleteAll
():
Int
}
}
@PrimaryKey
(
autoGenerate
=
true
)
var
id
:
Long
=
0
var
name
:
String
?
=
""
var
host
:
String
=
"198.199.101.152"
var
remotePort
:
Int
=
8388
var
password
:
String
=
"u1rRWTssNv0p"
var
method
:
String
=
"aes-256-cfb"
var
route
:
String
=
"all"
var
remoteDns
:
String
=
"8.8.8.8"
var
proxyApps
:
Boolean
=
false
var
bypass
:
Boolean
=
false
var
udpdns
:
Boolean
=
false
var
ipv6
:
Boolean
=
true
var
individual
:
String
=
""
var
tx
:
Long
=
0
var
rx
:
Long
=
0
var
userOrder
:
Long
=
0
var
plugin
:
String
?
=
null
var
udpFallback
:
Long
?
=
null
@Ignore
// not persisted in db, only used by direct boot
var
dirty
:
Boolean
=
false
val
formattedAddress
get
()
=
(
if
(
host
.
contains
(
":"
))
"[%s]:%d"
else
"%s:%d"
).
format
(
host
,
remotePort
)
val
formattedAddress
get
()
=
(
if
(
host
.
contains
(
":"
))
"[%s]:%d"
else
"%s:%d"
).
format
(
host
,
remotePort
)
val
formattedName
get
()
=
if
(
name
.
isNullOrEmpty
())
formattedAddress
else
name
!!
val
formattedName
get
()
=
if
(
name
.
isNullOrEmpty
())
formattedAddress
else
name
!!
...
...
mobile/build.gradle
View file @
f0141317
...
@@ -5,6 +5,7 @@ import java.util.regex.Pattern
...
@@ -5,6 +5,7 @@ import java.util.regex.Pattern
apply
plugin:
'com.android.application'
apply
plugin:
'com.android.application'
apply
plugin:
'io.fabric'
apply
plugin:
'io.fabric'
apply
plugin:
'kotlin-android'
apply
plugin:
'kotlin-android'
apply
plugin:
'kotlin-android-extensions'
def
getCurrentFlavor
()
{
def
getCurrentFlavor
()
{
String
task
=
getGradle
().
getStartParameter
().
getTaskRequests
().
toString
()
String
task
=
getGradle
().
getStartParameter
().
getTaskRequests
().
toString
()
...
@@ -49,6 +50,10 @@ android {
...
@@ -49,6 +50,10 @@ android {
new
File
(
project
(
':core'
).
buildDir
,
"intermediates/bundles/${getCurrentFlavor()}/jni"
)
new
File
(
project
(
':core'
).
buildDir
,
"intermediates/bundles/${getCurrentFlavor()}/jni"
)
}
}
androidExtensions
{
experimental
=
true
}
dependencies
{
dependencies
{
implementation
project
(
':core'
)
implementation
project
(
':core'
)
implementation
"androidx.browser:browser:1.0.0"
implementation
"androidx.browser:browser:1.0.0"
...
...
mobile/src/main/java/com/github/shadowsocks/MainActivity.kt
View file @
f0141317
...
@@ -23,6 +23,7 @@ package com.github.shadowsocks
...
@@ -23,6 +23,7 @@ package com.github.shadowsocks
import
android.app.Activity
import
android.app.Activity
import
android.app.backup.BackupManager
import
android.app.backup.BackupManager
import
android.content.ActivityNotFoundException
import
android.content.ActivityNotFoundException
import
android.content.DialogInterface
import
android.content.Intent
import
android.content.Intent
import
android.net.VpnService
import
android.net.VpnService
import
android.nfc.NdefMessage
import
android.nfc.NdefMessage
...
@@ -30,6 +31,7 @@ import android.nfc.NfcAdapter
...
@@ -30,6 +31,7 @@ import android.nfc.NfcAdapter
import
android.os.Bundle
import
android.os.Bundle
import
android.os.DeadObjectException
import
android.os.DeadObjectException
import
android.os.Handler
import
android.os.Handler
import
android.os.Parcelable
import
android.util.Log
import
android.util.Log
import
android.view.KeyCharacterMap
import
android.view.KeyCharacterMap
import
android.view.KeyEvent
import
android.view.KeyEvent
...
@@ -51,6 +53,8 @@ import com.github.shadowsocks.aidl.TrafficStats
...
@@ -51,6 +53,8 @@ import com.github.shadowsocks.aidl.TrafficStats
import
com.github.shadowsocks.bg.BaseService
import
com.github.shadowsocks.bg.BaseService
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.ProfileManager
import
com.github.shadowsocks.database.ProfileManager
import
com.github.shadowsocks.plugin.AlertDialogFragment
import
com.github.shadowsocks.plugin.Empty
import
com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
import
com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
import
com.github.shadowsocks.utils.Key
import
com.github.shadowsocks.utils.Key
...
@@ -58,6 +62,7 @@ import com.github.shadowsocks.widget.ServiceButton
...
@@ -58,6 +62,7 @@ import com.github.shadowsocks.widget.ServiceButton
import
com.github.shadowsocks.widget.StatsBar
import
com.github.shadowsocks.widget.StatsBar
import
com.google.android.material.navigation.NavigationView
import
com.google.android.material.navigation.NavigationView
import
com.google.android.material.snackbar.Snackbar
import
com.google.android.material.snackbar.Snackbar
import
kotlinx.android.parcel.Parcelize
class
MainActivity
:
AppCompatActivity
(),
ShadowsocksConnection
.
Callback
,
OnPreferenceDataStoreChangeListener
,
class
MainActivity
:
AppCompatActivity
(),
ShadowsocksConnection
.
Callback
,
OnPreferenceDataStoreChangeListener
,
NavigationView
.
OnNavigationItemSelectedListener
{
NavigationView
.
OnNavigationItemSelectedListener
{
...
@@ -68,6 +73,17 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
...
@@ -68,6 +73,17 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
var
stateListener
:
((
Int
)
->
Unit
)?
=
null
var
stateListener
:
((
Int
)
->
Unit
)?
=
null
}
}
@Parcelize
data class
ProfilesArg
(
val
profiles
:
List
<
Profile
>)
:
Parcelable
class
ImportProfilesDialogFragment
:
AlertDialogFragment
<
ProfilesArg
,
Empty
>()
{
override
fun
AlertDialog
.
Builder
.
prepare
(
listener
:
DialogInterface
.
OnClickListener
)
{
setTitle
(
R
.
string
.
add_profile_dialog
)
setPositiveButton
(
R
.
string
.
yes
)
{
_
,
_
->
arg
.
profiles
.
forEach
{
ProfileManager
.
createProfile
(
it
)
}
}
setNegativeButton
(
R
.
string
.
no
,
null
)
setMessage
(
arg
.
profiles
.
joinToString
(
"\n"
))
}
}
// UI
// UI
private
lateinit
var
fab
:
ServiceButton
private
lateinit
var
fab
:
ServiceButton
private
lateinit
var
stats
:
StatsBar
private
lateinit
var
stats
:
StatsBar
...
@@ -188,17 +204,8 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
...
@@ -188,17 +204,8 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Callback, OnPref
}
}
if
(
sharedStr
.
isNullOrEmpty
())
return
if
(
sharedStr
.
isNullOrEmpty
())
return
val
profiles
=
Profile
.
findAllUrls
(
sharedStr
,
Core
.
currentProfile
?.
first
).
toList
()
val
profiles
=
Profile
.
findAllUrls
(
sharedStr
,
Core
.
currentProfile
?.
first
).
toList
()
if
(
profiles
.
isEmpty
())
{
if
(
profiles
.
isEmpty
())
snackbar
().
setText
(
R
.
string
.
profile_invalid_input
).
show
()
snackbar
().
setText
(
R
.
string
.
profile_invalid_input
).
show
()
else
ImportProfilesDialogFragment
().
withArg
(
ProfilesArg
(
profiles
)).
show
(
supportFragmentManager
,
null
)
return
}
AlertDialog
.
Builder
(
this
)
.
setTitle
(
R
.
string
.
add_profile_dialog
)
.
setPositiveButton
(
R
.
string
.
yes
)
{
_
,
_
->
profiles
.
forEach
{
ProfileManager
.
createProfile
(
it
)
}
}
.
setNegativeButton
(
R
.
string
.
no
,
null
)
.
setMessage
(
profiles
.
joinToString
(
"\n"
))
.
create
()
.
show
()
}
}
override
fun
onPreferenceDataStoreChanged
(
store
:
PreferenceDataStore
,
key
:
String
?)
{
override
fun
onPreferenceDataStoreChanged
(
store
:
PreferenceDataStore
,
key
:
String
?)
{
...
...
mobile/src/main/java/com/github/shadowsocks/ProfileConfigActivity.kt
View file @
f0141317
...
@@ -21,12 +21,15 @@
...
@@ -21,12 +21,15 @@
package
com.github.shadowsocks
package
com.github.shadowsocks
import
android.app.Activity
import
android.app.Activity
import
android.content.DialogInterface
import
android.content.Intent
import
android.content.Intent
import
android.os.Bundle
import
android.os.Bundle
import
android.view.Menu
import
android.view.Menu
import
android.view.MenuItem
import
android.view.MenuItem
import
androidx.appcompat.app.AlertDialog
import
androidx.appcompat.app.AlertDialog
import
androidx.appcompat.app.AppCompatActivity
import
androidx.appcompat.app.AppCompatActivity
import
com.github.shadowsocks.plugin.AlertDialogFragment
import
com.github.shadowsocks.plugin.Empty
import
com.github.shadowsocks.plugin.PluginContract
import
com.github.shadowsocks.plugin.PluginContract
import
com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.preference.DataStore
...
@@ -35,6 +38,15 @@ class ProfileConfigActivity : AppCompatActivity() {
...
@@ -35,6 +38,15 @@ class ProfileConfigActivity : AppCompatActivity() {
const
val
REQUEST_CODE_PLUGIN_HELP
=
1
const
val
REQUEST_CODE_PLUGIN_HELP
=
1
}
}
class
UnsavedChangesDialogFragment
:
AlertDialogFragment
<
Empty
,
Empty
>()
{
override
fun
AlertDialog
.
Builder
.
prepare
(
listener
:
DialogInterface
.
OnClickListener
)
{
setTitle
(
R
.
string
.
unsaved_changes_prompt
)
setPositiveButton
(
R
.
string
.
yes
,
listener
)
setNegativeButton
(
R
.
string
.
no
,
listener
)
setNeutralButton
(
android
.
R
.
string
.
cancel
,
null
)
}
}
private
val
child
by
lazy
{
supportFragmentManager
.
findFragmentById
(
R
.
id
.
content
)
as
ProfileConfigFragment
}
private
val
child
by
lazy
{
supportFragmentManager
.
findFragmentById
(
R
.
id
.
content
)
as
ProfileConfigFragment
}
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
...
@@ -59,13 +71,8 @@ class ProfileConfigActivity : AppCompatActivity() {
...
@@ -59,13 +71,8 @@ class ProfileConfigActivity : AppCompatActivity() {
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
)
UnsavedChangesDialogFragment
().
show
(
child
,
ProfileConfigFragment
.
REQUEST_UNSAVED_CHANGES
)
.
setTitle
(
R
.
string
.
unsaved_changes_prompt
)
else
super
.
onBackPressed
()
.
setPositiveButton
(
R
.
string
.
yes
)
{
_
,
_
->
child
.
saveAndExit
()
}
.
setNegativeButton
(
R
.
string
.
no
)
{
_
,
_
->
finish
()
}
.
setNeutralButton
(
android
.
R
.
string
.
cancel
,
null
)
.
create
()
.
show
()
else
super
.
onBackPressed
()
}
}
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
?)
{
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
?)
{
...
...
mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt
View file @
f0141317
...
@@ -22,8 +22,10 @@ package com.github.shadowsocks
...
@@ -22,8 +22,10 @@ package com.github.shadowsocks
import
android.app.Activity
import
android.app.Activity
import
android.content.BroadcastReceiver
import
android.content.BroadcastReceiver
import
android.content.DialogInterface
import
android.content.Intent
import
android.content.Intent
import
android.os.Bundle
import
android.os.Bundle
import
android.os.Parcelable
import
android.view.MenuItem
import
android.view.MenuItem
import
androidx.appcompat.app.AlertDialog
import
androidx.appcompat.app.AlertDialog
import
androidx.core.os.bundleOf
import
androidx.core.os.bundleOf
...
@@ -33,10 +35,7 @@ import androidx.preference.SwitchPreference
...
@@ -33,10 +35,7 @@ import androidx.preference.SwitchPreference
import
com.github.shadowsocks.Core.app
import
com.github.shadowsocks.Core.app
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.ProfileManager
import
com.github.shadowsocks.database.ProfileManager
import
com.github.shadowsocks.plugin.PluginConfiguration
import
com.github.shadowsocks.plugin.*
import
com.github.shadowsocks.plugin.PluginContract
import
com.github.shadowsocks.plugin.PluginManager
import
com.github.shadowsocks.plugin.PluginOptions
import
com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.preference.DataStore
import
com.github.shadowsocks.preference.IconListPreference
import
com.github.shadowsocks.preference.IconListPreference
import
com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
import
com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
...
@@ -47,11 +46,26 @@ import com.github.shadowsocks.utils.Key
...
@@ -47,11 +46,26 @@ import com.github.shadowsocks.utils.Key
import
com.google.android.material.snackbar.Snackbar
import
com.google.android.material.snackbar.Snackbar
import
com.takisoft.preferencex.EditTextPreference
import
com.takisoft.preferencex.EditTextPreference
import
com.takisoft.preferencex.PreferenceFragmentCompat
import
com.takisoft.preferencex.PreferenceFragmentCompat
import
kotlinx.android.parcel.Parcelize
class
ProfileConfigFragment
:
PreferenceFragmentCompat
(),
class
ProfileConfigFragment
:
PreferenceFragmentCompat
(),
Preference
.
OnPreferenceChangeListener
,
OnPreferenceDataStoreChangeListener
{
Preference
.
OnPreferenceChangeListener
,
OnPreferenceDataStoreChangeListener
{
companion
object
{
companion
object
{
private
const
val
REQUEST_CODE_PLUGIN_CONFIGURE
=
1
private
const
val
REQUEST_CODE_PLUGIN_CONFIGURE
=
1
const
val
REQUEST_UNSAVED_CHANGES
=
2
}
@Parcelize
data class
ProfileIdArg
(
val
profileId
:
Long
)
:
Parcelable
class
DeleteConfirmationDialogFragment
:
AlertDialogFragment
<
ProfileIdArg
,
Empty
>()
{
override
fun
AlertDialog
.
Builder
.
prepare
(
listener
:
DialogInterface
.
OnClickListener
)
{
setTitle
(
R
.
string
.
delete_confirm_prompt
)
setPositiveButton
(
R
.
string
.
yes
)
{
_
,
_
->
ProfileManager
.
delProfile
(
arg
.
profileId
)
requireActivity
().
finish
()
}
setNegativeButton
(
R
.
string
.
no
,
null
)
}
}
}
private
var
profileId
=
-
1L
private
var
profileId
=
-
1L
...
@@ -116,7 +130,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
...
@@ -116,7 +130,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
bundleOf
(
Pair
(
"key"
,
Key
.
pluginConfigure
),
bundleOf
(
Pair
(
"key"
,
Key
.
pluginConfigure
),
Pair
(
PluginConfigurationDialogFragment
.
PLUGIN_ID_FRAGMENT_TAG
,
pluginConfiguration
.
selected
)))
Pair
(
PluginConfigurationDialogFragment
.
PLUGIN_ID_FRAGMENT_TAG
,
pluginConfiguration
.
selected
)))
fun
saveAndExit
()
{
private
fun
saveAndExit
()
{
val
profile
=
ProfileManager
.
getProfile
(
profileId
)
?:
Profile
()
val
profile
=
ProfileManager
.
getProfile
(
profileId
)
?:
Profile
()
profile
.
id
=
profileId
profile
.
id
=
profileId
profile
.
deserialize
()
profile
.
deserialize
()
...
@@ -161,28 +175,26 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
...
@@ -161,28 +175,26 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
}
}
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
?)
{
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
?)
{
if
(
requestCode
==
REQUEST_CODE_PLUGIN_CONFIGURE
)
when
(
resultCode
)
{
when
(
requestCode
)
{
REQUEST_CODE_PLUGIN_CONFIGURE
->
when
(
resultCode
)
{
Activity
.
RESULT_OK
->
{
Activity
.
RESULT_OK
->
{
val
options
=
data
?.
getStringExtra
(
PluginContract
.
EXTRA_OPTIONS
)
val
options
=
data
?.
getStringExtra
(
PluginContract
.
EXTRA_OPTIONS
)
pluginConfigure
.
text
=
options
pluginConfigure
.
text
=
options
onPreferenceChange
(
null
,
options
)
onPreferenceChange
(
null
,
options
)
}
}
PluginContract
.
RESULT_FALLBACK
->
showPluginEditor
()
PluginContract
.
RESULT_FALLBACK
->
showPluginEditor
()
}
else
super
.
onActivityResult
(
requestCode
,
resultCode
,
data
)
}
REQUEST_UNSAVED_CHANGES
->
when
(
resultCode
)
{
DialogInterface
.
BUTTON_POSITIVE
->
saveAndExit
()
DialogInterface
.
BUTTON_NEGATIVE
->
requireActivity
().
finish
()
}
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
()
DeleteConfirmationDialogFragment
().
withArg
(
ProfileIdArg
(
profileId
)).
show
(
this
)
AlertDialog
.
Builder
(
activity
)
.
setTitle
(
R
.
string
.
delete_confirm_prompt
)
.
setPositiveButton
(
R
.
string
.
yes
)
{
_
,
_
->
ProfileManager
.
delProfile
(
profileId
)
activity
.
finish
()
}
.
setNegativeButton
(
R
.
string
.
no
,
null
)
.
create
()
.
show
()
true
true
}
}
R
.
id
.
action_apply
->
{
R
.
id
.
action_apply
->
{
...
...
mobile/src/main/java/com/github/shadowsocks/acl/CustomRulesFragment.kt
View file @
f0141317
...
@@ -22,9 +22,12 @@ package com.github.shadowsocks.acl
...
@@ -22,9 +22,12 @@ package com.github.shadowsocks.acl
import
android.content.ClipData
import
android.content.ClipData
import
android.content.ClipboardManager
import
android.content.ClipboardManager
import
android.content.DialogInterface
import
android.content.Intent
import
android.content.res.Configuration
import
android.content.res.Configuration
import
android.os.Build
import
android.os.Build
import
android.os.Bundle
import
android.os.Bundle
import
android.os.Parcelable
import
android.text.Editable
import
android.text.Editable
import
android.text.TextWatcher
import
android.text.TextWatcher
import
android.view.*
import
android.view.*
...
@@ -43,10 +46,12 @@ import com.github.shadowsocks.R
...
@@ -43,10 +46,12 @@ import com.github.shadowsocks.R
import
com.github.shadowsocks.ToolbarFragment
import
com.github.shadowsocks.ToolbarFragment
import
com.github.shadowsocks.bg.BaseService
import
com.github.shadowsocks.bg.BaseService
import
com.github.shadowsocks.net.Subnet
import
com.github.shadowsocks.net.Subnet
import
com.github.shadowsocks.plugin.AlertDialogFragment
import
com.github.shadowsocks.utils.asIterable
import
com.github.shadowsocks.utils.asIterable
import
com.github.shadowsocks.utils.resolveResourceId
import
com.github.shadowsocks.utils.resolveResourceId
import
com.github.shadowsocks.widget.UndoSnackbarManager
import
com.github.shadowsocks.widget.UndoSnackbarManager
import
com.google.android.material.textfield.TextInputLayout
import
com.google.android.material.textfield.TextInputLayout
import
kotlinx.android.parcel.Parcelize
import
java.net.IDN
import
java.net.IDN
import
java.net.MalformedURLException
import
java.net.MalformedURLException
import
java.net.URL
import
java.net.URL
...
@@ -55,6 +60,8 @@ import java.util.regex.PatternSyntaxException
...
@@ -55,6 +60,8 @@ import java.util.regex.PatternSyntaxException
class
CustomRulesFragment
:
ToolbarFragment
(),
Toolbar
.
OnMenuItemClickListener
,
ActionMode
.
Callback
{
class
CustomRulesFragment
:
ToolbarFragment
(),
Toolbar
.
OnMenuItemClickListener
,
ActionMode
.
Callback
{
companion
object
{
companion
object
{
private
const
val
REQUEST_CODE_ADD
=
1
private
const
val
REQUEST_CODE_EDIT
=
2
private
const
val
TEMPLATE_REGEX_DOMAIN
=
"(^|\\.)%s$"
private
const
val
TEMPLATE_REGEX_DOMAIN
=
"(^|\\.)%s$"
private
const
val
SELECTED_SUBNETS
=
"com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_SUBNETS"
private
const
val
SELECTED_SUBNETS
=
"com.github.shadowsocks.acl.CustomRulesFragment.SELECTED_SUBNETS"
...
@@ -63,6 +70,14 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
...
@@ -63,6 +70,14 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
// unescaped: (?<=^(\(\^\|\\\.\)|\^\(\.\*\\\.\)\?)).*(?=\$$)
// unescaped: (?<=^(\(\^\|\\\.\)|\^\(\.\*\\\.\)\?)).*(?=\$$)
private
val
domainPattern
=
"(?<=^(\\(\\^\\|\\\\\\.\\)|\\^\\(\\.\\*\\\\\\.\\)\\?)).*(?=\\\$\$)"
.
toRegex
()
private
val
domainPattern
=
"(?<=^(\\(\\^\\|\\\\\\.\\)|\\^\\(\\.\\*\\\\\\.\\)\\?)).*(?=\\\$\$)"
.
toRegex
()
@Suppress
(
"FunctionName"
)
private
fun
AclItem
(
item
:
Any
)
=
when
(
item
)
{
is
String
->
AclItem
(
item
,
false
)
is
Subnet
->
AclItem
(
item
.
toString
(),
false
)
is
URL
->
AclItem
(
item
.
toString
(),
true
)
else
->
throw
IllegalArgumentException
(
"item"
)
}
}
}
private
enum
class
Template
{
private
enum
class
Template
{
...
@@ -70,53 +85,50 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
...
@@ -70,53 +85,50 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
Domain
,
Domain
,
Url
;
Url
;
}
}
private
inner
class
AclRuleDialog
(
item
:
Any
=
""
)
:
TextWatcher
,
AdapterView
.
OnItemSelectedListener
{
@Parcelize
val
builder
:
AlertDialog
.
Builder
data class
AclItem
(
val
item
:
String
=
""
,
val
isUrl
:
Boolean
=
false
)
:
Parcelable
{
val
templateSelector
:
Spinner
fun
toAny
()
=
if
(
isUrl
)
URL
(
item
)
else
Subnet
.
fromString
(
item
)
?:
item
val
editText
:
EditText
}
private
val
inputLayout
:
TextInputLayout
@Parcelize
private
lateinit
var
dialog
:
AlertDialog
data class
AclEditResult
(
val
edited
:
AclItem
,
val
replacing
:
AclItem
)
:
Parcelable
class
AclRuleDialogFragment
:
AlertDialogFragment
<
AclItem
,
AclEditResult
>(),
TextWatcher
,
AdapterView
.
OnItemSelectedListener
{
private
lateinit
var
templateSelector
:
Spinner
private
lateinit
var
editText
:
EditText
private
lateinit
var
inputLayout
:
TextInputLayout
private
lateinit
var
positive
:
Button
private
lateinit
var
positive
:
Button
init
{
override
fun
AlertDialog
.
Builder
.
prepare
(
listener
:
DialogInterface
.
OnClickListener
)
{
val
activity
=
requireActivity
()
val
activity
=
requireActivity
()
val
view
=
activity
.
layoutInflater
.
inflate
(
R
.
layout
.
dialog_acl_rule
,
null
)
val
view
=
activity
.
layoutInflater
.
inflate
(
R
.
layout
.
dialog_acl_rule
,
null
)
templateSelector
=
view
.
findViewById
(
R
.
id
.
template_selector
)
templateSelector
=
view
.
findViewById
(
R
.
id
.
template_selector
)
editText
=
view
.
findViewById
(
R
.
id
.
content
)
editText
=
view
.
findViewById
(
R
.
id
.
content
)
inputLayout
=
view
.
findViewById
(
R
.
id
.
content_layout
)
inputLayout
=
view
.
findViewById
(
R
.
id
.
content_layout
)
when
(
item
)
{
templateSelector
.
setSelection
(
Template
.
Generic
.
ordinal
)
is
String
->
{
editText
.
setText
(
arg
.
item
)
val
match
=
domainPattern
.
find
(
item
)
when
{
arg
.
isUrl
->
templateSelector
.
setSelection
(
Template
.
Url
.
ordinal
)
Subnet
.
fromString
(
arg
.
item
)
==
null
->
{
val
match
=
domainPattern
.
find
(
arg
.
item
)
if
(
match
!=
null
)
{
if
(
match
!=
null
)
{
templateSelector
.
setSelection
(
Template
.
Domain
.
ordinal
)
templateSelector
.
setSelection
(
Template
.
Domain
.
ordinal
)
editText
.
setText
(
IDN
.
toUnicode
(
match
.
value
.
replace
(
"\\."
,
"."
),
editText
.
setText
(
IDN
.
toUnicode
(
match
.
value
.
replace
(
"\\."
,
"."
),
IDN
.
ALLOW_UNASSIGNED
or
IDN
.
USE_STD3_ASCII_RULES
))
IDN
.
ALLOW_UNASSIGNED
or
IDN
.
USE_STD3_ASCII_RULES
))
}
else
{
templateSelector
.
setSelection
(
Template
.
Generic
.
ordinal
)
editText
.
setText
(
item
)
}
}
}
is
URL
->
{
templateSelector
.
setSelection
(
Template
.
Url
.
ordinal
)
editText
.
setText
(
item
.
toString
())
}
}
else
->
{
templateSelector
.
setSelection
(
Template
.
Generic
.
ordinal
)
editText
.
setText
(
item
.
toString
())
}
}
}
templateSelector
.
onItemSelectedListener
=
this
@AclRuleDialogFragment
templateSelector
.
onItemSelectedListener
=
this
editText
.
addTextChangedListener
(
this
@AclRuleDialogFragment
)
editText
.
addTextChangedListener
(
this
)
setTitle
(
R
.
string
.
edit_rule
)
builder
=
AlertDialog
.
Builder
(
activity
)
setPositiveButton
(
android
.
R
.
string
.
ok
,
listener
)
.
setTitle
(
R
.
string
.
edit_rule
)
setNegativeButton
(
android
.
R
.
string
.
cancel
,
null
)
.
setNegativeButton
(
android
.
R
.
string
.
cancel
,
null
)
if
(
arg
.
item
.
isNotEmpty
())
setNeutralButton
(
R
.
string
.
delete
,
listener
)
.
setView
(
view
)
setView
(
view
)
}
}
fun
show
()
{
override
fun
onStart
()
{
dialog
=
builder
.
create
()
super
.
onStart
()
dialog
.
show
()
positive
=
(
dialog
as
AlertDialog
).
getButton
(
AlertDialog
.
BUTTON_POSITIVE
)
positive
=
dialog
.
getButton
(
AlertDialog
.
BUTTON_POSITIVE
)
validate
()
validate
()
}
}
...
@@ -157,15 +169,14 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
...
@@ -157,15 +169,14 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
inputLayout
.
error
=
message
inputLayout
.
error
=
message
}
}
fun
add
():
Int
?
{
override
val
ret
get
()
=
AclEditResult
(
editText
.
text
.
toString
().
let
{
text
->
val
text
=
editText
.
text
.
toString
()
when
(
Template
.
values
()[
templateSelector
.
selectedItemPosition
])
{
return
when
(
Template
.
values
()[
templateSelector
.
selectedItemPosition
])
{
Template
.
Generic
->
AclItem
(
text
)
Template
.
Generic
->
adapter
.
addToProxy
(
text
)
Template
.
Domain
->
AclItem
(
TEMPLATE_REGEX_DOMAIN
.
format
(
Locale
.
ENGLISH
,
IDN
.
toASCII
(
text
,
Template
.
Domain
->
adapter
.
addHostname
(
TEMPLATE_REGEX_DOMAIN
.
format
(
Locale
.
ENGLISH
,
IDN
.
toASCII
(
text
,
IDN
.
ALLOW_UNASSIGNED
or
IDN
.
USE_STD3_ASCII_RULES
).
replace
(
"."
,
"\\."
)))
IDN
.
ALLOW_UNASSIGNED
or
IDN
.
USE_STD3_ASCII_RULES
).
replace
(
"."
,
"\\."
)))
Template
.
Url
->
adapter
.
addURL
(
URL
(
text
))
Template
.
Url
->
AclItem
(
text
,
true
)
}
}
}
},
arg
)
}
}
private
inner
class
AclRuleViewHolder
(
view
:
View
)
:
RecyclerView
.
ViewHolder
(
view
),
private
inner
class
AclRuleViewHolder
(
view
:
View
)
:
RecyclerView
.
ViewHolder
(
view
),
...
@@ -197,22 +208,10 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
...
@@ -197,22 +208,10 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
}
}
override
fun
onClick
(
v
:
View
?)
{
override
fun
onClick
(
v
:
View
?)
{
if
(
selectedItems
.
isNotEmpty
())
onLongClick
(
v
)
else
{
if
(
selectedItems
.
isNotEmpty
())
onLongClick
(
v
)
val
dialog
=
AclRuleDialog
(
item
)
else
AclRuleDialogFragment
().
withArg
(
AclItem
(
item
)).
show
(
this
@CustomRulesFragment
,
REQUEST_CODE_EDIT
)
dialog
.
builder
.
setNeutralButton
(
R
.
string
.
delete
)
{
_
,
_
->
adapter
.
remove
(
item
)
undoManager
.
remove
(
Pair
(-
1
,
item
))
}
.
setPositiveButton
(
android
.
R
.
string
.
ok
)
{
_
,
_
->
adapter
.
remove
(
item
)
val
index
=
dialog
.
add
()
?:
adapter
.
add
(
item
)
if
(
index
!=
null
)
list
.
post
{
list
.
scrollToPosition
(
index
)
}
}
dialog
.
show
()
}
}
}
override
fun
onLongClick
(
v
:
View
?):
Boolean
{
override
fun
onLongClick
(
p0
:
View
?):
Boolean
{
if
(!
selectedItems
.
add
(
item
))
selectedItems
.
remove
(
item
)
// toggle
if
(!
selectedItems
.
add
(
item
))
selectedItems
.
remove
(
item
)
// toggle
onSelectedItemsUpdated
()
onSelectedItemsUpdated
()
itemView
.
isSelected
=
!
itemView
.
isSelected
itemView
.
isSelected
=
!
itemView
.
isSelected
...
@@ -427,9 +426,7 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
...
@@ -427,9 +426,7 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
override
fun
onMenuItemClick
(
item
:
MenuItem
):
Boolean
=
when
(
item
.
itemId
)
{
override
fun
onMenuItemClick
(
item
:
MenuItem
):
Boolean
=
when
(
item
.
itemId
)
{
R
.
id
.
action_manual_settings
->
{
R
.
id
.
action_manual_settings
->
{
val
dialog
=
AclRuleDialog
()
AclRuleDialogFragment
().
withArg
(
AclItem
()).
show
(
this
,
REQUEST_CODE_ADD
)
dialog
.
builder
.
setPositiveButton
(
android
.
R
.
string
.
ok
)
{
_
,
_
->
dialog
.
add
()
}
dialog
.
show
()
true
true
}
}
R
.
id
.
action_import_clipboard
->
{
R
.
id
.
action_import_clipboard
->
{
...
@@ -451,6 +448,25 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
...
@@ -451,6 +448,25 @@ class CustomRulesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener,
else
->
false
else
->
false
}
}
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
?)
{
val
editing
=
when
(
requestCode
)
{
REQUEST_CODE_ADD
->
false
REQUEST_CODE_EDIT
->
true
else
->
return
super
.
onActivityResult
(
requestCode
,
resultCode
,
data
)
}
val
ret
by
lazy
{
AlertDialogFragment
.
getRet
<
AclEditResult
>(
data
!!
)
}
when
(
resultCode
)
{
DialogInterface
.
BUTTON_POSITIVE
->
{
if
(
editing
)
adapter
.
remove
(
ret
.
replacing
.
toAny
())
adapter
.
add
(
ret
.
edited
.
toAny
())
?.
also
{
list
.
post
{
list
.
scrollToPosition
(
it
)
}
}
}
DialogInterface
.
BUTTON_NEUTRAL
->
ret
.
replacing
.
toAny
().
let
{
item
->
adapter
.
remove
(
item
)
undoManager
.
remove
(
Pair
(-
1
,
item
))
}
}
}
override
fun
onDetach
()
{
override
fun
onDetach
()
{
undoManager
.
flush
()
undoManager
.
flush
()
mode
?.
finish
()
mode
?.
finish
()
...
...
plugin/CHANGES.md
View file @
f0141317
*
1.2.0:
*
New helper class
`AlertDialogFragment`
for creating
`AlertDialog`
that persists through configuration changes;
*
Dependency update:
`com.google.android.material:material:1.1.0-alpha03`
.
*
1.1.0:
*
1.1.0:
*
Having control characters in plugin options is no longer allowed.
*
Having control characters in plugin options is no longer allowed.
If this breaks your plugin, you are doing it wrong.
If this breaks your plugin, you are doing it wrong.
...
...
plugin/build.gradle
View file @
f0141317
apply
plugin:
'com.android.library'
apply
plugin:
'com.android.library'
apply
plugin:
'kotlin-android'
apply
plugin:
'kotlin-android'
apply
plugin:
'kotlin-android-extensions'
apply
from:
'gradle-mvn-push.gradle'
apply
from:
'gradle-mvn-push.gradle'
...
@@ -10,8 +11,8 @@ android {
...
@@ -10,8 +11,8 @@ android {
defaultConfig
{
defaultConfig
{
minSdkVersion
rootProject
.
minSdkVersion
minSdkVersion
rootProject
.
minSdkVersion
targetSdkVersion
rootProject
.
sdkVersion
targetSdkVersion
rootProject
.
sdkVersion
versionCode
8
versionCode
9
versionName
"1.
1
.0"
versionName
"1.
2
.0"
testInstrumentationRunner
"androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner
"androidx.test.runner.AndroidJUnitRunner"
}
}
...
@@ -23,6 +24,10 @@ android {
...
@@ -23,6 +24,10 @@ android {
}
}
}
}
androidExtensions
{
experimental
=
true
}
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-alpha03"
api
"com.google.android.material:material:1.1.0-alpha03"
...
...
plugin/src/main/java/com/github/shadowsocks/plugin/AlertDialogFragment.kt
0 → 100644
View file @
f0141317
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2019 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
package
com.github.shadowsocks.plugin
import
android.app.Activity
import
android.content.DialogInterface
import
android.content.Intent
import
android.os.Bundle
import
android.os.Parcelable
import
androidx.appcompat.app.AlertDialog
import
androidx.appcompat.app.AppCompatDialogFragment
import
androidx.fragment.app.Fragment
import
kotlinx.android.parcel.Parcelize
/**
* Based on: https://android.googlesource.com/platform/packages/apps/ExactCalculator/+/8c43f06/src/com/android/calculator2/AlertDialogFragment.java
*/
abstract
class
AlertDialogFragment
<
Arg
:
Parcelable
,
Ret
:
Parcelable
>
:
AppCompatDialogFragment
(),
DialogInterface
.
OnClickListener
{
companion
object
{
private
const
val
KEY_ARG
=
"arg"
private
const
val
KEY_RET
=
"ret"
fun
<
T
:
Parcelable
>
getRet
(
data
:
Intent
)
=
data
.
extras
!!
.
getParcelable
<
T
>(
KEY_RET
)
!!
}
protected
abstract
fun
AlertDialog
.
Builder
.
prepare
(
listener
:
DialogInterface
.
OnClickListener
)
protected
val
arg
by
lazy
{
arguments
!!
.
getParcelable
<
Arg
>(
KEY_ARG
)
!!
}
protected
open
val
ret
:
Ret
?
get
()
=
null
fun
withArg
(
arg
:
Arg
)
=
apply
{
arguments
=
Bundle
().
apply
{
putParcelable
(
KEY_ARG
,
arg
)
}
}
override
fun
onCreateDialog
(
savedInstanceState
:
Bundle
?):
AlertDialog
=
AlertDialog
.
Builder
(
requireContext
()).
also
{
it
.
prepare
(
this
)
}.
create
()
override
fun
onClick
(
dialog
:
DialogInterface
?,
which
:
Int
)
{
targetFragment
?.
onActivityResult
(
targetRequestCode
,
which
,
ret
?.
let
{
Intent
().
replaceExtras
(
Bundle
().
apply
{
putParcelable
(
KEY_RET
,
it
)
})
})
}
override
fun
onDismiss
(
dialog
:
DialogInterface
)
{
super
.
onDismiss
(
dialog
)
targetFragment
?.
onActivityResult
(
targetRequestCode
,
Activity
.
RESULT_CANCELED
,
null
)
}
fun
show
(
target
:
Fragment
,
requestCode
:
Int
=
0
,
tag
:
String
=
javaClass
.
simpleName
)
{
setTargetFragment
(
target
,
requestCode
)
show
(
target
.
fragmentManager
?:
return
,
tag
)
}
}
@Parcelize
class
Empty
:
Parcelable
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment