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
3d664377
Commit
3d664377
authored
Apr 29, 2013
by
Max Lv
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
clean up
parent
a74c3fde
Changes
29
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
2337 additions
and
2465 deletions
+2337
-2465
res/drawable/btn_cab_done_shadow.xml
res/drawable/btn_cab_done_shadow.xml
+6
-6
res/drawable/pressed_background_shadow.xml
res/drawable/pressed_background_shadow.xml
+1
-1
res/drawable/progress_horizontal_shadow.xml
res/drawable/progress_horizontal_shadow.xml
+12
-12
res/drawable/selectable_background_shadow.xml
res/drawable/selectable_background_shadow.xml
+5
-4
res/drawable/spinner_background_ab_shadow.xml
res/drawable/spinner_background_ab_shadow.xml
+7
-7
res/drawable/tab_indicator_ab_shadow.xml
res/drawable/tab_indicator_ab_shadow.xml
+21
-13
res/layout/layout_apps.xml
res/layout/layout_apps.xml
+7
-7
res/layout/layout_apps_item.xml
res/layout/layout_apps_item.xml
+25
-25
res/layout/layout_switch.xml
res/layout/layout_switch.xml
+19
-19
res/layout/overlay.xml
res/layout/overlay.xml
+4
-4
res/values-v14/styles_shadow.xml
res/values-v14/styles_shadow.xml
+34
-33
res/values/colors.xml
res/values/colors.xml
+6
-6
res/values/strings.xml
res/values/strings.xml
+115
-115
res/values/styles_shadow.xml
res/values/styles_shadow.xml
+61
-59
res/xml/pref_feature.xml
res/xml/pref_feature.xml
+44
-44
res/xml/pref_proxy.xml
res/xml/pref_proxy.xml
+31
-31
res/xml/shadowsocks_headers.xml
res/xml/shadowsocks_headers.xml
+10
-10
src/com/github/shadowsocks/AppManager.java
src/com/github/shadowsocks/AppManager.java
+281
-307
src/com/github/shadowsocks/Exec.java
src/com/github/shadowsocks/Exec.java
+33
-38
src/com/github/shadowsocks/ImageLoader.java
src/com/github/shadowsocks/ImageLoader.java
+167
-173
src/com/github/shadowsocks/ImageLoaderFactory.java
src/com/github/shadowsocks/ImageLoaderFactory.java
+6
-6
src/com/github/shadowsocks/ProxyedApp.java
src/com/github/shadowsocks/ProxyedApp.java
+54
-78
src/com/github/shadowsocks/Shadowsocks.java
src/com/github/shadowsocks/Shadowsocks.java
+459
-492
src/com/github/shadowsocks/ShadowsocksApplication.java
src/com/github/shadowsocks/ShadowsocksApplication.java
+9
-10
src/com/github/shadowsocks/ShadowsocksReceiver.java
src/com/github/shadowsocks/ShadowsocksReceiver.java
+36
-36
src/com/github/shadowsocks/ShadowsocksService.java
src/com/github/shadowsocks/ShadowsocksService.java
+505
-525
src/com/github/shadowsocks/System.java
src/com/github/shadowsocks/System.java
+4
-4
src/com/github/shadowsocks/Utils.java
src/com/github/shadowsocks/Utils.java
+356
-376
src/com/github/shadowsocks/preferences/SummaryEditTextPreference.java
...ub/shadowsocks/preferences/SummaryEditTextPreference.java
+19
-24
No files found.
res/drawable/btn_cab_done_shadow.xml
View file @
3d664377
...
@@ -18,10 +18,10 @@
...
@@ -18,10 +18,10 @@
-->
-->
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:state_pressed=
"true"
<item
android:state_pressed=
"true"
android:drawable=
"@drawable/btn_cab_done_pressed_shadow"
/>
android:drawable=
"@drawable/btn_cab_done_pressed_shadow"
/>
<item
android:state_focused=
"true"
android:state_enabled=
"true"
<item
android:state_focused=
"true"
android:state_enabled=
"true"
android:drawable=
"@drawable/btn_cab_done_focused_shadow"
/>
android:drawable=
"@drawable/btn_cab_done_focused_shadow"
/>
<item
android:state_enabled=
"true"
<item
android:state_enabled=
"true"
android:drawable=
"@drawable/btn_cab_done_default_shadow"
/>
android:drawable=
"@drawable/btn_cab_done_default_shadow"
/>
</selector>
</selector>
res/drawable/pressed_background_shadow.xml
View file @
3d664377
...
@@ -18,5 +18,5 @@
...
@@ -18,5 +18,5 @@
-->
-->
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:shape=
"rectangle"
>
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:shape=
"rectangle"
>
<solid
android:color=
"@color/pressed_shadow"
/>
<solid
android:color=
"@color/pressed_shadow"
/>
</shape>
</shape>
res/drawable/progress_horizontal_shadow.xml
View file @
3d664377
...
@@ -19,17 +19,17 @@
...
@@ -19,17 +19,17 @@
<layer-list
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<layer-list
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:id=
"@android:id/background"
<item
android:id=
"@android:id/background"
android:drawable=
"@drawable/progress_bg_shadow"
/>
android:drawable=
"@drawable/progress_bg_shadow"
/>
<item
android:id=
"@android:id/secondaryProgress"
>
<item
android:id=
"@android:id/secondaryProgress"
>
<scale
android:scaleWidth=
"100%"
<scale
android:scaleWidth=
"100%"
android:drawable=
"@drawable/progress_secondary_shadow"
/>
android:drawable=
"@drawable/progress_secondary_shadow"
/>
</item>
</item>
<item
android:id=
"@android:id/progress"
>
<item
android:id=
"@android:id/progress"
>
<scale
android:scaleWidth=
"100%"
<scale
android:scaleWidth=
"100%"
android:drawable=
"@drawable/progress_primary_shadow"
/>
android:drawable=
"@drawable/progress_primary_shadow"
/>
</item>
</item>
</layer-list>
</layer-list>
res/drawable/selectable_background_shadow.xml
View file @
3d664377
...
@@ -18,8 +18,9 @@
...
@@ -18,8 +18,9 @@
-->
-->
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:exitFadeDuration=
"@android:integer/config_mediumAnimTime"
>
android:exitFadeDuration=
"@android:integer/config_mediumAnimTime"
>
<item
android:state_pressed=
"false"
android:state_focused=
"true"
android:drawable=
"@drawable/list_focused_shadow"
/>
<item
android:state_pressed=
"false"
android:state_focused=
"true"
<item
android:state_pressed=
"true"
android:drawable=
"@drawable/pressed_background_shadow"
/>
android:drawable=
"@drawable/list_focused_shadow"
/>
<item
android:drawable=
"@android:color/transparent"
/>
<item
android:state_pressed=
"true"
android:drawable=
"@drawable/pressed_background_shadow"
/>
<item
android:drawable=
"@android:color/transparent"
/>
</selector>
</selector>
\ No newline at end of file
res/drawable/spinner_background_ab_shadow.xml
View file @
3d664377
...
@@ -18,11 +18,11 @@
...
@@ -18,11 +18,11 @@
-->
-->
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:state_enabled=
"false"
<item
android:state_enabled=
"false"
android:drawable=
"@drawable/spinner_ab_disabled_shadow"
/>
android:drawable=
"@drawable/spinner_ab_disabled_shadow"
/>
<item
android:state_pressed=
"true"
<item
android:state_pressed=
"true"
android:drawable=
"@drawable/spinner_ab_pressed_shadow"
/>
android:drawable=
"@drawable/spinner_ab_pressed_shadow"
/>
<item
android:state_pressed=
"false"
android:state_focused=
"true"
<item
android:state_pressed=
"false"
android:state_focused=
"true"
android:drawable=
"@drawable/spinner_ab_focused_shadow"
/>
android:drawable=
"@drawable/spinner_ab_focused_shadow"
/>
<item
android:drawable=
"@drawable/spinner_ab_default_shadow"
/>
<item
android:drawable=
"@drawable/spinner_ab_default_shadow"
/>
</selector>
</selector>
res/drawable/tab_indicator_ab_shadow.xml
View file @
3d664377
...
@@ -18,20 +18,28 @@
...
@@ -18,20 +18,28 @@
-->
-->
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<!-- Non focused states -->
<!-- Non focused states -->
<item
android:state_focused=
"false"
android:state_selected=
"false"
android:state_pressed=
"false"
android:drawable=
"@android:color/transparent"
/>
<item
android:state_focused=
"false"
android:state_selected=
"false"
android:state_pressed=
"false"
<item
android:state_focused=
"false"
android:state_selected=
"true"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_selected_shadow"
/>
android:drawable=
"@android:color/transparent"
/>
<item
android:state_focused=
"false"
android:state_selected=
"true"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_selected_shadow"
/>
<!-- Focused states -->
<!-- Focused states -->
<item
android:state_focused=
"true"
android:state_selected=
"false"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_unselected_focused_shadow"
/>
<item
android:state_focused=
"true"
android:state_selected=
"false"
android:state_pressed=
"false"
<item
android:state_focused=
"true"
android:state_selected=
"true"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_selected_focused_shadow"
/>
android:drawable=
"@drawable/tab_unselected_focused_shadow"
/>
<item
android:state_focused=
"true"
android:state_selected=
"true"
android:state_pressed=
"false"
android:drawable=
"@drawable/tab_selected_focused_shadow"
/>
<!-- Pressed -->
<!-- Pressed -->
<!-- Non focused states -->
<!-- Non focused states -->
<item
android:state_focused=
"false"
android:state_selected=
"false"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_unselected_pressed_shadow"
/>
<item
android:state_focused=
"false"
android:state_selected=
"false"
android:state_pressed=
"true"
<item
android:state_focused=
"false"
android:state_selected=
"true"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_selected_pressed_shadow"
/>
android:drawable=
"@drawable/tab_unselected_pressed_shadow"
/>
<item
android:state_focused=
"false"
android:state_selected=
"true"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_selected_pressed_shadow"
/>
<!-- Focused states -->
<!-- Focused states -->
<item
android:state_focused=
"true"
android:state_selected=
"false"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_unselected_pressed_shadow"
/>
<item
android:state_focused=
"true"
android:state_selected=
"false"
android:state_pressed=
"true"
<item
android:state_focused=
"true"
android:state_selected=
"true"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_selected_pressed_shadow"
/>
android:drawable=
"@drawable/tab_unselected_pressed_shadow"
/>
<item
android:state_focused=
"true"
android:state_selected=
"true"
android:state_pressed=
"true"
android:drawable=
"@drawable/tab_selected_pressed_shadow"
/>
</selector>
</selector>
res/layout/layout_apps.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width=
"fill_parent"
<LinearLayout
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_height=
"fill_parent"
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:orientation=
"vertical"
android:duplicateParentState=
"false"
>
android:orientation=
"vertical"
android:duplicateParentState=
"false"
>
<TextView
android:text=
"@string/proxied_help"
<TextView
android:text=
"@string/proxied_help"
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:textSize=
"16sp"
android:padding=
"3dp"
/>
android:textSize=
"16sp"
android:padding=
"3dp"
/>
<ListView
android:layout_width=
"fill_parent"
<ListView
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:id=
"@+id/applistview"
/>
android:layout_height=
"wrap_content"
android:id=
"@+id/applistview"
/>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
res/layout/layout_apps_item.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<TableLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"fill_parent"
android:layout_width=
"fill_parent"
android:layout_height=
"fill_parent"
android:layout_height=
"fill_parent"
android:stretchColumns=
"1"
>
android:stretchColumns=
"1"
>
<TableRow>
<TableRow>
<ImageView
<ImageView
android:id=
"@+id/itemicon"
android:id=
"@+id/itemicon"
android:layout_width=
"48dp"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:layout_height=
"48dp"
android:scaleType=
"fitCenter"
android:scaleType=
"fitCenter"
android:padding=
"3dp"
/>
android:padding=
"3dp"
/>
<TextView
<TextView
android:id=
"@+id/itemtext"
android:id=
"@+id/itemtext"
android:layout_width=
"wrap_content"
android:layout_width=
"wrap_content"
android:layout_height=
"fill_parent"
android:layout_height=
"fill_parent"
android:text=
"uid:packages"
android:text=
"uid:packages"
android:gravity=
"center_vertical"
android:gravity=
"center_vertical"
android:maxLength=
"25"
android:maxLength=
"25"
android:textSize=
"18sp"
android:textSize=
"18sp"
android:padding=
"3dp"
/>
android:padding=
"3dp"
/>
<CheckBox
<CheckBox
android:id=
"@+id/itemcheck"
android:id=
"@+id/itemcheck"
android:layout_width=
"wrap_content"
android:layout_width=
"wrap_content"
android:layout_height=
"fill_parent"
android:layout_height=
"fill_parent"
android:layout_marginRight=
"6dp"
/>
android:layout_marginRight=
"6dp"
/>
</TableRow>
</TableRow>
</TableLayout>
</TableLayout>
res/layout/layout_switch.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<RelativeLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
android:layout_height=
"match_parent"
>
<TextView
<TextView
android:id=
"@+id/title"
android:id=
"@+id/title"
android:layout_width=
"wrap_content"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:gravity=
"center_vertical"
android:gravity=
"center_vertical"
android:layout_marginLeft=
"16dp"
android:layout_marginLeft=
"16dp"
android:textSize=
"28sp"
android:textSize=
"28sp"
android:textColor=
"@color/title"
android:textColor=
"@color/title"
android:text=
"@string/app_name"
android:text=
"@string/app_name"
android:layout_alignParentLeft=
"true"
android:layout_alignParentLeft=
"true"
android:layout_centerVertical=
"true"
/>
android:layout_centerVertical=
"true"
/>
<org.jraf.android.backport.switchwidget.Switch
<org.jraf.android.backport.switchwidget.Switch
android:id=
"@+id/switchButton"
android:id=
"@+id/switchButton"
android:layout_width=
"wrap_content"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_alignParentRight=
"true"
android:layout_alignParentRight=
"true"
android:layout_centerVertical=
"true"
/>
android:layout_centerVertical=
"true"
/>
</RelativeLayout>
</RelativeLayout>
res/layout/overlay.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android=
"http://schemas.android.com/apk/res/android"
<TextView
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:minWidth=
"80dp"
android:maxWidth=
"80dp"
android:gravity=
"center"
android:minWidth=
"80dp"
android:maxWidth=
"80dp"
android:gravity=
"center"
android:textSize=
"70sp"
android:textColor=
"#ffffffff"
android:textSize=
"70sp"
android:textColor=
"#ffffffff"
android:background=
"#99000088"
android:padding=
"10dp"
android:background=
"#99000088"
android:padding=
"10dp"
android:visibility=
"invisible"
/>
android:visibility=
"invisible"
/>
\ No newline at end of file
\ No newline at end of file
res/values-v14/styles_shadow.xml
View file @
3d664377
...
@@ -19,38 +19,39 @@
...
@@ -19,38 +19,39 @@
<resources>
<resources>
<style
name=
"Theme.Shadow"
parent=
"@style/Theme.Sherlock.Light.DarkActionBar"
>
<style
name=
"Theme.Shadow"
parent=
"@style/Theme.Sherlock.Light.DarkActionBar"
>
<item
name=
"android:actionBarItemBackground"
>
@drawable/selectable_background_shadow
</item>
<item
name=
"android:actionBarItemBackground"
>
@drawable/selectable_background_shadow
</item>
<item
name=
"android:popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"android:popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"android:dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
<item
name=
"android:dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
<item
name=
"android:actionBarTabStyle"
>
@style/ActionBarTabStyle.Shadow
</item>
<item
name=
"android:actionBarTabStyle"
>
@style/ActionBarTabStyle.Shadow
</item>
<item
name=
"android:actionDropDownStyle"
>
@style/DropDownNav.Shadow
</item>
<item
name=
"android:actionDropDownStyle"
>
@style/DropDownNav.Shadow
</item>
<item
name=
"android:actionBarStyle"
>
@style/ActionBar.Solid.Shadow
</item>
<item
name=
"android:actionBarStyle"
>
@style/ActionBar.Solid.Shadow
</item>
<item
name=
"android:actionModeBackground"
>
@drawable/cab_background_top_shadow
</item>
<item
name=
"android:actionModeBackground"
>
@drawable/cab_background_top_shadow
</item>
<item
name=
"android:actionModeSplitBackground"
>
@drawable/cab_background_bottom_shadow
</item>
<item
name=
"android:actionModeSplitBackground"
>
@drawable/cab_background_bottom_shadow
</item>
<item
name=
"android:actionModeCloseButtonStyle"
>
@style/ActionButton.CloseMode.Shadow
</item>
<item
name=
"android:actionModeCloseButtonStyle"
>
@style/ActionButton.CloseMode.Shadow
</item>
<!-- Light.DarkActionBar specific -->
<!-- Light.DarkActionBar specific -->
<item
name=
"android:actionBarWidgetTheme"
>
@style/Theme.Shadow.Widget
</item>
<item
name=
"android:actionBarWidgetTheme"
>
@style/Theme.Shadow.Widget
</item>
</style>
</style>
<style
name=
"ActionBar.Solid.Shadow"
parent=
"@style/Widget.Sherlock.Light.ActionBar.Solid.Inverse"
>
<style
name=
"ActionBar.Solid.Shadow"
<item
name=
"android:background"
>
@drawable/ab_solid_shadow
</item>
parent=
"@style/Widget.Sherlock.Light.ActionBar.Solid.Inverse"
>
<item
name=
"android:backgroundStacked"
>
@drawable/ab_stacked_solid_shadow
</item>
<item
name=
"android:background"
>
@drawable/ab_solid_shadow
</item>
<item
name=
"android:backgroundSplit"
>
@drawable/ab_bottom_solid_shadow
</item>
<item
name=
"android:backgroundStacked"
>
@drawable/ab_stacked_solid_shadow
</item>
<item
name=
"android:progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
<item
name=
"android:backgroundSplit"
>
@drawable/ab_bottom_solid_shadow
</item>
</style>
<item
name=
"android:progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<style
name=
"ActionBar.Transparent.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar"
>
<item
name=
"android:background"
>
@drawable/ab_transparent_shadow
</item>
<style
name=
"ActionBar.Transparent.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar"
>
<item
name=
"android:progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
<item
name=
"android:background"
>
@drawable/ab_transparent_shadow
</item>
</style>
<item
name=
"android:progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<!-- this style is only referenced in a Light.DarkActionBar based theme -->
<style
name=
"Theme.Shadow.Widget"
parent=
"@style/Theme.Sherlock"
>
<!-- this style is only referenced in a Light.DarkActionBar based theme -->
<item
name=
"android:popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<style
name=
"Theme.Shadow.Widget"
parent=
"@style/Theme.Sherlock"
>
<item
name=
"android:dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
<item
name=
"android:popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
</style>
<item
name=
"android:dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
</style>
</resources>
</resources>
\ No newline at end of file
res/values/colors.xml
View file @
3d664377
...
@@ -16,11 +16,11 @@
...
@@ -16,11 +16,11 @@
<resources>
<resources>
<color
name=
"title_background"
>
#ff5e5e5e
</color>
<color
name=
"title_background"
>
#ff5e5e5e
</color>
<color
name=
"dialog_title_background"
>
#17170443
</color>
<color
name=
"dialog_title_background"
>
#17170443
</color>
<color
name=
"black"
>
#000000
</color>
<color
name=
"black"
>
#000000
</color>
<color
name=
"grey1"
>
#646464
</color>
<color
name=
"grey1"
>
#646464
</color>
<color
name=
"grey2"
>
#c8c8c8
</color>
<color
name=
"grey2"
>
#c8c8c8
</color>
<color
name=
"grey3"
>
#323232
</color>
<color
name=
"grey3"
>
#323232
</color>
</resources>
</resources>
\ No newline at end of file
res/values/strings.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources>
<string
name=
"app_name"
>
shadowsocks
</string>
<string
name=
"app_name"
>
shadowsocks
</string>
<!-- function category -->
<!-- function category -->
<string
name=
"function_cat"
>
Service Controller
</string>
<string
name=
"function_cat"
>
Service Controller
</string>
<string
name=
"service_controller"
>
Proxy Switch
</string>
<string
name=
"service_controller"
>
Proxy Switch
</string>
<string
name=
"service_summary"
>
Enable / Disable Proxy
</string>
<string
name=
"service_summary"
>
Enable / Disable Proxy
</string>
<!-- proxy category -->
<!-- proxy category -->
<string
name=
"proxy_cat"
>
Shadowsocks Settings
</string>
<string
name=
"proxy_cat"
>
Shadowsocks Settings
</string>
<string
name=
"proxy"
>
Server
</string>
<string
name=
"proxy"
>
Server
</string>
<string
name=
"proxy_summary"
>
Hostname of your Shadowsocks server
</string>
<string
name=
"proxy_summary"
>
Hostname of your Shadowsocks server
</string>
<string
name=
"remote_port"
>
Remote Port
</string>
<string
name=
"remote_port"
>
Remote Port
</string>
<string
name=
"remote_port_summary"
>
Remote port of shadowsocks server.
</string>
<string
name=
"remote_port_summary"
>
Remote port of shadowsocks server.
</string>
<string
name=
"port"
>
Local Port
</string>
<string
name=
"port"
>
Local Port
</string>
<string
name=
"port_summary"
>
Local port listening on
</string>
<string
name=
"port_summary"
>
Local port listening on
</string>
<string
name=
"sitekey"
>
Password
</string>
<string
name=
"sitekey"
>
Password
</string>
<string
name=
"sitekey_summary"
>
Password of your Shadowsocks server
</string>
<string
name=
"sitekey_summary"
>
Password of your Shadowsocks server
</string>
<string
name=
"enc_method"
>
Encrypt Method
</string>
<string
name=
"enc_method"
>
Encrypt Method
</string>
<array
name=
"enc_method_entry"
>
<array
name=
"enc_method_entry"
>
<item>
Table
</item>
<item>
Table
</item>
<item>
RC4
</item>
<item>
RC4
</item>
</array>
</array>
<array
name=
"enc_method_value"
>
<array
name=
"enc_method_value"
>
<item>
table
</item>
<item>
table
</item>
<item>
rc4
</item>
<item>
rc4
</item>
</array>
</array>
<!-- feature category -->
<!-- feature category -->
<string
name=
"feature_cat"
>
Feature Settings
</string>
<string
name=
"feature_cat"
>
Feature Settings
</string>
<string
name=
"http_proxy"
>
HTTP Proxy
</string>
<string
name=
"http_proxy"
>
HTTP Proxy
</string>
<string
name=
"http_proxy_summary"
>
Local HTTP Proxy
</string>
<string
name=
"http_proxy_summary"
>
Local HTTP Proxy
</string>
<string
name=
"dns_proxy"
>
DNS Proxy
</string>
<string
name=
"dns_proxy"
>
DNS Proxy
</string>
<string
name=
"dns_proxy_summary"
>
Redirect DNS queries to remote server.
</string>
<string
name=
"dns_proxy_summary"
>
Redirect DNS queries to remote server.
</string>
<string
name=
"auto_set_gfwlist"
>
CHN Route
</string>
<string
name=
"auto_set_gfwlist"
>
CHN Route
</string>
<string
name=
"auto_set_gfwlist_summary"
>
Bypass all sites located in China
<string
name=
"auto_set_gfwlist_summary"
>
Bypass all sites located in China
</string>
</string>
<string
name=
"auto_set_proxy"
>
Global Proxy
</string>
<string
name=
"auto_set_proxy"
>
Global Proxy
</string>
<string
name=
"auto_set_proxy_summary"
>
Set up system wide proxy
<string
name=
"auto_set_proxy_summary"
>
Set up system wide proxy
</string>
</string>
<string
name=
"proxied_apps"
>
Individual Proxy
</string>
<string
name=
"proxied_apps"
>
Individual Proxy
</string>
<string
name=
"proxied_apps_summary"
>
Set individual proxy for apps
<string
name=
"proxied_apps_summary"
>
Set individual proxy for apps
</string>
</string>
<string
name=
"proxied_help"
>
Select apps to use with Shadowsocks:
</string>
<string
name=
"proxied_help"
>
Select apps to use with Shadowsocks:
</string>
<string
name=
"bypass_apps"
>
Bypass Mode
</string>
<string
name=
"bypass_apps"
>
Bypass Mode
</string>
<string
name=
"bypass_apps_summary"
>
Enable this option to bypass selected apps
</string>
<string
name=
"bypass_apps_summary"
>
Enable this option to bypass selected apps
</string>
<string
name=
"auto_connect"
>
Auto Connect
</string>
<string
name=
"auto_connect"
>
Auto Connect
</string>
<string
name=
"auto_connect_summary"
>
Start Shadowsocks at the startup
</string>
<string
name=
"auto_connect_summary"
>
Start Shadowsocks at the startup
</string>
<!-- notification category -->
<!-- notification category -->
<string
name=
"forward_success"
>
Shadowsocks started
</string>
<string
name=
"forward_success"
>
Shadowsocks started
</string>
<string
name=
"service_running"
>
Running in the background
</string>
<string
name=
"service_running"
>
Running in the background
</string>
<string
name=
"forward_fail"
>
Shadowsocks failed to start
</string>
<string
name=
"forward_fail"
>
Shadowsocks failed to start
</string>
<string
name=
"service_stopped"
>
Shadowsocks stopped
</string>
<string
name=
"service_stopped"
>
Shadowsocks stopped
</string>
<string
name=
"forward_stop"
>
Shadowsocks stopped
</string>
<string
name=
"forward_stop"
>
Shadowsocks stopped
</string>
<string
name=
"service_failed"
>
Please check your network status and try again.
</string>
<string
name=
"service_failed"
>
Please check your network status and try again.
</string>
<!-- alert category -->
<!-- alert category -->
<string
name=
"ok_iknow"
>
OK
</string>
<string
name=
"ok_iknow"
>
OK
</string>
<string
name=
"port_alert"
>
The port number should be greater than 1024
</string>
<string
name=
"port_alert"
>
The port number should be greater than 1024
</string>
<string
name=
"port_empty"
>
Port should not be empty
</string>
<string
name=
"port_empty"
>
Port should not be empty
</string>
<string
name=
"proxy_empty"
>
Proxy should not be empty
</string>
<string
name=
"proxy_empty"
>
Proxy should not be empty
</string>
<string
name=
"crash_alert"
>
An unexpected exit detected, context has been recovered
<string
name=
"crash_alert"
>
An unexpected exit detected, context has been recovered
</string>
</string>
<string
name=
"connecting"
>
Connecting…
</string>
<string
name=
"connecting"
>
Connecting…
</string>
<string
name=
"initializing"
>
Initializing…
</string>
<string
name=
"initializing"
>
Initializing…
</string>
<string
name=
"recovering"
>
Reseting…
</string>
<string
name=
"recovering"
>
Reseting…
</string>
<string
name=
"loading"
>
Loading app list…
</string>
<string
name=
"loading"
>
Loading app list…
</string>
<!-- menu category -->
<!-- menu category -->
<string
name=
"recovery"
>
Reset
</string>
<string
name=
"recovery"
>
Reset
</string>
<string
name=
"about"
>
About
</string>
<string
name=
"about"
>
About
</string>
<string
name=
"copy_rights"
>
Shadowsocks is an open source socks proxy.\n\nYou can find more details
<string
name=
"copy_rights"
>
Shadowsocks is an open source socks proxy.\n\nYou can find more details
here:\ngithub.com/shadowsocks
here:\ngithub.com/shadowsocks
</string>
</string>
<!-- array category -->
<!-- array category -->
<string-array
name=
"chn_list"
>
<string-array
name=
"chn_list"
>
<item>
0.0.0.0/7
</item>
<item>
0.0.0.0/7
</item>
<item>
10.0.0.0/8
</item>
<item>
10.0.0.0/8
</item>
<item>
14.0.0.0/8
</item>
<item>
14.0.0.0/8
</item>
<item>
27.0.0.0/8
</item>
<item>
27.0.0.0/8
</item>
<item>
36.0.0.0/8
</item>
<item>
36.0.0.0/8
</item>
<item>
39.0.0.0/8
</item>
<item>
39.0.0.0/8
</item>
<item>
42.0.0.0/7
</item>
<item>
42.0.0.0/7
</item>
<item>
49.0.0.0/8
</item>
<item>
49.0.0.0/8
</item>
<item>
58.0.0.0/7
</item>
<item>
58.0.0.0/7
</item>
<item>
60.0.0.0/7
</item>
<item>
60.0.0.0/7
</item>
<item>
101.0.0.0/8
</item>
<item>
101.0.0.0/8
</item>
<item>
103.0.0.0/8
</item>
<item>
103.0.0.0/8
</item>
<item>
106.0.0.0/8
</item>
<item>
106.0.0.0/8
</item>
<item>
110.0.0.0/7
</item>
<item>
110.0.0.0/7
</item>
<item>
112.0.0.0/4
</item>
<item>
112.0.0.0/4
</item>
<item>
134.128.0.0/9
</item>
<item>
134.128.0.0/9
</item>
<item>
139.0.0.0/9
</item>
<item>
139.0.0.0/9
</item>
<item>
139.128.0.0/9
</item>
<item>
139.128.0.0/9
</item>
<item>
140.0.0.0/9
</item>
<item>
140.0.0.0/9
</item>
<item>
140.128.0.0/9
</item>
<item>
140.128.0.0/9
</item>
<item>
144.0.0.0/9
</item>
<item>
144.0.0.0/9
</item>
<item>
144.128.0.0/9
</item>
<item>
144.128.0.0/9
</item>
<item>
150.0.0.0/8
</item>
<item>
150.0.0.0/8
</item>
<item>
153.0.0.0/8
</item>
<item>
153.0.0.0/8
</item>
<item>
157.0.0.0/9
</item>
<item>
157.0.0.0/9
</item>
<item>
157.128.0.0/9
</item>
<item>
157.128.0.0/9
</item>
<item>
159.128.0.0/9
</item>
<item>
159.128.0.0/9
</item>
<item>
161.128.0.0/9
</item>
<item>
161.128.0.0/9
</item>
<item>
162.0.0.0/9
</item>
<item>
162.0.0.0/9
</item>
<item>
163.0.0.0/8
</item>
<item>
163.0.0.0/8
</item>
<item>
166.0.0.0/9
</item>
<item>
166.0.0.0/9
</item>
<item>
167.128.0.0/9
</item>
<item>
167.128.0.0/9
</item>
<item>
168.128.0.0/9
</item>
<item>
168.128.0.0/9
</item>
<item>
171.0.0.0/8
</item>
<item>
171.0.0.0/8
</item>
<item>
172.0.0.0/12
</item>
<item>
172.0.0.0/12
</item>
<item>
175.0.0.0/8
</item>
<item>
175.0.0.0/8
</item>
<item>
180.0.0.0/8
</item>
<item>
180.0.0.0/8
</item>
<item>
182.0.0.0/7
</item>
<item>
182.0.0.0/7
</item>
<item>
192.112.0.0/12
</item>
<item>
192.112.0.0/12
</item>
<item>
192.128.0.0/9
</item>
<item>
192.128.0.0/9
</item>
<item>
202.0.0.0/7
</item>
<item>
202.0.0.0/7
</item>
<item>
210.0.0.0/7
</item>
<item>
210.0.0.0/7
</item>
<item>
218.0.0.0/7
</item>
<item>
218.0.0.0/7
</item>
<item>
220.0.0.0/6
</item>
<item>
220.0.0.0/6
</item>
</string-array>
</string-array>
</resources>
</resources>
res/values/styles_shadow.xml
View file @
3d664377
...
@@ -19,64 +19,66 @@
...
@@ -19,64 +19,66 @@
<resources>
<resources>
<style
name=
"Theme.Shadow"
parent=
"@style/Theme.Sherlock.Light.DarkActionBar"
>
<style
name=
"Theme.Shadow"
parent=
"@style/Theme.Sherlock.Light.DarkActionBar"
>
<item
name=
"actionBarItemBackground"
>
@drawable/selectable_background_shadow
</item>
<item
name=
"actionBarItemBackground"
>
@drawable/selectable_background_shadow
</item>
<item
name=
"popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
<item
name=
"dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
<item
name=
"actionBarTabStyle"
>
@style/ActionBarTabStyle.Shadow
</item>
<item
name=
"actionBarTabStyle"
>
@style/ActionBarTabStyle.Shadow
</item>
<item
name=
"actionDropDownStyle"
>
@style/DropDownNav.Shadow
</item>
<item
name=
"actionDropDownStyle"
>
@style/DropDownNav.Shadow
</item>
<item
name=
"actionBarStyle"
>
@style/ActionBar.Solid.Shadow
</item>
<item
name=
"actionBarStyle"
>
@style/ActionBar.Solid.Shadow
</item>
<item
name=
"actionModeBackground"
>
@drawable/cab_background_top_shadow
</item>
<item
name=
"actionModeBackground"
>
@drawable/cab_background_top_shadow
</item>
<item
name=
"actionModeSplitBackground"
>
@drawable/cab_background_bottom_shadow
</item>
<item
name=
"actionModeSplitBackground"
>
@drawable/cab_background_bottom_shadow
</item>
<item
name=
"actionModeCloseButtonStyle"
>
@style/ActionButton.CloseMode.Shadow
</item>
<item
name=
"actionModeCloseButtonStyle"
>
@style/ActionButton.CloseMode.Shadow
</item>
<!-- Light.DarkActionBar specific -->
<!-- Light.DarkActionBar specific -->
<item
name=
"actionBarWidgetTheme"
>
@style/Theme.Shadow.Widget
</item>
<item
name=
"actionBarWidgetTheme"
>
@style/Theme.Shadow.Widget
</item>
</style>
</style>
<style
name=
"ActionBar.Solid.Shadow"
parent=
"@style/Widget.Sherlock.Light.ActionBar.Solid.Inverse"
>
<style
name=
"ActionBar.Solid.Shadow"
<item
name=
"background"
>
@drawable/ab_solid_shadow
</item>
parent=
"@style/Widget.Sherlock.Light.ActionBar.Solid.Inverse"
>
<item
name=
"backgroundStacked"
>
@drawable/ab_stacked_solid_shadow
</item>
<item
name=
"background"
>
@drawable/ab_solid_shadow
</item>
<item
name=
"backgroundSplit"
>
@drawable/ab_bottom_solid_shadow
</item>
<item
name=
"backgroundStacked"
>
@drawable/ab_stacked_solid_shadow
</item>
<item
name=
"progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
<item
name=
"backgroundSplit"
>
@drawable/ab_bottom_solid_shadow
</item>
</style>
<item
name=
"progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<style
name=
"ActionBar.Transparent.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar"
>
<item
name=
"background"
>
@drawable/ab_transparent_shadow
</item>
<style
name=
"ActionBar.Transparent.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar"
>
<item
name=
"progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
<item
name=
"background"
>
@drawable/ab_transparent_shadow
</item>
</style>
<item
name=
"progressBarStyle"
>
@style/ProgressBar.Shadow
</item>
</style>
<style
name=
"PopupMenu.Shadow"
parent=
"@style/Widget.Sherlock.ListPopupWindow"
>
<item
name=
"android:popupBackground"
>
@drawable/menu_dropdown_panel_shadow
</item>
<style
name=
"PopupMenu.Shadow"
parent=
"@style/Widget.Sherlock.ListPopupWindow"
>
</style>
<item
name=
"android:popupBackground"
>
@drawable/menu_dropdown_panel_shadow
</item>
</style>
<style
name=
"DropDownListView.Shadow"
parent=
"@style/Widget.Sherlock.ListView.DropDown"
>
<item
name=
"android:listSelector"
>
@drawable/selectable_background_shadow
</item>
<style
name=
"DropDownListView.Shadow"
parent=
"@style/Widget.Sherlock.ListView.DropDown"
>
</style>
<item
name=
"android:listSelector"
>
@drawable/selectable_background_shadow
</item>
</style>
<style
name=
"ActionBarTabStyle.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar.TabView"
>
<item
name=
"android:background"
>
@drawable/tab_indicator_ab_shadow
</item>
<style
name=
"ActionBarTabStyle.Shadow"
parent=
"@style/Widget.Sherlock.ActionBar.TabView"
>
</style>
<item
name=
"android:background"
>
@drawable/tab_indicator_ab_shadow
</item>
</style>
<style
name=
"DropDownNav.Shadow"
parent=
"@style/Widget.Sherlock.Spinner.DropDown.ActionBar"
>
<item
name=
"android:background"
>
@drawable/spinner_background_ab_shadow
</item>
<style
name=
"DropDownNav.Shadow"
parent=
"@style/Widget.Sherlock.Spinner.DropDown.ActionBar"
>
<item
name=
"android:popupBackground"
>
@drawable/menu_dropdown_panel_shadow
</item>
<item
name=
"android:background"
>
@drawable/spinner_background_ab_shadow
</item>
<item
name=
"android:dropDownSelector"
>
@drawable/selectable_background_shadow
</item>
<item
name=
"android:popupBackground"
>
@drawable/menu_dropdown_panel_shadow
</item>
</style>
<item
name=
"android:dropDownSelector"
>
@drawable/selectable_background_shadow
</item>
</style>
<style
name=
"ProgressBar.Shadow"
parent=
"@style/Widget.Sherlock.ProgressBar.Horizontal"
>
<item
name=
"android:progressDrawable"
>
@drawable/progress_horizontal_shadow
</item>
<style
name=
"ProgressBar.Shadow"
parent=
"@style/Widget.Sherlock.ProgressBar.Horizontal"
>
</style>
<item
name=
"android:progressDrawable"
>
@drawable/progress_horizontal_shadow
</item>
</style>
<style
name=
"ActionButton.CloseMode.Shadow"
parent=
"@style/Widget.Sherlock.ActionButton.CloseMode"
>
<item
name=
"android:background"
>
@drawable/btn_cab_done_shadow
</item>
<style
name=
"ActionButton.CloseMode.Shadow"
</style>
parent=
"@style/Widget.Sherlock.ActionButton.CloseMode"
>
<item
name=
"android:background"
>
@drawable/btn_cab_done_shadow
</item>
<!-- this style is only referenced in a Light.DarkActionBar based theme -->
</style>
<style
name=
"Theme.Shadow.Widget"
parent=
"@style/Theme.Sherlock"
>
<item
name=
"popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<!-- this style is only referenced in a Light.DarkActionBar based theme -->
<item
name=
"dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
<style
name=
"Theme.Shadow.Widget"
parent=
"@style/Theme.Sherlock"
>
</style>
<item
name=
"popupMenuStyle"
>
@style/PopupMenu.Shadow
</item>
<item
name=
"dropDownListViewStyle"
>
@style/DropDownListView.Shadow
</item>
</style>
</resources>
</resources>
\ No newline at end of file
res/xml/pref_feature.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<PreferenceScreen
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<CheckBoxPreference
<CheckBoxPreference
android:defaultValue=
"false"
android:defaultValue=
"false"
android:key=
"isHTTPProxy"
android:key=
"isHTTPProxy"
android:summary=
"@string/http_proxy_summary"
android:summary=
"@string/http_proxy_summary"
android:title=
"@string/http_proxy"
>
android:title=
"@string/http_proxy"
>
</CheckBoxPreference>
</CheckBoxPreference>
<CheckBoxPreference
<CheckBoxPreference
android:defaultValue=
"true"
android:defaultValue=
"true"
android:key=
"isDNSProxy"
android:key=
"isDNSProxy"
android:summary=
"@string/dns_proxy_summary"
android:summary=
"@string/dns_proxy_summary"
android:title=
"@string/dns_proxy"
>
android:title=
"@string/dns_proxy"
>
</CheckBoxPreference>
</CheckBoxPreference>
<CheckBoxPreference
<CheckBoxPreference
android:defaultValue=
"true"
android:defaultValue=
"true"
android:key=
"isGFWList"
android:key=
"isGFWList"
android:summary=
"@string/auto_set_gfwlist_summary"
android:summary=
"@string/auto_set_gfwlist_summary"
android:title=
"@string/auto_set_gfwlist"
>
android:title=
"@string/auto_set_gfwlist"
>
</CheckBoxPreference>
</CheckBoxPreference>
<CheckBoxPreference
<CheckBoxPreference
android:defaultValue=
"true"
android:defaultValue=
"true"
android:key=
"isGlobalProxy"
android:key=
"isGlobalProxy"
android:disableDependentsState=
"true"
android:disableDependentsState=
"true"
android:summary=
"@string/auto_set_proxy_summary"
android:summary=
"@string/auto_set_proxy_summary"
android:title=
"@string/auto_set_proxy"
>
android:title=
"@string/auto_set_proxy"
>
</CheckBoxPreference>
</CheckBoxPreference>
<CheckBoxPreference
<CheckBoxPreference
android:defaultValue=
"false"
android:defaultValue=
"false"
android:dependency=
"isGlobalProxy"
android:dependency=
"isGlobalProxy"
android:key=
"isBypassApps"
android:key=
"isBypassApps"
android:summary=
"@string/bypass_apps_summary"
android:summary=
"@string/bypass_apps_summary"
android:title=
"@string/bypass_apps"
>
android:title=
"@string/bypass_apps"
>
</CheckBoxPreference>
</CheckBoxPreference>
<Preference
<Preference
android:key=
"proxyedApps"
android:key=
"proxyedApps"
android:dependency=
"isGlobalProxy"
android:dependency=
"isGlobalProxy"
android:summary=
"@string/proxied_apps_summary"
android:summary=
"@string/proxied_apps_summary"
android:title=
"@string/proxied_apps"
>
android:title=
"@string/proxied_apps"
>
<intent
android:action=
"com.github.shadowsocks.AppManager"
/>
<intent
android:action=
"com.github.shadowsocks.AppManager"
/>
</Preference>
</Preference>
<CheckBoxPreference
<CheckBoxPreference
android:key=
"isAutoConnect"
android:key=
"isAutoConnect"
android:summary=
"@string/auto_connect_summary"
android:summary=
"@string/auto_connect_summary"
android:title=
"@string/auto_connect"
>
android:title=
"@string/auto_connect"
>
</CheckBoxPreference>
</CheckBoxPreference>
</PreferenceScreen>
</PreferenceScreen>
res/xml/pref_proxy.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<PreferenceScreen
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"198.199.101.152"
android:defaultValue=
"198.199.101.152"
android:key=
"proxy"
android:key=
"proxy"
android:summary=
"@string/proxy_summary"
android:summary=
"@string/proxy_summary"
android:title=
"@string/proxy"
>
android:title=
"@string/proxy"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"443"
android:defaultValue=
"443"
android:key=
"remotePort"
android:key=
"remotePort"
android:summary=
"@string/remote_port_summary"
android:summary=
"@string/remote_port_summary"
android:title=
"@string/remote_port"
>
android:title=
"@string/remote_port"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"1080"
android:defaultValue=
"1080"
android:key=
"port"
android:key=
"port"
android:summary=
"@string/port_summary"
android:summary=
"@string/port_summary"
android:title=
"@string/port"
>
android:title=
"@string/port"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue=
"u1rRWTssNv0p"
android:defaultValue=
"u1rRWTssNv0p"
android:key=
"sitekey"
android:key=
"sitekey"
android:summary=
"@string/sitekey_summary"
android:summary=
"@string/sitekey_summary"
android:title=
"@string/sitekey"
>
android:title=
"@string/sitekey"
>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<ListPreference
<ListPreference
android:defaultValue=
"rc4"
android:defaultValue=
"rc4"
android:key=
"encMethod"
android:key=
"encMethod"
android:entries=
"@array/enc_method_entry"
android:entries=
"@array/enc_method_entry"
android:entryValues=
"@array/enc_method_value"
android:entryValues=
"@array/enc_method_value"
android:title=
"@string/enc_method"
>
android:title=
"@string/enc_method"
>
</ListPreference>
</ListPreference>
</PreferenceScreen>
</PreferenceScreen>
res/xml/shadowsocks_headers.xml
View file @
3d664377
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<preference-headers
xmlns:android=
"http://schemas.android.com/apk/res/android"
<preference-headers
xmlns:unified=
"http://schemas.android.com/apk/res-auto"
>
xmlns:unified=
"http://schemas.android.com/apk/res-auto"
>
<header
<header
unified:fragment=
"com.github.shadowsocks.Shadowsocks$ProxyFragment"
unified:fragment=
"com.github.shadowsocks.Shadowsocks$ProxyFragment"
unified:title=
"@string/proxy_cat"
unified:title=
"@string/proxy_cat"
unified:preferenceRes=
"@xml/pref_proxy"
/>
unified:preferenceRes=
"@xml/pref_proxy"
/>
<header
<header
unified:fragment=
"com.github.shadowsocks.Shadowsocks$FeatureFragment"
unified:fragment=
"com.github.shadowsocks.Shadowsocks$FeatureFragment"
unified:title=
"@string/feature_cat"
unified:title=
"@string/feature_cat"
unified:preferenceRes=
"@xml/pref_feature"
/>
unified:preferenceRes=
"@xml/pref_feature"
/>
</preference-headers>
</preference-headers>
\ No newline at end of file
src/com/github/shadowsocks/AppManager.java
View file @
3d664377
...
@@ -21,388 +21,362 @@ import android.view.View.OnClickListener;
...
@@ -21,388 +21,362 @@ import android.view.View.OnClickListener;
import
android.view.ViewGroup
;
import
android.view.ViewGroup
;
import
android.view.ViewGroup.LayoutParams
;
import
android.view.ViewGroup.LayoutParams
;
import
android.view.WindowManager
;
import
android.view.WindowManager
;
import
android.widget.
*
;
import
android.widget.
AbsListView
;
import
android.widget.AbsListView.OnScrollListener
;
import
android.widget.AbsListView.OnScrollListener
;
import
android.widget.ArrayAdapter
;
import
android.widget.CheckBox
;
import
android.widget.CompoundButton
;
import
android.widget.CompoundButton.OnCheckedChangeListener
;
import
android.widget.CompoundButton.OnCheckedChangeListener
;
import
android.widget.ImageView
;
import
android.widget.ListAdapter
;
import
android.widget.ListView
;
import
android.widget.TextView
;
import
com.actionbarsherlock.app.SherlockActivity
;
import
com.actionbarsherlock.app.SherlockActivity
;
import
java.util.Arrays
;
import
java.util.Comparator
;
import
java.util.Iterator
;
import
java.util.List
;
import
java.util.StringTokenizer
;
import
java.util.Vector
;
public
class
AppManager
extends
SherlockActivity
implements
OnCheckedChangeListener
,
OnClickListener
{
public
final
static
String
PREFS_KEY_PROXYED
=
"Proxyed"
;
private
static
final
int
MSG_LOAD_START
=
1
;
private
static
final
int
MSG_LOAD_FINISH
=
2
;
final
Handler
handler
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
switch
(
msg
.
what
)
{
case
MSG_LOAD_START:
pd
=
ProgressDialog
.
show
(
AppManager
.
this
,
""
,
getString
(
R
.
string
.
loading
),
true
,
true
);
break
;
case
MSG_LOAD_FINISH:
import
java.util.*
;
listApps
.
setAdapter
(
adapter
);
public
class
AppManager
extends
SherlockActivity
implements
OnCheckedChangeListener
,
OnClickListener
{
public
final
static
String
PREFS_KEY_PROXYED
=
"Proxyed"
;
private
static
final
int
MSG_LOAD_START
=
1
;
private
static
final
int
MSG_LOAD_FINISH
=
2
;
final
Handler
handler
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
switch
(
msg
.
what
)
{
case
MSG_LOAD_START:
pd
=
ProgressDialog
.
show
(
AppManager
.
this
,
""
,
getString
(
R
.
string
.
loading
),
true
,
true
);
break
;
case
MSG_LOAD_FINISH:
listApps
.
setAdapter
(
adapter
);
listApps
.
setOnScrollListener
(
new
OnScrollListener
()
{
boolean
visible
;
@Override
public
void
onScroll
(
AbsListView
view
,
int
firstVisibleItem
,
int
visibleItemCount
,
int
totalItemCount
)
{
if
(
visible
)
{
String
name
=
apps
[
firstVisibleItem
].
getName
();
if
(
name
!=
null
&&
name
.
length
()
>
1
)
overlay
.
setText
(
apps
[
firstVisibleItem
]
.
getName
().
substring
(
0
,
1
));
else
overlay
.
setText
(
"*"
);
overlay
.
setVisibility
(
View
.
VISIBLE
);
}
}
@Override
public
void
onScrollStateChanged
(
AbsListView
view
,
int
scrollState
)
{
visible
=
true
;
if
(
scrollState
==
OnScrollListener
.
SCROLL_STATE_IDLE
)
{
overlay
.
setVisibility
(
View
.
INVISIBLE
);
}
}
});
if
(
pd
!=
null
)
{
pd
.
dismiss
();
pd
=
null
;
}
break
;
}
super
.
handleMessage
(
msg
);
}
};
private
ProxyedApp
[]
apps
=
null
;
private
ListView
listApps
;
private
AppManager
mAppManager
;
private
TextView
overlay
;
private
ProgressDialog
pd
=
null
;
private
ListAdapter
adapter
;
private
ImageLoader
dm
;
private
boolean
appsLoaded
=
false
;
public
static
ProxyedApp
[]
getProxyedApps
(
Context
context
)
{
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
String
tordAppString
=
prefs
.
getString
(
PREFS_KEY_PROXYED
,
""
);
String
[]
tordApps
;
StringTokenizer
st
=
new
StringTokenizer
(
tordAppString
,
"|"
);
tordApps
=
new
String
[
st
.
countTokens
()];
int
tordIdx
=
0
;
while
(
st
.
hasMoreTokens
())
{
tordApps
[
tordIdx
++]
=
st
.
nextToken
();
}
Arrays
.
sort
(
tordApps
);
// else load the apps up
PackageManager
pMgr
=
context
.
getPackageManager
();
List
<
ApplicationInfo
>
lAppInfo
=
pMgr
.
getInstalledApplications
(
0
);
Iterator
<
ApplicationInfo
>
itAppInfo
=
lAppInfo
.
iterator
();
Vector
<
ProxyedApp
>
vectorApps
=
new
Vector
<
ProxyedApp
>();
ApplicationInfo
aInfo
=
null
;
while
(
itAppInfo
.
hasNext
())
{
aInfo
=
itAppInfo
.
next
();
// ignore system apps
if
(
aInfo
.
uid
<
10000
)
continue
;
ProxyedApp
app
=
new
ProxyedApp
();
app
.
setUid
(
aInfo
.
uid
);
listApps
.
setOnScrollListener
(
new
OnScrollListener
()
{
app
.
setUsername
(
pMgr
.
getNameForUid
(
app
.
getUid
()))
;
boolean
visible
;
// check if this application is allowed
@Override
if
(
aInfo
.
packageName
!=
null
public
void
onScroll
(
AbsListView
view
,
int
firstVisibleItem
,
int
visibleItemCount
,
&&
aInfo
.
packageName
.
equals
(
"com.github.shadowsocks"
))
{
int
totalItemCount
)
{
app
.
setProxyed
(
true
);
if
(
visible
)
{
}
else
if
(
Arrays
.
binarySearch
(
tordApps
,
app
.
getUsername
())
>=
0
)
{
String
name
=
apps
[
firstVisibleItem
].
getName
();
app
.
setProxyed
(
true
);
if
(
name
!=
null
&&
name
.
length
()
>
1
)
{
}
else
{
overlay
.
setText
(
apps
[
firstVisibleItem
].
getName
().
substring
(
0
,
1
));
app
.
setProxyed
(
false
);
}
else
{
overlay
.
setText
(
"*"
);
}
overlay
.
setVisibility
(
View
.
VISIBLE
);
}
}
}
vectorApps
.
add
(
app
);
@Override
public
void
onScrollStateChanged
(
AbsListView
view
,
int
scrollState
)
{
}
visible
=
true
;
if
(
scrollState
==
OnScrollListener
.
SCROLL_STATE_IDLE
)
{
ProxyedApp
[]
apps
=
new
ProxyedApp
[
vectorApps
.
size
()];
overlay
.
setVisibility
(
View
.
INVISIBLE
);
vectorApps
.
toArray
(
apps
);
}
return
apps
;
}
});
if
(
pd
!=
null
)
{
pd
.
dismiss
();
pd
=
null
;
}
break
;
}
super
.
handleMessage
(
msg
);
}
};
private
ProxyedApp
[]
apps
=
null
;
private
ListView
listApps
;
private
AppManager
mAppManager
;
private
TextView
overlay
;
private
ProgressDialog
pd
=
null
;
private
ListAdapter
adapter
;
private
ImageLoader
dm
;
private
boolean
appsLoaded
=
false
;
public
static
ProxyedApp
[]
getProxyedApps
(
Context
context
)
{
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
String
tordAppString
=
prefs
.
getString
(
PREFS_KEY_PROXYED
,
""
);
String
[]
tordApps
;
StringTokenizer
st
=
new
StringTokenizer
(
tordAppString
,
"|"
);
tordApps
=
new
String
[
st
.
countTokens
()];
int
tordIdx
=
0
;
while
(
st
.
hasMoreTokens
())
{
tordApps
[
tordIdx
++]
=
st
.
nextToken
();
}
}
public
void
getApps
(
Context
context
)
{
Arrays
.
sort
(
tordApps
);
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
String
tordAppString
=
prefs
.
getString
(
PREFS_KEY_PROXYED
,
""
);
// else load the apps up
String
[]
tordApps
;
PackageManager
pMgr
=
context
.
getPackageManager
()
;
StringTokenizer
st
=
new
StringTokenizer
(
tordAppString
,
"|"
);
List
<
ApplicationInfo
>
lAppInfo
=
pMgr
.
getInstalledApplications
(
0
);
tordApps
=
new
String
[
st
.
countTokens
()];
int
tordIdx
=
0
;
while
(
st
.
hasMoreTokens
())
{
tordApps
[
tordIdx
++]
=
st
.
nextToken
();
}
Arrays
.
sort
(
tordApps
);
Iterator
<
ApplicationInfo
>
itAppInfo
=
lAppInfo
.
iterator
(
);
Vector
<
ProxyedApp
>
vectorApps
=
new
Vector
<
ProxyedApp
>();
Vector
<
ProxyedApp
>
vectorApps
=
new
Vector
<
ProxyedApp
>();
// else load the apps up
ApplicationInfo
aInfo
=
null
;
PackageManager
pMgr
=
context
.
getPackageManager
();
List
<
ApplicationInfo
>
lAppInfo
=
pMgr
.
getInstalledApplications
(
0
);
while
(
itAppInfo
.
hasNext
())
{
aInfo
=
itAppInfo
.
next
();
Iterator
<
ApplicationInfo
>
itAppInfo
=
lAppInfo
.
iterator
();
// ignore system apps
if
(
aInfo
.
uid
<
10000
)
continue
;
ApplicationInfo
aInfo
=
null
;
ProxyedApp
app
=
new
ProxyedApp
()
;
while
(
itAppInfo
.
hasNext
())
{
app
.
setUid
(
aInfo
.
uid
);
aInfo
=
itAppInfo
.
next
();
// ignore system apps
app
.
setUsername
(
pMgr
.
getNameForUid
(
app
.
getUid
()));
if
(
aInfo
.
uid
<
10000
)
continue
;
if
(
aInfo
.
processName
==
null
)
// check if this application is allowed
continue
;
if
(
aInfo
.
packageName
!=
null
&&
aInfo
.
packageName
.
equals
(
"com.github.shadowsocks"
))
{
if
(
pMgr
.
getApplicationLabel
(
aInfo
)
==
null
app
.
setProxyed
(
true
);
||
pMgr
.
getApplicationLabel
(
aInfo
).
toString
().
equals
(
""
))
}
else
if
(
Arrays
.
binarySearch
(
tordApps
,
app
.
getUsername
())
>=
0
)
{
continue
;
app
.
setProxyed
(
true
);
if
(
pMgr
.
getApplicationIcon
(
aInfo
)
==
null
)
}
else
{
continue
;
app
.
setProxyed
(
false
);
}
ProxyedApp
tApp
=
new
ProxyedApp
();
vectorApps
.
add
(
app
);
}
tApp
.
setEnabled
(
aInfo
.
enabled
);
ProxyedApp
[]
apps
=
new
ProxyedApp
[
vectorApps
.
size
()];
tApp
.
setUid
(
aInfo
.
uid
);
vectorApps
.
toArray
(
apps
);
tApp
.
setUsername
(
pMgr
.
getNameForUid
(
tApp
.
getUid
()));
return
apps
;
tApp
.
setProcname
(
aInfo
.
processName
);
}
tApp
.
setName
(
pMgr
.
getApplicationLabel
(
aInfo
).
toString
());
// check if this application is allowed
public
void
getApps
(
Context
context
)
{
if
(
Arrays
.
binarySearch
(
tordApps
,
tApp
.
getUsername
())
>=
0
)
{
tApp
.
setProxyed
(
true
);
}
else
{
tApp
.
setProxyed
(
false
);
}
vectorApps
.
add
(
tApp
);
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
}
apps
=
new
ProxyedApp
[
vectorApps
.
size
()]
;
String
tordAppString
=
prefs
.
getString
(
PREFS_KEY_PROXYED
,
""
)
;
vectorApps
.
toArray
(
apps
)
;
String
[]
tordApps
;
StringTokenizer
st
=
new
StringTokenizer
(
tordAppString
,
"|"
);
tordApps
=
new
String
[
st
.
countTokens
()];
int
tordIdx
=
0
;
while
(
st
.
hasMoreTokens
())
{
tordApps
[
tordIdx
++]
=
st
.
nextToken
();
}
}
private
void
loadApps
()
{
Arrays
.
sort
(
tordApps
);
getApps
(
this
);
Arrays
.
sort
(
apps
,
new
Comparator
<
ProxyedApp
>()
{
Vector
<
ProxyedApp
>
vectorApps
=
new
Vector
<
ProxyedApp
>();
@Override
public
int
compare
(
ProxyedApp
o1
,
ProxyedApp
o2
)
{
if
(
o1
==
null
||
o2
==
null
||
o1
.
getName
()
==
null
||
o2
.
getName
()
==
null
)
return
1
;
if
(
o1
.
isProxyed
()
==
o2
.
isProxyed
())
return
o1
.
getName
().
compareTo
(
o2
.
getName
());
if
(
o1
.
isProxyed
())
return
-
1
;
return
1
;
}
});
final
LayoutInflater
inflater
=
getLayoutInflater
();
// else load the apps up
PackageManager
pMgr
=
context
.
getPackageManager
();
adapter
=
new
ArrayAdapter
<
ProxyedApp
>(
this
,
R
.
layout
.
layout_apps_item
,
List
<
ApplicationInfo
>
lAppInfo
=
pMgr
.
getInstalledApplications
(
0
);
R
.
id
.
itemtext
,
apps
)
{
@Override
public
View
getView
(
int
position
,
View
convertView
,
ViewGroup
parent
)
{
ListEntry
entry
;
if
(
convertView
==
null
)
{
// Inflate a new view
convertView
=
inflater
.
inflate
(
R
.
layout
.
layout_apps_item
,
parent
,
false
);
entry
=
new
ListEntry
();
entry
.
icon
=
(
ImageView
)
convertView
.
findViewById
(
R
.
id
.
itemicon
);
entry
.
box
=
(
CheckBox
)
convertView
.
findViewById
(
R
.
id
.
itemcheck
);
entry
.
text
=
(
TextView
)
convertView
.
findViewById
(
R
.
id
.
itemtext
);
entry
.
text
.
setOnClickListener
(
mAppManager
);
entry
.
text
.
setOnClickListener
(
mAppManager
);
convertView
.
setTag
(
entry
);
entry
.
box
.
setOnCheckedChangeListener
(
mAppManager
);
}
else
{
// Convert an existing view
entry
=
(
ListEntry
)
convertView
.
getTag
();
}
final
ProxyedApp
app
=
apps
[
position
]
;
Iterator
<
ApplicationInfo
>
itAppInfo
=
lAppInfo
.
iterator
()
;
entry
.
icon
.
setTag
(
app
.
getUid
())
;
ApplicationInfo
aInfo
=
null
;
dm
.
DisplayImage
(
app
.
getUid
(),
while
(
itAppInfo
.
hasNext
())
{
(
Activity
)
convertView
.
getContext
(),
entry
.
icon
);
aInfo
=
itAppInfo
.
next
(
);
entry
.
text
.
setText
(
app
.
getName
());
// ignore system apps
if
(
aInfo
.
uid
<
10000
)
continue
;
final
CheckBox
box
=
entry
.
box
;
if
(
aInfo
.
processName
==
null
)
continue
;
box
.
setTag
(
app
);
if
(
pMgr
.
getApplicationLabel
(
aInfo
)
==
null
||
pMgr
.
getApplicationLabel
(
aInfo
)
box
.
setChecked
(
app
.
isProxyed
());
.
toString
()
.
equals
(
""
))
{
continue
;
}
if
(
pMgr
.
getApplicationIcon
(
aInfo
)
==
null
)
continue
;
entry
.
text
.
setTag
(
box
);
ProxyedApp
tApp
=
new
ProxyedApp
(
);
return
convertView
;
tApp
.
setEnabled
(
aInfo
.
enabled
);
}
tApp
.
setUid
(
aInfo
.
uid
);
};
tApp
.
setUsername
(
pMgr
.
getNameForUid
(
tApp
.
getUid
()));
tApp
.
setProcname
(
aInfo
.
processName
);
tApp
.
setName
(
pMgr
.
getApplicationLabel
(
aInfo
).
toString
());
appsLoaded
=
true
;
// check if this application is allowed
if
(
Arrays
.
binarySearch
(
tordApps
,
tApp
.
getUsername
())
>=
0
)
{
tApp
.
setProxyed
(
true
);
}
else
{
tApp
.
setProxyed
(
false
);
}
vectorApps
.
add
(
tApp
);
}
}
/**
apps
=
new
ProxyedApp
[
vectorApps
.
size
()];
* Called an application is check/unchecked
vectorApps
.
toArray
(
apps
);
*/
}
@Override
public
void
onCheckedChanged
(
CompoundButton
buttonView
,
boolean
isChecked
)
{
private
void
loadApps
()
{
final
ProxyedApp
app
=
(
ProxyedApp
)
buttonView
.
getTag
();
getApps
(
this
);
if
(
app
!=
null
)
{
app
.
setProxyed
(
isChecked
);
Arrays
.
sort
(
apps
,
new
Comparator
<
ProxyedApp
>()
{
@Override
public
int
compare
(
ProxyedApp
o1
,
ProxyedApp
o2
)
{
if
(
o1
==
null
||
o2
==
null
||
o1
.
getName
()
==
null
||
o2
.
getName
()
==
null
)
return
1
;
if
(
o1
.
isProxyed
()
==
o2
.
isProxyed
())
return
o1
.
getName
().
compareTo
(
o2
.
getName
());
if
(
o1
.
isProxyed
())
return
-
1
;
return
1
;
}
});
final
LayoutInflater
inflater
=
getLayoutInflater
();
adapter
=
new
ArrayAdapter
<
ProxyedApp
>(
this
,
R
.
layout
.
layout_apps_item
,
R
.
id
.
itemtext
,
apps
)
{
@Override
public
View
getView
(
int
position
,
View
convertView
,
ViewGroup
parent
)
{
ListEntry
entry
;
if
(
convertView
==
null
)
{
// Inflate a new view
convertView
=
inflater
.
inflate
(
R
.
layout
.
layout_apps_item
,
parent
,
false
);
entry
=
new
ListEntry
();
entry
.
icon
=
(
ImageView
)
convertView
.
findViewById
(
R
.
id
.
itemicon
);
entry
.
box
=
(
CheckBox
)
convertView
.
findViewById
(
R
.
id
.
itemcheck
);
entry
.
text
=
(
TextView
)
convertView
.
findViewById
(
R
.
id
.
itemtext
);
entry
.
text
.
setOnClickListener
(
mAppManager
);
entry
.
text
.
setOnClickListener
(
mAppManager
);
convertView
.
setTag
(
entry
);
entry
.
box
.
setOnCheckedChangeListener
(
mAppManager
);
}
else
{
// Convert an existing view
entry
=
(
ListEntry
)
convertView
.
getTag
();
}
}
saveAppSettings
(
this
)
;
final
ProxyedApp
app
=
apps
[
position
]
;
}
entry
.
icon
.
setTag
(
app
.
getUid
());
@Override
dm
.
DisplayImage
(
app
.
getUid
(),
(
Activity
)
convertView
.
getContext
(),
entry
.
icon
);
public
void
onClick
(
View
v
)
{
CheckBox
cbox
=
(
CheckBox
)
v
.
getTag
(
);
entry
.
text
.
setText
(
app
.
getName
()
);
final
ProxyedApp
app
=
(
ProxyedApp
)
cbox
.
getTag
();
final
CheckBox
box
=
entry
.
box
;
if
(
app
!=
null
)
{
box
.
setTag
(
app
);
app
.
setProxyed
(!
app
.
isProxyed
());
box
.
setChecked
(
app
.
isProxyed
());
cbox
.
setChecked
(
app
.
isProxyed
());
}
saveAppSettings
(
this
);
entry
.
text
.
setTag
(
box
);
}
return
convertView
;
}
};
@Override
appsLoaded
=
true
;
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
}
super
.
onCreate
(
savedInstanceState
);
this
.
setContentView
(
R
.
layout
.
layout_apps
);
/** Called an application is check/unchecked */
@Override
public
void
onCheckedChanged
(
CompoundButton
buttonView
,
boolean
isChecked
)
{
final
ProxyedApp
app
=
(
ProxyedApp
)
buttonView
.
getTag
();
if
(
app
!=
null
)
{
app
.
setProxyed
(
isChecked
);
}
this
.
dm
=
ImageLoaderFactory
.
getImageLoader
(
this
);
saveAppSettings
(
this
);
}
this
.
overlay
=
(
TextView
)
View
.
inflate
(
this
,
R
.
layout
.
overlay
,
null
);
@Override
getWindowManager
()
public
void
onClick
(
View
v
)
{
.
addView
(
overlay
,
new
WindowManager
.
LayoutParams
(
LayoutParams
.
WRAP_CONTENT
,
LayoutParams
.
WRAP_CONTENT
,
WindowManager
.
LayoutParams
.
TYPE_APPLICATION
,
WindowManager
.
LayoutParams
.
FLAG_NOT_FOCUSABLE
|
WindowManager
.
LayoutParams
.
FLAG_NOT_TOUCHABLE
,
PixelFormat
.
TRANSLUCENT
));
mAppManager
=
this
;
CheckBox
cbox
=
(
CheckBox
)
v
.
getTag
()
;
final
ProxyedApp
app
=
(
ProxyedApp
)
cbox
.
getTag
();
if
(
app
!=
null
)
{
app
.
setProxyed
(!
app
.
isProxyed
());
cbox
.
setChecked
(
app
.
isProxyed
());
}
}
@Override
saveAppSettings
(
this
);
protected
void
onResume
()
{
}
super
.
onResume
();
new
Thread
()
{
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
@Override
this
.
setContentView
(
R
.
layout
.
layout_apps
);
public
void
run
()
{
handler
.
sendEmptyMessage
(
MSG_LOAD_START
);
listApps
=
(
ListView
)
findViewById
(
R
.
id
.
applistview
);
this
.
dm
=
ImageLoaderFactory
.
getImageLoader
(
this
);
if
(!
appsLoaded
)
this
.
overlay
=
(
TextView
)
View
.
inflate
(
this
,
R
.
layout
.
overlay
,
null
);
loadApps
();
getWindowManager
().
addView
(
overlay
,
handler
.
sendEmptyMessage
(
MSG_LOAD_FINISH
);
new
WindowManager
.
LayoutParams
(
LayoutParams
.
WRAP_CONTENT
,
LayoutParams
.
WRAP_CONTENT
,
}
WindowManager
.
LayoutParams
.
TYPE_APPLICATION
,
}.
start
();
WindowManager
.
LayoutParams
.
FLAG_NOT_FOCUSABLE
|
WindowManager
.
LayoutParams
.
FLAG_NOT_TOUCHABLE
,
PixelFormat
.
TRANSLUCENT
));
}
mAppManager
=
this
;
}
/*
@Override
* (non-Javadoc)
protected
void
onResume
()
{
*
super
.
onResume
();
* @see android.app.Activity#onStop()
*/
@Override
protected
void
onStop
()
{
super
.
onStop
();
// Log.d(getClass().getName(),"Exiting Preferences");
new
Thread
()
{
}
public
void
saveAppSettings
(
Context
context
)
{
@Override
if
(
apps
==
null
)
public
void
run
()
{
return
;
handler
.
sendEmptyMessage
(
MSG_LOAD_START
)
;
SharedPreferences
prefs
=
PreferenceManager
listApps
=
(
ListView
)
findViewById
(
R
.
id
.
applistview
);
.
getDefaultSharedPreferences
(
this
);
// final SharedPreferences prefs =
if
(!
appsLoaded
)
loadApps
();
// context.getSharedPreferences(PREFS_KEY, 0);
handler
.
sendEmptyMessage
(
MSG_LOAD_FINISH
);
}
}.
start
();
}
StringBuilder
tordApps
=
new
StringBuilder
();
/*
* (non-Javadoc)
*
* @see android.app.Activity#onStop()
*/
@Override
protected
void
onStop
()
{
super
.
onStop
();
for
(
int
i
=
0
;
i
<
apps
.
length
;
i
++)
{
// Log.d(getClass().getName(),"Exiting Preferences");
if
(
apps
[
i
].
isProxyed
())
{
}
tordApps
.
append
(
apps
[
i
].
getUsername
());
tordApps
.
append
(
"|"
);
}
}
Editor
edit
=
prefs
.
edit
();
public
void
saveAppSettings
(
Context
context
)
{
edit
.
putString
(
PREFS_KEY_PROXYED
,
tordApps
.
toString
());
if
(
apps
==
null
)
return
;
edit
.
commit
();
}
SharedPreferences
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
private
static
class
ListEntry
{
// final SharedPreferences prefs =
private
CheckBox
box
;
// context.getSharedPreferences(PREFS_KEY, 0);
private
TextView
text
;
private
ImageView
icon
;
StringBuilder
tordApps
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
apps
.
length
;
i
++)
{
if
(
apps
[
i
].
isProxyed
())
{
tordApps
.
append
(
apps
[
i
].
getUsername
());
tordApps
.
append
(
"|"
);
}
}
}
Editor
edit
=
prefs
.
edit
();
edit
.
putString
(
PREFS_KEY_PROXYED
,
tordApps
.
toString
());
edit
.
commit
();
}
private
static
class
ListEntry
{
private
CheckBox
box
;
private
TextView
text
;
private
ImageView
icon
;
}
}
}
src/com/github/shadowsocks/Exec.java
View file @
3d664377
...
@@ -27,46 +27,41 @@ import java.io.FileDescriptor;
...
@@ -27,46 +27,41 @@ import java.io.FileDescriptor;
*/
*/
public
class
Exec
{
public
class
Exec
{
static
{
static
{
java
.
lang
.
System
.
loadLibrary
(
"exec"
);
java
.
lang
.
System
.
loadLibrary
(
"exec"
);
}
}
/**
/** Close a given file descriptor. */
* Close a given file descriptor.
public
static
native
void
close
(
FileDescriptor
fd
);
*/
public
static
native
void
close
(
FileDescriptor
fd
);
/**
/**
* Create a subprocess. Differs from java.lang.ProcessBuilder in that a pty
* Create a subprocess. Differs from java.lang.ProcessBuilder in that a pty
* is used to communicate with the subprocess.
* is used to communicate with the subprocess.
* <p/>
* <p/>
* Callers are responsible for calling Exec.close() on the returned file
* Callers are responsible for calling Exec.close() on the returned file
* descriptor.
* descriptor.
*
*
* @param rdt Whether redirect stdout and stderr
* @param rdt Whether redirect stdout and stderr
* @param cmd The command to execute.
* @param cmd The command to execute.
* @param args An array of arguments to the command.
* @param args An array of arguments to the command.
* @param envVars An array of strings of the form "VAR=value" to be added to the
* @param envVars An array of strings of the form "VAR=value" to be added to the
* environment of the process.
* environment of the process.
* @param scripts The scripts to execute.
* @param scripts The scripts to execute.
* @param processId A one-element array to which the process ID of the started
* @param processId A one-element array to which the process ID of the started
* process will be written.
* process will be written.
* @return File descriptor
* @return File descriptor
*/
*/
public
static
native
FileDescriptor
createSubprocess
(
int
rdt
,
String
cmd
,
public
static
native
FileDescriptor
createSubprocess
(
int
rdt
,
String
cmd
,
String
[]
args
,
String
[]
args
,
String
[]
envVars
,
String
[]
envVars
,
String
scripts
,
int
[]
processId
);
String
scripts
,
int
[]
processId
);
/**
/** Send SIGHUP to a process group. */
* Send SIGHUP to a process group.
public
static
native
void
hangupProcessGroup
(
int
processId
);
*/
public
static
native
void
hangupProcessGroup
(
int
processId
);
/**
/**
* Causes the calling thread to wait for the process associated with the
* Causes the calling thread to wait for the process associated with the
* receiver to finish executing.
* receiver to finish executing.
*
*
* @return The exit value of the Process being waited on
* @return The exit value of the Process being waited on
*/
*/
public
static
native
int
waitFor
(
int
processId
);
public
static
native
int
waitFor
(
int
processId
);
}
}
src/com/github/shadowsocks/ImageLoader.java
View file @
3d664377
...
@@ -45,7 +45,6 @@ import android.graphics.Bitmap;
...
@@ -45,7 +45,6 @@ import android.graphics.Bitmap;
import
android.graphics.BitmapFactory
;
import
android.graphics.BitmapFactory
;
import
android.graphics.drawable.BitmapDrawable
;
import
android.graphics.drawable.BitmapDrawable
;
import
android.widget.ImageView
;
import
android.widget.ImageView
;
import
java.io.File
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.FileInputStream
;
import
java.io.FileNotFoundException
;
import
java.io.FileNotFoundException
;
...
@@ -54,195 +53,190 @@ import java.util.Stack;
...
@@ -54,195 +53,190 @@ import java.util.Stack;
public
class
ImageLoader
{
public
class
ImageLoader
{
final
int
stub_id
=
R
.
drawable
.
sym_def_app_icon
;
final
int
stub_id
=
R
.
drawable
.
sym_def_app_icon
;
PhotosQueue
photosQueue
=
new
PhotosQueue
();
PhotosQueue
photosQueue
=
new
PhotosQueue
();
PhotosLoader
photoLoaderThread
=
new
PhotosLoader
();
PhotosLoader
photoLoaderThread
=
new
PhotosLoader
();
// the simplest in-memory cache implementation. This should be replaced with
// the simplest in-memory cache implementation. This should be replaced with
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
private
HashMap
<
Integer
,
Bitmap
>
cache
=
new
HashMap
<
Integer
,
Bitmap
>();
private
HashMap
<
Integer
,
Bitmap
>
cache
=
new
HashMap
<
Integer
,
Bitmap
>();
private
File
cacheDir
;
private
File
cacheDir
;
private
Context
context
;
private
Context
context
;
public
ImageLoader
(
Context
c
)
{
public
ImageLoader
(
Context
c
)
{
// Make the background thead low priority. This way it will not affect
// Make the background thead low priority. This way it will not affect
// the UI performance
// the UI performance
photoLoaderThread
.
setPriority
(
Thread
.
NORM_PRIORITY
-
1
);
photoLoaderThread
.
setPriority
(
Thread
.
NORM_PRIORITY
-
1
);
context
=
c
;
context
=
c
;
// Find the dir to save cached images
// Find the dir to save cached images
cacheDir
=
context
.
getCacheDir
();
cacheDir
=
context
.
getCacheDir
();
}
public
void
clearCache
()
{
// clear memory cache
cache
.
clear
();
// clear SD cache
File
[]
files
=
cacheDir
.
listFiles
();
for
(
File
f
:
files
)
f
.
delete
();
}
// decodes image and scales it to reduce memory consumption
private
Bitmap
decodeFile
(
File
f
)
{
try
{
// decode image size
BitmapFactory
.
Options
o
=
new
BitmapFactory
.
Options
();
o
.
inJustDecodeBounds
=
true
;
BitmapFactory
.
decodeStream
(
new
FileInputStream
(
f
),
null
,
o
);
// Find the correct scale value. It should be the power of 2.
final
int
REQUIRED_SIZE
=
70
;
int
width_tmp
=
o
.
outWidth
,
height_tmp
=
o
.
outHeight
;
int
scale
=
1
;
while
(
true
)
{
if
(
width_tmp
/
2
<
REQUIRED_SIZE
||
height_tmp
/
2
<
REQUIRED_SIZE
)
break
;
width_tmp
/=
2
;
height_tmp
/=
2
;
scale
*=
2
;
}
// decode with inSampleSize
BitmapFactory
.
Options
o2
=
new
BitmapFactory
.
Options
();
o2
.
inSampleSize
=
scale
;
return
BitmapFactory
.
decodeStream
(
new
FileInputStream
(
f
),
null
,
o2
);
}
catch
(
FileNotFoundException
ignored
)
{
}
}
return
null
;
public
void
clearCache
()
{
}
// clear memory cache
cache
.
clear
();
public
void
DisplayImage
(
int
uid
,
Activity
activity
,
ImageView
imageView
)
{
if
(
cache
.
containsKey
(
uid
))
{
// clear SD cache
imageView
.
setImageBitmap
(
cache
.
get
(
uid
));
File
[]
files
=
cacheDir
.
listFiles
();
}
else
{
for
(
File
f
:
files
)
queuePhoto
(
uid
,
activity
,
imageView
);
f
.
delete
(
);
imageView
.
setImageResource
(
stub_id
);
}
}
}
// decodes image and scales it to reduce memory consumption
private
Bitmap
decodeFile
(
File
f
)
{
private
Bitmap
getBitmap
(
int
uid
)
{
try
{
// I identify images by hashcode. Not a perfect solution, good for the
// decode image size
// demo.
BitmapFactory
.
Options
o
=
new
BitmapFactory
.
Options
();
String
filename
=
String
.
valueOf
(
uid
);
o
.
inJustDecodeBounds
=
true
;
File
f
=
new
File
(
cacheDir
,
filename
);
BitmapFactory
.
decodeStream
(
new
FileInputStream
(
f
),
null
,
o
);
// from SD cache
// Find the correct scale value. It should be the power of 2.
Bitmap
b
=
decodeFile
(
f
);
final
int
REQUIRED_SIZE
=
70
;
if
(
b
!=
null
)
return
b
;
int
width_tmp
=
o
.
outWidth
,
height_tmp
=
o
.
outHeight
;
int
scale
=
1
;
// from web
while
(
true
)
{
try
{
if
(
width_tmp
/
2
<
REQUIRED_SIZE
BitmapDrawable
icon
=
(
BitmapDrawable
)
Utils
.
getAppIcon
(
context
,
uid
);
||
height_tmp
/
2
<
REQUIRED_SIZE
)
return
icon
.
getBitmap
();
break
;
}
catch
(
Exception
ex
)
{
width_tmp
/=
2
;
return
null
;
height_tmp
/=
2
;
scale
*=
2
;
}
// decode with inSampleSize
BitmapFactory
.
Options
o2
=
new
BitmapFactory
.
Options
();
o2
.
inSampleSize
=
scale
;
return
BitmapFactory
.
decodeStream
(
new
FileInputStream
(
f
),
null
,
o2
);
}
catch
(
FileNotFoundException
ignored
)
{
}
return
null
;
}
}
}
public
void
DisplayImage
(
int
uid
,
Activity
activity
,
ImageView
imageView
)
{
if
(
cache
.
containsKey
(
uid
))
private
void
queuePhoto
(
int
uid
,
Activity
activity
,
ImageView
imageView
)
{
imageView
.
setImageBitmap
(
cache
.
get
(
uid
));
// This ImageView may be used for other images before. So there may be
else
{
// some old tasks in the queue. We need to discard them.
queuePhoto
(
uid
,
activity
,
imageView
);
photosQueue
.
Clean
(
imageView
);
imageView
.
setImageResource
(
stub_id
);
PhotoToLoad
p
=
new
PhotoToLoad
(
uid
,
imageView
);
}
synchronized
(
photosQueue
.
photosToLoad
)
{
photosQueue
.
photosToLoad
.
push
(
p
);
photosQueue
.
photosToLoad
.
notifyAll
();
}
}
private
Bitmap
getBitmap
(
int
uid
)
{
// start thread if it's not started yet
// I identify images by hashcode. Not a perfect solution, good for the
if
(
photoLoaderThread
.
getState
()
==
Thread
.
State
.
NEW
)
photoLoaderThread
.
start
();
// demo.
}
String
filename
=
String
.
valueOf
(
uid
);
File
f
=
new
File
(
cacheDir
,
filename
);
// from SD cache
Bitmap
b
=
decodeFile
(
f
);
if
(
b
!=
null
)
return
b
;
// from web
try
{
BitmapDrawable
icon
=
(
BitmapDrawable
)
Utils
.
getAppIcon
(
context
,
uid
);
return
icon
.
getBitmap
();
}
catch
(
Exception
ex
)
{
return
null
;
}
}
private
void
queuePhoto
(
int
uid
,
Activity
activity
,
ImageView
imageView
)
{
public
void
stopThread
()
{
// This ImageView may be used for other images before. So there may be
photoLoaderThread
.
interrupt
();
// some old tasks in the queue. We need to discard them.
}
photosQueue
.
Clean
(
imageView
);
PhotoToLoad
p
=
new
PhotoToLoad
(
uid
,
imageView
);
synchronized
(
photosQueue
.
photosToLoad
)
{
photosQueue
.
photosToLoad
.
push
(
p
);
photosQueue
.
photosToLoad
.
notifyAll
();
}
// start thread if it's not started yet
// Used to display bitmap in the UI thread
if
(
photoLoaderThread
.
getState
()
==
Thread
.
State
.
NEW
)
class
BitmapDisplayer
implements
Runnable
{
photoLoaderThread
.
start
()
;
Bitmap
bitmap
;
}
ImageView
imageView
;
public
void
stopThread
()
{
public
BitmapDisplayer
(
Bitmap
b
,
ImageView
i
)
{
photoLoaderThread
.
interrupt
();
bitmap
=
b
;
imageView
=
i
;
}
}
// Used to display bitmap in the UI thread
@Override
class
BitmapDisplayer
implements
Runnable
{
public
void
run
()
{
Bitmap
bitmap
;
if
(
bitmap
!=
null
)
{
ImageView
imageView
;
imageView
.
setImageBitmap
(
bitmap
);
}
else
{
public
BitmapDisplayer
(
Bitmap
b
,
ImageView
i
)
{
imageView
.
setImageResource
(
stub_id
);
bitmap
=
b
;
}
imageView
=
i
;
}
@Override
public
void
run
()
{
if
(
bitmap
!=
null
)
imageView
.
setImageBitmap
(
bitmap
);
else
imageView
.
setImageResource
(
stub_id
);
}
}
}
}
class
PhotosLoader
extends
Thread
{
@Override
class
PhotosLoader
extends
Thread
{
public
void
run
()
{
@Override
try
{
public
void
run
()
{
while
(
true
)
{
try
{
// thread waits until there are any images to load in the
while
(
true
)
{
// queue
// thread waits until there are any images to load in the
if
(
photosQueue
.
photosToLoad
.
size
()
==
0
)
// queue
synchronized
(
photosQueue
.
photosToLoad
)
{
if
(
photosQueue
.
photosToLoad
.
size
()
==
0
)
{
photosQueue
.
photosToLoad
.
wait
();
synchronized
(
photosQueue
.
photosToLoad
)
{
}
photosQueue
.
photosToLoad
.
wait
();
if
(
photosQueue
.
photosToLoad
.
size
()
!=
0
)
{
}
PhotoToLoad
photoToLoad
;
}
synchronized
(
photosQueue
.
photosToLoad
)
{
if
(
photosQueue
.
photosToLoad
.
size
()
!=
0
)
{
photoToLoad
=
photosQueue
.
photosToLoad
.
pop
();
PhotoToLoad
photoToLoad
;
}
synchronized
(
photosQueue
.
photosToLoad
)
{
Bitmap
bmp
=
getBitmap
(
photoToLoad
.
uid
);
photoToLoad
=
photosQueue
.
photosToLoad
.
pop
();
cache
.
put
(
photoToLoad
.
uid
,
bmp
);
Object
tag
=
photoToLoad
.
imageView
.
getTag
();
if
(
tag
!=
null
&&
((
Integer
)
tag
)
==
photoToLoad
.
uid
)
{
BitmapDisplayer
bd
=
new
BitmapDisplayer
(
bmp
,
photoToLoad
.
imageView
);
Activity
a
=
(
Activity
)
photoToLoad
.
imageView
.
getContext
();
a
.
runOnUiThread
(
bd
);
}
}
if
(
Thread
.
interrupted
())
break
;
}
}
catch
(
InterruptedException
e
)
{
// allow thread to exit
}
}
Bitmap
bmp
=
getBitmap
(
photoToLoad
.
uid
);
cache
.
put
(
photoToLoad
.
uid
,
bmp
);
Object
tag
=
photoToLoad
.
imageView
.
getTag
();
if
(
tag
!=
null
&&
((
Integer
)
tag
)
==
photoToLoad
.
uid
)
{
BitmapDisplayer
bd
=
new
BitmapDisplayer
(
bmp
,
photoToLoad
.
imageView
);
Activity
a
=
(
Activity
)
photoToLoad
.
imageView
.
getContext
();
a
.
runOnUiThread
(
bd
);
}
}
if
(
Thread
.
interrupted
())
break
;
}
}
}
catch
(
InterruptedException
e
)
{
// allow thread to exit
}
}
}
}
// stores list of photos to download
class
PhotosQueue
{
// stores list of photos to download
private
final
Stack
<
PhotoToLoad
>
photosToLoad
=
new
Stack
<
PhotoToLoad
>();
class
PhotosQueue
{
private
final
Stack
<
PhotoToLoad
>
photosToLoad
=
new
Stack
<
PhotoToLoad
>();
// removes all instances of this ImageView
public
void
Clean
(
ImageView
image
)
{
// removes all instances of this ImageView
synchronized
(
photosToLoad
)
{
public
void
Clean
(
ImageView
image
)
{
for
(
int
j
=
0
;
j
<
photosToLoad
.
size
();
)
{
synchronized
(
photosToLoad
)
{
if
(
photosToLoad
.
get
(
j
).
imageView
==
image
)
photosToLoad
.
remove
(
j
);
for
(
int
j
=
0
;
j
<
photosToLoad
.
size
();
)
{
else
++
j
;
if
(
photosToLoad
.
get
(
j
).
imageView
==
image
)
{
}
photosToLoad
.
remove
(
j
);
}
}
else
{
++
j
;
}
}
}
}
}
}
}
// Task for the queue
// Task for the queue
private
class
PhotoToLoad
{
private
class
PhotoToLoad
{
public
int
uid
;
public
int
uid
;
public
ImageView
imageView
;
public
ImageView
imageView
;
public
PhotoToLoad
(
int
u
,
ImageView
i
)
{
public
PhotoToLoad
(
int
u
,
ImageView
i
)
{
uid
=
u
;
uid
=
u
;
imageView
=
i
;
imageView
=
i
;
}
}
}
}
}
}
src/com/github/shadowsocks/ImageLoaderFactory.java
View file @
3d664377
...
@@ -42,12 +42,12 @@ import android.content.Context;
...
@@ -42,12 +42,12 @@ import android.content.Context;
public
class
ImageLoaderFactory
{
public
class
ImageLoaderFactory
{
private
static
ImageLoader
il
=
null
;
private
static
ImageLoader
il
=
null
;
public
static
ImageLoader
getImageLoader
(
Context
context
)
{
public
static
ImageLoader
getImageLoader
(
Context
context
)
{
if
(
il
==
null
)
{
if
(
il
==
null
)
{
il
=
new
ImageLoader
(
context
);
il
=
new
ImageLoader
(
context
);
}
return
il
;
}
}
return
il
;
}
}
}
src/com/github/shadowsocks/ProxyedApp.java
View file @
3d664377
...
@@ -40,94 +40,70 @@ package com.github.shadowsocks;
...
@@ -40,94 +40,70 @@ package com.github.shadowsocks;
public
class
ProxyedApp
{
public
class
ProxyedApp
{
private
boolean
enabled
;
private
boolean
enabled
;
private
int
uid
;
private
int
uid
;
private
String
username
;
private
String
username
;
private
String
procname
;
private
String
procname
;
private
String
name
;
private
String
name
;
private
boolean
proxyed
=
false
;
private
boolean
proxyed
=
false
;
/**
/** @return the name */
* @return the name
public
String
getName
()
{
*/
return
name
;
public
String
getName
()
{
}
return
name
;
}
/**
/** @param name the name to set */
* @param name the name to set
public
void
setName
(
String
name
)
{
*/
this
.
name
=
name
;
public
void
setName
(
String
name
)
{
}
this
.
name
=
name
;
}
/**
/** @return the procname */
* @return the procname
public
String
getProcname
()
{
*/
return
procname
;
public
String
getProcname
()
{
}
return
procname
;
}
/**
/** @param procname the procname to set */
* @param procname the procname to set
public
void
setProcname
(
String
procname
)
{
*/
this
.
procname
=
procname
;
public
void
setProcname
(
String
procname
)
{
}
this
.
procname
=
procname
;
}
/**
/** @return the uid */
* @return the uid
public
int
getUid
()
{
*/
return
uid
;
public
int
getUid
()
{
}
return
uid
;
}
/**
/** @param uid the uid to set */
* @param uid the uid to set
public
void
setUid
(
int
uid
)
{
*/
this
.
uid
=
uid
;
public
void
setUid
(
int
uid
)
{
}
this
.
uid
=
uid
;
}
/**
/** @return the username */
* @return the username
public
String
getUsername
()
{
*/
return
username
;
public
String
getUsername
()
{
}
return
username
;
}
/**
/** @param username the username to set */
* @param username the username to set
public
void
setUsername
(
String
username
)
{
*/
this
.
username
=
username
;
public
void
setUsername
(
String
username
)
{
}
this
.
username
=
username
;
}
/**
/** @return the enabled */
* @return the enabled
public
boolean
isEnabled
()
{
*/
return
enabled
;
public
boolean
isEnabled
()
{
}
return
enabled
;
}
/**
/** @param enabled the enabled to set */
* @param enabled the enabled to set
public
void
setEnabled
(
boolean
enabled
)
{
*/
this
.
enabled
=
enabled
;
public
void
setEnabled
(
boolean
enabled
)
{
}
this
.
enabled
=
enabled
;
}
/**
/** @return the proxyed */
* @return the proxyed
public
boolean
isProxyed
()
{
*/
return
proxyed
;
public
boolean
isProxyed
()
{
}
return
proxyed
;
}
/**
/** @param proxyed the proxyed to set */
* @param proxyed the proxyed to set
public
void
setProxyed
(
boolean
proxyed
)
{
*/
this
.
proxyed
=
proxyed
;
public
void
setProxyed
(
boolean
proxyed
)
{
}
this
.
proxyed
=
proxyed
;
}
}
}
src/com/github/shadowsocks/Shadowsocks.java
View file @
3d664377
...
@@ -52,7 +52,8 @@ import android.graphics.Typeface;
...
@@ -52,7 +52,8 @@ import android.graphics.Typeface;
import
android.os.Bundle
;
import
android.os.Bundle
;
import
android.os.Handler
;
import
android.os.Handler
;
import
android.os.Message
;
import
android.os.Message
;
import
android.preference.*
;
import
android.preference.Preference
;
import
android.preference.PreferenceManager
;
import
android.util.Log
;
import
android.util.Log
;
import
android.view.KeyEvent
;
import
android.view.KeyEvent
;
import
android.widget.CompoundButton
;
import
android.widget.CompoundButton
;
...
@@ -61,588 +62,554 @@ import android.widget.TextView;
...
@@ -61,588 +62,554 @@ import android.widget.TextView;
import
com.actionbarsherlock.view.Menu
;
import
com.actionbarsherlock.view.Menu
;
import
com.actionbarsherlock.view.MenuItem
;
import
com.actionbarsherlock.view.MenuItem
;
import
com.google.analytics.tracking.android.EasyTracker
;
import
com.google.analytics.tracking.android.EasyTracker
;
import
net.saik0.android.unifiedpreference.UnifiedPreferenceFragment
;
import
net.saik0.android.unifiedpreference.UnifiedSherlockPreferenceActivity
;
import
org.jraf.android.backport.switchwidget.Switch
;
import
de.keyboardsurfer.android.widget.crouton.Crouton
;
import
de.keyboardsurfer.android.widget.crouton.Crouton
;
import
de.keyboardsurfer.android.widget.crouton.Style
;
import
de.keyboardsurfer.android.widget.crouton.Style
;
import
java.io.FileOutputStream
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStream
;
import
java.io.OutputStream
;
import
java.io.OutputStream
;
import
java.util.Hashtable
;
import
java.util.Hashtable
;
import
net.saik0.android.unifiedpreference.UnifiedPreferenceFragment
;
import
net.saik0.android.unifiedpreference.UnifiedSherlockPreferenceActivity
;
import
org.jraf.android.backport.switchwidget.Switch
;
public
class
Shadowsocks
extends
UnifiedSherlockPreferenceActivity
public
class
Shadowsocks
extends
UnifiedSherlockPreferenceActivity
implements
CompoundButton
.
OnCheckedChangeListener
,
OnSharedPreferenceChangeListener
{
implements
CompoundButton
.
OnCheckedChangeListener
,
OnSharedPreferenceChangeListener
{
public
static
final
String
PREFS_NAME
=
"Shadowsocks"
;
public
static
final
String
PREFS_NAME
=
"Shadowsocks"
;
public
static
final
String
[]
PROXY_PREFS
=
{
"proxy"
,
"remotePort"
,
"port"
public
static
final
String
[]
PROXY_PREFS
=
{
,
"sitekey"
,
"encMethod"
};
"proxy"
,
"remotePort"
,
"port"
,
"sitekey"
,
"encMethod"
public
static
final
String
[]
FEATRUE_PREFS
=
{
"isHTTPProxy"
,
"isDNSProxy"
,
"isGFWList"
,
"isGlobalProxy"
,
"isBypassApps"
};
,
"proxyedApps"
,
"isAutoConnect"
};
public
static
final
String
[]
FEATRUE_PREFS
=
{
private
static
final
String
TAG
=
"Shadowsocks"
;
"isHTTPProxy"
,
"isDNSProxy"
,
"isGFWList"
,
"isGlobalProxy"
,
"isBypassApps"
,
"proxyedApps"
,
private
static
final
int
MSG_CRASH_RECOVER
=
1
;
"isAutoConnect"
private
static
final
int
MSG_INITIAL_FINISH
=
2
;
};
private
static
ProgressDialog
mProgressDialog
=
null
;
private
static
final
String
TAG
=
"Shadowsocks"
;
final
Handler
handler
=
new
Handler
()
{
private
static
final
int
MSG_CRASH_RECOVER
=
1
;
@Override
private
static
final
int
MSG_INITIAL_FINISH
=
2
;
public
void
handleMessage
(
Message
msg
)
{
private
static
ProgressDialog
mProgressDialog
=
null
;
SharedPreferences
settings
=
PreferenceManager
final
Handler
handler
=
new
Handler
()
{
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
@Override
Editor
ed
=
settings
.
edit
();
public
void
handleMessage
(
Message
msg
)
{
switch
(
msg
.
what
)
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
case
MSG_CRASH_RECOVER:
Editor
ed
=
settings
.
edit
();
Crouton
.
makeText
(
Shadowsocks
.
this
,
R
.
string
.
crash_alert
,
Style
.
ALERT
).
show
();
switch
(
msg
.
what
)
{
ed
.
putBoolean
(
"isRunning"
,
false
);
case
MSG_CRASH_RECOVER:
break
;
Crouton
.
makeText
(
Shadowsocks
.
this
,
R
.
string
.
crash_alert
,
Style
.
ALERT
).
show
();
case
MSG_INITIAL_FINISH:
ed
.
putBoolean
(
"isRunning"
,
false
);
if
(
mProgressDialog
!=
null
)
{
break
;
mProgressDialog
.
dismiss
();
case
MSG_INITIAL_FINISH:
mProgressDialog
=
null
;
if
(
mProgressDialog
!=
null
)
{
}
mProgressDialog
.
dismiss
();
break
;
mProgressDialog
=
null
;
}
}
ed
.
commit
();
break
;
super
.
handleMessage
(
msg
);
}
}
ed
.
commit
();
};
super
.
handleMessage
(
msg
);
private
Switch
switchButton
;
}
};
private
Switch
switchButton
;
private
void
copyAssets
(
String
path
)
{
private
void
copyAssets
(
String
path
)
{
AssetManager
assetManager
=
getAssets
();
AssetManager
assetManager
=
getAssets
();
String
[]
files
=
null
;
String
[]
files
=
null
;
try
{
files
=
assetManager
.
list
(
path
);
}
catch
(
IOException
e
)
{
Log
.
e
(
TAG
,
e
.
getMessage
());
}
if
(
files
!=
null
)
{
for
(
String
file
:
files
)
{
InputStream
in
=
null
;
OutputStream
out
=
null
;
try
{
try
{
files
=
assetManager
.
list
(
path
);
if
(
path
.
length
()
>
0
)
{
}
catch
(
IOException
e
)
{
in
=
assetManager
.
open
(
path
+
"/"
+
file
);
Log
.
e
(
TAG
,
e
.
getMessage
());
}
else
{
}
in
=
assetManager
.
open
(
file
);
if
(
files
!=
null
)
{
}
for
(
String
file
:
files
)
{
out
=
new
FileOutputStream
(
"/data/data/com.github.shadowsocks/"
+
file
);
InputStream
in
=
null
;
copyFile
(
in
,
out
);
OutputStream
out
=
null
;
in
.
close
();
try
{
in
=
null
;
if
(
path
.
length
()
>
0
)
{
out
.
flush
();
in
=
assetManager
.
open
(
path
+
"/"
+
file
);
out
.
close
();
}
else
{
out
=
null
;
in
=
assetManager
.
open
(
file
);
}
catch
(
Exception
e
)
{
}
Log
.
e
(
TAG
,
e
.
getMessage
());
out
=
new
FileOutputStream
(
"/data/data/com.github.shadowsocks/"
+
file
);
copyFile
(
in
,
out
);
in
.
close
();
in
=
null
;
out
.
flush
();
out
.
close
();
out
=
null
;
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
e
.
getMessage
());
}
}
}
}
}
}
}
}
private
void
copyFile
(
InputStream
in
,
OutputStream
out
)
throws
IOException
{
private
void
copyFile
(
InputStream
in
,
OutputStream
out
)
throws
IOException
{
byte
[]
buffer
=
new
byte
[
1024
];
byte
[]
buffer
=
new
byte
[
1024
];
int
read
;
int
read
;
while
((
read
=
in
.
read
(
buffer
))
!=
-
1
)
{
while
((
read
=
in
.
read
(
buffer
))
!=
-
1
)
{
out
.
write
(
buffer
,
0
,
read
);
out
.
write
(
buffer
,
0
,
read
);
}
}
}
}
private
void
crash_recovery
()
{
private
void
crash_recovery
()
{
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -F OUTPUT"
).
append
(
"\n"
);
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
).
append
(
"\n"
);
sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -F OUTPUT"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/polipo.pid`"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 pdnsd"
).
append
(
"\n"
);
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/polipo.pid`"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 redsocks"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 pdnsd"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 shadowsocks"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 redsocks"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 polipo"
).
append
(
"\n"
);
sb
.
append
(
"killall -9 shadowsocks"
).
append
(
"\n"
);
Utils
.
runRootCommand
(
sb
.
toString
());
sb
.
append
(
"killall -9 polipo"
).
append
(
"\n"
);
Utils
.
runRootCommand
(
sb
.
toString
());
}
private
boolean
isTextEmpty
(
String
s
,
String
msg
)
{
if
(
s
==
null
||
s
.
length
()
<=
0
)
{
showAToast
(
msg
);
return
true
;
}
}
return
false
;
private
boolean
isTextEmpty
(
String
s
,
String
msg
)
{
}
if
(
s
==
null
||
s
.
length
()
<=
0
)
{
showAToast
(
msg
);
@Override
return
true
;
public
void
onCheckedChanged
(
CompoundButton
compoundButton
,
boolean
b
)
{
}
if
(
compoundButton
==
switchButton
)
{
return
false
;
if
(!
serviceStart
())
{
switchButton
.
setChecked
(
false
);
}
}
}
}
@Override
/** Called when the activity is first created. */
public
void
onCheckedChanged
(
CompoundButton
compoundButton
,
boolean
b
)
{
@Override
if
(
compoundButton
==
switchButton
)
{
public
void
onCreate
(
Bundle
savedInstanceState
)
{
if
(!
serviceStart
())
{
setHeaderRes
(
R
.
xml
.
shadowsocks_headers
);
switchButton
.
setChecked
(
false
);
super
.
onCreate
(
savedInstanceState
);
}
}
}
/**
RelativeLayout
switchLayout
=
* Called when the activity is first created.
(
RelativeLayout
)
getLayoutInflater
().
inflate
(
R
.
layout
.
layout_switch
,
null
);
*/
@Override
public
void
onCreate
(
Bundle
savedInstanceState
)
{
setHeaderRes
(
R
.
xml
.
shadowsocks_headers
);
super
.
onCreate
(
savedInstanceState
);
RelativeLayout
switchLayout
=
(
RelativeLayout
)
getSupportActionBar
().
setCustomView
(
switchLayout
);
getLayoutInflater
().
inflate
(
R
.
layout
.
layout_switch
,
null
);
getSupportActionBar
().
setDisplayShowTitleEnabled
(
false
);
getSupportActionBar
().
setDisplayShowCustomEnabled
(
true
);
getSupportActionBar
().
setDisplayShowHomeEnabled
(
false
);
getSupportActionBar
().
setCustomView
(
switchLayout
);
switchButton
=
(
Switch
)
switchLayout
.
findViewById
(
R
.
id
.
switchButton
);
getSupportActionBar
().
setDisplayShowTitleEnabled
(
false
);
getSupportActionBar
().
setDisplayShowCustomEnabled
(
true
);
getSupportActionBar
().
setDisplayShowHomeEnabled
(
false
);
TextView
title
=
(
TextView
)
switchLayout
.
findViewById
(
R
.
id
.
title
);
Typeface
tf
=
Typefaces
.
get
(
this
,
"fonts/Iceland.ttf"
);
if
(
tf
!=
null
)
title
.
setTypeface
(
tf
);
title
.
setText
(
R
.
string
.
app_name
);
switchButton
=
(
Switch
)
switchLayout
.
findViewById
(
R
.
id
.
switchButton
);
final
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
TextView
title
=
(
TextView
)
switchLayout
.
findViewById
(
R
.
id
.
title
);
boolean
init
=
Typeface
tf
=
Typefaces
.
get
(
this
,
"fonts/Iceland.ttf"
);
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
if
(
tf
!=
null
)
title
.
setTypeface
(
tf
);
title
.
setText
(
R
.
string
.
app_name
);
}
if
(
init
)
{
if
(
mProgressDialog
==
null
)
{
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
initializing
),
true
,
true
);
}
@Override
new
Thread
()
{
public
boolean
onCreateOptionsMenu
(
Menu
menu
)
{
@Override
menu
.
add
(
0
,
0
,
0
,
R
.
string
.
recovery
)
public
void
run
()
{
.
setIcon
(
android
.
R
.
drawable
.
ic_menu_revert
)
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_WITH_TEXT
);
menu
.
add
(
0
,
1
,
1
,
R
.
string
.
about
)
.
setIcon
(
android
.
R
.
drawable
.
ic_menu_info_details
)
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_WITH_TEXT
);
return
true
;
}
/**
Utils
.
isRoot
();
* Called when the activity is closed.
*/
@Override
public
void
onDestroy
()
{
SharedPreferences
settings
=
getSharedPreferences
(
PREFS_NAME
,
0
);
SharedPreferences
.
Editor
editor
=
settings
.
edit
();
String
versionName
;
editor
.
putBoolean
(
"isConnected"
,
ShadowsocksService
.
isServiceStarted
());
try
{
editor
.
commit
();
versionName
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
).
versionName
;
}
catch
(
NameNotFoundException
e
)
{
versionName
=
"NONE"
;
}
super
.
onDestroy
();
if
(!
settings
.
getBoolean
(
versionName
,
false
))
{
}
Editor
edit
=
settings
.
edit
();
edit
.
putBoolean
(
versionName
,
true
);
edit
.
commit
();
reset
();
}
@Override
handler
.
sendEmptyMessage
(
MSG_INITIAL_FINISH
);
public
boolean
onKeyDown
(
int
keyCode
,
KeyEvent
event
)
{
if
(
keyCode
==
KeyEvent
.
KEYCODE_BACK
&&
event
.
getRepeatCount
()
==
0
)
{
try
{
finish
();
}
catch
(
Exception
ignore
)
{
// Nothing
}
return
true
;
}
}
return
super
.
onKeyDown
(
keyCode
,
event
);
}.
start
(
);
}
}
}
@Override
public
boolean
onOptionsItemSelected
(
MenuItem
item
)
{
@Override
switch
(
item
.
getItemId
())
{
public
boolean
onCreateOptionsMenu
(
Menu
menu
)
{
case
0
:
menu
.
add
(
0
,
0
,
0
,
R
.
string
.
recovery
)
recovery
();
.
setIcon
(
android
.
R
.
drawable
.
ic_menu_revert
)
break
;
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_WITH_TEXT
);
case
1
:
menu
.
add
(
0
,
1
,
1
,
R
.
string
.
about
)
String
versionName
=
""
;
.
setIcon
(
android
.
R
.
drawable
.
ic_menu_info_details
)
try
{
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_WITH_TEXT
);
versionName
=
getPackageManager
().
getPackageInfo
(
return
true
;
getPackageName
(),
0
).
versionName
;
}
}
catch
(
NameNotFoundException
e
)
{
versionName
=
""
;
/** Called when the activity is closed. */
}
@Override
showAToast
(
getString
(
R
.
string
.
about
)
+
" ("
+
versionName
+
")\n\n"
public
void
onDestroy
()
{
+
getString
(
R
.
string
.
copy_rights
));
SharedPreferences
settings
=
getSharedPreferences
(
PREFS_NAME
,
0
);
break
;
SharedPreferences
.
Editor
editor
=
settings
.
edit
();
editor
.
putBoolean
(
"isConnected"
,
ShadowsocksService
.
isServiceStarted
());
editor
.
commit
();
super
.
onDestroy
();
}
@Override
public
boolean
onKeyDown
(
int
keyCode
,
KeyEvent
event
)
{
if
(
keyCode
==
KeyEvent
.
KEYCODE_BACK
&&
event
.
getRepeatCount
()
==
0
)
{
try
{
finish
();
}
catch
(
Exception
ignore
)
{
// Nothing
}
return
true
;
}
return
super
.
onKeyDown
(
keyCode
,
event
);
}
@Override
public
boolean
onOptionsItemSelected
(
MenuItem
item
)
{
switch
(
item
.
getItemId
())
{
case
0
:
recovery
();
break
;
case
1
:
String
versionName
=
""
;
try
{
versionName
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
).
versionName
;
}
catch
(
NameNotFoundException
e
)
{
versionName
=
""
;
}
}
showAToast
(
getString
(
R
.
string
.
about
)
+
" ("
+
versionName
+
")\n\n"
+
getString
(
return
super
.
onOptionsItemSelected
(
item
);
R
.
string
.
copy_rights
));
break
;
}
}
@Override
return
super
.
onOptionsItemSelected
(
item
);
protected
void
onPause
()
{
}
super
.
onPause
();
PreferenceManager
.
getDefaultSharedPreferences
(
this
)
.
unregisterOnSharedPreferenceChangeListener
(
this
);
}
@Override
@Override
protected
void
onResume
()
{
protected
void
onPause
()
{
super
.
onResume
();
super
.
onPause
();
SharedPreferences
settings
=
PreferenceManager
PreferenceManager
.
getDefaultSharedPreferences
(
this
)
.
getDefaultSharedPreferences
(
this
);
.
unregisterOnSharedPreferenceChangeListener
(
this
);
}
Editor
edit
=
settings
.
edit
();
@Override
protected
void
onResume
()
{
super
.
onResume
();
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
if
(
ShadowsocksService
.
isServiceStarted
())
{
Editor
edit
=
settings
.
edit
();
edit
.
putBoolean
(
"isRunning"
,
true
);
switchButton
.
setChecked
(
true
);
}
else
{
if
(
settings
.
getBoolean
(
"isRunning"
,
false
))
{
new
Thread
()
{
@Override
public
void
run
()
{
crash_recovery
();
handler
.
sendEmptyMessage
(
MSG_CRASH_RECOVER
);
}
}.
start
();
}
edit
.
putBoolean
(
"isRunning"
,
false
);
switchButton
.
setChecked
(
false
);
}
edit
.
commit
();
setPreferenceEnabled
();
switchButton
.
setOnCheckedChangeListener
(
this
);
PreferenceManager
.
getDefaultSharedPreferences
(
this
)
.
registerOnSharedPreferenceChangeListener
(
this
);
if
(
ShadowsocksService
.
isServiceStarted
())
{
edit
.
putBoolean
(
"isRunning"
,
true
);
switchButton
.
setChecked
(
true
);
}
else
{
if
(
settings
.
getBoolean
(
"isRunning"
,
false
))
{
new
Thread
()
{
@Override
public
void
run
()
{
crash_recovery
();
handler
.
sendEmptyMessage
(
MSG_CRASH_RECOVER
);
}
}.
start
();
}
edit
.
putBoolean
(
"isRunning"
,
false
);
switchButton
.
setChecked
(
false
);
}
}
private
void
setPreferenceEnabled
()
{
edit
.
commit
();
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
boolean
enabled
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
setPreferenceEnabled
();
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
for
(
String
name
:
PROXY_PREFS
)
{
switchButton
.
setOnCheckedChangeListener
(
this
);
Preference
pref
=
findPreference
(
name
);
PreferenceManager
.
getDefaultSharedPreferences
(
this
)
if
(
pref
!=
null
)
{
.
registerOnSharedPreferenceChangeListener
(
this
);
pref
.
setEnabled
(
enabled
);
}
}
}
for
(
String
name
:
FEATRUE_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
if
(
name
.
equals
(
"isBypassApps"
)
||
name
.
equals
(
"proxyedApps"
))
{
boolean
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
pref
.
setEnabled
(
enabled
&&
!
isGlobalProxy
);
}
else
{
pref
.
setEnabled
(
enabled
);
}
}
}
}
@Override
private
void
setPreferenceEnabled
()
{
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
String
key
)
{
// Let's do something a preference value changes
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
if
(
key
.
equals
(
"isRunning"
))
{
if
(
settings
.
getBoolean
(
"isRunning"
,
false
))
{
switchButton
.
setChecked
(
true
);
}
else
{
switchButton
.
setChecked
(
false
);
}
}
if
(
key
.
equals
(
"isConnecting"
))
{
boolean
enabled
=
if
(
settings
.
getBoolean
(
"isConnecting"
,
false
))
{
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
Log
.
d
(
TAG
,
"Connecting start"
);
if
(
mProgressDialog
==
null
)
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
connecting
),
true
,
true
);
}
else
{
Log
.
d
(
TAG
,
"Connecting finish"
);
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
}
}
}
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
for
(
String
name
:
PROXY_PREFS
)
{
setPreferenceEnabled
();
Preference
pref
=
findPreference
(
name
);
}
if
(
pref
!=
null
)
{
pref
.
setEnabled
(
enabled
);
}
}
}
for
(
String
name
:
FEATRUE_PREFS
)
{
@Override
Preference
pref
=
findPreference
(
name
);
public
void
onStart
()
{
if
(
pref
!=
null
)
{
super
.
onStart
();
if
(
name
.
equals
(
"isBypassApps"
)
||
name
.
equals
(
"proxyedApps"
))
{
EasyTracker
.
getInstance
().
activityStart
(
this
);
boolean
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
pref
.
setEnabled
(
enabled
&&
!
isGlobalProxy
);
final
SharedPreferences
settings
=
PreferenceManager
}
else
{
.
getDefaultSharedPreferences
(
Shadowsocks
.
this
);
pref
.
setEnabled
(
enabled
);
boolean
init
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
if
(
init
)
{
if
(
mProgressDialog
==
null
)
{
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
initializing
),
true
,
true
);
}
new
Thread
()
{
@Override
public
void
run
()
{
Utils
.
isRoot
();
String
versionName
;
try
{
versionName
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
).
versionName
;
}
catch
(
NameNotFoundException
e
)
{
versionName
=
"NONE"
;
}
if
(!
settings
.
getBoolean
(
versionName
,
false
))
{
Editor
edit
=
settings
.
edit
();
edit
.
putBoolean
(
versionName
,
true
);
edit
.
commit
();
reset
();
}
handler
.
sendEmptyMessage
(
MSG_INITIAL_FINISH
);
}
}.
start
();
}
}
}
}
}
@Override
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
// Let's do something a preference value changes
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
if
(
key
.
equals
(
"isRunning"
))
{
if
(
settings
.
getBoolean
(
"isRunning"
,
false
))
{
switchButton
.
setChecked
(
true
);
}
else
{
switchButton
.
setChecked
(
false
);
}
}
}
@Override
if
(
key
.
equals
(
"isConnecting"
))
{
public
void
onStop
()
{
if
(
settings
.
getBoolean
(
"isConnecting"
,
false
))
{
super
.
onStop
();
Log
.
d
(
TAG
,
"Connecting start"
);
EasyTracker
.
getInstance
().
activityStop
(
this
);
if
(
mProgressDialog
==
null
)
{
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
connecting
),
true
,
true
);
}
}
else
{
Log
.
d
(
TAG
,
"Connecting finish"
);
if
(
mProgressDialog
!=
null
)
{
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
mProgressDialog
=
null
;
}
}
}
}
}
public
void
reset
()
{
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
setPreferenceEnabled
();
crash_recovery
();
copyAssets
(
""
);
copyAssets
(
Utils
.
getABI
());
Utils
.
runCommand
(
"chmod 755 /data/data/com.github.shadowsocks/iptables\n"
+
"chmod 755 /data/data/com.github.shadowsocks/redsocks\n"
+
"chmod 755 /data/data/com.github.shadowsocks/pdnsd\n"
+
"chmod 755 /data/data/com.github.shadowsocks/shadowsocks\n"
+
"chmod 755 /data/data/com.github.shadowsocks/polipo\n"
);
}
}
}
@Override
public
void
onStart
()
{
super
.
onStart
();
EasyTracker
.
getInstance
().
activityStart
(
this
);
}
@Override
public
void
onStop
()
{
super
.
onStop
();
EasyTracker
.
getInstance
().
activityStop
(
this
);
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
}
}
private
void
recovery
()
{
public
void
reset
()
{
if
(
mProgressDialog
==
null
)
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
recovering
),
true
,
true
);
final
Handler
h
=
new
Handler
()
{
@Override
public
void
handleMessage
(
Message
msg
)
{
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
mProgressDialog
=
null
;
}
}
};
stopService
(
new
Intent
(
this
,
ShadowsocksService
.
class
)
);
crash_recovery
(
);
new
Thread
()
{
copyAssets
(
""
);
@Override
copyAssets
(
Utils
.
getABI
());
public
void
run
()
{
reset
();
Utils
.
runCommand
(
"chmod 755 /data/data/com.github.shadowsocks/iptables\n"
+
"chmod 755 /data/data/com.github.shadowsocks/redsocks\n"
+
"chmod 755 /data/data/com.github.shadowsocks/pdnsd\n"
+
"chmod 755 /data/data/com.github.shadowsocks/shadowsocks\n"
+
"chmod 755 /data/data/com.github.shadowsocks/polipo\n"
);
}
h
.
sendEmptyMessage
(
0
);
private
void
recovery
()
{
}
}.
start
();
if
(
mProgressDialog
==
null
)
{
mProgressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
recovering
),
true
,
true
);
}
}
/**
final
Handler
h
=
new
Handler
()
{
* Called when connect button is clicked.
@Override
*/
public
void
handleMessage
(
Message
msg
)
{
public
boolean
serviceStart
()
{
if
(
mProgressDialog
!=
null
)
{
mProgressDialog
.
dismiss
();
if
(
ShadowsocksService
.
isServiceStarted
())
{
mProgressDialog
=
null
;
stopService
(
new
Intent
(
this
,
ShadowsocksService
.
class
));
return
false
;
}
}
}
};
SharedPreferences
settings
=
PreferenceManager
stopService
(
new
Intent
(
this
,
ShadowsocksService
.
class
));
.
getDefaultSharedPreferences
(
this
);
final
String
proxy
=
settings
.
getString
(
"proxy"
,
""
);
new
Thread
()
{
if
(
isTextEmpty
(
proxy
,
getString
(
R
.
string
.
proxy_empty
)))
@Override
return
false
;
public
void
run
()
{
String
portText
=
settings
.
getString
(
"port"
,
""
);
reset
();
if
(
isTextEmpty
(
portText
,
getString
(
R
.
string
.
port_empty
)))
return
false
;
try
{
int
port
=
Integer
.
valueOf
(
portText
);
if
(
port
<=
1024
)
{
this
.
showAToast
(
getString
(
R
.
string
.
port_alert
));
return
false
;
}
}
catch
(
Exception
e
)
{
this
.
showAToast
(
getString
(
R
.
string
.
port_alert
));
return
false
;
}
Intent
it
=
new
Intent
(
this
,
ShadowsocksService
.
class
);
h
.
sendEmptyMessage
(
0
);
startService
(
it
);
}
}.
start
();
}
return
true
;
/** Called when connect button is clicked. */
}
public
boolean
serviceStart
()
{
private
void
showAToast
(
String
msg
)
{
if
(
ShadowsocksService
.
isServiceStarted
())
{
AlertDialog
.
Builder
builder
=
new
AlertDialog
.
Builder
(
this
);
stopService
(
new
Intent
(
this
,
ShadowsocksService
.
class
));
builder
.
setMessage
(
msg
)
return
false
;
.
setCancelable
(
false
)
.
setNegativeButton
(
getString
(
R
.
string
.
ok_iknow
),
new
DialogInterface
.
OnClickListener
()
{
@Override
public
void
onClick
(
DialogInterface
dialog
,
int
id
)
{
dialog
.
cancel
();
}
});
AlertDialog
alert
=
builder
.
create
();
alert
.
show
();
}
}
public
static
class
ProxyFragment
extends
UnifiedPreferenceFragment
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
implements
OnSharedPreferenceChangeListener
{
private
void
setPreferenceEnabled
()
{
final
String
proxy
=
settings
.
getString
(
"proxy"
,
""
);
SharedPreferences
settings
=
PreferenceManager
if
(
isTextEmpty
(
proxy
,
getString
(
R
.
string
.
proxy_empty
)))
return
false
;
.
getDefaultSharedPreferences
(
getActivity
());
boolean
enabled
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
String
portText
=
settings
.
getString
(
"port"
,
""
);
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
if
(
isTextEmpty
(
portText
,
getString
(
R
.
string
.
port_empty
)))
return
false
;
try
{
int
port
=
Integer
.
valueOf
(
portText
);
if
(
port
<=
1024
)
{
this
.
showAToast
(
getString
(
R
.
string
.
port_alert
));
return
false
;
}
}
catch
(
Exception
e
)
{
this
.
showAToast
(
getString
(
R
.
string
.
port_alert
));
return
false
;
}
for
(
String
name
:
PROXY_PREFS
)
{
Intent
it
=
new
Intent
(
this
,
ShadowsocksService
.
class
);
Preference
pref
=
findPreference
(
name
);
startService
(
it
);
if
(
pref
!=
null
)
{
pref
.
setEnabled
(
enabled
);
return
true
;
}
}
}
}
private
void
showAToast
(
String
msg
)
{
AlertDialog
.
Builder
builder
=
new
AlertDialog
.
Builder
(
this
);
builder
.
setMessage
(
msg
)
.
setCancelable
(
false
)
.
setNegativeButton
(
getString
(
R
.
string
.
ok_iknow
),
new
DialogInterface
.
OnClickListener
()
{
@Override
public
void
onClick
(
DialogInterface
dialog
,
int
id
)
{
dialog
.
cancel
();
}
});
AlertDialog
alert
=
builder
.
create
();
alert
.
show
();
}
public
static
class
ProxyFragment
extends
UnifiedPreferenceFragment
implements
OnSharedPreferenceChangeListener
{
@Override
private
void
setPreferenceEnabled
()
{
public
void
onResume
()
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
getActivity
());
super
.
onResume
();
setPreferenceEnabled
();
// Set up a listener whenever a key changes
boolean
enabled
=
getPreferenceScreen
().
getSharedPreferences
()
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
.
registerOnSharedPreferenceChangeListener
(
this
);
for
(
String
name
:
PROXY_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
pref
.
setEnabled
(
enabled
);
}
}
}
}
@Override
@Override
public
void
onPause
()
{
public
void
onResume
()
{
super
.
onPause
();
super
.
onResume
();
// Unregister the listener whenever a key changes
setPreferenceEnabled
();
getPreferenceScreen
().
getSharedPreferences
()
.
unregisterOnSharedPreferenceChangeListener
(
this
);
}
@Override
// Set up a listener whenever a key changes
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
getPreferenceScreen
().
getSharedPreferences
().
registerOnSharedPreferenceChangeListener
(
this
);
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
setPreferenceEnabled
();
}
}
}
}
public
static
class
FeatureFragment
extends
UnifiedPreferenceFragment
@Override
implements
OnSharedPreferenceChangeListener
{
public
void
onPause
()
{
super
.
onPause
();
private
void
setPreferenceEnabled
()
{
// Unregister the listener whenever a key changes
SharedPreferences
settings
=
PreferenceManager
getPreferenceScreen
().
getSharedPreferences
().
unregisterOnSharedPreferenceChangeListener
(
this
);
.
getDefaultSharedPreferences
(
getActivity
());
}
boolean
enabled
=
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
for
(
String
name
:
FEATRUE_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
if
(
name
.
equals
(
"isBypassApps"
)
||
name
.
equals
(
"proxyedApps"
))
{
boolean
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
pref
.
setEnabled
(
enabled
&&
!
isGlobalProxy
);
}
else
{
pref
.
setEnabled
(
enabled
);
}
}
}
}
@Override
@Override
public
void
onResume
()
{
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
super
.
onResume
();
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
setPreferenceEnabled
();
setPreferenceEnabled
();
}
}
}
// Set up a listener whenever a key changes
public
static
class
FeatureFragment
extends
UnifiedPreferenceFragment
getPreferenceScreen
().
getSharedPreferences
()
implements
OnSharedPreferenceChangeListener
{
.
registerOnSharedPreferenceChangeListener
(
this
);
}
@Override
private
void
setPreferenceEnabled
()
{
public
void
onPause
()
{
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
getActivity
());
super
.
onPause
();
// Unregister the listener whenever a key changes
boolean
enabled
=
getPreferenceScreen
().
getSharedPreferences
()
!
settings
.
getBoolean
(
"isRunning"
,
false
)
&&
!
settings
.
getBoolean
(
"isConnecting"
,
false
);
.
unregisterOnSharedPreferenceChangeListener
(
this
);
for
(
String
name
:
FEATRUE_PREFS
)
{
Preference
pref
=
findPreference
(
name
);
if
(
pref
!=
null
)
{
if
(
name
.
equals
(
"isBypassApps"
)
||
name
.
equals
(
"proxyedApps"
))
{
boolean
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
pref
.
setEnabled
(
enabled
&&
!
isGlobalProxy
);
}
else
{
pref
.
setEnabled
(
enabled
);
}
}
}
}
}
@Override
@Override
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
public
void
onResume
()
{
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
super
.
onResume
();
setPreferenceEnabled
();
setPreferenceEnabled
();
}
}
// Set up a listener whenever a key changes
getPreferenceScreen
().
getSharedPreferences
().
registerOnSharedPreferenceChangeListener
(
this
);
}
}
public
static
class
Typefaces
{
@Override
private
static
final
String
TAG
=
"Typefaces"
;
public
void
onPause
()
{
super
.
onPause
();
private
static
final
Hashtable
<
String
,
Typeface
>
cache
=
new
Hashtable
<
String
,
Typeface
>();
// Unregister the listener whenever a key changes
getPreferenceScreen
().
getSharedPreferences
().
unregisterOnSharedPreferenceChangeListener
(
this
);
public
static
Typeface
get
(
Context
c
,
String
assetPath
)
{
synchronized
(
cache
)
{
if
(!
cache
.
containsKey
(
assetPath
))
{
try
{
Typeface
t
=
Typeface
.
createFromAsset
(
c
.
getAssets
(),
assetPath
);
cache
.
put
(
assetPath
,
t
);
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
"Could not get typeface '"
+
assetPath
+
"' because "
+
e
.
getMessage
());
return
null
;
}
}
return
cache
.
get
(
assetPath
);
}
}
}
}
@Override
public
void
onSharedPreferenceChanged
(
SharedPreferences
sharedPreferences
,
String
key
)
{
if
(
key
.
equals
(
"isRunning"
)
||
key
.
equals
(
"isGlobalProxy"
))
{
setPreferenceEnabled
();
}
}
}
public
static
class
Typefaces
{
private
static
final
String
TAG
=
"Typefaces"
;
private
static
final
Hashtable
<
String
,
Typeface
>
cache
=
new
Hashtable
<
String
,
Typeface
>();
public
static
Typeface
get
(
Context
c
,
String
assetPath
)
{
synchronized
(
cache
)
{
if
(!
cache
.
containsKey
(
assetPath
))
{
try
{
Typeface
t
=
Typeface
.
createFromAsset
(
c
.
getAssets
(),
assetPath
);
cache
.
put
(
assetPath
,
t
);
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
"Could not get typeface '"
+
assetPath
+
"' because "
+
e
.
getMessage
());
return
null
;
}
}
return
cache
.
get
(
assetPath
);
}
}
}
}
}
src/com/github/shadowsocks/ShadowsocksApplication.java
View file @
3d664377
...
@@ -43,16 +43,15 @@ import com.google.analytics.tracking.android.EasyTracker;
...
@@ -43,16 +43,15 @@ import com.google.analytics.tracking.android.EasyTracker;
public
class
ShadowsocksApplication
extends
Application
{
public
class
ShadowsocksApplication
extends
Application
{
private
static
String
sTmpDir
;
private
static
String
sTmpDir
;
public
static
String
getTmpDir
()
{
public
static
String
getTmpDir
()
{
return
sTmpDir
;
return
sTmpDir
;
}
}
@Override
public
void
onCreate
()
{
EasyTracker
.
getInstance
().
setContext
(
this
);
sTmpDir
=
getCacheDir
().
getAbsolutePath
();
}
@Override
public
void
onCreate
()
{
EasyTracker
.
getInstance
().
setContext
(
this
);
sTmpDir
=
getCacheDir
().
getAbsolutePath
();
}
}
}
src/com/github/shadowsocks/ShadowsocksReceiver.java
View file @
3d664377
...
@@ -47,47 +47,47 @@ import android.preference.PreferenceManager;
...
@@ -47,47 +47,47 @@ import android.preference.PreferenceManager;
public
class
ShadowsocksReceiver
extends
BroadcastReceiver
{
public
class
ShadowsocksReceiver
extends
BroadcastReceiver
{
private
static
final
String
TAG
=
"Shadowsocks"
;
private
static
final
String
TAG
=
"Shadowsocks"
;
@Override
@Override
public
void
onReceive
(
Context
context
,
Intent
intent
)
{
public
void
onReceive
(
Context
context
,
Intent
intent
)
{
if
(
intent
!=
null
)
{
if
(
intent
!=
null
)
{
final
String
action
=
intent
.
getAction
();
final
String
action
=
intent
.
getAction
();
if
(
action
.
equals
(
Intent
.
ACTION_SHUTDOWN
))
{
if
(
action
.
equals
(
Intent
.
ACTION_SHUTDOWN
))
{
context
.
stopService
(
new
Intent
(
context
,
ShadowsocksService
.
class
));
context
.
stopService
(
new
Intent
(
context
,
ShadowsocksService
.
class
));
return
;
return
;
}
}
}
}
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
String
versionName
;
try
{
versionName
=
context
.
getPackageManager
().
getPackageInfo
(
context
.
getPackageName
(),
0
).
versionName
;
}
catch
(
PackageManager
.
NameNotFoundException
e
)
{
versionName
=
"NONE"
;
}
final
boolean
isAutoConnect
=
settings
.
getBoolean
(
"isAutoConnect"
,
false
);
SharedPreferences
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
context
);
final
boolean
isInstalled
=
settings
.
getBoolean
(
versionName
,
false
);
String
versionName
;
try
{
versionName
=
context
.
getPackageManager
().
getPackageInfo
(
context
.
getPackageName
(),
0
).
versionName
;
}
catch
(
PackageManager
.
NameNotFoundException
e
)
{
versionName
=
"NONE"
;
}
if
(
isAutoConnect
&&
isInstalled
)
{
final
boolean
isAutoConnect
=
settings
.
getBoolean
(
"isAutoConnect"
,
false
);
final
String
portText
=
settings
.
getString
(
"port"
,
""
);
final
boolean
isInstalled
=
settings
.
getBoolean
(
versionName
,
false
);
if
(
portText
==
null
||
portText
.
length
()
<=
0
)
{
return
;
}
try
{
int
port
=
Integer
.
valueOf
(
portText
);
if
(
port
<=
1024
)
{
return
;
}
}
catch
(
Exception
e
)
{
return
;
}
Intent
it
=
new
Intent
(
context
,
ShadowsocksService
.
class
);
if
(
isAutoConnect
&&
isInstalled
)
{
context
.
startService
(
it
);
final
String
portText
=
settings
.
getString
(
"port"
,
""
);
if
(
portText
==
null
||
portText
.
length
()
<=
0
)
{
return
;
}
try
{
int
port
=
Integer
.
valueOf
(
portText
);
if
(
port
<=
1024
)
{
return
;
}
}
}
}
catch
(
Exception
e
)
{
return
;
}
Intent
it
=
new
Intent
(
context
,
ShadowsocksService
.
class
);
context
.
startService
(
it
);
}
}
}
}
src/com/github/shadowsocks/ShadowsocksService.java
View file @
3d664377
...
@@ -55,8 +55,6 @@ import android.os.PowerManager;
...
@@ -55,8 +55,6 @@ import android.os.PowerManager;
import
android.preference.PreferenceManager
;
import
android.preference.PreferenceManager
;
import
android.util.Log
;
import
android.util.Log
;
import
com.google.analytics.tracking.android.EasyTracker
;
import
com.google.analytics.tracking.android.EasyTracker
;
import
org.apache.http.conn.util.InetAddressUtils
;
import
java.io.BufferedReader
;
import
java.io.BufferedReader
;
import
java.io.FileNotFoundException
;
import
java.io.FileNotFoundException
;
import
java.io.FileReader
;
import
java.io.FileReader
;
...
@@ -68,601 +66,583 @@ import java.net.Inet6Address;
...
@@ -68,601 +66,583 @@ import java.net.Inet6Address;
import
java.net.InetAddress
;
import
java.net.InetAddress
;
import
java.net.UnknownHostException
;
import
java.net.UnknownHostException
;
import
java.util.HashSet
;
import
java.util.HashSet
;
import
org.apache.http.conn.util.InetAddressUtils
;
public
class
ShadowsocksService
extends
Service
{
public
class
ShadowsocksService
extends
Service
{
public
static
final
String
BASE
=
"/data/data/com.github.shadowsocks/"
;
public
static
final
String
BASE
=
"/data/data/com.github.shadowsocks/"
;
final
static
String
REDSOCKS_CONF
=
"base {"
+
final
static
String
REDSOCKS_CONF
=
"base {"
+
" log_debug = off;"
+
" log_debug = off;"
+
" log_info = off;"
+
" log_info = off;"
+
" log = stderr;"
+
" log = stderr;"
+
" daemon = on;"
+
" daemon = on;"
+
" redirector = iptables;"
+
" redirector = iptables;"
+
"}"
+
"}"
+
"redsocks {"
+
"redsocks {"
+
" local_ip = 127.0.0.1;"
+
" local_ip = 127.0.0.1;"
+
" local_port = 8123;"
+
" local_port = 8123;"
+
" ip = 127.0.0.1;"
+
" ip = 127.0.0.1;"
+
" port = %d;"
+
" port = %d;"
+
" type = socks5;"
+
" type = socks5;"
+
"}"
;
"}"
;
final
static
String
SHADOWSOCKS_CONF
=
final
static
String
SHADOWSOCKS_CONF
=
"{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"
;
"{\"server\": [%s], \"server_port\": %d, \"local_port\": %d, \"password\": %s, \"timeout\": %d}"
;
final
static
String
CMD_IPTABLES_RETURN
=
" -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN\n"
;
final
static
String
CMD_IPTABLES_RETURN
=
" -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN\n"
;
final
static
String
CMD_IPTABLES_REDIRECT_ADD_SOCKS
=
" -t nat -A OUTPUT -p tcp "
final
static
String
CMD_IPTABLES_REDIRECT_ADD_SOCKS
=
+
"-j REDIRECT --to 8123\n"
;
" -t nat -A OUTPUT -p tcp "
+
"-j REDIRECT --to 8123\n"
;
final
static
String
CMD_IPTABLES_DNAT_ADD_SOCKS
=
" -t nat -A OUTPUT -p tcp "
final
static
String
CMD_IPTABLES_DNAT_ADD_SOCKS
=
+
"-j DNAT --to-destination 127.0.0.1:8123\n"
;
" -t nat -A OUTPUT -p tcp "
+
"-j DNAT --to-destination 127.0.0.1:8123\n"
;
private
static
final
int
MSG_CONNECT_START
=
0
;
private
static
final
int
MSG_CONNECT_START
=
0
;
private
static
final
int
MSG_CONNECT_FINISH
=
1
;
private
static
final
int
MSG_CONNECT_FINISH
=
1
;
private
static
final
int
MSG_CONNECT_SUCCESS
=
2
;
private
static
final
int
MSG_CONNECT_SUCCESS
=
2
;
private
static
final
int
MSG_CONNECT_FAIL
=
3
;
private
static
final
int
MSG_CONNECT_FAIL
=
3
;
private
static
final
int
MSG_HOST_CHANGE
=
4
;
private
static
final
int
MSG_HOST_CHANGE
=
4
;
private
static
final
int
MSG_STOP_SELF
=
5
;
private
static
final
int
MSG_STOP_SELF
=
5
;
private
static
final
String
TAG
=
"ShadowsocksService"
;
private
static
final
String
TAG
=
"ShadowsocksService"
;
private
final
static
int
DNS_PORT
=
8153
;
private
final
static
int
DNS_PORT
=
8153
;
private
static
final
Class
<?>[]
mStartForegroundSignature
=
new
Class
[]{
private
static
final
Class
<?>[]
mStartForegroundSignature
=
new
Class
[]
{
int
.
class
,
Notification
.
class
};
int
.
class
,
Notification
.
class
private
static
final
Class
<?>[]
mStopForegroundSignature
=
new
Class
[]{
boolean
.
class
};
};
private
static
final
Class
<?>[]
mSetForegroundSignature
=
new
Class
[]{
boolean
.
class
};
private
static
final
Class
<?>[]
mStopForegroundSignature
=
new
Class
[]
{
boolean
.
class
};
/*
private
static
final
Class
<?>[]
mSetForegroundSignature
=
new
Class
[]
{
boolean
.
class
};
* This is a hack see
/*
* http://www.mail-archive.com/android-developers@googlegroups
* This is a hack see
* .com/msg18298.html we are not really able to decide if the service was
* http://www.mail-archive.com/android-developers@googlegroups
* started. So we remember a week reference to it. We set it if we are
* .com/msg18298.html we are not really able to decide if the service was
* running and clear it if we are stopped. If anything goes wrong, the
* started. So we remember a week reference to it. We set it if we are
* reference will hopefully vanish
* running and clear it if we are stopped. If anything goes wrong, the
*/
* reference will hopefully vanish
private
static
WeakReference
<
ShadowsocksService
>
sRunningInstance
=
null
;
*/
final
Handler
handler
=
new
Handler
()
{
private
static
WeakReference
<
ShadowsocksService
>
sRunningInstance
=
null
;
@Override
final
Handler
handler
=
new
Handler
()
{
public
void
handleMessage
(
Message
msg
)
{
@Override
Editor
ed
=
settings
.
edit
();
public
void
handleMessage
(
Message
msg
)
{
switch
(
msg
.
what
)
{
Editor
ed
=
settings
.
edit
();
case
MSG_CONNECT_START:
switch
(
msg
.
what
)
{
ed
.
putBoolean
(
"isConnecting"
,
true
);
case
MSG_CONNECT_START:
ed
.
putBoolean
(
"isConnecting"
,
true
);
PowerManager
pm
=
(
PowerManager
)
getSystemService
(
Context
.
POWER_SERVICE
);
mWakeLock
=
pm
.
newWakeLock
(
PowerManager
.
SCREEN_DIM_WAKE_LOCK
PowerManager
pm
=
(
PowerManager
)
getSystemService
(
Context
.
POWER_SERVICE
);
|
PowerManager
.
ON_AFTER_RELEASE
,
"GAEProxy"
);
mWakeLock
=
pm
.
newWakeLock
(
PowerManager
.
SCREEN_DIM_WAKE_LOCK
|
PowerManager
.
ON_AFTER_RELEASE
,
mWakeLock
.
acquire
();
"GAEProxy"
);
break
;
mWakeLock
.
acquire
();
case
MSG_CONNECT_FINISH:
ed
.
putBoolean
(
"isConnecting"
,
false
);
break
;
case
MSG_CONNECT_FINISH:
if
(
mWakeLock
!=
null
&&
mWakeLock
.
isHeld
())
ed
.
putBoolean
(
"isConnecting"
,
false
);
mWakeLock
.
release
();
if
(
mWakeLock
!=
null
&&
mWakeLock
.
isHeld
())
mWakeLock
.
release
();
break
;
case
MSG_CONNECT_SUCCESS:
break
;
ed
.
putBoolean
(
"isRunning"
,
true
);
case
MSG_CONNECT_SUCCESS:
break
;
ed
.
putBoolean
(
"isRunning"
,
true
);
case
MSG_CONNECT_FAIL:
break
;
ed
.
putBoolean
(
"isRunning"
,
false
);
case
MSG_CONNECT_FAIL:
break
;
ed
.
putBoolean
(
"isRunning"
,
false
);
case
MSG_HOST_CHANGE:
break
;
ed
.
putString
(
"appHost"
,
appHost
);
case
MSG_HOST_CHANGE:
break
;
ed
.
putString
(
"appHost"
,
appHost
);
case
MSG_STOP_SELF:
break
;
stopSelf
();
case
MSG_STOP_SELF:
break
;
stopSelf
();
}
break
;
ed
.
commit
();
}
super
.
handleMessage
(
msg
);
ed
.
commit
();
}
super
.
handleMessage
(
msg
);
};
private
Notification
notification
;
private
NotificationManager
notificationManager
;
private
PendingIntent
pendIntent
;
private
PowerManager
.
WakeLock
mWakeLock
;
private
String
appHost
;
private
int
remotePort
;
private
int
localPort
;
private
String
sitekey
;
private
SharedPreferences
settings
=
null
;
private
boolean
hasRedirectSupport
=
true
;
private
boolean
isGlobalProxy
=
false
;
private
boolean
isGFWList
=
false
;
private
boolean
isBypassApps
=
false
;
private
boolean
isDNSProxy
=
false
;
private
boolean
isHTTPProxy
=
false
;
private
String
encMethod
;
private
ProxyedApp
apps
[];
private
Method
mSetForeground
;
private
Method
mStartForeground
;
private
Method
mStopForeground
;
private
Object
[]
mSetForegroundArgs
=
new
Object
[
1
];
private
Object
[]
mStartForegroundArgs
=
new
Object
[
2
];
private
Object
[]
mStopForegroundArgs
=
new
Object
[
1
];
public
static
boolean
isServiceStarted
()
{
final
boolean
isServiceStarted
;
if
(
sRunningInstance
==
null
)
{
isServiceStarted
=
false
;
}
else
if
(
sRunningInstance
.
get
()
==
null
)
{
isServiceStarted
=
false
;
sRunningInstance
=
null
;
}
else
{
isServiceStarted
=
true
;
}
return
isServiceStarted
;
}
}
};
private
int
getPid
(
String
name
)
{
private
Notification
notification
;
try
{
private
NotificationManager
notificationManager
;
BufferedReader
reader
=
new
BufferedReader
(
new
FileReader
(
BASE
+
name
+
".pid"
));
private
PendingIntent
pendIntent
;
final
String
line
=
reader
.
readLine
();
private
PowerManager
.
WakeLock
mWakeLock
;
return
Integer
.
valueOf
(
line
);
private
String
appHost
;
}
catch
(
FileNotFoundException
e
)
{
private
int
remotePort
;
Log
.
e
(
TAG
,
"Cannot open pid file: "
+
name
);
private
int
localPort
;
}
catch
(
IOException
e
)
{
private
String
sitekey
;
Log
.
e
(
TAG
,
"Cannot read pid file: "
+
name
);
private
SharedPreferences
settings
=
null
;
}
catch
(
NumberFormatException
e
)
{
private
boolean
hasRedirectSupport
=
true
;
Log
.
e
(
TAG
,
"Invalid pid"
,
e
);
private
boolean
isGlobalProxy
=
false
;
}
private
boolean
isGFWList
=
false
;
return
-
1
;
private
boolean
isBypassApps
=
false
;
private
boolean
isDNSProxy
=
false
;
private
boolean
isHTTPProxy
=
false
;
private
String
encMethod
;
private
ProxyedApp
apps
[];
private
Method
mSetForeground
;
private
Method
mStartForeground
;
private
Method
mStopForeground
;
private
Object
[]
mSetForegroundArgs
=
new
Object
[
1
];
private
Object
[]
mStartForegroundArgs
=
new
Object
[
2
];
private
Object
[]
mStopForegroundArgs
=
new
Object
[
1
];
public
static
boolean
isServiceStarted
()
{
final
boolean
isServiceStarted
;
if
(
sRunningInstance
==
null
)
{
isServiceStarted
=
false
;
}
else
if
(
sRunningInstance
.
get
()
==
null
)
{
isServiceStarted
=
false
;
sRunningInstance
=
null
;
}
else
{
isServiceStarted
=
true
;
}
}
return
isServiceStarted
;
private
void
startShadowsocksDaemon
()
{
}
new
Thread
()
{
@Override
private
int
getPid
(
String
name
)
{
public
void
run
()
{
try
{
final
String
cmd
=
String
.
format
(
BASE
+
BufferedReader
reader
=
new
BufferedReader
(
new
FileReader
(
BASE
+
name
+
".pid"
));
"shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f "
final
String
line
=
reader
.
readLine
();
+
BASE
+
"shadowsocks.pid"
,
return
Integer
.
valueOf
(
line
);
appHost
,
remotePort
,
localPort
,
sitekey
,
encMethod
);
}
catch
(
FileNotFoundException
e
)
{
System
.
exec
(
cmd
);
Log
.
e
(
TAG
,
"Cannot open pid file: "
+
name
);
}
}
catch
(
IOException
e
)
{
}.
start
();
Log
.
e
(
TAG
,
"Cannot read pid file: "
+
name
);
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
TAG
,
"Invalid pid"
,
e
);
}
}
return
-
1
;
private
void
startPolipoDaemon
()
{
}
new
Thread
()
{
@Override
private
void
startShadowsocksDaemon
()
{
public
void
run
()
{
new
Thread
()
{
final
String
cmd
=
String
.
format
(
BASE
+
@Override
"polipo proxyPort=%d socksParentProxy=127.0.0.1:%d daemonise=true pidFile=\"%s\" logLevel=1 logFile=\"%s\""
,
public
void
run
()
{
localPort
+
1
,
localPort
,
BASE
+
"polipo.pid"
,
BASE
+
"polipo.log"
);
final
String
cmd
=
String
.
format
(
BASE
System
.
exec
(
cmd
);
+
}
"shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f "
}.
start
();
+
BASE
+
"shadowsocks.pid"
,
appHost
,
remotePort
,
localPort
,
sitekey
,
encMethod
);
System
.
exec
(
cmd
);
}
}.
start
();
}
private
void
startPolipoDaemon
()
{
new
Thread
()
{
@Override
public
void
run
()
{
final
String
cmd
=
String
.
format
(
BASE
+
"polipo proxyPort=%d socksParentProxy=127.0.0.1:%d daemonise=true pidFile=\"%s\" logLevel=1 logFile=\"%s\""
,
localPort
+
1
,
localPort
,
BASE
+
"polipo.pid"
,
BASE
+
"polipo.log"
);
System
.
exec
(
cmd
);
}
}.
start
();
}
private
void
startDnsDaemon
()
{
final
String
cmd
=
BASE
+
"pdnsd -c "
+
BASE
+
"pdnsd.conf"
;
Utils
.
runCommand
(
cmd
);
}
private
String
getVersionName
()
{
String
version
;
try
{
PackageInfo
pi
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
);
version
=
pi
.
versionName
;
}
catch
(
PackageManager
.
NameNotFoundException
e
)
{
version
=
"Package name not found"
;
}
}
return
version
;
}
private
void
startDnsDaemon
()
{
public
void
handleCommand
(
Intent
intent
)
{
final
String
cmd
=
BASE
+
"pdnsd -c "
+
BASE
+
"pdnsd.conf"
;
Utils
.
runCommand
(
cmd
);
}
private
String
getVersionName
()
{
if
(
intent
==
null
)
{
String
version
;
stopSelf
();
try
{
return
;
PackageInfo
pi
=
getPackageManager
().
getPackageInfo
(
getPackageName
(),
0
);
version
=
pi
.
versionName
;
}
catch
(
PackageManager
.
NameNotFoundException
e
)
{
version
=
"Package name not found"
;
}
return
version
;
}
}
public
void
handleCommand
(
Intent
intent
)
{
appHost
=
settings
.
getString
(
"proxy"
,
"127.0.0.1"
);
sitekey
=
settings
.
getString
(
"sitekey"
,
"default"
);
if
(
intent
==
null
)
{
encMethod
=
settings
.
getString
(
"encMethod"
,
"table"
);
stopSelf
();
try
{
return
;
remotePort
=
Integer
.
valueOf
(
settings
.
getString
(
"remotePort"
,
"1984"
));
}
}
catch
(
NumberFormatException
ex
)
{
remotePort
=
1984
;
appHost
=
settings
.
getString
(
"proxy"
,
"127.0.0.1"
);
}
sitekey
=
settings
.
getString
(
"sitekey"
,
"default"
);
try
{
encMethod
=
settings
.
getString
(
"encMethod"
,
"table"
);
localPort
=
Integer
.
valueOf
(
settings
.
getString
(
"port"
,
"1984"
));
try
{
}
catch
(
NumberFormatException
ex
)
{
remotePort
=
Integer
.
valueOf
(
settings
.
getString
(
"remotePort"
,
"1984"
));
localPort
=
1984
;
}
catch
(
NumberFormatException
ex
)
{
}
remotePort
=
1984
;
}
try
{
localPort
=
Integer
.
valueOf
(
settings
.
getString
(
"port"
,
"1984"
));
}
catch
(
NumberFormatException
ex
)
{
localPort
=
1984
;
}
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
isGFWList
=
settings
.
getBoolean
(
"isGFWList"
,
false
);
isBypassApps
=
settings
.
getBoolean
(
"isBypassApps"
,
false
);
isDNSProxy
=
settings
.
getBoolean
(
"isDNSProxy"
,
false
);
isHTTPProxy
=
settings
.
getBoolean
(
"isHTTPProxy"
,
false
);
if
(
isHTTPProxy
)
{
isGlobalProxy
=
settings
.
getBoolean
(
"isGlobalProxy"
,
false
);
localPort
--;
isGFWList
=
settings
.
getBoolean
(
"isGFWList"
,
false
);
}
isBypassApps
=
settings
.
getBoolean
(
"isBypassApps"
,
false
);
isDNSProxy
=
settings
.
getBoolean
(
"isDNSProxy"
,
false
);
isHTTPProxy
=
settings
.
getBoolean
(
"isHTTPProxy"
,
false
);
new
Thread
(
new
Runnable
(
)
{
if
(
isHTTPProxy
)
{
@Override
localPort
--;
public
void
run
()
{
}
handler
.
sendEmptyMessage
(
MSG_CONNECT_START
);
new
Thread
(
new
Runnable
()
{
@Override
public
void
run
()
{
boolean
resolved
=
false
;
handler
.
sendEmptyMessage
(
MSG_CONNECT_START
)
;
if
(
appHost
!=
null
)
{
boolean
resolved
=
false
;
boolean
isIPv6Support
=
Utils
.
isIPv6Support
();
if
(
appHost
!=
null
)
{
InetAddress
addr
=
null
;
if
(
isIPv6Support
)
{
boolean
isIPv6Support
=
Utils
.
isIPv6Support
();
try
{
InetAddress
addr
=
null
;
addr
=
Inet6Address
.
getByName
(
appHost
);
}
catch
(
UnknownHostException
ignored
)
{
addr
=
null
;
}
}
if
(
addr
==
null
)
{
if
(
isIPv6Support
)
{
try
{
try
{
addr
=
Inet
Address
.
getByName
(
appHost
);
addr
=
Inet6
Address
.
getByName
(
appHost
);
}
catch
(
UnknownHostException
ignored
)
{
}
catch
(
UnknownHostException
ignored
)
{
addr
=
null
;
addr
=
null
;
}
}
}
}
if
(
addr
!=
null
)
{
if
(
addr
==
null
)
{
appHost
=
addr
.
getHostAddress
();
try
{
resolved
=
true
;
addr
=
InetAddress
.
getByName
(
appHost
);
}
}
catch
(
UnknownHostException
ignored
)
{
}
addr
=
null
;
}
}
Log
.
d
(
TAG
,
"IPTABLES: "
+
Utils
.
getIptables
());
if
(
addr
!=
null
)
{
appHost
=
addr
.
getHostAddress
();
resolved
=
true
;
}
}
// Test for Redirect Support
Log
.
d
(
TAG
,
"IPTABLES: "
+
Utils
.
getIptables
());
hasRedirectSupport
=
Utils
.
getHasRedirectSupport
();
if
(
resolved
&&
handleConnection
())
{
// Test for Redirect Support
// Connection and forward successful
hasRedirectSupport
=
Utils
.
getHasRedirectSupport
();
notifyAlert
(
getString
(
R
.
string
.
forward_success
),
getString
(
R
.
string
.
service_running
));
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_SUCCESS
,
500
);
if
(
resolved
&&
handleConnection
())
{
// Connection and forward successful
notifyAlert
(
getString
(
R
.
string
.
forward_success
),
getString
(
R
.
string
.
service_running
));
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_SUCCESS
,
500
);
}
else
{
// Connection or forward unsuccessful
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
getString
(
R
.
string
.
service_failed
));
}
else
{
stopSelf
();
// Connection or forward unsuccessful
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FAIL
,
500
);
notifyAlert
(
getString
(
R
.
string
.
forward_fail
),
}
getString
(
R
.
string
.
service_failed
));
stopSelf
();
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FINISH
,
500
);
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FAIL
,
500
);
}
}
}).
start
();
markServiceStarted
();
}
private
void
startRedsocksDaemon
()
{
String
conf
=
String
.
format
(
REDSOCKS_CONF
,
localPort
);
String
cmd
=
String
.
format
(
"%sredsocks -p %sredsocks.pid -c %sredsocks.conf"
,
BASE
,
BASE
,
BASE
);
Utils
.
runRootCommand
(
"echo \""
+
conf
+
"\" > "
+
BASE
+
"redsocks.conf\n"
+
cmd
);
}
private
boolean
waitForProcess
(
final
String
name
)
{
final
int
pid
=
getPid
(
name
);
if
(
pid
==
-
1
)
return
false
;
Exec
.
hangupProcessGroup
(-
pid
);
Thread
t
=
new
Thread
()
{
@Override
public
void
run
()
{
Exec
.
waitFor
(-
pid
);
Log
.
d
(
TAG
,
"Successfully exit pid: "
+
pid
);
}
};
t
.
start
();
try
{
t
.
join
(
300
);
}
catch
(
InterruptedException
ignored
)
{
}
return
!
t
.
isAlive
();
}
handler
.
sendEmptyMessageDelayed
(
MSG_CONNECT_FINISH
,
500
);
/** Called when the activity is first created. */
public
boolean
handleConnection
()
{
}
if
(
isHTTPProxy
)
{
}).
start
();
startPolipoDaemon
();
markServiceStarted
();
}
}
startShadowsocksDaemon
();
private
void
startRedsocksDaemon
()
{
startDnsDaemon
();
String
conf
=
String
.
format
(
REDSOCKS_CONF
,
localPort
);
startRedsocksDaemon
();
String
cmd
=
String
.
format
(
"%sredsocks -p %sredsocks.pid -c %sredsocks.conf"
,
setupIptables
();
BASE
,
BASE
,
BASE
);
Utils
.
runRootCommand
(
"echo \""
+
conf
+
"\" > "
+
BASE
+
"redsocks.conf\n"
return
true
;
+
cmd
);
}
private
void
initSoundVibrateLights
(
Notification
notification
)
{
notification
.
sound
=
null
;
notification
.
defaults
|=
Notification
.
DEFAULT_LIGHTS
;
}
void
invokeMethod
(
Method
method
,
Object
[]
args
)
{
try
{
method
.
invoke
(
this
,
mStartForegroundArgs
);
}
catch
(
InvocationTargetException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke method"
,
e
);
}
catch
(
IllegalAccessException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke method"
,
e
);
}
}
private
void
markServiceStarted
()
{
sRunningInstance
=
new
WeakReference
<
ShadowsocksService
>(
this
);
}
private
void
markServiceStopped
()
{
sRunningInstance
=
null
;
}
private
void
notifyAlert
(
String
title
,
String
info
)
{
notification
.
icon
=
R
.
drawable
.
ic_stat_shadowsocks
;
notification
.
tickerText
=
title
;
notification
.
flags
=
Notification
.
FLAG_ONGOING_EVENT
;
initSoundVibrateLights
(
notification
);
// notification.defaults = Notification.DEFAULT_SOUND;
notification
.
setLatestEventInfo
(
this
,
getString
(
R
.
string
.
app_name
),
info
,
pendIntent
);
startForegroundCompat
(
1
,
notification
);
}
private
void
notifyAlert
(
String
title
,
String
info
,
int
flags
)
{
notification
.
icon
=
R
.
drawable
.
ic_stat_shadowsocks
;
notification
.
tickerText
=
title
;
notification
.
flags
=
flags
;
initSoundVibrateLights
(
notification
);
notification
.
setLatestEventInfo
(
this
,
getString
(
R
.
string
.
app_name
),
info
,
pendIntent
);
notificationManager
.
notify
(
0
,
notification
);
}
@Override
public
IBinder
onBind
(
Intent
intent
)
{
return
null
;
}
@Override
public
void
onCreate
()
{
super
.
onCreate
();
EasyTracker
.
getTracker
().
trackEvent
(
"service"
,
"start"
,
getVersionName
(),
0L
);
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
notificationManager
=
(
NotificationManager
)
this
.
getSystemService
(
NOTIFICATION_SERVICE
);
Intent
intent
=
new
Intent
(
this
,
Shadowsocks
.
class
);
intent
.
setFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
);
pendIntent
=
PendingIntent
.
getActivity
(
this
,
0
,
intent
,
0
);
notification
=
new
Notification
();
try
{
mStartForeground
=
getClass
().
getMethod
(
"startForeground"
,
mStartForegroundSignature
);
mStopForeground
=
getClass
().
getMethod
(
"stopForeground"
,
mStopForegroundSignature
);
}
catch
(
NoSuchMethodException
e
)
{
// Running on an older platform.
mStartForeground
=
mStopForeground
=
null
;
}
}
private
boolean
waitForProcess
(
final
String
name
)
{
try
{
final
int
pid
=
getPid
(
name
);
mSetForeground
=
getClass
().
getMethod
(
"setForeground"
,
mSetForegroundSignature
);
if
(
pid
==
-
1
)
return
false
;
}
catch
(
NoSuchMethodException
e
)
{
throw
new
IllegalStateException
(
Exec
.
hangupProcessGroup
(-
pid
);
"OS doesn't have Service.startForeground OR Service.setForeground!"
);
Thread
t
=
new
Thread
()
{
@Override
public
void
run
()
{
Exec
.
waitFor
(-
pid
);
Log
.
d
(
TAG
,
"Successfully exit pid: "
+
pid
);
}
};
t
.
start
();
try
{
t
.
join
(
300
);
}
catch
(
InterruptedException
ignored
)
{
}
return
!
t
.
isAlive
();
}
}
}
/**
/** Called when the activity is closed. */
* Called when the activity is first created.
@Override
*/
public
void
onDestroy
()
{
public
boolean
handleConnection
()
{
if
(
isHTTPProxy
)
{
EasyTracker
.
getTracker
().
trackEvent
(
"service"
,
"stop"
,
getVersionName
(),
0L
);
startPolipoDaemon
();
}
startShadowsocksDaemon
();
startDnsDaemon
();
startRedsocksDaemon
();
setupIptables
();
return
true
;
stopForegroundCompat
(
1
);
}
private
void
initSoundVibrateLights
(
Notification
notification
)
{
notifyAlert
(
getString
(
R
.
string
.
forward_stop
),
getString
(
R
.
string
.
service_stopped
),
notification
.
sound
=
null
;
Notification
.
FLAG_AUTO_CANCEL
);
notification
.
defaults
|=
Notification
.
DEFAULT_LIGHTS
;
}
void
invokeMethod
(
Method
method
,
Object
[]
args
)
{
new
Thread
()
{
try
{
@Override
method
.
invoke
(
this
,
mStartForegroundArgs
);
public
void
run
()
{
}
catch
(
InvocationTargetException
e
)
{
// Make sure the connection is closed, important here
// Should not happen.
onDisconnect
();
Log
.
w
(
TAG
,
"Unable to invoke method"
,
e
);
}
}
catch
(
IllegalAccessException
e
)
{
}.
start
();
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke method"
,
e
);
}
}
private
void
markServiceStarted
()
{
Editor
ed
=
settings
.
edit
();
sRunningInstance
=
new
WeakReference
<
ShadowsocksService
>(
this
);
ed
.
putBoolean
(
"isRunning"
,
false
);
}
ed
.
putBoolean
(
"isConnecting"
,
false
);
ed
.
commit
();
private
void
markServiceStopped
()
{
try
{
sRunningInstance
=
null
;
notificationManager
.
cancel
(
0
);
}
catch
(
Exception
ignore
)
{
// Nothing
}
}
private
void
notifyAlert
(
String
title
,
String
info
)
{
super
.
onDestroy
();
notification
.
icon
=
R
.
drawable
.
ic_stat_shadowsocks
;
notification
.
tickerText
=
title
;
notification
.
flags
=
Notification
.
FLAG_ONGOING_EVENT
;
initSoundVibrateLights
(
notification
);
// notification.defaults = Notification.DEFAULT_SOUND;
notification
.
setLatestEventInfo
(
this
,
getString
(
R
.
string
.
app_name
),
info
,
pendIntent
);
startForegroundCompat
(
1
,
notification
);
}
private
void
notifyAlert
(
String
title
,
String
info
,
int
flags
)
{
markServiceStopped
();
notification
.
icon
=
R
.
drawable
.
ic_stat_shadowsocks
;
}
notification
.
tickerText
=
title
;
notification
.
flags
=
flags
;
initSoundVibrateLights
(
notification
);
notification
.
setLatestEventInfo
(
this
,
getString
(
R
.
string
.
app_name
),
info
,
pendIntent
);
notificationManager
.
notify
(
0
,
notification
);
}
@Override
private
void
onDisconnect
()
{
public
IBinder
onBind
(
Intent
intent
)
{
Utils
.
runRootCommand
(
Utils
.
getIptables
()
+
" -t nat -F OUTPUT"
);
return
null
;
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
).
append
(
"\n"
);
if
(!
waitForProcess
(
"pdnsd"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
).
append
(
"\n"
);
}
}
if
(!
waitForProcess
(
"shadowsocks"
))
{
@Override
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`"
).
append
(
"\n"
);
public
void
onCreate
()
{
super
.
onCreate
();
EasyTracker
.
getTracker
().
trackEvent
(
"service"
,
"start"
,
getVersionName
(),
0L
);
settings
=
PreferenceManager
.
getDefaultSharedPreferences
(
this
);
notificationManager
=
(
NotificationManager
)
this
.
getSystemService
(
NOTIFICATION_SERVICE
);
Intent
intent
=
new
Intent
(
this
,
Shadowsocks
.
class
);
intent
.
setFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
);
pendIntent
=
PendingIntent
.
getActivity
(
this
,
0
,
intent
,
0
);
notification
=
new
Notification
();
try
{
mStartForeground
=
getClass
().
getMethod
(
"startForeground"
,
mStartForegroundSignature
);
mStopForeground
=
getClass
().
getMethod
(
"stopForeground"
,
mStopForegroundSignature
);
}
catch
(
NoSuchMethodException
e
)
{
// Running on an older platform.
mStartForeground
=
mStopForeground
=
null
;
}
try
{
mSetForeground
=
getClass
().
getMethod
(
"setForeground"
,
mSetForegroundSignature
);
}
catch
(
NoSuchMethodException
e
)
{
throw
new
IllegalStateException
(
"OS doesn't have Service.startForeground OR Service.setForeground!"
);
}
}
}
if
(
isHTTPProxy
)
{
if
(!
waitForProcess
(
"polipo"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/polipo.pid`"
).
append
(
"\n"
);
}
}
Utils
.
runRootCommand
(
sb
.
toString
());
}
/**
// This is the old onStart method that will be called on the pre-2.0
* Called when the activity is closed.
// platform. On 2.0 or later we override onStartCommand() so this
*/
// method will not be called.
@Override
@Override
public
void
onDestroy
(
)
{
public
void
onStart
(
Intent
intent
,
int
startId
)
{
EasyTracker
.
getTracker
().
trackEvent
(
"service"
,
"stop"
,
handleCommand
(
intent
);
getVersionName
(),
0L
);
}
stopForegroundCompat
(
1
);
@Override
public
int
onStartCommand
(
Intent
intent
,
int
flags
,
int
startId
)
{
handleCommand
(
intent
);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return
START_STICKY
;
}
notifyAlert
(
getString
(
R
.
string
.
forward_stop
),
private
boolean
setupIptables
()
{
getString
(
R
.
string
.
service_stopped
),
Notification
.
FLAG_AUTO_CANCEL
);
new
Thread
()
{
StringBuilder
init_sb
=
new
StringBuilder
();
@Override
public
void
run
()
{
// Make sure the connection is closed, important here
onDisconnect
();
}
}.
start
();
Editor
ed
=
settings
.
edit
();
StringBuilder
http_sb
=
new
StringBuilder
();
ed
.
putBoolean
(
"isRunning"
,
false
);
ed
.
putBoolean
(
"isConnecting"
,
false
);
ed
.
commit
();
try
{
init_sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -F OUTPUT\n"
);
notificationManager
.
cancel
(
0
);
}
catch
(
Exception
ignore
)
{
// Nothing
}
super
.
onDestroy
()
;
String
cmd_bypass
=
Utils
.
getIptables
()
+
CMD_IPTABLES_RETURN
;
markServiceStopped
();
if
(!
InetAddressUtils
.
isIPv6Address
(
appHost
.
toUpperCase
()))
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-d "
+
appHost
));
}
}
init_sb
.
append
(
cmd_bypass
.
replace
(
"0.0.0.0"
,
"127.0.0.1"
));
private
void
onDisconnect
()
{
if
(!
isDNSProxy
)
{
Utils
.
runRootCommand
(
Utils
.
getIptables
()
+
" -t nat -F OUTPUT"
);
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"--dport "
+
53
));
StringBuilder
sb
=
new
StringBuilder
();
init_sb
.
append
(
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`"
).
append
(
"\n"
);
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-m owner --uid-owner "
+
getApplicationInfo
().
uid
));
if
(!
waitForProcess
(
"pdnsd"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`"
).
append
(
"\n"
);
}
if
(!
waitForProcess
(
"shadowsocks"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`"
).
append
(
"\n"
);
}
if
(
isHTTPProxy
)
{
if
(!
waitForProcess
(
"polipo"
))
{
sb
.
append
(
"kill -9 `cat /data/data/com.github.shadowsocks/polipo.pid`"
).
append
(
"\n"
);
}
}
Utils
.
runRootCommand
(
sb
.
toString
());
}
}
// This is the old onStart method that will be called on the pre-2.0
if
(
hasRedirectSupport
)
{
// platform. On 2.0 or later we override onStartCommand() so this
init_sb
.
append
(
Utils
.
getIptables
())
// method will not be called.
.
append
(
" -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to "
)
@Override
.
append
(
DNS_PORT
)
public
void
onStart
(
Intent
intent
,
int
startId
)
{
.
append
(
"\n"
);
}
else
{
init_sb
.
append
(
Utils
.
getIptables
())
.
append
(
" -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:"
)
.
append
(
DNS_PORT
)
.
append
(
"\n"
);
}
handleCommand
(
intent
);
if
(
isGFWList
)
{
String
[]
chn_list
=
getResources
().
getStringArray
(
R
.
array
.
chn_list
);
for
(
String
item
:
chn_list
)
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"0.0.0.0"
,
item
));
}
}
}
if
(
isGlobalProxy
||
isBypassApps
)
{
@Override
http_sb
.
append
(
hasRedirectSupport
?
Utils
.
getIptables
()
+
CMD_IPTABLES_REDIRECT_ADD_SOCKS
public
int
onStartCommand
(
Intent
intent
,
int
flags
,
int
startId
)
{
:
Utils
.
getIptables
()
+
CMD_IPTABLES_DNAT_ADD_SOCKS
);
handleCommand
(
intent
);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return
START_STICKY
;
}
}
if
(!
isGlobalProxy
)
{
private
boolean
setupIptables
()
{
// for proxy specified apps
if
(
apps
==
null
||
apps
.
length
<=
0
)
apps
=
AppManager
.
getProxyedApps
(
this
);
StringBuilder
init_sb
=
new
StringBuilder
();
HashSet
<
Integer
>
uidSet
=
new
HashSet
<
Integer
>();
StringBuilder
http_sb
=
new
StringBuilder
();
for
(
ProxyedApp
app
:
apps
)
{
if
(
app
.
isProxyed
())
{
init_sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -F OUTPUT\n"
);
uidSet
.
add
(
app
.
getUid
());
String
cmd_bypass
=
Utils
.
getIptables
()
+
CMD_IPTABLES_RETURN
;
if
(!
InetAddressUtils
.
isIPv6Address
(
appHost
.
toUpperCase
()))
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-d "
+
appHost
));
}
init_sb
.
append
(
cmd_bypass
.
replace
(
"0.0.0.0"
,
"127.0.0.1"
));
if
(!
isDNSProxy
)
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"--dport "
+
53
));
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-m owner --uid-owner "
+
getApplicationInfo
().
uid
));
}
}
}
if
(
hasRedirectSupport
)
{
for
(
int
uid
:
uidSet
)
{
init_sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to "
).
append
(
DNS_PORT
).
append
(
"\n"
);
if
(!
isBypassApps
)
{
http_sb
.
append
((
hasRedirectSupport
?
Utils
.
getIptables
()
+
CMD_IPTABLES_REDIRECT_ADD_SOCKS
:
Utils
.
getIptables
()
+
CMD_IPTABLES_DNAT_ADD_SOCKS
).
replace
(
"-t nat"
,
"-t nat -m owner --uid-owner "
+
uid
));
}
else
{
}
else
{
init_sb
.
append
(
Utils
.
getIptables
()).
append
(
" -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:"
).
append
(
DNS_PORT
).
append
(
"\n"
);
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-m owner --uid-owner "
+
uid
));
}
if
(
isGFWList
)
{
String
[]
chn_list
=
getResources
().
getStringArray
(
R
.
array
.
chn_list
);
for
(
String
item
:
chn_list
)
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"0.0.0.0"
,
item
));
}
}
if
(
isGlobalProxy
||
isBypassApps
)
{
http_sb
.
append
(
hasRedirectSupport
?
Utils
.
getIptables
()
+
CMD_IPTABLES_REDIRECT_ADD_SOCKS
:
Utils
.
getIptables
()
+
CMD_IPTABLES_DNAT_ADD_SOCKS
);
}
if
(!
isGlobalProxy
)
{
// for proxy specified apps
if
(
apps
==
null
||
apps
.
length
<=
0
)
apps
=
AppManager
.
getProxyedApps
(
this
);
HashSet
<
Integer
>
uidSet
=
new
HashSet
<
Integer
>();
for
(
ProxyedApp
app
:
apps
)
{
if
(
app
.
isProxyed
())
{
uidSet
.
add
(
app
.
getUid
());
}
}
for
(
int
uid
:
uidSet
)
{
if
(!
isBypassApps
)
{
http_sb
.
append
((
hasRedirectSupport
?
Utils
.
getIptables
()
+
CMD_IPTABLES_REDIRECT_ADD_SOCKS
:
Utils
.
getIptables
()
+
CMD_IPTABLES_DNAT_ADD_SOCKS
).
replace
(
"-t nat"
,
"-t nat -m owner --uid-owner "
+
uid
));
}
else
{
init_sb
.
append
(
cmd_bypass
.
replace
(
"-d 0.0.0.0"
,
"-m owner --uid-owner "
+
uid
));
}
}
}
}
}
}
String
init_rules
=
init_sb
.
toString
();
String
init_rules
=
init_sb
.
toString
();
Utils
.
runRootCommand
(
init_rules
,
30
*
1000
);
Utils
.
runRootCommand
(
init_rules
,
30
*
1000
);
String
redt_rules
=
http_sb
.
toString
();
String
redt_rules
=
http_sb
.
toString
();
Utils
.
runRootCommand
(
redt_rules
);
Utils
.
runRootCommand
(
redt_rules
);
return
true
;
return
true
;
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void
startForegroundCompat
(
int
id
,
Notification
notification
)
{
// If we have the new startForeground API, then use it.
if
(
mStartForeground
!=
null
)
{
mStartForegroundArgs
[
0
]
=
id
;
mStartForegroundArgs
[
1
]
=
notification
;
invokeMethod
(
mStartForeground
,
mStartForegroundArgs
);
return
;
}
}
/**
// Fall back on the old API.
* This is a wrapper around the new startForeground method, using the older
mSetForegroundArgs
[
0
]
=
Boolean
.
TRUE
;
* APIs if it is not available.
invokeMethod
(
mSetForeground
,
mSetForegroundArgs
);
*/
notificationManager
.
notify
(
id
,
notification
);
void
startForegroundCompat
(
int
id
,
Notification
notification
)
{
}
// If we have the new startForeground API, then use it.
if
(
mStartForeground
!=
null
)
{
/**
mStartForegroundArgs
[
0
]
=
id
;
* This is a wrapper around the new stopForeground method, using the older
mStartForegroundArgs
[
1
]
=
notification
;
* APIs if it is not available.
invokeMethod
(
mStartForeground
,
mStartForegroundArgs
);
*/
return
;
void
stopForegroundCompat
(
int
id
)
{
}
// If we have the new stopForeground API, then use it.
if
(
mStopForeground
!=
null
)
{
// Fall back on the old API.
mStopForegroundArgs
[
0
]
=
Boolean
.
TRUE
;
mSetForegroundArgs
[
0
]
=
Boolean
.
TRUE
;
try
{
invokeMethod
(
mSetForeground
,
mSetForegroundArgs
);
mStopForeground
.
invoke
(
this
,
mStopForegroundArgs
);
notificationManager
.
notify
(
id
,
notification
);
}
catch
(
InvocationTargetException
e
)
{
}
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke stopForeground"
,
e
);
/**
}
catch
(
IllegalAccessException
e
)
{
* This is a wrapper around the new stopForeground method, using the older
// Should not happen.
* APIs if it is not available.
Log
.
w
(
TAG
,
"Unable to invoke stopForeground"
,
e
);
*/
}
void
stopForegroundCompat
(
int
id
)
{
return
;
// If we have the new stopForeground API, then use it.
if
(
mStopForeground
!=
null
)
{
mStopForegroundArgs
[
0
]
=
Boolean
.
TRUE
;
try
{
mStopForeground
.
invoke
(
this
,
mStopForegroundArgs
);
}
catch
(
InvocationTargetException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke stopForeground"
,
e
);
}
catch
(
IllegalAccessException
e
)
{
// Should not happen.
Log
.
w
(
TAG
,
"Unable to invoke stopForeground"
,
e
);
}
return
;
}
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
notificationManager
.
cancel
(
id
);
mSetForegroundArgs
[
0
]
=
Boolean
.
FALSE
;
invokeMethod
(
mSetForeground
,
mSetForegroundArgs
);
}
}
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
notificationManager
.
cancel
(
id
);
mSetForegroundArgs
[
0
]
=
Boolean
.
FALSE
;
invokeMethod
(
mSetForeground
,
mSetForegroundArgs
);
}
}
}
src/com/github/shadowsocks/System.java
View file @
3d664377
...
@@ -39,9 +39,9 @@
...
@@ -39,9 +39,9 @@
package
com.github.shadowsocks
;
package
com.github.shadowsocks
;
public
class
System
{
public
class
System
{
static
{
static
{
java
.
lang
.
System
.
loadLibrary
(
"system"
);
java
.
lang
.
System
.
loadLibrary
(
"system"
);
}
}
public
static
native
void
exec
(
String
cmd
);
public
static
native
void
exec
(
String
cmd
);
}
}
src/com/github/shadowsocks/Utils.java
View file @
3d664377
...
@@ -45,471 +45,451 @@ import android.content.pm.PackageManager.NameNotFoundException;
...
@@ -45,471 +45,451 @@ import android.content.pm.PackageManager.NameNotFoundException;
import
android.graphics.drawable.Drawable
;
import
android.graphics.drawable.Drawable
;
import
android.os.Environment
;
import
android.os.Environment
;
import
android.util.Log
;
import
android.util.Log
;
import
org.apache.http.conn.util.InetAddressUtils
;
import
java.io.BufferedReader
;
import
java.io.File
;
import
java.io.*
;
import
java.io.FileDescriptor
;
import
java.io.FileInputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.net.InetAddress
;
import
java.net.InetAddress
;
import
java.net.NetworkInterface
;
import
java.net.NetworkInterface
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.List
;
import
org.apache.http.conn.util.InetAddressUtils
;
public
class
Utils
{
public
class
Utils
{
public
final
static
String
TAG
=
"Shadowsocks"
;
public
final
static
String
TAG
=
"Shadowsocks"
;
public
final
static
String
ABI_PROP
=
"ro.product.cpu.abi"
;
public
final
static
String
ABI_PROP
=
"ro.product.cpu.abi"
;
public
final
static
String
ABI2_PROP
=
"ro.product.cpu.abi2"
;
public
final
static
String
ABI2_PROP
=
"ro.product.cpu.abi2"
;
public
final
static
String
ARM_ABI
=
"arm"
;
public
final
static
String
ARM_ABI
=
"arm"
;
public
final
static
String
X86_ABI
=
"x86"
;
public
final
static
String
X86_ABI
=
"x86"
;
public
final
static
String
MIPS_ABI
=
"mips"
;
public
final
static
String
MIPS_ABI
=
"mips"
;
public
final
static
String
DEFAULT_SHELL
=
"/system/bin/sh"
;
public
final
static
String
DEFAULT_SHELL
=
"/system/bin/sh"
;
public
final
static
String
DEFAULT_ROOT
=
"/system/bin/su"
;
public
final
static
String
DEFAULT_ROOT
=
"/system/bin/su"
;
public
final
static
String
ALTERNATIVE_ROOT
=
"/system/xbin/su"
;
public
final
static
String
ALTERNATIVE_ROOT
=
"/system/xbin/su"
;
public
final
static
String
DEFAULT_IPTABLES
=
"/data/data/com.github.shadowsocks/iptables"
;
public
final
static
String
DEFAULT_IPTABLES
=
"/data/data/com.github.shadowsocks/iptables"
;
public
final
static
String
ALTERNATIVE_IPTABLES
=
"/system/bin/iptables"
;
public
final
static
String
ALTERNATIVE_IPTABLES
=
"/system/bin/iptables"
;
public
final
static
int
TIME_OUT
=
-
99
;
public
final
static
int
TIME_OUT
=
-
99
;
private
static
boolean
initialized
=
false
;
private
static
boolean
initialized
=
false
;
private
static
int
hasRedirectSupport
=
-
1
;
private
static
int
hasRedirectSupport
=
-
1
;
private
static
int
isRoot
=
-
1
;
private
static
int
isRoot
=
-
1
;
private
static
String
shell
=
null
;
private
static
String
shell
=
null
;
private
static
String
root_shell
=
null
;
private
static
String
root_shell
=
null
;
private
static
String
iptables
=
null
;
private
static
String
iptables
=
null
;
private
static
String
data_path
=
null
;
private
static
String
data_path
=
null
;
/**
/** Get IP address from first non-localhost interface */
* Get IP address from first non-localhost interface
public
static
boolean
isIPv6Support
()
{
*/
try
{
public
static
boolean
isIPv6Support
()
{
List
<
NetworkInterface
>
interfaces
=
Collections
.
list
(
NetworkInterface
.
getNetworkInterfaces
());
try
{
for
(
NetworkInterface
intf
:
interfaces
)
{
List
<
NetworkInterface
>
interfaces
=
Collections
.
list
(
NetworkInterface
.
getNetworkInterfaces
());
List
<
InetAddress
>
addrs
=
Collections
.
list
(
intf
.
getInetAddresses
());
for
(
NetworkInterface
intf
:
interfaces
)
{
for
(
InetAddress
addr
:
addrs
)
{
List
<
InetAddress
>
addrs
=
Collections
.
list
(
intf
.
getInetAddresses
());
if
(!
addr
.
isLoopbackAddress
()
&&
!
addr
.
isLinkLocalAddress
())
{
for
(
InetAddress
addr
:
addrs
)
{
String
sAddr
=
addr
.
getHostAddress
().
toUpperCase
();
if
(!
addr
.
isLoopbackAddress
()
&&
!
addr
.
isLinkLocalAddress
())
{
boolean
isIPv6
=
InetAddressUtils
.
isIPv6Address
(
sAddr
);
String
sAddr
=
addr
.
getHostAddress
().
toUpperCase
();
if
(
isIPv6
)
{
boolean
isIPv6
=
InetAddressUtils
.
isIPv6Address
(
sAddr
);
Log
.
d
(
TAG
,
"IPv6 address detected"
);
if
(
isIPv6
)
{
return
true
;
Log
.
d
(
TAG
,
"IPv6 address detected"
);
return
true
;
}
}
}
}
}
catch
(
Exception
ex
)
{
// Ignore
}
// for now eat exceptions
return
false
;
}
public
static
String
getABI
()
{
String
abi
=
getSystemProperty
(
ABI_PROP
);
if
(
abi
!=
null
)
{
if
(
abi
.
toLowerCase
().
contains
(
ARM_ABI
))
{
return
ARM_ABI
;
}
else
if
(
abi
.
toLowerCase
().
contains
(
X86_ABI
))
{
return
X86_ABI
;
}
}
}
}
}
}
}
catch
(
Exception
ex
)
{
// Ignore
}
// for now eat exceptions
return
false
;
}
public
static
String
getABI
()
{
String
abi
=
getSystemProperty
(
ABI_PROP
);
if
(
abi
!=
null
)
{
if
(
abi
.
toLowerCase
().
contains
(
ARM_ABI
))
{
return
ARM_ABI
;
return
ARM_ABI
;
}
else
if
(
abi
.
toLowerCase
().
contains
(
X86_ABI
))
{
return
X86_ABI
;
}
}
}
return
ARM_ABI
;
/**
}
* Returns a SystemProperty
*
/**
* @param propName The Property to retrieve
* Returns a SystemProperty
* @return The Property, or NULL if not found
*
*/
* @param propName The Property to retrieve
private
static
String
getSystemProperty
(
String
propName
)
{
* @return The Property, or NULL if not found
String
line
;
*/
BufferedReader
input
=
null
;
private
static
String
getSystemProperty
(
String
propName
)
{
String
line
;
BufferedReader
input
=
null
;
try
{
Process
p
=
Runtime
.
getRuntime
().
exec
(
"getprop "
+
propName
);
input
=
new
BufferedReader
(
new
InputStreamReader
(
p
.
getInputStream
()),
1024
);
line
=
input
.
readLine
();
input
.
close
();
}
catch
(
IOException
ex
)
{
Log
.
e
(
TAG
,
"Unable to read sysprop "
+
propName
,
ex
);
return
null
;
}
finally
{
if
(
input
!=
null
)
{
try
{
try
{
Process
p
=
Runtime
.
getRuntime
().
exec
(
"getprop "
+
propName
);
input
.
close
();
input
=
new
BufferedReader
(
new
InputStreamReader
(
p
.
getInputStream
()),
1024
);
}
catch
(
IOException
e
)
{
line
=
input
.
readLine
();
Log
.
e
(
TAG
,
"Exception while closing InputStream"
,
e
);
input
.
close
();
}
catch
(
IOException
ex
)
{
Log
.
e
(
TAG
,
"Unable to read sysprop "
+
propName
,
ex
);
return
null
;
}
finally
{
if
(
input
!=
null
)
{
try
{
input
.
close
();
}
catch
(
IOException
e
)
{
Log
.
e
(
TAG
,
"Exception while closing InputStream"
,
e
);
}
}
}
}
return
line
;
}
}
}
return
line
;
}
private
static
void
checkIptables
()
{
private
static
void
checkIptables
()
{
if
(!
isRoot
())
{
iptables
=
DEFAULT_IPTABLES
;
return
;
}
// Check iptables binary
if
(!
isRoot
())
{
iptables
=
DEFAULT_IPTABLES
;
iptables
=
DEFAULT_IPTABLES
;
return
;
}
String
lines
=
null
;
// Check iptables binary
iptables
=
DEFAULT_IPTABLES
;
boolean
compatible
=
false
;
String
lines
=
null
;
boolean
version
=
false
;
StringBuilder
sb
=
new
StringBuilder
();
boolean
compatible
=
false
;
String
command
=
iptables
+
" --version\n"
+
iptables
boolean
version
=
false
;
+
" -L -t nat -n\n"
+
"exit\n"
;
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
StringBuilder
sb
=
new
StringBuilder
();
String
command
=
iptables
+
" --version\n"
+
iptables
+
" -L -t nat -n\n"
+
"exit\n"
;
if
(
exitcode
==
TIME_OUT
)
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
return
;
lines
=
sb
.
toString
()
;
if
(
exitcode
==
TIME_OUT
)
return
;
if
(
lines
.
contains
(
"OUTPUT"
))
{
lines
=
sb
.
toString
();
compatible
=
true
;
}
if
(
lines
.
contains
(
"v1.4."
))
{
version
=
true
;
}
if
(!
compatible
||
!
version
)
{
if
(
lines
.
contains
(
"OUTPUT"
))
{
iptables
=
ALTERNATIVE_IPTABLES
;
compatible
=
true
;
if
(!
new
File
(
iptables
).
exists
())
}
iptables
=
"iptables"
;
if
(
lines
.
contains
(
"v1.4."
))
{
}
version
=
true
;
}
if
(!
compatible
||
!
version
)
{
iptables
=
ALTERNATIVE_IPTABLES
;
if
(!
new
File
(
iptables
).
exists
())
iptables
=
"iptables"
;
}
}
}
public
static
Drawable
getAppIcon
(
Context
c
,
int
uid
)
{
public
static
Drawable
getAppIcon
(
Context
c
,
int
uid
)
{
PackageManager
pm
=
c
.
getPackageManager
();
PackageManager
pm
=
c
.
getPackageManager
();
Drawable
appIcon
=
c
.
getResources
().
getDrawable
(
Drawable
appIcon
=
c
.
getResources
().
getDrawable
(
android
.
R
.
drawable
.
sym_def_app_icon
);
android
.
R
.
drawable
.
sym_def_app_icon
);
String
[]
packages
=
pm
.
getPackagesForUid
(
uid
);
String
[]
packages
=
pm
.
getPackagesForUid
(
uid
);
if
(
packages
!=
null
)
{
if
(
packages
.
length
==
1
)
{
try
{
ApplicationInfo
appInfo
=
pm
.
getApplicationInfo
(
packages
[
0
],
0
);
appIcon
=
pm
.
getApplicationIcon
(
appInfo
);
}
catch
(
NameNotFoundException
e
)
{
Log
.
e
(
c
.
getPackageName
(),
"No package found matching with the uid "
+
uid
);
}
}
}
else
{
Log
.
e
(
c
.
getPackageName
(),
"Package not found for uid "
+
uid
);
}
return
appIcon
;
if
(
packages
!=
null
)
{
if
(
packages
.
length
==
1
)
{
try
{
ApplicationInfo
appInfo
=
pm
.
getApplicationInfo
(
packages
[
0
],
0
);
appIcon
=
pm
.
getApplicationIcon
(
appInfo
);
}
catch
(
NameNotFoundException
e
)
{
Log
.
e
(
c
.
getPackageName
(),
"No package found matching with the uid "
+
uid
);
}
}
}
else
{
Log
.
e
(
c
.
getPackageName
(),
"Package not found for uid "
+
uid
);
}
}
public
static
String
getDataPath
(
Context
ctx
)
{
return
appIcon
;
}
if
(
data_path
==
null
)
{
public
static
String
getDataPath
(
Context
ctx
)
{
if
(
Environment
.
MEDIA_MOUNTED
.
equals
(
Environment
if
(
data_path
==
null
)
{
.
getExternalStorageState
()))
{
data_path
=
Environment
.
getExternalStorageDirectory
()
.
getAbsolutePath
();
}
else
{
data_path
=
ctx
.
getFilesDir
().
getAbsolutePath
();
}
Log
.
d
(
TAG
,
"Python Data Path: "
+
data_path
);
if
(
Environment
.
MEDIA_MOUNTED
.
equals
(
Environment
.
getExternalStorageState
()))
{
}
data_path
=
Environment
.
getExternalStorageDirectory
().
getAbsolutePath
();
}
else
{
data_path
=
ctx
.
getFilesDir
().
getAbsolutePath
();
}
return
data_path
;
Log
.
d
(
TAG
,
"Python Data Path: "
+
data_path
)
;
}
}
public
static
boolean
getHasRedirectSupport
()
{
return
data_path
;
if
(
hasRedirectSupport
==
-
1
)
}
initHasRedirectSupported
();
return
hasRedirectSupport
==
1
;
}
public
static
String
getIptables
()
{
public
static
boolean
getHasRedirectSupport
()
{
if
(
iptables
==
null
)
if
(
hasRedirectSupport
==
-
1
)
initHasRedirectSupported
();
checkIptables
();
return
hasRedirectSupport
==
1
;
return
iptables
;
}
}
private
static
String
getShell
()
{
public
static
String
getIptables
()
{
if
(
shell
==
null
)
{
if
(
iptables
==
null
)
checkIptables
();
shell
=
DEFAULT_SHELL
;
return
iptables
;
if
(!
new
File
(
shell
).
exists
())
}
shell
=
"sh"
;
}
private
static
String
getShell
()
{
return
shell
;
if
(
shell
==
null
)
{
shell
=
DEFAULT_SHELL
;
if
(!
new
File
(
shell
).
exists
())
shell
=
"sh"
;
}
}
return
shell
;
}
public
static
void
initHasRedirectSupported
()
{
public
static
void
initHasRedirectSupported
()
{
if
(!
Utils
.
isRoot
())
if
(!
Utils
.
isRoot
())
return
;
return
;
StringBuilder
sb
=
new
StringBuilder
();
StringBuilder
sb
=
new
StringBuilder
();
String
command
=
Utils
.
getIptables
()
String
command
=
+
" -t nat -A OUTPUT -p udp --dport 54 -j REDIRECT --to 8154"
;
Utils
.
getIptables
()
+
" -t nat -A OUTPUT -p udp --dport 54 -j REDIRECT --to 8154"
;
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
String
lines
=
sb
.
toString
();
String
lines
=
sb
.
toString
();
hasRedirectSupport
=
1
;
hasRedirectSupport
=
1
;
// flush the check command
// flush the check command
Utils
.
runRootCommand
(
command
.
replace
(
"-A"
,
"-D"
));
Utils
.
runRootCommand
(
command
.
replace
(
"-A"
,
"-D"
));
if
(
exitcode
==
TIME_OUT
)
if
(
exitcode
==
TIME_OUT
)
return
;
return
;
if
(
lines
.
contains
(
"No chain/target/match"
))
{
if
(
lines
.
contains
(
"No chain/target/match"
))
{
hasRedirectSupport
=
0
;
hasRedirectSupport
=
0
;
}
}
}
}
public
static
boolean
isInitialized
()
{
if
(
initialized
)
public
static
boolean
isInitialized
()
{
return
true
;
if
(
initialized
)
{
else
{
return
true
;
initialized
=
true
;
}
else
{
return
false
;
initialized
=
true
;
}
return
false
;
}
}
}
public
static
boolean
isRoot
()
{
public
static
boolean
isRoot
()
{
if
(
isRoot
!=
-
1
)
return
isRoot
==
1
;
// switch between binaries
if
(
isRoot
!=
-
1
)
return
isRoot
==
1
;
if
(
new
File
(
DEFAULT_ROOT
).
exists
())
{
root_shell
=
DEFAULT_ROOT
;
}
else
if
(
new
File
(
ALTERNATIVE_ROOT
).
exists
())
{
root_shell
=
ALTERNATIVE_ROOT
;
}
else
{
root_shell
=
"su"
;
}
String
lines
=
null
;
// switch between binaries
if
(
new
File
(
DEFAULT_ROOT
).
exists
())
{
root_shell
=
DEFAULT_ROOT
;
}
else
if
(
new
File
(
ALTERNATIVE_ROOT
).
exists
())
{
root_shell
=
ALTERNATIVE_ROOT
;
}
else
{
root_shell
=
"su"
;
}
StringBuilder
sb
=
new
StringBuilder
();
String
lines
=
null
;
String
command
=
"ls /\n exit\n"
;
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
StringBuilder
sb
=
new
StringBuilder
();
String
command
=
"ls /\n exit\n"
;
if
(
exitcode
==
TIME_OUT
)
{
int
exitcode
=
runScript
(
command
,
sb
,
10
*
1000
,
true
);
return
false
;
}
lines
=
sb
.
toString
();
if
(
exitcode
==
TIME_OUT
)
{
return
false
;
}
if
(
lines
.
contains
(
"system"
))
{
lines
=
sb
.
toString
();
isRoot
=
1
;
}
return
isRoot
==
1
;
if
(
lines
.
contains
(
"system"
))
{
isRoot
=
1
;
}
}
public
static
boolean
runCommand
(
String
command
)
{
return
isRoot
==
1
;
}
return
runCommand
(
command
,
10
*
1000
);
public
static
boolean
runCommand
(
String
command
)
{
}
return
runCommand
(
command
,
10
*
1000
);
}
public
static
boolean
runCommand
(
String
command
,
int
timeout
)
{
public
static
boolean
runCommand
(
String
command
,
int
timeout
)
{
Log
.
d
(
TAG
,
command
);
Log
.
d
(
TAG
,
command
);
runScript
(
command
,
null
,
timeout
,
false
);
runScript
(
command
,
null
,
timeout
,
false
);
return
true
;
return
true
;
}
}
public
static
boolean
runRootCommand
(
String
command
)
{
public
static
boolean
runRootCommand
(
String
command
)
{
return
runRootCommand
(
command
,
10
*
1000
);
return
runRootCommand
(
command
,
10
*
1000
);
}
}
public
static
boolean
runRootCommand
(
String
command
,
int
timeout
)
{
public
static
boolean
runRootCommand
(
String
command
,
int
timeout
)
{
if
(!
isRoot
())
{
Log
.
e
(
TAG
,
"Cannot get ROOT permission: "
+
root_shell
);
return
false
;
}
if
(!
isRoot
())
{
Log
.
d
(
TAG
,
command
);
Log
.
e
(
TAG
,
"Cannot get ROOT permission: "
+
root_shell
);
return
false
;
runScript
(
command
,
null
,
timeout
,
true
);
}
return
true
;
}
private
synchronized
static
int
runScript
(
String
script
,
StringBuilder
result
,
long
timeout
,
boolean
asroot
)
{
final
ScriptRunner
runner
=
new
ScriptRunner
(
script
,
result
,
asroot
);
runner
.
start
();
try
{
if
(
timeout
>
0
)
{
runner
.
join
(
timeout
);
}
else
{
runner
.
join
();
}
if
(
runner
.
isAlive
())
{
// Timed-out
runner
.
destroy
();
runner
.
join
(
1000
);
return
TIME_OUT
;
}
}
catch
(
InterruptedException
ex
)
{
return
TIME_OUT
;
}
return
runner
.
exitcode
;
}
/** Internal thread used to execute scripts (as root or not). */
private
static
final
class
ScriptRunner
extends
Thread
{
private
final
String
scripts
;
private
final
StringBuilder
result
;
private
final
boolean
asroot
;
public
int
exitcode
=
-
1
;
private
int
[]
pid
=
new
int
[
1
];
private
FileDescriptor
pipe
;
@Override
public
void
destroy
()
{
if
(
pid
[
0
]
!=
-
1
)
{
Exec
.
hangupProcessGroup
(
pid
[
0
]);
pid
[
0
]
=
-
1
;
}
if
(
pipe
!=
null
)
{
Exec
.
close
(
pipe
);
pipe
=
null
;
}
}
Log
.
d
(
TAG
,
command
);
/**
* Creates a new script runner.
*
* @param script script to run
* @param result result output
* @param asroot if true, executes the script as root
*/
public
ScriptRunner
(
String
script
,
StringBuilder
result
,
boolean
asroot
)
{
this
.
scripts
=
script
;
this
.
result
=
result
;
this
.
asroot
=
asroot
;
}
runScript
(
command
,
null
,
timeout
,
true
);
private
FileDescriptor
createSubprocess
(
int
[]
processId
,
String
cmd
)
{
ArrayList
<
String
>
argList
=
parse
(
cmd
);
String
arg0
=
argList
.
get
(
0
);
String
[]
args
=
argList
.
toArray
(
new
String
[
1
]);
return
true
;
return
Exec
.
createSubprocess
(
result
!=
null
?
1
:
0
,
arg0
,
args
,
null
,
scripts
+
"\nexit\n"
,
processId
);
}
}
private
synchronized
static
int
runScript
(
String
script
,
StringBuilder
result
,
private
ArrayList
<
String
>
parse
(
String
cmd
)
{
long
timeout
,
boolean
asroot
)
{
final
int
PLAIN
=
0
;
final
ScriptRunner
runner
=
new
ScriptRunner
(
script
,
result
,
asroot
);
final
int
WHITESPACE
=
1
;
runner
.
start
();
final
int
INQUOTE
=
2
;
try
{
int
state
=
WHITESPACE
;
if
(
timeout
>
0
)
{
ArrayList
<
String
>
result
=
new
ArrayList
<
String
>();
runner
.
join
(
timeout
);
int
cmdLen
=
cmd
.
length
();
}
else
{
StringBuilder
builder
=
new
StringBuilder
();
runner
.
join
();
for
(
int
i
=
0
;
i
<
cmdLen
;
i
++)
{
char
c
=
cmd
.
charAt
(
i
);
if
(
state
==
PLAIN
)
{
if
(
Character
.
isWhitespace
(
c
))
{
result
.
add
(
builder
.
toString
());
builder
.
delete
(
0
,
builder
.
length
());
state
=
WHITESPACE
;
}
else
if
(
c
==
'"'
)
{
state
=
INQUOTE
;
}
else
{
builder
.
append
(
c
);
}
}
else
if
(
state
==
WHITESPACE
)
{
if
(
Character
.
isWhitespace
(
c
))
{
// do nothing
}
else
if
(
c
==
'"'
)
{
state
=
INQUOTE
;
}
else
{
state
=
PLAIN
;
builder
.
append
(
c
);
}
}
else
if
(
state
==
INQUOTE
)
{
if
(
c
==
'\\'
)
{
if
(
i
+
1
<
cmdLen
)
{
i
+=
1
;
builder
.
append
(
cmd
.
charAt
(
i
));
}
}
if
(
runner
.
isAlive
())
{
}
else
if
(
c
==
'"'
)
{
// Timed-out
state
=
PLAIN
;
runner
.
destroy
();
}
else
{
runner
.
join
(
1000
);
builder
.
append
(
c
);
return
TIME_OUT
;
}
}
}
catch
(
InterruptedException
ex
)
{
return
TIME_OUT
;
}
}
return
runner
.
exitcode
;
}
if
(
builder
.
length
()
>
0
)
{
result
.
add
(
builder
.
toString
());
}
return
result
;
}
}
/**
@Override
* Internal thread used to execute scripts (as root or not).
public
void
run
()
{
*/
pid
[
0
]
=
-
1
;
private
static
final
class
ScriptRunner
extends
Thread
{
private
final
String
scripts
;
try
{
private
final
StringBuilder
result
;
private
final
boolean
asroot
;
if
(
this
.
asroot
)
{
public
int
exitcode
=
-
1
;
// Create the "su" request to run the script
pipe
=
createSubprocess
(
pid
,
root_shell
);
private
int
[]
pid
=
new
int
[
1
];
}
else
{
private
FileDescriptor
pipe
;
// Create the "sh" request to run the script
pipe
=
createSubprocess
(
pid
,
getShell
());
@Override
public
void
destroy
()
{
if
(
pid
[
0
]
!=
-
1
)
{
Exec
.
hangupProcessGroup
(
pid
[
0
]);
pid
[
0
]
=
-
1
;
}
if
(
pipe
!=
null
)
{
Exec
.
close
(
pipe
);
pipe
=
null
;
}
}
}
/**
if
(
pid
[
0
]
!=
-
1
)
{
* Creates a new script runner.
exitcode
=
Exec
.
waitFor
(
pid
[
0
]);
*
* @param script script to run
* @param result result output
* @param asroot if true, executes the script as root
*/
public
ScriptRunner
(
String
script
,
StringBuilder
result
,
boolean
asroot
)
{
this
.
scripts
=
script
;
this
.
result
=
result
;
this
.
asroot
=
asroot
;
}
}
private
FileDescriptor
createSubprocess
(
int
[]
processId
,
String
cmd
)
{
if
(
result
==
null
||
pipe
==
null
)
return
;
ArrayList
<
String
>
argList
=
parse
(
cmd
);
String
arg0
=
argList
.
get
(
0
);
String
[]
args
=
argList
.
toArray
(
new
String
[
1
]);
return
Exec
.
createSubprocess
(
result
!=
null
?
1
:
0
,
arg0
,
args
,
null
,
final
InputStream
stdout
=
new
FileInputStream
(
pipe
);
scripts
+
"\nexit\n"
,
processId
)
;
final
byte
buf
[]
=
new
byte
[
8192
]
;
}
int
read
=
0
;
private
ArrayList
<
String
>
parse
(
String
cmd
)
{
// Read stdout
final
int
PLAIN
=
0
;
while
(
stdout
.
available
()
>
0
)
{
final
int
WHITESPACE
=
1
;
read
=
stdout
.
read
(
buf
);
final
int
INQUOTE
=
2
;
result
.
append
(
new
String
(
buf
,
0
,
read
));
int
state
=
WHITESPACE
;
ArrayList
<
String
>
result
=
new
ArrayList
<
String
>();
int
cmdLen
=
cmd
.
length
();
StringBuilder
builder
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
cmdLen
;
i
++)
{
char
c
=
cmd
.
charAt
(
i
);
if
(
state
==
PLAIN
)
{
if
(
Character
.
isWhitespace
(
c
))
{
result
.
add
(
builder
.
toString
());
builder
.
delete
(
0
,
builder
.
length
());
state
=
WHITESPACE
;
}
else
if
(
c
==
'"'
)
{
state
=
INQUOTE
;
}
else
{
builder
.
append
(
c
);
}
}
else
if
(
state
==
WHITESPACE
)
{
if
(
Character
.
isWhitespace
(
c
))
{
// do nothing
}
else
if
(
c
==
'"'
)
{
state
=
INQUOTE
;
}
else
{
state
=
PLAIN
;
builder
.
append
(
c
);
}
}
else
if
(
state
==
INQUOTE
)
{
if
(
c
==
'\\'
)
{
if
(
i
+
1
<
cmdLen
)
{
i
+=
1
;
builder
.
append
(
cmd
.
charAt
(
i
));
}
}
else
if
(
c
==
'"'
)
{
state
=
PLAIN
;
}
else
{
builder
.
append
(
c
);
}
}
}
if
(
builder
.
length
()
>
0
)
{
result
.
add
(
builder
.
toString
());
}
return
result
;
}
}
}
catch
(
Exception
ex
)
{
@Override
Log
.
e
(
TAG
,
"Cannot execute command"
,
ex
);
public
void
run
()
{
if
(
result
!=
null
)
result
.
append
(
"\n"
).
append
(
ex
);
pid
[
0
]
=
-
1
;
}
finally
{
if
(
pipe
!=
null
)
{
try
{
Exec
.
close
(
pipe
);
}
if
(
this
.
asroot
)
{
if
(
pid
[
0
]
!=
-
1
)
{
// Create the "su" request to run the script
Exec
.
hangupProcessGroup
(
pid
[
0
]);
pipe
=
createSubprocess
(
pid
,
root_shell
);
}
else
{
// Create the "sh" request to run the script
pipe
=
createSubprocess
(
pid
,
getShell
());
}
if
(
pid
[
0
]
!=
-
1
)
{
exitcode
=
Exec
.
waitFor
(
pid
[
0
]);
}
if
(
result
==
null
||
pipe
==
null
)
return
;
final
InputStream
stdout
=
new
FileInputStream
(
pipe
);
final
byte
buf
[]
=
new
byte
[
8192
];
int
read
=
0
;
// Read stdout
while
(
stdout
.
available
()
>
0
)
{
read
=
stdout
.
read
(
buf
);
result
.
append
(
new
String
(
buf
,
0
,
read
));
}
}
catch
(
Exception
ex
)
{
Log
.
e
(
TAG
,
"Cannot execute command"
,
ex
);
if
(
result
!=
null
)
result
.
append
(
"\n"
).
append
(
ex
);
}
finally
{
if
(
pipe
!=
null
)
{
Exec
.
close
(
pipe
);
}
if
(
pid
[
0
]
!=
-
1
)
{
Exec
.
hangupProcessGroup
(
pid
[
0
]);
}
}
}
}
}
}
}
}
}
}
src/com/github/shadowsocks/preferences/SummaryEditTextPreference.java
View file @
3d664377
...
@@ -3,36 +3,31 @@ package com.github.shadowsocks.preferences;
...
@@ -3,36 +3,31 @@ package com.github.shadowsocks.preferences;
import
android.content.Context
;
import
android.content.Context
;
import
android.preference.EditTextPreference
;
import
android.preference.EditTextPreference
;
import
android.util.AttributeSet
;
import
android.util.AttributeSet
;
import
com.github.shadowsocks.R
;
public
class
SummaryEditTextPreference
extends
EditTextPreference
{
public
class
SummaryEditTextPreference
extends
EditTextPreference
{
private
CharSequence
mDefaultSummary
;
private
CharSequence
mDefaultSummary
;
public
SummaryEditTextPreference
(
Context
context
,
AttributeSet
attrs
,
int
defStyle
)
{
public
SummaryEditTextPreference
(
Context
context
,
AttributeSet
attrs
,
int
defStyle
)
{
super
(
context
,
attrs
,
defStyle
);
super
(
context
,
attrs
,
defStyle
);
mDefaultSummary
=
getSummary
();
mDefaultSummary
=
getSummary
();
}
}
public
SummaryEditTextPreference
(
Context
context
,
AttributeSet
attrs
)
{
public
SummaryEditTextPreference
(
Context
context
,
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
super
(
context
,
attrs
);
mDefaultSummary
=
getSummary
();
mDefaultSummary
=
getSummary
();
}
}
public
SummaryEditTextPreference
(
Context
context
)
{
public
SummaryEditTextPreference
(
Context
context
)
{
super
(
context
);
super
(
context
);
}
}
@Override
@Override
public
void
onDialogClosed
(
boolean
positiveResult
)
{
public
void
setSummary
(
CharSequence
summary
)
{
super
.
onDialogClosed
(
positiveResult
);
if
(
summary
.
toString
().
isEmpty
())
{
if
(
positiveResult
)
{
super
.
setSummary
(
mDefaultSummary
);
String
value
=
getEditText
().
getText
().
toString
();
}
else
{
if
(
value
.
isEmpty
())
{
super
.
setSummary
(
summary
);
setSummary
(
mDefaultSummary
);
}
else
{
setSummary
(
value
);
}
}
}
}
}
}
}
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