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
99d97d1d
Commit
99d97d1d
authored
Jan 20, 2017
by
Mygod
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix launching plugin
parent
b78c10e8
Changes
21
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
213 additions
and
159 deletions
+213
-159
mobile/build.sbt
mobile/build.sbt
+0
-6
mobile/src/main/res/values/dimen.xml
mobile/src/main/res/values/dimen.xml
+1
-1
mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala
.../scala/com/github/shadowsocks/ProfileConfigFragment.scala
+33
-19
mobile/src/main/scala/com/github/shadowsocks/plugin/NativePlugin.scala
...in/scala/com/github/shadowsocks/plugin/NativePlugin.scala
+12
-9
mobile/src/main/scala/com/github/shadowsocks/plugin/Plugin.scala
...src/main/scala/com/github/shadowsocks/plugin/Plugin.scala
+1
-0
mobile/src/main/scala/com/github/shadowsocks/plugin/PluginConfiguration.scala
...a/com/github/shadowsocks/plugin/PluginConfiguration.scala
+6
-2
mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala
...n/scala/com/github/shadowsocks/plugin/PluginManager.scala
+41
-31
mobile/src/main/scala/com/github/shadowsocks/plugin/ProviderPlugin.scala
.../scala/com/github/shadowsocks/plugin/ProviderPlugin.scala
+0
-16
mobile/src/main/scala/com/github/shadowsocks/plugin/ResolvedPlugin.scala
.../scala/com/github/shadowsocks/plugin/ResolvedPlugin.scala
+22
-0
mobile/src/main/scala/com/github/shadowsocks/preference/BottomSheetPreferenceDialogFragment.scala
...ocks/preference/BottomSheetPreferenceDialogFragment.scala
+32
-17
mobile/src/main/scala/com/github/shadowsocks/preference/IconListPreference.scala
...om/github/shadowsocks/preference/IconListPreference.scala
+1
-0
mobile/src/main/scala/com/github/shadowsocks/preference/PluginConfigurationDialogFragment.scala
...wsocks/preference/PluginConfigurationDialogFragment.scala
+4
-4
mobile/src/main/scala/com/github/shadowsocks/utils/IOUtils.scala
...src/main/scala/com/github/shadowsocks/utils/IOUtils.scala
+1
-1
plugin/src/main/java/com/github/shadowsocks/plugin/PluginContract.java
...in/java/com/github/shadowsocks/plugin/PluginContract.java
+34
-29
plugin/src/main/java/com/github/shadowsocks/plugin/PluginOptions.java
...ain/java/com/github/shadowsocks/plugin/PluginOptions.java
+9
-11
plugin/src/main/scala/com/github/shadowsocks/plugin/ConfigurationActivity.scala
...com/github/shadowsocks/plugin/ConfigurationActivity.scala
+1
-1
plugin/src/main/scala/com/github/shadowsocks/plugin/HelpCallback.scala
...in/scala/com/github/shadowsocks/plugin/HelpCallback.scala
+1
-1
plugin/src/main/scala/com/github/shadowsocks/plugin/NativePluginProvider.scala
.../com/github/shadowsocks/plugin/NativePluginProvider.scala
+2
-2
plugin/src/main/scala/com/github/shadowsocks/plugin/OptionsCapableActivity.scala
...om/github/shadowsocks/plugin/OptionsCapableActivity.scala
+1
-1
plugin/src/main/scala/com/github/shadowsocks/plugin/PathProvider.scala
...in/scala/com/github/shadowsocks/plugin/PathProvider.scala
+10
-7
project/plugins.sbt
project/plugins.sbt
+1
-1
No files found.
mobile/build.sbt
View file @
99d97d1d
...
...
@@ -6,12 +6,6 @@ version := "4.0.0"
versionCode
:=
Some
(
174
)
proguardOptions
++=
"-keep class android.support.v14.preference.SwitchPreference { <init>(...); }"
::
"-keep class android.support.v7.preference.DropDownPreference { <init>(...); }"
::
"-keep class android.support.v7.preference.PreferenceScreen { <init>(...); }"
::
"-keep class be.mygod.preference.EditTextPreference { <init>(...); }"
::
"-keep class be.mygod.preference.NumberPickerPreference { <init>(...); }"
::
"-keep class be.mygod.preference.PreferenceCategory { <init>(...); }"
::
"-keep class com.github.shadowsocks.System { *; }"
::
"-dontwarn com.google.android.gms.internal.**"
::
"-dontwarn com.j256.ormlite.**"
::
...
...
mobile/src/main/res/values/dimen.xml
View file @
99d97d1d
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen
name=
"profile_item_max_width"
>
480dp
</dimen>
<dimen
name=
"icon_list_item_padding"
>
8
dp
</dimen>
<dimen
name=
"icon_list_item_padding"
>
16
dp
</dimen>
<!-- TODO: Remove when this issue is fixed: https://github.com/JorgeCastilloPrz/FABProgressCircle/issues/19 -->
<dimen
name=
"fab_margin_top"
>
-44dp
</dimen>
</resources>
mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala
View file @
99d97d1d
...
...
@@ -20,8 +20,9 @@
package
com.github.shadowsocks
import
android.app.Activity
import
android.content.SharedPreferences.OnSharedPreferenceChangeListener
import
android.content.
{
DialogInterface
,
Intent
,
SharedPreferences
}
import
android.content.
_
import
android.os.
{
Build
,
Bundle
,
UserManager
}
import
android.support.design.widget.Snackbar
import
android.support.v14.preference.SwitchPreference
...
...
@@ -33,7 +34,7 @@ import android.view.MenuItem
import
be.mygod.preference.
{
EditTextPreference
,
PreferenceFragment
}
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.plugin.
{
PluginConfiguration
,
PluginInterface
,
PluginManager
,
PluginOptions
}
import
com.github.shadowsocks.plugin.
_
import
com.github.shadowsocks.preference.
{
IconListPreference
,
PluginConfigurationDialogFragment
}
import
com.github.shadowsocks.utils.
{
Action
,
Key
,
Utils
}
...
...
@@ -79,29 +80,21 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
pluginConfiguration
=
new
PluginConfiguration
(
pluginConfiguration
.
pluginsOptions
,
selected
)
app
.
editor
.
putString
(
Key
.
plugin
,
pluginConfiguration
.
toString
).
putBoolean
(
Key
.
dirty
,
true
).
apply
()
pluginConfigure
.
setEnabled
(!
TextUtils
.
isEmpty
(
selected
))
pluginConfigure
.
setText
(
pluginConfiguration
.
selectedOptions
.
toString
)
true
})
pluginConfigure
.
setOnPreferenceChangeListener
((
_
,
value
)
=>
try
{
val
selected
=
pluginConfiguration
.
selected
pluginConfiguration
=
new
PluginConfiguration
(
pluginConfiguration
.
pluginsOptions
+
(
pluginConfiguration
.
selected
->
new
PluginOptions
(
selected
,
value
.
asInstanceOf
[
String
])),
selected
)
app
.
editor
.
putString
(
Key
.
plugin
,
pluginConfiguration
.
toString
).
putBoolean
(
Key
.
dirty
,
true
).
apply
()
true
}
catch
{
case
exc
:
IllegalArgumentException
=>
Snackbar
.
make
(
getActivity
.
findViewById
(
R
.
id
.
snackbar
),
exc
.
getLocalizedMessage
,
Snackbar
.
LENGTH_LONG
).
show
()
false
})
pluginConfigure
.
setOnPreferenceChangeListener
(
onPluginConfigureChanged
)
initPlugins
()
app
.
listenForPackageChanges
(
i
f
(
getView
!=
null
)
getView
.
post
(
initPlugins
))
app
.
listenForPackageChanges
(
i
nitPlugins
(
))
app
.
settings
.
registerOnSharedPreferenceChangeListener
(
this
)
}
def
initPlugins
()
{
val
plugins
=
PluginManager
.
fetchPlugins
()
plugin
.
setEntries
(
plugins
.
map
(
_
.
label
))
plugin
.
setEntryValues
(
plugins
.
map
(
_
.
id
.
asInstanceOf
[
CharSequence
]))
plugin
.
setEntryIcons
(
plugins
.
map
(
_
.
icon
))
plugin
.
setEntries
(
plugins
.
map
(
_
.
_2
.
label
).
toArray
)
plugin
.
setEntryValues
(
plugins
.
map
(
_
.
_2
.
id
.
asInstanceOf
[
CharSequence
]).
toArray
)
plugin
.
setEntryIcons
(
plugins
.
map
(
_
.
_2
.
icon
).
toArray
)
plugin
.
entryPackageNames
=
plugins
.
map
(
_
.
_2
.
packageName
).
toArray
pluginConfiguration
=
new
PluginConfiguration
(
app
.
settings
.
getString
(
Key
.
plugin
,
null
))
plugin
.
setValue
(
pluginConfiguration
.
selected
)
plugin
.
checkSummary
()
...
...
@@ -109,6 +102,18 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
pluginConfigure
.
setText
(
pluginConfiguration
.
selectedOptions
.
toString
)
}
private
def
onPluginConfigureChanged
(
p
:
Preference
,
value
:
Any
)
=
try
{
val
selected
=
pluginConfiguration
.
selected
pluginConfiguration
=
new
PluginConfiguration
(
pluginConfiguration
.
pluginsOptions
+
(
pluginConfiguration
.
selected
->
new
PluginOptions
(
selected
,
value
.
asInstanceOf
[
String
])),
selected
)
app
.
editor
.
putString
(
Key
.
plugin
,
pluginConfiguration
.
toString
).
putBoolean
(
Key
.
dirty
,
true
).
apply
()
true
}
catch
{
case
exc
:
IllegalArgumentException
=>
Snackbar
.
make
(
getActivity
.
findViewById
(
R
.
id
.
snackbar
),
exc
.
getLocalizedMessage
,
Snackbar
.
LENGTH_LONG
).
show
()
false
}
override
def
onSharedPreferenceChanged
(
sharedPreferences
:
SharedPreferences
,
key
:
String
)
:
Unit
=
if
(
key
!=
Key
.
proxyApps
&&
findPreference
(
key
)
!=
null
)
app
.
editor
.
putBoolean
(
Key
.
dirty
,
true
).
apply
()
...
...
@@ -124,9 +129,9 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
override
def
onDisplayPreferenceDialog
(
preference
:
Preference
)
:
Unit
=
if
(
preference
eq
pluginConfigure
)
{
val
selected
=
pluginConfiguration
.
selected
val
intent
=
new
Intent
(
PluginInterface
.
ACTION_CONFIGURE
(
selected
)
)
val
intent
=
PluginManager
.
buildIntent
(
selected
,
PluginContract
.
ACTION_CONFIGURE
)
if
(
intent
.
resolveActivity
(
getActivity
.
getPackageManager
)
!=
null
)
startActivityForResult
(
intent
.
putExtra
(
Plugin
Interface
.
EXTRA_OPTIONS
,
startActivityForResult
(
intent
.
putExtra
(
Plugin
Contract
.
EXTRA_OPTIONS
,
pluginConfiguration
.
selectedOptions
.
toString
),
REQUEST_CODE_CONFIGURE
)
else
{
val
bundle
=
new
Bundle
()
bundle
.
putString
(
PluginConfigurationDialogFragment
.
PLUGIN_ID_FRAGMENT_TAG
,
selected
)
...
...
@@ -134,6 +139,15 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
}
}
else
super
.
onDisplayPreferenceDialog
(
preference
)
override
def
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
)
:
Unit
=
requestCode
match
{
case
REQUEST_CODE_CONFIGURE
=>
if
(
resultCode
==
Activity
.
RESULT_OK
)
{
val
options
=
data
.
getStringExtra
(
PluginContract
.
EXTRA_OPTIONS
)
pluginConfigure
.
setText
(
options
)
onPluginConfigureChanged
(
null
,
options
)
}
case
_
=>
super
.
onActivityResult
(
requestCode
,
resultCode
,
data
)
}
override
def
onMenuItemClick
(
item
:
MenuItem
)
:
Boolean
=
item
.
getItemId
match
{
case
R
.
id
.
action_delete
=>
new
AlertDialog
.
Builder
(
getActivity
)
...
...
mobile/src/main/scala/com/github/shadowsocks/plugin/NativePlugin.scala
View file @
99d97d1d
package
com.github.shadowsocks.plugin
import
android.content.pm.
{
PackageManager
,
ProviderInfo
}
import
android.content.ContentResolver
import
android.content.pm.
{
PackageManager
,
ResolveInfo
}
import
android.net.Uri
import
android.os.Bundle
/**
* @author Mygod
*/
class
NativePlugin
(
providerInfo
:
Provider
Info
,
packageManager
:
PackageManager
)
extends
ResolvedPlugin
(
provider
Info
,
packageManager
)
{
assert
(
providerInfo
!=
null
)
assert
(
providerInfo
.
authority
.
startsWith
(
PluginInterface
.
AUTHORITY_BASE
))
override
final
lazy
val
id
:
String
=
providerInfo
.
authority
.
substring
(
PluginInterface
.
AUTHORITY_BASE
.
length
)
override
final
lazy
val
defaultConfig
:
String
=
providerInfo
.
metaData
.
getString
(
PluginInterface
.
METADATA_KEY_DEFAULT_CONFIG
)
final
class
NativePlugin
(
resolveInfo
:
Resolve
Info
,
packageManager
:
PackageManager
)
extends
ResolvedPlugin
(
resolve
Info
,
packageManager
)
{
assert
(
resolveInfo
.
providerInfo
!=
null
)
override
protected
def
metaData
:
Bundle
=
resolveInfo
.
providerInfo
.
metaData
private
lazy
val
uriBuilder
:
Uri.Builder
=
new
Uri
.
Builder
(
)
.
scheme
(
ContentResolver
.
SCHEME_CONTENT
)
.
authority
(
resolveInfo
.
providerInfo
.
authority
)
}
mobile/src/main/scala/com/github/shadowsocks/plugin/Plugin.scala
View file @
99d97d1d
...
...
@@ -10,4 +10,5 @@ abstract class Plugin {
def
label
:
CharSequence
def
icon
:
Drawable
=
null
def
defaultConfig
:
String
=
null
def
packageName
:
String
=
null
}
mobile/src/main/scala/com/github/shadowsocks/plugin/PluginConfiguration.scala
View file @
99d97d1d
...
...
@@ -19,7 +19,7 @@ class PluginConfiguration(val pluginsOptions: Map[String, PluginOptions], val se
val
args
=
mutable
.
Queue
(
Commandline
.
translateCommandline
(
line
)
:
_
*
)
args
.
dequeue
()
while
(
args
.
nonEmpty
)
args
.
dequeue
()
match
{
case
"--nocomp"
=>
opt
.
put
(
"nocomp"
,
"1"
)
case
"--nocomp"
=>
opt
.
put
(
"nocomp"
,
null
)
case
option
if
option
.
startsWith
(
"--"
)
=>
opt
.
put
(
option
.
substring
(
2
),
args
.
dequeue
())
case
option
=>
throw
new
IllegalArgumentException
(
"Unknown kcptun parameter: "
+
option
)
}
...
...
@@ -30,7 +30,10 @@ class PluginConfiguration(val pluginsOptions: Map[String, PluginOptions], val se
case
line
=>
new
PluginOptions
(
line
)
})
def
getOptions
(
id
:
String
)
:
PluginOptions
=
if
(
id
.
isEmpty
)
new
PluginOptions
()
else
pluginsOptions
(
id
)
def
getOptions
(
id
:
String
)
:
PluginOptions
=
if
(
id
.
isEmpty
)
new
PluginOptions
()
else
pluginsOptions
.
get
(
id
)
match
{
case
Some
(
options
)
=>
options
case
None
=>
new
PluginOptions
(
id
,
PluginManager
.
fetchPlugins
()(
id
).
defaultConfig
)
}
def
selectedOptions
:
PluginOptions
=
getOptions
(
selected
)
override
def
toString
:
String
=
{
...
...
@@ -39,6 +42,7 @@ class PluginConfiguration(val pluginsOptions: Map[String, PluginOptions], val se
case
this
.
selected
=>
result
.
prepend
(
opt
)
case
_
=>
result
.
append
(
opt
)
}
if
(!
pluginsOptions
.
contains
(
selected
))
result
.
prepend
(
selectedOptions
)
result
.
map
(
_
.
toString
(
false
)).
mkString
(
"\n"
)
}
}
mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala
View file @
99d97d1d
...
...
@@ -5,36 +5,43 @@ import java.io.{File, FileNotFoundException, FileOutputStream, IOException}
import
android.content.pm.PackageManager
import
android.content.
{
BroadcastReceiver
,
ContentResolver
,
Intent
}
import
android.net.Uri
import
android.text.TextUtils
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.utils.CloseUtils.autoClose
import
com.github.shadowsocks.utils.IOUtils
import
com.github.shadowsocks.utils.
{
Commandline
,
IOUtils
}
import
eu.chainfire.libsuperuser.Shell
import
scala.collection.JavaConver
ter
s._
import
scala.collection.JavaConver
sion
s._
/**
* @author Mygod
*/
object
PluginManager
{
private
var
receiver
:
BroadcastReceiver
=
_
private
var
cachedPlugins
:
Array
[
Plugin
]
=
_
def
fetchPlugins
()
:
Array
[
Plugin
]
=
{
private
var
cachedPlugins
:
Map
[
String
,
Plugin
]
=
_
def
fetchPlugins
()
:
Map
[
String
,
Plugin
]
=
{
if
(
receiver
==
null
)
receiver
=
app
.
listenForPackageChanges
(
synchronized
(
cachedPlugins
=
null
))
synchronized
(
if
(
cachedPlugins
==
null
)
{
val
pm
=
app
.
getPackageManager
cachedPlugins
=
(
NoPlugin
+:
pm
.
query
ContentProviders
(
null
,
0
,
PackageManager
.
GET_META_DATA
).
asScala
.
filter
(
_
!=
null
)
.
filter
(
_
.
authority
.
startsWith
(
PluginInterface
.
AUTHORITY_BASE
))
.
map
(
new
NativePlugin
(
_
,
pm
))).
toArray
cachedPlugins
=
(
pm
.
query
IntentContentProviders
(
new
Intent
(
PluginContract
.
ACTION_NATIVE_PLUGIN
),
PackageManager
.
GET_META_DATA
)
.
map
(
new
NativePlugin
(
_
,
pm
))
:+
NoPlugin
).
map
(
plugin
=>
plugin
.
id
->
plugin
).
toMap
})
cachedPlugins
}
private
def
buildUri
(
id
:
String
)
=
new
Uri
.
Builder
()
.
scheme
(
PluginContract
.
SCHEME
)
.
authority
(
PluginContract
.
AUTHORITY
)
.
path
(
'/'
+
id
)
.
build
()
def
buildIntent
(
id
:
String
,
action
:
String
)
:
Intent
=
new
Intent
(
action
,
buildUri
(
id
))
// the following parts are meant to be used by :bg
@throws
[
Throwable
]
def
initPlugin
(
id
:
String
)
:
String
=
{
if
(
TextUtils
.
isEmpty
(
id
)
)
return
null
if
(
id
.
isEmpty
)
return
null
var
throwable
:
Throwable
=
null
try
initNativePlugin
(
id
)
match
{
...
...
@@ -56,32 +63,35 @@ object PluginManager {
@throws
[
IndexOutOfBoundsException
]
@throws
[
AssertionError
]
private
def
initNativePlugin
(
id
:
String
)
:
String
=
{
val
providers
=
app
.
getPackageManager
.
queryIntentContentProviders
(
new
Intent
(
PluginContract
.
ACTION_NATIVE_PLUGIN
,
buildUri
(
id
)),
0
)
assert
(
providers
.
length
==
1
)
val
builder
=
new
Uri
.
Builder
()
.
scheme
(
ContentResolver
.
SCHEME_CONTENT
)
.
authority
(
PluginInterface
.
getAuthority
(
id
)
)
.
authority
(
providers
(
0
).
providerInfo
.
authority
)
val
cr
=
app
.
getContentResolver
val
cursor
=
cr
.
query
(
builder
.
build
(),
Array
(
PluginInterface
.
COLUMN_PATH
),
null
,
null
,
null
)
val
cursor
=
cr
.
query
(
builder
.
build
(),
Array
(
PluginContract
.
COLUMN_PATH
,
PluginContract
.
COLUMN_MODE
),
null
,
null
,
null
)
if
(
cursor
!=
null
)
{
var
initialized
=
false
val
pluginDir
=
new
File
(
app
.
getFilesDir
,
"plugin"
)
if
(
cursor
.
moveToFirst
())
{
try
{
def
entryNotFound
()
=
throw
new
IndexOutOfBoundsException
(
"Plugin entry binary not found"
)
if
(!
cursor
.
moveToFirst
())
entryNotFound
()
IOUtils
.
deleteRecursively
(
pluginDir
)
}
catch
{
case
ex
:
IOException
=>
// Ignore
}
if
(!
pluginDir
.
mkdirs
())
throw
new
FileNotFoundException
(
"Unable to create plugin directory"
)
val
pluginDirPath
=
pluginDir
.
getAbsolutePath
+
'/'
var
list
:
List
[
String
]
=
Nil
do
{
val
path
=
cursor
.
getString
(
0
)
val
file
=
new
File
(
pluginDir
,
path
)
assert
(
file
.
getAbsolutePath
.
startsWith
(
pluginDirPath
))
autoClose
(
cr
.
openInputStream
(
builder
.
path
(
path
).
build
()))(
in
=>
autoClose
(
new
FileOutputStream
(
file
))(
out
=>
IOUtils
.
copy
(
in
,
out
)))
list
+:=
Commandline
.
toString
(
Array
(
"chmod"
,
cursor
.
getString
(
1
),
file
.
getAbsolutePath
))
if
(
path
==
id
)
initialized
=
true
}
while
(
cursor
.
moveToNext
())
}
if
(!
initialized
)
throw
new
IndexOutOfBoundsException
(
"Plugin entry binary not found"
)
if
(!
initialized
)
entryNotFound
()
Shell
.
SH
.
run
(
list
)
new
File
(
pluginDir
,
id
).
getAbsolutePath
}
else
null
}
...
...
mobile/src/main/scala/com/github/shadowsocks/plugin/ProviderPlugin.scala
deleted
100644 → 0
View file @
b78c10e8
package
com.github.shadowsocks.plugin
import
android.content.pm.
{
PackageManager
,
ProviderInfo
}
import
android.graphics.drawable.Drawable
import
com.github.shadowsocks.ShadowsocksApplication.app
/**
* Base class for any kind of plugin that can be used.
*
* @author Mygod
*/
abstract
class
ResolvedPlugin
(
providerInfo
:
ProviderInfo
,
packageManager
:
PackageManager
=
app
.
getPackageManager
)
extends
Plugin
{
override
final
lazy
val
label
:
CharSequence
=
packageManager
.
getApplicationLabel
(
providerInfo
.
applicationInfo
)
override
final
lazy
val
icon
:
Drawable
=
packageManager
.
getApplicationIcon
(
providerInfo
.
applicationInfo
)
}
mobile/src/main/scala/com/github/shadowsocks/plugin/ResolvedPlugin.scala
0 → 100644
View file @
99d97d1d
package
com.github.shadowsocks.plugin
import
android.content.pm.
{
PackageManager
,
ResolveInfo
}
import
android.graphics.drawable.Drawable
import
android.os.Bundle
import
com.github.shadowsocks.ShadowsocksApplication.app
/**
* Base class for any kind of plugin that can be used.
*
* @author Mygod
*/
abstract
class
ResolvedPlugin
(
resolveInfo
:
ResolveInfo
,
packageManager
:
PackageManager
=
app
.
getPackageManager
)
extends
Plugin
{
protected
def
metaData
:
Bundle
override
final
lazy
val
id
:
String
=
metaData
.
getString
(
PluginContract
.
METADATA_KEY_ID
)
override
final
lazy
val
label
:
CharSequence
=
resolveInfo
.
loadLabel
(
packageManager
)
override
final
lazy
val
icon
:
Drawable
=
resolveInfo
.
loadIcon
(
packageManager
)
override
final
lazy
val
defaultConfig
:
String
=
metaData
.
getString
(
PluginContract
.
METADATA_KEY_DEFAULT_CONFIG
)
override
final
def
packageName
:
String
=
resolveInfo
.
resolvePackageName
}
mobile/src/main/scala/com/github/shadowsocks/preference/BottomSheetPreferenceDialogFragment.scala
View file @
99d97d1d
package
com.github.shadowsocks.preference
import
android.content.
{
ActivityNotFoundException
,
Intent
}
import
android.graphics.Typeface
import
android.graphics.drawable.Drawable
import
android.net.Uri
import
android.os.Bundle
import
android.provider.Settings
import
android.support.design.widget.BottomSheetDialog
import
android.support.v14.preference.PreferenceDialogFragment
import
android.support.v7.widget.RecyclerView.ViewHolder
import
android.support.v7.widget.
{
LinearLayoutManager
,
RecyclerView
}
import
android.view.ViewGroup.LayoutParams
import
android.view.
{
LayoutInflater
,
View
,
ViewGroup
}
import
android.widget.
TextView
import
android.widget.
{
ImageView
,
TextView
}
import
com.github.shadowsocks.R
/**
...
...
@@ -19,6 +22,7 @@ final class BottomSheetPreferenceDialogFragment extends PreferenceDialogFragment
private
lazy
val
preference
=
getPreference
.
asInstanceOf
[
IconListPreference
]
private
lazy
val
index
:
Int
=
preference
.
selectedEntry
private
lazy
val
entries
:
Array
[
CharSequence
]
=
preference
.
getEntries
private
lazy
val
entryValues
:
Array
[
CharSequence
]
=
preference
.
getEntryValues
private
lazy
val
entryIcons
:
Array
[
Drawable
]
=
preference
.
getEntryIcons
private
var
clickedIndex
=
-
1
...
...
@@ -39,30 +43,41 @@ final class BottomSheetPreferenceDialogFragment extends PreferenceDialogFragment
if
(
preference
.
callChangeListener
(
value
))
preference
.
setValue
(
value
)
}
private
final
class
IconListViewHolder
(
val
dialog
:
BottomSheetDialog
,
v
al
view
:
Text
View
)
extends
ViewHolder
(
view
)
with
View
.
OnClickListener
{
private
final
class
IconListViewHolder
(
val
dialog
:
BottomSheetDialog
,
v
iew
:
View
)
extends
ViewHolder
(
view
)
with
View
.
OnClickListener
with
View
.
OnLongClickListener
{
private
var
index
:
Int
=
_
{
val
padding
=
getResources
.
getDimension
(
R
.
dimen
.
icon_list_item_padding
).
toInt
view
.
setPadding
(
padding
,
padding
,
padding
,
padding
)
view
.
setCompoundDrawablePadding
(
padding
)
private
val
text1
=
view
.
findViewById
(
android
.
R
.
id
.
text1
).
asInstanceOf
[
TextView
]
private
val
text2
=
view
.
findViewById
(
android
.
R
.
id
.
text2
).
asInstanceOf
[
TextView
]
private
val
icon
=
view
.
findViewById
(
android
.
R
.
id
.
icon
).
asInstanceOf
[
ImageView
]
view
.
setOnClickListener
(
this
)
val
typedArray
=
dialog
.
getContext
.
obtainStyledAttributes
(
Array
(
android
.
R
.
attr
.
selectableItemBackground
))
view
.
setBackgroundResource
(
typedArray
.
getResourceId
(
0
,
0
))
typedArray
.
recycle
()
}
view
.
setOnLongClickListener
(
this
)
def
bind
(
i
:
Int
,
selected
:
Boolean
=
false
)
{
view
.
setText
(
entries
(
i
))
view
.
setCompoundDrawablesWithIntrinsicBounds
(
entryIcons
(
i
),
null
,
null
,
null
)
view
.
setTypeface
(
null
,
if
(
selected
)
Typeface
.
BOLD
else
Typeface
.
NORMAL
)
text1
.
setText
(
entries
(
i
))
text2
.
setText
(
entryValues
(
i
))
val
typeface
=
if
(
selected
)
Typeface
.
BOLD
else
Typeface
.
NORMAL
text1
.
setTypeface
(
null
,
typeface
)
text2
.
setTypeface
(
null
,
typeface
)
text2
.
setVisibility
(
if
(
entryValues
(
i
).
length
>
0
)
View
.
VISIBLE
else
View
.
GONE
)
icon
.
setImageDrawable
(
entryIcons
(
i
))
index
=
i
}
def
onClick
(
v
:
View
)
{
clickedIndex
=
index
dialog
.
dismiss
()
}
override
def
onLongClick
(
v
:
View
)
:
Boolean
=
preference
.
entryPackageNames
(
index
)
match
{
case
null
=>
false
case
pn
=>
try
{
startActivity
(
new
Intent
(
Settings
.
ACTION_APPLICATION_DETAILS_SETTINGS
,
new
Uri
.
Builder
()
.
authority
(
Settings
.
AUTHORITY
)
.
opaquePart
(
pn
)
.
build
()))
true
}
catch
{
case
_:
ActivityNotFoundException
=>
false
}
}
}
private
final
class
IconListAdapter
(
dialog
:
BottomSheetDialog
)
extends
RecyclerView
.
Adapter
[
IconListViewHolder
]
{
def
getItemCount
:
Int
=
entries
.
length
...
...
@@ -72,6 +87,6 @@ final class BottomSheetPreferenceDialogFragment extends PreferenceDialogFragment
case
_
=>
vh
.
bind
(
i
-
1
)
}
def
onCreateViewHolder
(
vg
:
ViewGroup
,
i
:
Int
)
:
IconListViewHolder
=
new
IconListViewHolder
(
dialog
,
LayoutInflater
.
from
(
vg
.
getContext
).
inflate
(
android
.
R
.
layout
.
simple_list_item_1
,
vg
,
false
).
asInstanceOf
[
TextView
]
)
LayoutInflater
.
from
(
vg
.
getContext
).
inflate
(
R
.
layout
.
icon_list_item_2
,
vg
,
false
)
)
}
}
mobile/src/main/scala/com/github/shadowsocks/preference/IconListPreference.scala
View file @
99d97d1d
...
...
@@ -19,6 +19,7 @@ class IconListPreference(context: Context, attrs: AttributeSet = null) extends L
def
selectedEntry
:
Int
=
getEntryValues
.
indexOf
(
getValue
)
var
unknownValueSummary
:
String
=
_
var
entryPackageNames
:
Array
[
String
]
=
_
private
var
listener
:
OnPreferenceChangeListener
=
_
override
def
getOnPreferenceChangeListener
:
OnPreferenceChangeListener
=
listener
...
...
mobile/src/main/scala/com/github/shadowsocks/preference/PluginConfigurationDialogFragment.scala
View file @
99d97d1d
...
...
@@ -23,7 +23,7 @@ package com.github.shadowsocks.preference
import
android.app.
{
Activity
,
AlertDialog
}
import
android.content.
{
DialogInterface
,
Intent
}
import
be.mygod.preference.EditTextPreferenceDialogFragment
import
com.github.shadowsocks.plugin.
PluginInterface
import
com.github.shadowsocks.plugin.
{
PluginContract
,
PluginManager
}
/**
* @author Mygod
...
...
@@ -38,9 +38,9 @@ class PluginConfigurationDialogFragment extends EditTextPreferenceDialogFragment
override
def
onPrepareDialogBuilder
(
builder
:
AlertDialog.Builder
)
{
super
.
onPrepareDialogBuilder
(
builder
)
val
intent
=
new
Intent
(
PluginInterface
.
ACTION_HELP
(
getArguments
.
getString
(
PLUGIN_ID_FRAGMENT_TAG
))
)
val
intent
=
PluginManager
.
buildIntent
(
getArguments
.
getString
(
PLUGIN_ID_FRAGMENT_TAG
),
PluginContract
.
ACTION_HELP
)
if
(
intent
.
resolveActivity
(
getActivity
.
getPackageManager
)
!=
null
)
builder
.
setNeutralButton
(
"?"
,
((
_
,
_
)
=>
startActivityForResult
(
intent
.
putExtra
(
Plugin
Interface
.
EXTRA_OPTIONS
,
editText
.
getText
.
toString
),
startActivityForResult
(
intent
.
putExtra
(
Plugin
Contract
.
EXTRA_OPTIONS
,
editText
.
getText
.
toString
),
REQUEST_CODE_HELP
))
:
DialogInterface.OnClickListener
)
}
...
...
@@ -48,7 +48,7 @@ class PluginConfigurationDialogFragment extends EditTextPreferenceDialogFragment
case
REQUEST_CODE_HELP
=>
requestCode
match
{
case
Activity
.
RESULT_OK
=>
new
AlertDialog
.
Builder
(
getContext
)
.
setTitle
(
"?"
)
.
setMessage
(
data
.
getCharSequenceExtra
(
Plugin
Interface
.
EXTRA_HELP_MESSAGE
))
.
setMessage
(
data
.
getCharSequenceExtra
(
Plugin
Contract
.
EXTRA_HELP_MESSAGE
))
.
show
()
case
_
=>
}
...
...
mobile/src/main/scala/com/github/shadowsocks/utils/IOUtils.scala
View file @
99d97d1d
...
...
@@ -41,7 +41,7 @@ object IOUtils {
autoClose
(
new
FileWriter
(
file
))(
writer
=>
writer
.
write
(
content
))
@throws
[
FileNotFoundException
]
def
deleteRecursively
(
file
:
File
)
{
def
deleteRecursively
(
file
:
File
)
:
Unit
=
if
(
file
.
exists
())
{
if
(
file
.
isDirectory
)
file
.
listFiles
().
foreach
(
deleteRecursively
)
if
(!
file
.
delete
())
throw
new
FileNotFoundException
(
"Failed to delete: "
+
file
.
getAbsolutePath
)
}
...
...
plugin/src/main/java/com/github/shadowsocks/plugin/Plugin
Interface
.java
→
plugin/src/main/java/com/github/shadowsocks/plugin/Plugin
Contract
.java
View file @
99d97d1d
package
com.github.shadowsocks.plugin
;
/**
* Th
is class provides String constants that will be used in plugin interfaces
.
* Th
e contract between the plugin provider and host. Contains definitions for the supported actions, extras, etc
.
*
* This class is written in Java to keep Java interoperability.
*
* @author Mygod
*/
public
final
class
Plugin
Interface
{
private
Plugin
Interface
()
{
}
public
final
class
Plugin
Contract
{
private
Plugin
Contract
()
{
}
/**
*
Should be a
NativePluginProvider.
*
ContentProvider Action: Used for
NativePluginProvider.
*
* Constant Value: "com.github.shadowsocks.plugin.
CATEGORY
_NATIVE_PLUGIN"
* Constant Value: "com.github.shadowsocks.plugin.
ACTION
_NATIVE_PLUGIN"
*/
public
static
final
String
CATEGORY_NATIVE_PLUGIN
=
"com.github.shadowsocks.plugin.CATEGORY_NATIVE_PLUGIN"
;
public
static
final
String
ACTION_NATIVE_PLUGIN
=
"com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"
;
/**
* Activity Action: Used for ConfigurationActivity.
*
* Constant Value: "com.github.shadowsocks.plugin.ACTION_CONFIGURE"
*/
public
static
final
String
ACTION_CONFIGURE
=
"com.github.shadowsocks.plugin.ACTION_CONFIGURE"
;
/**
* Activity Action: Used for HelpActivity or HelpCallback.
*
* Constant Value: "com.github.shadowsocks.plugin.ACTION_CONFIGURE"
*/
public
static
final
String
ACTION_HELP
=
"com.github.shadowsocks.plugin.ACTION_HELP"
;
/**
* The lookup key for a string that provides the whole command as a string.
...
...
@@ -35,6 +48,12 @@ public final class PluginInterface {
*/
public
static
final
String
EXTRA_HELP_MESSAGE
=
"com.github.shadowsocks.plugin.EXTRA_HELP_MESSAGE"
;
/**
* The metadata key to retrieve plugin id. Required for plugins.
*
* Constant Value: "com.github.shadowsocks.plugin.id"
*/
public
static
final
String
METADATA_KEY_ID
=
"com.github.shadowsocks.plugin.id"
;
/**
* The metadata key to retrieve default configuration. Default value is empty.
*
...
...
@@ -50,35 +69,21 @@ public final class PluginInterface {
* Type: String
*/
public
static
final
String
COLUMN_PATH
=
"path"
;
static
final
String
AUTHORITY_BASE
=
"com.github.shadowsocks.plugin."
;
/**
* Authority to use for native plugin ContentProvider.
* File mode bits. Default value is "644".
*
* Example: "755"
*
* @param pluginId Plugin ID.
* @return com.github.shadowsocks.plugin.$PLUGIN_ID
* Type: String
*/
public
static
String
getAuthority
(
String
pluginId
)
{
return
AUTHORITY_BASE
+
pluginId
;
}
public
static
final
String
COLUMN_MODE
=
"mode"
;
/**
* Activity Action: Used for ConfigurationActivity.
*
* @param pluginId Plugin ID.
* @return com.github.shadowsocks.plugin.$PLUGIN_ID.ACTION_CONFIGURE
* The scheme for general plugin actions.
*/
public
static
String
ACTION_CONFIGURE
(
String
pluginId
)
{
return
getAuthority
(
pluginId
)
+
".ACTION_CONFIGURE"
;
}
public
static
final
String
SCHEME
=
"plugin"
;
/**
* Activity Action: Used for HelpActivity or HelpCallback.
*
* @param pluginId Plugin ID.
* @return com.github.shadowsocks.plugin.$PLUGIN_ID.ACTION_HELP
* The authority for general plugin actions.
*/
public
static
String
ACTION_HELP
(
String
pluginId
)
{
return
getAuthority
(
pluginId
)
+
".ACTION_HELP"
;
}
public
static
final
String
AUTHORITY
=
"com.github.shadowsocks"
;
}
plugin/src/main/java/com/github/shadowsocks/plugin/PluginOptions.java
View file @
99d97d1d
...
...
@@ -25,8 +25,9 @@ public final class PluginOptions extends HashMap<String, String> {
}
private
PluginOptions
(
String
options
,
boolean
parseId
)
{
this
();
if
(
TextUtils
.
isEmpty
(
options
))
return
;
final
StringTokenizer
tokenizer
=
new
StringTokenizer
(
options
,
"\\=;"
,
true
);
final
StringTokenizer
tokenizer
=
new
StringTokenizer
(
options
+
';'
,
"\\=;"
,
true
);
final
StringBuilder
current
=
new
StringBuilder
();
String
key
=
null
;
while
(
tokenizer
.
hasMoreTokens
())
{
...
...
@@ -35,18 +36,15 @@ public final class PluginOptions extends HashMap<String, String> {
else
if
(
"="
.
equals
(
nextToken
)
&&
key
==
null
)
{
key
=
current
.
toString
();
current
.
setLength
(
0
);
}
else
if
(
";"
.
equals
(
nextToken
))
}
else
if
(
";"
.
equals
(
nextToken
))
{
if
(
key
!=
null
)
{
put
(
key
,
current
.
toString
());
key
=
null
;
}
else
if
(
parseId
)
{
id
=
current
.
toString
();
parseId
=
false
;
}
else
{
put
(
current
.
toString
(),
null
);
}
else
if
(
current
.
length
()
>
0
)
if
(
parseId
)
id
=
current
.
toString
();
else
put
(
current
.
toString
(),
null
);
current
.
setLength
(
0
);
}
else
current
.
append
(
nextToken
);
parseId
=
false
;
}
else
current
.
append
(
nextToken
);
}
}
public
PluginOptions
(
String
options
)
{
...
...
@@ -57,7 +55,7 @@ public final class PluginOptions extends HashMap<String, String> {
this
.
id
=
id
;
}
public
String
id
;
public
String
id
=
""
;
private
static
void
append
(
StringBuilder
result
,
String
str
)
{
for
(
int
i
=
0
;
i
<
str
.
length
();
++
i
)
{
...
...
plugin/src/main/scala/com/github/shadowsocks/plugin/ConfigurationActivity.scala
View file @
99d97d1d
...
...
@@ -35,5 +35,5 @@ trait ConfigurationActivity extends OptionsCapableActivity {
* @param options PluginOptions to save.
*/
final
def
saveChanges
(
options
:
PluginOptions
)
:
Unit
=
setResult
(
Activity
.
RESULT_OK
,
new
Intent
().
putExtra
(
Plugin
Interface
.
EXTRA_OPTIONS
,
options
.
toString
))
setResult
(
Activity
.
RESULT_OK
,
new
Intent
().
putExtra
(
Plugin
Contract
.
EXTRA_OPTIONS
,
options
.
toString
))
}
plugin/src/main/scala/com/github/shadowsocks/plugin/HelpCallback.scala
View file @
99d97d1d
...
...
@@ -14,5 +14,5 @@ trait HelpCallback extends HelpActivity {
def
produceHelpMessage
(
options
:
PluginOptions
)
:
CharSequence
override
protected
def
onInitializePluginOptions
(
options
:
PluginOptions
)
:
Unit
=
setResult
(
Activity
.
RESULT_OK
,
new
Intent
().
putExtra
(
Plugin
Interface
.
EXTRA_HELP_MESSAGE
,
produceHelpMessage
(
options
)))
new
Intent
().
putExtra
(
Plugin
Contract
.
EXTRA_HELP_MESSAGE
,
produceHelpMessage
(
options
)))
}
plugin/src/main/scala/com/github/shadowsocks/plugin/NativePluginProvider.scala
View file @
99d97d1d
...
...
@@ -17,7 +17,7 @@ import android.os.ParcelFileDescriptor
* <provider android:name="com.github.shadowsocks.$PLUGIN_ID.BinaryProvider"
* android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID">
* <intent-filter>
* <category android:name="com.github.shadowsocks.plugin.
CATEGORY
_NATIVE_PLUGIN" />
* <category android:name="com.github.shadowsocks.plugin.
ACTION
_NATIVE_PLUGIN" />
* </intent-filter>
* </provider>
* ...
...
...
@@ -47,7 +47,7 @@ abstract class NativePluginProvider extends ContentProvider {
override
def
query
(
uri
:
Uri
,
projection
:
Array
[
String
],
selection
:
String
,
selectionArgs
:
Array
[
String
],
sortOrder
:
String
)
:
Cursor
=
{
assert
(
selection
==
null
&&
selectionArgs
==
null
&&
sortOrder
==
null
)
val
result
=
new
MatrixCursor
(
projection
.
filter
(
_
==
PluginInterface
.
COLUMN_PATH
)
)
val
result
=
new
MatrixCursor
(
projection
)
populateFiles
(
new
PathProvider
(
uri
,
result
))
result
}
...
...
plugin/src/main/scala/com/github/shadowsocks/plugin/OptionsCapableActivity.scala
View file @
99d97d1d
...
...
@@ -12,7 +12,7 @@ import android.widget.Toast
*/
trait
OptionsCapableActivity
extends
Activity
{
protected
def
pluginOptions
(
intent
:
Intent
=
getIntent
)
:
PluginOptions
=
try
new
PluginOptions
(
intent
.
getStringExtra
(
Plugin
Interface
.
EXTRA_OPTIONS
))
catch
{
try
new
PluginOptions
(
intent
.
getStringExtra
(
Plugin
Contract
.
EXTRA_OPTIONS
))
catch
{
case
exc
:
IllegalArgumentException
=>
Toast
.
makeText
(
this
,
exc
.
getMessage
,
Toast
.
LENGTH_SHORT
).
show
()
null
...
...
plugin/src/main/scala/com/github/shadowsocks/plugin/PathProvider.scala
View file @
99d97d1d
...
...
@@ -16,21 +16,24 @@ final class PathProvider private[plugin](baseUri: Uri, cursor: MatrixCursor) {
case
p
=>
p
.
stripPrefix
(
"/"
).
stripSuffix
(
"/"
)
}
def
addPath
(
path
:
String
)
:
PathProvider
=
{
def
addPath
(
path
:
String
,
mode
:
String
=
"644"
)
:
PathProvider
=
{
val
stripped
=
path
.
stripPrefix
(
"/"
).
stripSuffix
(
"/"
)
if
(
stripped
.
startsWith
(
basePath
))
cursor
.
newRow
().
add
(
PluginInterface
.
COLUMN_PATH
,
stripped
)
if
(
stripped
.
startsWith
(
basePath
))
cursor
.
newRow
()
.
add
(
PluginContract
.
COLUMN_PATH
,
stripped
)
.
add
(
PluginContract
.
COLUMN_MODE
,
mode
)
this
}
def
addTo
(
file
:
File
,
to
:
String
=
""
)
:
PathProvider
=
{
def
addTo
(
file
:
File
,
to
:
String
=
""
,
mode
:
String
=
"644"
)
:
PathProvider
=
{
var
sub
=
to
+
file
.
getName
if
(
basePath
.
startsWith
(
sub
))
if
(
file
.
isDirectory
)
{
sub
+=
'/'
file
.
listFiles
().
foreach
(
addTo
(
_
,
sub
))
}
else
addPath
(
sub
)
file
.
listFiles
().
foreach
(
addTo
(
_
,
sub
,
mode
))
}
else
addPath
(
sub
,
mode
)
this
}
def
addAt
(
file
:
File
,
at
:
String
=
""
)
:
PathProvider
=
{
if
(
basePath
.
startsWith
(
at
))
if
(
file
.
isDirectory
)
file
.
listFiles
().
foreach
(
addTo
(
_
,
at
))
else
addPath
(
at
)
def
addAt
(
file
:
File
,
at
:
String
=
""
,
mode
:
String
=
"644"
)
:
PathProvider
=
{
if
(
basePath
.
startsWith
(
at
))
if
(
file
.
isDirectory
)
file
.
listFiles
().
foreach
(
addTo
(
_
,
at
,
mode
))
else
addPath
(
at
,
mode
)
this
}
}
project/plugins.sbt
View file @
99d97d1d
addSbtPlugin
(
"org.scala-android"
%
"sbt-android"
%
"1.7.
2
"
)
addSbtPlugin
(
"org.scala-android"
%
"sbt-android"
%
"1.7.
3
"
)
addSbtPlugin
(
"com.timushev.sbt"
%
"sbt-updates"
%
"0.1.10"
)
...
...
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