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
dbb137c5
Commit
dbb137c5
authored
Dec 16, 2016
by
Mygod
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial redesign draft for #762
parent
7eddf8be
Changes
34
Show whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
1069 additions
and
729 deletions
+1069
-729
build.sbt
build.sbt
+2
-1
src/main/AndroidManifest.xml
src/main/AndroidManifest.xml
+19
-25
src/main/java/com/github/shadowsocks/DialogFragment.java
src/main/java/com/github/shadowsocks/DialogFragment.java
+15
-0
src/main/res/drawable/ic_action_delete.xml
src/main/res/drawable/ic_action_delete.xml
+9
-0
src/main/res/drawable/ic_action_done.xml
src/main/res/drawable/ic_action_done.xml
+9
-0
src/main/res/drawable/ic_image_edit.xml
src/main/res/drawable/ic_image_edit.xml
+9
-0
src/main/res/layout/layout_global_settings.xml
src/main/res/layout/layout_global_settings.xml
+12
-0
src/main/res/layout/layout_main.xml
src/main/res/layout/layout_main.xml
+76
-72
src/main/res/layout/layout_profile_config.xml
src/main/res/layout/layout_profile_config.xml
+12
-0
src/main/res/layout/layout_profiles.xml
src/main/res/layout/layout_profiles.xml
+9
-64
src/main/res/layout/layout_profiles_item.xml
src/main/res/layout/layout_profiles_item.xml
+46
-36
src/main/res/menu/profile_config_menu.xml
src/main/res/menu/profile_config_menu.xml
+32
-0
src/main/res/menu/profile_manager_menu.xml
src/main/res/menu/profile_manager_menu.xml
+20
-0
src/main/res/values-v21/dimen.xml
src/main/res/values-v21/dimen.xml
+1
-1
src/main/res/values/colors.xml
src/main/res/values/colors.xml
+2
-1
src/main/res/values/dimen.xml
src/main/res/values/dimen.xml
+1
-1
src/main/res/values/styles.xml
src/main/res/values/styles.xml
+2
-2
src/main/res/xml/pref_global.xml
src/main/res/xml/pref_global.xml
+16
-0
src/main/res/xml/pref_profile.xml
src/main/res/xml/pref_profile.xml
+1
-40
src/main/scala/com/github/shadowsocks/AppManager.scala
src/main/scala/com/github/shadowsocks/AppManager.scala
+11
-24
src/main/scala/com/github/shadowsocks/GlobalConfigFragment.scala
...n/scala/com/github/shadowsocks/GlobalConfigFragment.scala
+69
-0
src/main/scala/com/github/shadowsocks/GlobalSettingsFragment.scala
...scala/com/github/shadowsocks/GlobalSettingsFragment.scala
+14
-0
src/main/scala/com/github/shadowsocks/MainActivity.scala
src/main/scala/com/github/shadowsocks/MainActivity.scala
+152
-167
src/main/scala/com/github/shadowsocks/ProfileConfigActivity.scala
.../scala/com/github/shadowsocks/ProfileConfigActivity.scala
+20
-0
src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala
.../scala/com/github/shadowsocks/ProfileConfigFragment.scala
+103
-0
src/main/scala/com/github/shadowsocks/ProfilesFragment.scala
src/main/scala/com/github/shadowsocks/ProfilesFragment.scala
+270
-0
src/main/scala/com/github/shadowsocks/QRCodeDialog.scala
src/main/scala/com/github/shadowsocks/QRCodeDialog.scala
+41
-0
src/main/scala/com/github/shadowsocks/ServiceBoundContext.scala
...in/scala/com/github/shadowsocks/ServiceBoundContext.scala
+2
-2
src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala
...cala/com/github/shadowsocks/ShadowsocksNotification.scala
+1
-1
src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
...in/scala/com/github/shadowsocks/ShadowsocksSettings.scala
+0
-286
src/main/scala/com/github/shadowsocks/ToolbarFragment.scala
src/main/scala/com/github/shadowsocks/ToolbarFragment.scala
+17
-0
src/main/scala/com/github/shadowsocks/database/Profile.scala
src/main/scala/com/github/shadowsocks/database/Profile.scala
+40
-1
src/main/scala/com/github/shadowsocks/utils/Constants.scala
src/main/scala/com/github/shadowsocks/utils/Constants.scala
+8
-5
src/main/scala/com/github/shadowsocks/utils/Typefaces.scala
src/main/scala/com/github/shadowsocks/utils/Typefaces.scala
+28
-0
No files found.
build.sbt
View file @
dbb137c5
...
@@ -31,17 +31,18 @@ resConfigs := Seq("ja", "ru", "zh-rCN", "zh-rTW")
...
@@ -31,17 +31,18 @@ resConfigs := Seq("ja", "ru", "zh-rCN", "zh-rTW")
resolvers
+=
Resolver
.
jcenterRepo
resolvers
+=
Resolver
.
jcenterRepo
libraryDependencies
++=
libraryDependencies
++=
"com.android.support"
%
"cardview-v7"
%
"25.1.0"
::
"com.android.support"
%
"design"
%
"25.1.0"
::
"com.android.support"
%
"design"
%
"25.1.0"
::
"com.android.support"
%
"gridlayout-v7"
%
"25.1.0"
::
"com.android.support"
%
"gridlayout-v7"
%
"25.1.0"
::
"com.android.support"
%
"preference-v14"
%
"25.1.0"
::
"com.android.support"
%
"preference-v14"
%
"25.1.0"
::
"com.evernote"
%
"android-job"
%
"1.1.3"
::
"com.evernote"
%
"android-job"
%
"1.1.3"
::
"com.github.clans"
%
"fab"
%
"1.6.4"
::
"com.github.jorgecastilloprz"
%
"fabprogresscircle"
%
"1.01"
::
"com.github.jorgecastilloprz"
%
"fabprogresscircle"
%
"1.01"
::
"com.github.kevinsawicki"
%
"http-request"
%
"6.0"
::
"com.github.kevinsawicki"
%
"http-request"
%
"6.0"
::
"com.google.android.gms"
%
"play-services-ads"
%
"10.0.1"
::
"com.google.android.gms"
%
"play-services-ads"
%
"10.0.1"
::
"com.google.android.gms"
%
"play-services-analytics"
%
"10.0.1"
::
"com.google.android.gms"
%
"play-services-analytics"
%
"10.0.1"
::
"com.google.android.gms"
%
"play-services-gcm"
%
"10.0.1"
::
"com.google.android.gms"
%
"play-services-gcm"
%
"10.0.1"
::
"com.j256.ormlite"
%
"ormlite-android"
%
"5.0"
::
"com.j256.ormlite"
%
"ormlite-android"
%
"5.0"
::
"com.mikepenz"
%
"materialdrawer"
%
"5.8.1"
::
"com.twofortyfouram"
%
"android-plugin-api-for-locale"
%
"1.0.2"
::
"com.twofortyfouram"
%
"android-plugin-api-for-locale"
%
"1.0.2"
::
"dnsjava"
%
"dnsjava"
%
"2.1.7"
::
"dnsjava"
%
"dnsjava"
%
"2.1.7"
::
"eu.chainfire"
%
"libsuperuser"
%
"1.0.0.201608240809"
::
"eu.chainfire"
%
"libsuperuser"
%
"1.0.0.201608240809"
::
...
...
src/main/AndroidManifest.xml
View file @
dbb137c5
...
@@ -38,7 +38,7 @@
...
@@ -38,7 +38,7 @@
android:value=
"AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg"
/>
android:value=
"AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg"
/>
<activity
<activity
android:name=
".
Shadowsocks
"
android:name=
".
MainActivity
"
android:label=
"@string/app_name"
android:label=
"@string/app_name"
android:launchMode=
"singleTask"
>
android:launchMode=
"singleTask"
>
<intent-filter>
<intent-filter>
...
@@ -49,28 +49,6 @@
...
@@ -49,28 +49,6 @@
<intent-filter>
<intent-filter>
<action
android:name=
"android.service.quicksettings.action.QS_TILE_PREFERENCES"
/>
<action
android:name=
"android.service.quicksettings.action.QS_TILE_PREFERENCES"
/>
</intent-filter>
</intent-filter>
<meta-data
android:name=
"android.app.shortcuts"
android:resource=
"@xml/shortcuts"
/>
</activity>
<activity
android:name=
".ShadowsocksRunnerActivity"
android:theme=
"@android:style/Theme.Translucent.NoTitleBar"
android:excludeFromRecents=
"true"
android:taskAffinity=
""
android:launchMode=
"singleTask"
>
</activity>
<activity
android:name=
".ProfileManagerActivity"
android:label=
"@string/profiles"
android:parentActivityName=
".Shadowsocks"
android:excludeFromRecents=
"true"
android:launchMode=
"singleTask"
>
<intent-filter>
<action
android:name=
"com.github.shadowsocks.ProfileManagerActivity"
/>
<category
android:name=
"android.intent.category.DEFAULT"
/>
</intent-filter>
<intent-filter>
<intent-filter>
<action
android:name=
"android.intent.action.VIEW"
/>
<action
android:name=
"android.intent.action.VIEW"
/>
<category
android:name=
"android.intent.category.DEFAULT"
/>
<category
android:name=
"android.intent.category.DEFAULT"
/>
...
@@ -82,18 +60,34 @@
...
@@ -82,18 +60,34 @@
<category
android:name=
"android.intent.category.DEFAULT"
/>
<category
android:name=
"android.intent.category.DEFAULT"
/>
<data
android:scheme=
"ss"
/>
<data
android:scheme=
"ss"
/>
</intent-filter>
</intent-filter>
<meta-data
android:name=
"android.app.shortcuts"
android:resource=
"@xml/shortcuts"
/>
</activity>
</activity>
<activity
android:name=
".ProfileConfigActivity"
android:excludeFromRecents=
"true"
android:label=
"Profile config"
android:launchMode=
"singleTask"
android:parentActivityName=
".MainActivity"
/>
<activity
android:name=
".ShadowsocksRunnerActivity"
android:theme=
"@android:style/Theme.Translucent.NoTitleBar"
android:excludeFromRecents=
"true"
android:taskAffinity=
""
android:launchMode=
"singleTask"
/>
<activity
<activity
android:name=
".ScannerActivity"
android:name=
".ScannerActivity"
android:label=
"@string/add_profile_methods_scan_qr_code"
android:label=
"@string/add_profile_methods_scan_qr_code"
android:parentActivityName=
".
ProfileManager
Activity"
android:parentActivityName=
".
Main
Activity"
android:excludeFromRecents=
"true"
/>
android:excludeFromRecents=
"true"
/>
<activity
<activity
android:name=
".AppManager"
android:name=
".AppManager"
android:label=
"@string/proxied_apps"
android:label=
"@string/proxied_apps"
android:parentActivityName=
".
Shadowsocks
"
android:parentActivityName=
".
ProfileConfigActivity
"
android:excludeFromRecents=
"true"
android:excludeFromRecents=
"true"
android:launchMode=
"singleTask"
/>
android:launchMode=
"singleTask"
/>
...
...
src/main/java/com/github/shadowsocks/DialogFragment.java
0 → 100644
View file @
dbb137c5
package
com.github.shadowsocks
;
import
android.app.Activity
;
/**
* This helper class is used to solve Java-Scala interop problem. Use onAttach(Context) and remove this class
* when minSdkVersion >= 23.
*
* @author Mygod
*/
public
class
DialogFragment
extends
android
.
app
.
DialogFragment
{
protected
void
superOnAttach
(
Activity
activity
)
{
super
.
onAttach
(
activity
);
}
}
src/main/res/drawable/ic_action_delete.xml
0 → 100644
View file @
dbb137c5
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24.0"
android:viewportHeight=
"24.0"
>
<path
android:fillColor=
"#FFFFFFFF"
android:pathData=
"M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"
/>
</vector>
src/main/res/drawable/ic_action_done.xml
0 → 100644
View file @
dbb137c5
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24.0"
android:viewportHeight=
"24.0"
>
<path
android:fillColor=
"#FFFFFFFF"
android:pathData=
"M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"
/>
</vector>
src/main/res/drawable/ic_
social_share
.xml
→
src/main/res/drawable/ic_
image_edit
.xml
View file @
dbb137c5
...
@@ -5,5 +5,5 @@
...
@@ -5,5 +5,5 @@
android:viewportHeight=
"24.0"
>
android:viewportHeight=
"24.0"
>
<path
<path
android:fillColor=
"#FF000000"
android:fillColor=
"#FF000000"
android:pathData=
"M
18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92
z"
/>
android:pathData=
"M
3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83
z"
/>
</vector>
</vector>
src/main/res/layout/layout_global_settings.xml
0 → 100644
View file @
dbb137c5
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:orientation=
"vertical"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<include
layout=
"@layout/toolbar_light_dark"
/>
<fragment
android:id=
"@+id/content"
class=
"com.github.shadowsocks.GlobalConfigFragment"
android:layout_width=
"match_parent"
android:layout_height=
"0dp"
android:layout_weight=
"1"
/>
</LinearLayout>
src/main/res/layout/layout_main.xml
View file @
dbb137c5
...
@@ -3,17 +3,23 @@
...
@@ -3,17 +3,23 @@
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
tools:ignore=
"UnknownIdInLayout"
android:clipChildren=
"false"
>
tools:ignore=
"UnknownIdInLayout"
android:clipChildren=
"false"
>
<LinearLayout
android:id=
"@+id/title_bar"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
<FrameLayout
android:id=
"@+id/content"
android:orientation=
"vertical"
android:layout_below=
"@+id/toolbar"
android:layout_width=
"match_parent"
android:animateLayoutChanges=
"true"
>
android:layout_height=
"match_parent"
<include
layout=
"@layout/toolbar_light_dark"
/>
android:layout_above=
"@+id/stat"
/>
<LinearLayout
android:id=
"@+id/stat"
android:elevation=
"4dp"
android:visibility=
"gone"
<LinearLayout
android:id=
"@+id/stat"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_width=
"match_parent"
android:minHeight=
"?android:attr/listPreferredItemHeight"
android:gravity=
"center_vertical"
android:layout_height=
"wrap_content"
android:paddingStart=
"16dip"
android:paddingEnd=
"16dip"
android:focusable=
"true"
android:elevation=
"4dp"
android:paddingTop=
"16dip"
android:paddingBottom=
"16dip"
android:paddingStart=
"16dip"
android:background=
"@drawable/background_stat"
android:orientation=
"vertical"
android:paddingEnd=
"16dip"
android:nextFocusRight=
"@+id/fab"
>
android:paddingTop=
"16dip"
android:paddingBottom=
"16dip"
android:focusable=
"true"
android:background=
"@drawable/background_stat"
android:orientation=
"vertical"
android:nextFocusRight=
"@+id/fab"
android:layout_alignParentBottom=
"true"
>
<LinearLayout
android:layout_width=
"match_parent"
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
>
android:layout_height=
"wrap_content"
>
<TextView
android:text=
"@string/stat"
<TextView
android:text=
"@string/stat"
...
@@ -69,17 +75,15 @@
...
@@ -69,17 +75,15 @@
app:layout_gravity=
"fill_horizontal"
android:gravity=
"end"
/>
app:layout_gravity=
"fill_horizontal"
android:gravity=
"end"
/>
</android.support.v7.widget.GridLayout>
</android.support.v7.widget.GridLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<com.github.jorgecastilloprz.FABProgressCircle
android:id=
"@+id/fabProgressCircle"
<fragment
android:name=
"com.github.shadowsocks.ShadowsocksSettings"
app:reusable=
"true"
android:id=
"@android:id/content"
android:layout_below=
"@+id/title_bar"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
<com.github.jorgecastilloprz.FABProgressCircle
android:id=
"@+id/fabProgressCircle"
app:reusable=
"true"
android:layout_width=
"wrap_content"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:elevation=
"6dp"
android:layout_height=
"wrap_content"
android:layout_alignBottom=
"@+id/title_bar"
android:elevation=
"6dp"
android:layout_alignTop=
"@+id/stat"
android:layout_alignParentEnd=
"true"
android:layout_alignParentEnd=
"true"
android:layout_marginEnd=
"16dp"
android:layout_marginEnd=
"16dp"
android:layout_margin
Bottom=
"@dimen/fab_margin_bottom
"
>
android:layout_margin
Top=
"@dimen/fab_margin_top
"
>
<android.support.design.widget.FloatingActionButton
android:id=
"@+id/fab"
android:layout_width=
"wrap_content"
<android.support.design.widget.FloatingActionButton
android:id=
"@+id/fab"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
app:backgroundTint=
"@color/material_blue_grey_700"
app:backgroundTint=
"@color/material_blue_grey_700"
...
...
src/main/res/layout/layout_profile_config.xml
0 → 100644
View file @
dbb137c5
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:orientation=
"vertical"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<include
layout=
"@layout/toolbar_light_dark"
/>
<fragment
android:id=
"@+id/content"
class=
"com.github.shadowsocks.ProfileConfigFragment"
android:layout_width=
"match_parent"
android:layout_height=
"0dp"
android:layout_weight=
"1"
/>
</LinearLayout>
src/main/res/layout/layout_profiles.xml
View file @
dbb137c5
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:layout_width=
"match_parent"
android:orientation=
"vertical"
android:layout_height=
"match_parent"
>
<include
layout=
"@layout/toolbar_light_dark"
/>
<android.support.design.widget.CoordinatorLayout
android:layout_width=
"match_parent"
android:layout_height=
"0dp"
android:layout_weight=
"1"
>
<android.support.v7.widget.RecyclerView
android:id=
"@+id/profilesList"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
<com.github.clans.fab.FloatingActionMenu
android:id=
"@+id/menu"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_height=
"match_parent"
android:layout_gravity=
"bottom|end"
android:orientation=
"vertical"
>
android:layout_marginBottom=
"16dp"
<include
layout=
"@layout/toolbar_light_dark"
/>
android:layout_marginEnd=
"16dp"
<android.support.v7.widget.RecyclerView
xmlns:android=
"http://schemas.android.com/apk/res/android"
app:menu_colorNormal=
"@color/material_primary_500"
android:id=
"@+id/profilesList"
app:menu_colorPressed=
"@color/material_primary_700"
android:layout_width=
"match_parent"
app:fab_colorRipple=
"@android:color/white"
android:layout_height=
"0dp"
app:layout_behavior=
"com.github.shadowsocks.widget.FloatingActionMenuBehavior"
>
android:layout_weight=
"1"
android:paddingBottom=
"8dp"
/>
<com.github.clans.fab.FloatingActionButton
android:id=
"@+id/fab_manual_add"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
app:fab_colorNormal=
"@color/material_primary_500"
app:fab_colorPressed=
"@color/material_primary_700"
app:fab_colorRipple=
"@android:color/white"
app:fab_size=
"mini"
app:fab_label=
"@string/add_profile_methods_manual_settings"
/>
<com.github.clans.fab.FloatingActionButton
android:id=
"@+id/fab_qrcode_add"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
app:fab_colorNormal=
"@color/material_primary_500"
app:fab_colorPressed=
"@color/material_primary_700"
app:fab_colorRipple=
"@android:color/white"
app:fab_size=
"mini"
app:fab_label=
"@string/add_profile_methods_scan_qr_code"
/>
<com.github.clans.fab.FloatingActionButton
android:id=
"@+id/fab_nfc_add"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
app:fab_colorNormal=
"@color/material_primary_500"
app:fab_colorPressed=
"@color/material_primary_700"
app:fab_colorRipple=
"@android:color/white"
app:fab_size=
"mini"
app:fab_label=
"NFC"
/>
<com.github.clans.fab.FloatingActionButton
android:id=
"@+id/fab_import_add"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
app:fab_colorNormal=
"@color/material_primary_500"
app:fab_colorPressed=
"@color/material_primary_700"
app:fab_colorRipple=
"@android:color/white"
app:fab_size=
"mini"
app:fab_label=
"@string/action_import"
/>
</com.github.clans.fab.FloatingActionMenu>
</android.support.design.widget.CoordinatorLayout>
</LinearLayout>
</LinearLayout>
src/main/res/layout/layout_profiles_item.xml
View file @
dbb137c5
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!-- Based on: https://github.com/android/platform_frameworks_base/blob/
master/core/res/res/layout/select_dialog_singlechoice_material
.xml -->
<!-- Based on: https://github.com/android/platform_frameworks_base/blob/
505e3ab/core/res/res/layout/simple_list_item_2
.xml -->
<
LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<
android.support.v7.widget.CardView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:id=
"@+id/container"
android:background=
"?android:attr/selectableItemBackground"
android:background=
"?android:attr/selectableItemBackground"
android:paddingStart=
"20dp"
android:paddingEnd=
"?attr/dialogPreferredPadding"
android:focusable=
"true"
android:focusable=
"true"
android:nextFocusRight=
"@+id/share"
>
android:nextFocusRight=
"@+id/edit"
<CheckedTextView
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_marginTop=
"8dp"
android:id=
"@android:id/text1"
android:layout_marginStart=
"8dp"
android:layout_marginEnd=
"8dp"
>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
android:layout_margin=
"8dp"
>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:id=
"@+id/container"
>
<TextView
android:id=
"@android:id/text1"
android:layout_width=
"0dp"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:layout_gravity=
"center_vertical"
android:layout_weight=
"1"
android:layout_weight=
"1"
android:minHeight=
"?android:attr/listPreferredItemHeightSmall"
android:textAppearance=
"?android:attr/textAppearanceLarge"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textColor=
"?android:attr/textColorPrimary"
android:textColor=
"?android:attr/textColorAlertDialogListItem"
android:gravity=
"center_vertical"
android:gravity=
"center_vertical"
android:drawableStart=
"?android:attr/listChoiceIndicatorSingle"
android:maxLines=
"2"
android:drawablePadding=
"20dp"
android:ellipsize=
"end"
/>
android:maxLines=
"4"
<ImageView
android:id=
"@+id/edit"
android:scrollHorizontally=
"false"
/>
<ImageView
android:id=
"@+id/share"
android:layout_width=
"wrap_content"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:padding=
"5
dp"
android:padding=
"4
dp"
android:layout_gravity=
"center_vertical
"
android:layout_gravity=
"top
"
app:srcCompat=
"@drawable/ic_social_share
"
app:srcCompat=
"@drawable/ic_image_edit
"
android:background=
"?attr/selectableItemBackgroundBorderless"
android:background=
"?attr/selectableItemBackgroundBorderless"
android:contentDescription=
"@string/share
"
android:contentDescription=
"Edit
"
android:focusable=
"true"
android:focusable=
"true"
android:nextFocusLeft=
"@+id/container"
/>
android:nextFocusLeft=
"@+id/container"
/>
</LinearLayout>
</LinearLayout>
<TextView
android:id=
"@android:id/text2"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textColor=
"?android:attr/textColorSecondary"
/>
</LinearLayout>
</android.support.v7.widget.CardView>
src/main/res/menu/profile_config_menu.xml
0 → 100644
View file @
dbb137c5
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
>
<item
android:title=
"@string/share"
android:icon=
"@drawable/abc_ic_menu_share_mtrl_alpha"
android:alphabeticShortcut=
"s"
android:numericShortcut=
"1"
app:showAsAction=
"always"
>
<menu>
<item
android:id=
"@+id/action_qr_code_nfc"
android:title=
"QR code/NFC"
android:alphabeticShortcut=
"q"
android:numericShortcut=
"1"
/>
<item
android:id=
"@+id/action_export"
android:title=
"@string/action_export"
android:alphabeticShortcut=
"e"
android:numericShortcut=
"2"
/>
</menu>
</item>
<item
android:id=
"@+id/action_delete"
android:title=
"Delete"
android:icon=
"@drawable/ic_action_delete"
android:alphabeticShortcut=
"d"
android:numericShortcut=
"2"
app:showAsAction=
"always"
/>
<item
android:id=
"@+id/action_apply"
android:title=
"Apply"
android:icon=
"@drawable/ic_action_done"
android:alphabeticShortcut=
"a"
android:numericShortcut=
"3"
app:showAsAction=
"always"
/>
</menu>
src/main/res/menu/profile_manager_menu.xml
View file @
dbb137c5
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android=
"http://schemas.android.com/apk/res/android"
<menu
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
>
xmlns:app=
"http://schemas.android.com/apk/res-auto"
>
<item
android:title=
"@string/add_profile"
android:icon=
"@drawable/ic_content_add"
android:alphabeticShortcut=
"a"
android:numericShortcut=
"0"
app:showAsAction=
"always"
>
<menu>
<item
android:id=
"@+id/action_scan_qr_code"
android:title=
"@string/add_profile_methods_scan_qr_code"
android:alphabeticShortcut=
"s"
android:numericShortcut=
"1"
/>
<item
android:id=
"@+id/action_import"
android:title=
"@string/action_import"
android:alphabeticShortcut=
"i"
android:numericShortcut=
"2"
/>
<item
android:id=
"@+id/action_manual_settings"
android:title=
"@string/add_profile_methods_manual_settings"
android:alphabeticShortcut=
"m"
android:numericShortcut=
"3"
/>
</menu>
</item>
<item
android:id=
"@+id/action_export"
<item
android:id=
"@+id/action_export"
android:title=
"@string/action_export"
android:title=
"@string/action_export"
android:alphabeticShortcut=
"e"
android:alphabeticShortcut=
"e"
...
...
src/main/res/values-v21/dimen.xml
View file @
dbb137c5
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources>
<dimen
name=
"fab_margin_
bottom
"
>
-28dp
</dimen>
<dimen
name=
"fab_margin_
top
"
>
-28dp
</dimen>
</resources>
</resources>
src/main/res/values/colors.xml
View file @
dbb137c5
...
@@ -3,12 +3,13 @@
...
@@ -3,12 +3,13 @@
<resources>
<resources>
<color
name=
"material_green_700"
>
#388E3C
</color>
<color
name=
"material_green_700"
>
#388E3C
</color>
<color
name=
"material_green_a700"
>
#00C853
</color>
<color
name=
"material_blue_grey_300"
>
#90A4AE
</color>
<color
name=
"material_blue_grey_300"
>
#90A4AE
</color>
<color
name=
"material_blue_grey_500"
>
#607D8B
</color>
<color
name=
"material_blue_grey_500"
>
#607D8B
</color>
<color
name=
"material_blue_grey_700"
>
#455A64
</color>
<color
name=
"material_blue_grey_700"
>
#455A64
</color>
<color
name=
"material_primary_300"
>
@color/material_blue_grey_300
</color>
<color
name=
"material_primary_300"
>
@color/material_blue_grey_300
</color>
<color
name=
"material_primary_500"
>
@color/material_blue_grey_500
</color>
<color
name=
"material_primary_500"
>
@color/material_blue_grey_500
</color>
<color
name=
"material_primary_700"
>
@color/material_blue_grey_700
</color>
<color
name=
"material_primary_700"
>
@color/material_blue_grey_700
</color>
<color
name=
"material_accent_200"
>
@color/material_
blue_grey_5
00
</color>
<color
name=
"material_accent_200"
>
@color/material_
green_a7
00
</color>
</resources>
</resources>
src/main/res/values/dimen.xml
View file @
dbb137c5
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources>
<!-- TODO: Remove when this issue is fixed: https://github.com/JorgeCastilloPrz/FABProgressCircle/issues/19 -->
<!-- TODO: Remove when this issue is fixed: https://github.com/JorgeCastilloPrz/FABProgressCircle/issues/19 -->
<dimen
name=
"fab_margin_
bottom
"
>
-44dp
</dimen>
<dimen
name=
"fab_margin_
top
"
>
-44dp
</dimen>
</resources>
</resources>
src/main/res/values/styles.xml
View file @
dbb137c5
...
@@ -6,11 +6,11 @@
...
@@ -6,11 +6,11 @@
<item
name=
"android:textSize"
>
24sp
</item>
<item
name=
"android:textSize"
>
24sp
</item>
</style>
</style>
<style
name=
"Theme.MaterialBase"
parent=
"
Theme.AppCompat.Light.NoActionB
ar"
>
<style
name=
"Theme.MaterialBase"
parent=
"
MaterialDrawerTheme.Light.DarkToolb
ar"
>
<!--suppress NewApi -->
<!--suppress NewApi -->
<item
name=
"android:navigationBarColor"
>
@color/material_primary_700
</item>
<item
name=
"android:navigationBarColor"
>
@color/material_primary_700
</item>
<item
name=
"colorAccent"
>
@color/material_primary_500
</item>
<item
name=
"colorAccent"
>
@color/material_primary_500
</item>
<item
name=
"colorButtonNormal"
>
@color/material_
primary_5
00
</item>
<item
name=
"colorButtonNormal"
>
@color/material_
accent_2
00
</item>
<item
name=
"colorPrimary"
>
@color/material_primary_500
</item>
<item
name=
"colorPrimary"
>
@color/material_primary_500
</item>
<item
name=
"colorPrimaryDark"
>
@color/material_primary_700
</item>
<item
name=
"colorPrimaryDark"
>
@color/material_primary_700
</item>
<item
name=
"preferenceTheme"
>
@style/Theme.Material.PreferenceThemeOverlay
</item>
<item
name=
"preferenceTheme"
>
@style/Theme.Material.PreferenceThemeOverlay
</item>
...
...
src/main/res/xml/pref_global.xml
0 → 100644
View file @
dbb137c5
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<SwitchPreference
android:key=
"isAutoConnect"
android:persistent=
"false"
android:summary=
"@string/auto_connect_summary"
android:title=
"@string/auto_connect"
/>
<SwitchPreference
android:key=
"tcp_fastopen"
android:summary=
"@string/tcp_fastopen_summary"
android:title=
"TCP Fast Open"
/>
<SwitchPreference
android:key=
"isNAT"
android:title=
"@string/nat"
android:summary=
"@string/nat_summary"
/>
<Preference
android:key=
"recovery"
android:title=
"@string/recovery"
android:summary=
"@string/recovery_summary"
/>
</PreferenceScreen>
src/main/res/xml/pref_
all
.xml
→
src/main/res/xml/pref_
profile
.xml
View file @
dbb137c5
...
@@ -7,44 +7,37 @@
...
@@ -7,44 +7,37 @@
<be.mygod.preference.EditTextPreference
<be.mygod.preference.EditTextPreference
android:key=
"profileName"
android:key=
"profileName"
android:persistent=
"false"
android:summary=
"%s"
android:summary=
"%s"
android:title=
"@string/profile_name"
/>
android:title=
"@string/profile_name"
/>
<be.mygod.preference.EditTextPreference
<be.mygod.preference.EditTextPreference
android:key=
"proxy"
android:key=
"proxy"
android:persistent=
"false"
android:summary=
"%s"
android:summary=
"%s"
android:title=
"@string/proxy"
/>
android:title=
"@string/proxy"
/>
<be.mygod.preference.NumberPickerPreference
<be.mygod.preference.NumberPickerPreference
app:min=
"1"
app:min=
"1"
app:max=
"65535"
app:max=
"65535"
android:key=
"remotePortNum"
android:key=
"remotePortNum"
android:persistent=
"false"
android:summary=
"%d"
android:summary=
"%d"
android:title=
"@string/remote_port"
/>
android:title=
"@string/remote_port"
/>
<be.mygod.preference.NumberPickerPreference
<be.mygod.preference.NumberPickerPreference
app:min=
"1025"
app:min=
"1025"
app:max=
"65535"
app:max=
"65535"
android:key=
"localPortNum"
android:key=
"localPortNum"
android:persistent=
"false"
android:summary=
"%d"
android:summary=
"%d"
android:title=
"@string/port"
/>
android:title=
"@string/port"
/>
<be.mygod.preference.EditTextPreference
<be.mygod.preference.EditTextPreference
android:inputType=
"textPassword"
android:inputType=
"textPassword"
android:key=
"sitekey"
android:key=
"sitekey"
android:persistent=
"false"
android:summary=
"%s"
android:summary=
"%s"
android:title=
"@string/sitekey"
/>
android:title=
"@string/sitekey"
/>
<DropDownPreference
<DropDownPreference
android:key=
"encMethod"
android:key=
"encMethod"
android:persistent=
"false"
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:summary=
"%s"
android:summary=
"%s"
android:title=
"@string/enc_method"
/>
android:title=
"@string/enc_method"
/>
<SwitchPreference
<SwitchPreference
android:key=
"isAuth"
android:key=
"isAuth"
android:persistent=
"false"
android:summary=
"@string/onetime_auth_summary"
android:summary=
"@string/onetime_auth_summary"
android:title=
"@string/onetime_auth"
/>
android:title=
"@string/onetime_auth"
/>
...
@@ -55,7 +48,6 @@
...
@@ -55,7 +48,6 @@
<DropDownPreference
<DropDownPreference
android:key=
"route"
android:key=
"route"
android:persistent=
"false"
android:entries=
"@array/route_entry"
android:entries=
"@array/route_entry"
android:entryValues=
"@array/route_value"
android:entryValues=
"@array/route_value"
android:summary=
"%s"
android:summary=
"%s"
...
@@ -63,20 +55,17 @@
...
@@ -63,20 +55,17 @@
<be.mygod.preference.EditTextPreference
<be.mygod.preference.EditTextPreference
android:key=
"remoteDns"
android:key=
"remoteDns"
android:title=
"@string/remote_dns"
android:title=
"@string/remote_dns"
android:summary=
"
@string/remote_dns_summary
"
/>
android:summary=
"
%s
"
/>
<SwitchPreference
<SwitchPreference
android:key=
"isIpv6"
android:key=
"isIpv6"
android:persistent=
"false"
android:summary=
"@string/ipv6_summary"
android:summary=
"@string/ipv6_summary"
android:title=
"@string/ipv6"
/>
android:title=
"@string/ipv6"
/>
<SwitchPreference
<SwitchPreference
android:key=
"isProxyApps"
android:key=
"isProxyApps"
android:persistent=
"false"
android:summary=
"@string/proxied_apps_summary"
android:summary=
"@string/proxied_apps_summary"
android:title=
"@string/proxied_apps"
/>
android:title=
"@string/proxied_apps"
/>
<SwitchPreference
<SwitchPreference
android:key=
"isUdpDns"
android:key=
"isUdpDns"
android:persistent=
"false"
android:summary=
"@string/udp_dns_summary"
android:summary=
"@string/udp_dns_summary"
android:title=
"@string/udp_dns"
/>
android:title=
"@string/udp_dns"
/>
...
@@ -87,7 +76,6 @@
...
@@ -87,7 +76,6 @@
<SwitchPreference
<SwitchPreference
android:key=
"kcp"
android:key=
"kcp"
android:persistent=
"false"
android:summary=
"@string/kcp_summary"
android:summary=
"@string/kcp_summary"
android:title=
"@string/kcp"
/>
android:title=
"@string/kcp"
/>
...
@@ -95,41 +83,14 @@
...
@@ -95,41 +83,14 @@
app:min=
"1"
app:min=
"1"
app:max=
"65535"
app:max=
"65535"
android:key=
"kcpPort"
android:key=
"kcpPort"
android:persistent=
"false"
android:summary=
"@string/kcp_port_summary"
android:summary=
"@string/kcp_port_summary"
android:title=
"@string/kcp_port"
/>
android:title=
"@string/kcp_port"
/>
<be.mygod.preference.EditTextPreference
<be.mygod.preference.EditTextPreference
android:key=
"kcpcli"
android:key=
"kcpcli"
android:persistent=
"false"
android:summary=
"%s"
android:summary=
"%s"
android:title=
"@string/kcpcli"
/>
android:title=
"@string/kcpcli"
/>
</be.mygod.preference.PreferenceCategory>
</be.mygod.preference.PreferenceCategory>
<be.mygod.preference.PreferenceCategory
android:title=
"@string/misc_cat"
>
<SwitchPreference
android:key=
"isAutoConnect"
android:persistent=
"false"
android:summary=
"@string/auto_connect_summary"
android:title=
"@string/auto_connect"
/>
<SwitchPreference
android:key=
"tcp_fastopen"
android:summary=
"@string/tcp_fastopen_summary"
android:title=
"TCP Fast Open"
/>
<SwitchPreference
android:key=
"isNAT"
android:title=
"@string/nat"
android:summary=
"@string/nat_summary"
/>
<Preference
android:key=
"recovery"
android:title=
"@string/recovery"
android:summary=
"@string/recovery_summary"
/>
<Preference
android:key=
"about"
android:title=
"@string/about"
/>
</be.mygod.preference.PreferenceCategory>
</PreferenceScreen>
</PreferenceScreen>
src/main/scala/com/github/shadowsocks/AppManager.scala
View file @
dbb137c5
...
@@ -98,10 +98,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
...
@@ -98,10 +98,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
proxiedApps
.
add
(
item
.
packageName
)
proxiedApps
.
add
(
item
.
packageName
)
check
.
setChecked
(
true
)
check
.
setChecked
(
true
)
}
}
if
(!
appsLoading
.
get
)
{
if
(!
appsLoading
.
get
)
app
.
editor
.
putString
(
Key
.
individual
,
proxiedApps
.
mkString
(
"\n"
)).
apply
()
profile
.
individual
=
proxiedApps
.
mkString
(
"\n"
)
app
.
profileManager
.
updateProfile
(
profile
)
}
}
}
}
}
...
@@ -124,9 +121,9 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
...
@@ -124,9 +121,9 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
private
var
loadingView
:
View
=
_
private
var
loadingView
:
View
=
_
private
val
appsLoading
=
new
AtomicBoolean
private
val
appsLoading
=
new
AtomicBoolean
private
var
handler
:
Handler
=
_
private
var
handler
:
Handler
=
_
private
val
profile
=
app
.
currentProfile
.
orNull
private
def
initProxiedApps
(
str
:
String
=
profile
.
individual
)
=
proxiedApps
=
str
.
split
(
'\n'
).
to
[
mutable.HashSet
]
private
def
initProxiedApps
(
str
:
String
=
app
.
settings
.
getString
(
Key
.
individual
,
null
))
=
proxiedApps
=
str
.
split
(
'\n'
).
to
[
mutable.HashSet
]
override
def
onDestroy
()
{
override
def
onDestroy
()
{
instance
=
null
instance
=
null
...
@@ -143,7 +140,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
...
@@ -143,7 +140,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
case
R
.
id
.
action_apply_all
=>
case
R
.
id
.
action_apply_all
=>
app
.
profileManager
.
getAllProfiles
match
{
app
.
profileManager
.
getAllProfiles
match
{
case
Some
(
profiles
)
=>
case
Some
(
profiles
)
=>
val
proxiedAppString
=
profile
.
individual
val
proxiedAppString
=
app
.
settings
.
getString
(
Key
.
individual
,
null
)
profiles
.
foreach
(
p
=>
{
profiles
.
foreach
(
p
=>
{
p
.
individual
=
proxiedAppString
p
.
individual
=
proxiedAppString
app
.
profileManager
.
updateProfile
(
p
)
app
.
profileManager
.
updateProfile
(
p
)
...
@@ -153,8 +150,8 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
...
@@ -153,8 +150,8 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
}
}
return
true
return
true
case
R
.
id
.
action_export
=>
case
R
.
id
.
action_export
=>
val
bypass
=
profile
.
bypass
val
bypass
=
app
.
settings
.
getBoolean
(
Key
.
bypass
,
false
)
val
proxiedAppString
=
profile
.
individual
val
proxiedAppString
=
app
.
settings
.
getString
(
Key
.
individual
,
null
)
val
clip
=
ClipData
.
newPlainText
(
Key
.
individual
,
bypass
+
"\n"
+
proxiedAppString
)
val
clip
=
ClipData
.
newPlainText
(
Key
.
individual
,
bypass
+
"\n"
+
proxiedAppString
)
clipboard
.
setPrimaryClip
(
clip
)
clipboard
.
setPrimaryClip
(
clip
)
Toast
.
makeText
(
this
,
R
.
string
.
action_export_msg
,
Toast
.
LENGTH_SHORT
).
show
()
Toast
.
makeText
(
this
,
R
.
string
.
action_export_msg
,
Toast
.
LENGTH_SHORT
).
show
()
...
@@ -170,8 +167,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
...
@@ -170,8 +167,7 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
val
(
enabled
,
apps
)
=
if
(
i
<
0
)
(
proxiedAppString
,
""
)
val
(
enabled
,
apps
)
=
if
(
i
<
0
)
(
proxiedAppString
,
""
)
else
(
proxiedAppString
.
substring
(
0
,
i
),
proxiedAppString
.
substring
(
i
+
1
))
else
(
proxiedAppString
.
substring
(
0
,
i
),
proxiedAppString
.
substring
(
i
+
1
))
bypassSwitch
.
setChecked
(
enabled
.
toBoolean
)
bypassSwitch
.
setChecked
(
enabled
.
toBoolean
)
profile
.
individual
=
apps
app
.
editor
.
putString
(
Key
.
individual
,
apps
).
apply
()
app
.
profileManager
.
updateProfile
(
profile
)
Toast
.
makeText
(
this
,
R
.
string
.
action_import_msg
,
Toast
.
LENGTH_SHORT
).
show
()
Toast
.
makeText
(
this
,
R
.
string
.
action_import_msg
,
Toast
.
LENGTH_SHORT
).
show
()
appListView
.
setVisibility
(
View
.
GONE
)
appListView
.
setVisibility
(
View
.
GONE
)
loadingView
.
setVisibility
(
View
.
VISIBLE
)
loadingView
.
setVisibility
(
View
.
VISIBLE
)
...
@@ -194,8 +190,6 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
...
@@ -194,8 +190,6 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
protected
override
def
onCreate
(
savedInstanceState
:
Bundle
)
{
protected
override
def
onCreate
(
savedInstanceState
:
Bundle
)
{
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
if
(
profile
==
null
)
finish
()
handler
=
new
Handler
()
handler
=
new
Handler
()
this
.
setContentView
(
R
.
layout
.
layout_apps
)
this
.
setContentView
(
R
.
layout
.
layout_apps
)
...
@@ -211,23 +205,16 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
...
@@ -211,23 +205,16 @@ class AppManager extends AppCompatActivity with OnMenuItemClickListener {
toolbar
.
inflateMenu
(
R
.
menu
.
app_manager_menu
)
toolbar
.
inflateMenu
(
R
.
menu
.
app_manager_menu
)
toolbar
.
setOnMenuItemClickListener
(
this
)
toolbar
.
setOnMenuItemClickListener
(
this
)
if
(!
profile
.
proxyApps
)
{
if
(!
app
.
settings
.
getBoolean
(
Key
.
proxyApps
,
false
))
app
.
editor
.
putBoolean
(
Key
.
proxyApps
,
true
).
apply
()
profile
.
proxyApps
=
true
app
.
profileManager
.
updateProfile
(
profile
)
}
findViewById
(
R
.
id
.
onSwitch
).
asInstanceOf
[
Switch
]
findViewById
(
R
.
id
.
onSwitch
).
asInstanceOf
[
Switch
]
.
setOnCheckedChangeListener
((
_
,
checked
)
=>
{
.
setOnCheckedChangeListener
((
_
,
checked
)
=>
{
profile
.
proxyApps
=
checked
app
.
editor
.
putBoolean
(
Key
.
proxyApps
,
checked
).
apply
()
app
.
profileManager
.
updateProfile
(
profile
)
finish
()
finish
()
})
})
bypassSwitch
=
findViewById
(
R
.
id
.
bypassSwitch
).
asInstanceOf
[
Switch
]
bypassSwitch
=
findViewById
(
R
.
id
.
bypassSwitch
).
asInstanceOf
[
Switch
]
bypassSwitch
.
setChecked
(
profile
.
bypass
)
bypassSwitch
.
setChecked
(
app
.
settings
.
getBoolean
(
Key
.
bypass
,
false
))
bypassSwitch
.
setOnCheckedChangeListener
((
_
,
checked
)
=>
{
bypassSwitch
.
setOnCheckedChangeListener
((
_
,
checked
)
=>
app
.
editor
.
putBoolean
(
Key
.
bypass
,
checked
).
apply
())
profile
.
bypass
=
checked
app
.
profileManager
.
updateProfile
(
profile
)
})
initProxiedApps
()
initProxiedApps
()
loadingView
=
findViewById
(
R
.
id
.
loading
)
loadingView
=
findViewById
(
R
.
id
.
loading
)
...
...
src/main/scala/com/github/shadowsocks/GlobalConfigFragment.scala
0 → 100644
View file @
dbb137c5
package
com.github.shadowsocks
import
android.app.ProgressDialog
import
android.os.
{
Bundle
,
Handler
,
Message
}
import
android.support.design.widget.Snackbar
import
android.support.v14.preference.SwitchPreference
import
be.mygod.preference.PreferenceFragment
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.utils.
{
Key
,
TcpFastOpen
,
Utils
}
class
GlobalConfigFragment
extends
PreferenceFragment
{
private
var
progressDialog
:
ProgressDialog
=
_
override
def
onCreatePreferences
(
bundle
:
Bundle
,
key
:
String
)
{
addPreferencesFromResource
(
R
.
xml
.
pref_global
)
val
switch
=
findPreference
(
Key
.
isAutoConnect
).
asInstanceOf
[
SwitchPreference
]
switch
.
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
BootReceiver
.
setEnabled
(
getActivity
,
value
.
asInstanceOf
[
Boolean
])
true
})
if
(
getPreferenceManager
.
getSharedPreferences
.
getBoolean
(
Key
.
isAutoConnect
,
false
))
{
BootReceiver
.
setEnabled
(
getActivity
,
true
)
getPreferenceManager
.
getSharedPreferences
.
edit
.
remove
(
Key
.
isAutoConnect
).
apply
()
}
switch
.
setChecked
(
BootReceiver
.
getEnabled
(
getActivity
))
val
tfo
=
findPreference
(
Key
.
tfo
).
asInstanceOf
[
SwitchPreference
]
tfo
.
setChecked
(
TcpFastOpen
.
sendEnabled
)
tfo
.
setOnPreferenceChangeListener
((
_
,
v
)
=>
{
val
value
=
v
.
asInstanceOf
[
Boolean
]
val
result
=
TcpFastOpen
.
enabled
(
value
)
if
(
result
!=
null
&&
result
!=
"Success."
)
Snackbar
.
make
(
getActivity
.
findViewById
(
android
.
R
.
id
.
content
),
result
,
Snackbar
.
LENGTH_LONG
).
show
()
value
==
TcpFastOpen
.
sendEnabled
})
if
(!
TcpFastOpen
.
supported
)
{
tfo
.
setEnabled
(
false
)
tfo
.
setSummary
(
getString
(
R
.
string
.
tcp_fastopen_summary_unsupported
,
java
.
lang
.
System
.
getProperty
(
"os.version"
)))
}
findPreference
(
"recovery"
).
setOnPreferenceClickListener
(
_
=>
{
app
.
track
(
"GlobalConfigFragment"
,
"reset"
)
Utils
.
stopSsService
(
getActivity
)
val
h
=
showProgress
(
R
.
string
.
recovering
)
Utils
.
ThrowableFuture
{
app
.
copyAssets
()
h
.
sendEmptyMessage
(
0
)
}
true
})
}
private
def
showProgress
(
msg
:
Int
)
:
Handler
=
{
clearDialog
()
progressDialog
=
ProgressDialog
.
show
(
getActivity
,
""
,
getString
(
msg
),
true
,
false
)
new
Handler
{
override
def
handleMessage
(
msg
:
Message
)
{
clearDialog
()
}
}
}
def
clearDialog
()
{
if
(
progressDialog
!=
null
&&
progressDialog
.
isShowing
)
{
if
(!
getActivity
.
isDestroyed
)
progressDialog
.
dismiss
()
progressDialog
=
null
}
}
}
src/main/scala/com/github/shadowsocks/GlobalSettingsFragment.scala
0 → 100644
View file @
dbb137c5
package
com.github.shadowsocks
import
android.os.Bundle
import
android.view.
{
LayoutInflater
,
View
,
ViewGroup
}
class
GlobalSettingsFragment
extends
ToolbarFragment
{
override
def
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
,
savedInstanceState
:
Bundle
)
:
View
=
inflater
.
inflate
(
R
.
layout
.
layout_global_settings
,
container
,
false
)
override
def
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
toolbar
.
setTitle
(
R
.
string
.
settings
)
}
}
src/main/scala/com/github/shadowsocks/
Shadowsocks
.scala
→
src/main/scala/com/github/shadowsocks/
MainActivity
.scala
View file @
dbb137c5
...
@@ -22,69 +22,58 @@ package com.github.shadowsocks
...
@@ -22,69 +22,58 @@ package com.github.shadowsocks
import
java.lang.System.currentTimeMillis
import
java.lang.System.currentTimeMillis
import
java.net.
{
HttpURLConnection
,
URL
}
import
java.net.
{
HttpURLConnection
,
URL
}
import
java.util
import
java.util.Locale
import
java.util.
{
GregorianCalendar
,
Locale
}
import
android.app.Activity
import
android.app.backup.BackupManager
import
android.app.backup.BackupManager
import
android.
app.
{
Activity
,
ProgressDialog
}
import
android.
content.SharedPreferences.OnSharedPreferenceChangeListener
import
android.content._
import
android.content._
import
android.
graphics.Typeface
import
android.
net.
{
Uri
,
VpnService
}
import
android.n
et.VpnService
import
android.n
fc.
{
NdefMessage
,
NfcAdapter
}
import
android.os.
_
import
android.os.
{
Build
,
Bundle
,
Handler
}
import
android.support.design.widget.
{
FloatingActionButton
,
Snackbar
}
import
android.support.design.widget.
{
FloatingActionButton
,
Snackbar
}
import
android.support.v4.content.ContextCompat
import
android.support.v4.content.ContextCompat
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.app.AlertDialog
import
android.support.v7.widget.Toolbar
import
android.support.v7.widget.RecyclerView.ViewHolder
import
android.text.TextUtils
import
android.util.Log
import
android.util.Log
import
android.view.
{
View
,
ViewGroup
}
import
android.view.View
import
android.widget._
import
android.webkit.
{
WebView
,
WebViewClient
}
import
android.widget.
{
TextView
,
Toast
}
import
com.github.jorgecastilloprz.FABProgressCircle
import
com.github.jorgecastilloprz.FABProgressCircle
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import
com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import
com.github.shadowsocks.database.
_
import
com.github.shadowsocks.database.
Profile
import
com.github.shadowsocks.utils.CloseUtils.
_
import
com.github.shadowsocks.utils.CloseUtils.
autoDisconnect
import
com.github.shadowsocks.utils._
import
com.github.shadowsocks.utils._
import
com.google.android.gms.ads.
{
AdRequest
,
AdSize
,
AdView
}
import
com.google.android.gms.ads.AdView
import
com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
import
com.mikepenz.materialdrawer.model.
{
PrimaryDrawerItem
,
SecondaryDrawerItem
}
import
com.mikepenz.materialdrawer.
{
Drawer
,
DrawerBuilder
}
import
scala.util.Random
object
MainActivity
{
private
final
val
TAG
=
"MainActivity"
object
Typefaces
{
def
get
(
c
:
Context
,
assetPath
:
String
)
:
Typeface
=
{
cache
synchronized
{
if
(!
cache
.
containsKey
(
assetPath
))
{
try
{
cache
.
put
(
assetPath
,
Typeface
.
createFromAsset
(
c
.
getAssets
,
assetPath
))
}
catch
{
case
e
:
Exception
=>
Log
.
e
(
TAG
,
"Could not get typeface '"
+
assetPath
+
"' because "
+
e
.
getMessage
)
app
.
track
(
e
)
return
null
}
}
return
cache
.
get
(
assetPath
)
}
}
private
final
val
TAG
=
"Typefaces"
private
final
val
cache
=
new
util
.
Hashtable
[
String
,
Typeface
]
}
object
Shadowsocks
{
// Constants
private
final
val
TAG
=
"Shadowsocks"
private
final
val
REQUEST_CONNECT
=
1
private
final
val
REQUEST_CONNECT
=
1
private
final
val
DRAWER_PROFILES
=
0L
// sticky drawer items have negative ids
private
final
val
DRAWER_GLOBAL_SETTINGS
=
-
1L
private
final
val
DRAWER_ABOUT
=
-
2L
}
}
class
Shadowsocks
extends
AppCompatActivity
with
ServiceBoundContext
{
class
MainActivity
extends
Activity
with
ServiceBoundContext
with
Drawer
.
OnDrawerItemClickListener
import
Shadowsocks._
with
OnSharedPreferenceChangeListener
{
import
MainActivity._
// Variables
// Variables
var
serviceStarted
:
Boolean
=
_
var
serviceStarted
:
Boolean
=
_
var
fab
:
FloatingActionButton
=
_
var
fab
:
FloatingActionButton
=
_
var
fabProgressCircle
:
FABProgressCircle
=
_
var
fabProgressCircle
:
FABProgressCircle
=
_
var
progressDialog
:
ProgressDialog
=
_
var
state
=
State
.
STOPPED
var
state
=
State
.
STOPPED
var
currentProfile
=
new
Profile
var
currentProfile
=
new
Profile
var
drawer
:
Drawer
=
_
private
lazy
val
profilesFragment
=
new
ProfilesFragment
()
private
lazy
val
globalSettingsFragment
=
new
GlobalSettingsFragment
()
// Services
// Services
private
val
callback
=
new
IShadowsocksServiceCallback
.
Stub
{
private
val
callback
=
new
IShadowsocksServiceCallback
.
Stub
{
...
@@ -96,8 +85,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -96,8 +85,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
fab
.
setImageResource
(
R
.
drawable
.
ic_start_busy
)
fab
.
setImageResource
(
R
.
drawable
.
ic_start_busy
)
fab
.
setEnabled
(
false
)
fab
.
setEnabled
(
false
)
fabProgressCircle
.
show
()
fabProgressCircle
.
show
()
preferences
.
setEnabled
(
false
)
//stat.setVisibility(View.GONE)
stat
.
setVisibility
(
View
.
GONE
)
case
State
.
CONNECTED
=>
case
State
.
CONNECTED
=>
fab
.
setBackgroundTintList
(
greenTint
)
fab
.
setBackgroundTintList
(
greenTint
)
if
(
state
==
State
.
CONNECTING
)
{
if
(
state
==
State
.
CONNECTING
)
{
...
@@ -107,7 +95,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -107,7 +95,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
}
}
fab
.
setEnabled
(
true
)
fab
.
setEnabled
(
true
)
changeSwitch
(
checked
=
true
)
changeSwitch
(
checked
=
true
)
preferences
.
setEnabled
(
false
)
stat
.
setVisibility
(
View
.
VISIBLE
)
stat
.
setVisibility
(
View
.
VISIBLE
)
if
(
app
.
isNatEnabled
)
connectionTestText
.
setVisibility
(
View
.
GONE
)
else
{
if
(
app
.
isNatEnabled
)
connectionTestText
.
setVisibility
(
View
.
GONE
)
else
{
connectionTestText
.
setVisibility
(
View
.
VISIBLE
)
connectionTestText
.
setVisibility
(
View
.
VISIBLE
)
...
@@ -121,20 +108,17 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -121,20 +108,17 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
if
(
m
!=
null
)
{
if
(
m
!=
null
)
{
val
snackbar
=
Snackbar
.
make
(
findViewById
(
android
.
R
.
id
.
content
),
val
snackbar
=
Snackbar
.
make
(
findViewById
(
android
.
R
.
id
.
content
),
getString
(
R
.
string
.
vpn_error
).
formatLocal
(
Locale
.
ENGLISH
,
m
),
Snackbar
.
LENGTH_LONG
)
getString
(
R
.
string
.
vpn_error
).
formatLocal
(
Locale
.
ENGLISH
,
m
),
Snackbar
.
LENGTH_LONG
)
if
(
m
==
getString
(
R
.
string
.
nat_no_root
))
snackbar
.
setAction
(
R
.
string
.
switch_to_vpn
,
if
(
m
==
getString
(
R
.
string
.
nat_no_root
))
addDisableNatToSnackbar
(
snackbar
)
(
_
=>
preferences
.
natSwitch
.
setChecked
(
false
))
:
View.OnClickListener
)
snackbar
.
show
()
snackbar
.
show
()
Log
.
e
(
TAG
,
"Error to start VPN service: "
+
m
)
Log
.
e
(
TAG
,
"Error to start VPN service: "
+
m
)
}
}
preferences
.
setEnabled
(
true
)
//stat.setVisibility(View.GONE)
stat
.
setVisibility
(
View
.
GONE
)
case
State
.
STOPPING
=>
case
State
.
STOPPING
=>
fab
.
setBackgroundTintList
(
greyTint
)
fab
.
setBackgroundTintList
(
greyTint
)
fab
.
setImageResource
(
R
.
drawable
.
ic_start_busy
)
fab
.
setImageResource
(
R
.
drawable
.
ic_start_busy
)
fab
.
setEnabled
(
false
)
fab
.
setEnabled
(
false
)
if
(
state
==
State
.
CONNECTED
)
fabProgressCircle
.
show
()
// ignore for stopped
if
(
state
==
State
.
CONNECTED
)
fabProgressCircle
.
show
()
// ignore for stopped
preferences
.
setEnabled
(
false
)
//stat.setVisibility(View.GONE)
stat
.
setVisibility
(
View
.
GONE
)
}
}
state
=
s
state
=
s
})
})
...
@@ -159,11 +143,14 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -159,11 +143,14 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
updateState
()
updateState
()
if
(
Build
.
VERSION
.
SDK_INT
>=
21
&&
app
.
isNatEnabled
)
{
if
(
Build
.
VERSION
.
SDK_INT
>=
21
&&
app
.
isNatEnabled
)
{
val
snackbar
=
Snackbar
.
make
(
findViewById
(
android
.
R
.
id
.
content
),
R
.
string
.
nat_deprecated
,
Snackbar
.
LENGTH_LONG
)
val
snackbar
=
Snackbar
.
make
(
findViewById
(
android
.
R
.
id
.
content
),
R
.
string
.
nat_deprecated
,
Snackbar
.
LENGTH_LONG
)
snackbar
.
setAction
(
R
.
string
.
switch_to_vpn
,
(
_
=>
preferences
.
natSwitch
.
setChecked
(
false
))
:
View.OnClickListene
r
)
addDisableNatToSnackbar
(
snackba
r
)
snackbar
.
show
()
snackbar
.
show
()
}
}
}
}
private
def
addDisableNatToSnackbar
(
snackbar
:
Snackbar
)
=
snackbar
.
setAction
(
R
.
string
.
switch_to_vpn
,
(
_
=>
if
(
state
==
State
.
STOPPED
)
app
.
editor
.
putBoolean
(
Key
.
isNAT
,
false
))
:
View.OnClickListener
)
override
def
onServiceDisconnected
()
{
override
def
onServiceDisconnected
()
{
if
(
fab
!=
null
)
fab
.
setEnabled
(
false
)
if
(
fab
!=
null
)
fab
.
setEnabled
(
false
)
}
}
...
@@ -184,10 +171,13 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -184,10 +171,13 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
private
lazy
val
greyTint
=
ContextCompat
.
getColorStateList
(
this
,
R
.
color
.
material_blue_grey_700
)
private
lazy
val
greyTint
=
ContextCompat
.
getColorStateList
(
this
,
R
.
color
.
material_blue_grey_700
)
private
lazy
val
greenTint
=
ContextCompat
.
getColorStateList
(
this
,
R
.
color
.
material_green_700
)
private
lazy
val
greenTint
=
ContextCompat
.
getColorStateList
(
this
,
R
.
color
.
material_green_700
)
private
var
adView
:
AdView
=
_
private
var
adView
:
AdView
=
_
private
lazy
val
preferences
=
getFragmentManager
.
findFragmentById
(
android
.
R
.
id
.
content
).
asInstanceOf
[
ShadowsocksSettings
]
val
handler
=
new
Handler
()
val
handler
=
new
Handler
()
private
val
connectedListener
:
BroadcastReceiver
=
(
_
,
_
)
=>
if
(
app
.
isNatEnabled
)
connectionTestText
.
setVisibility
(
View
.
GONE
)
else
{
connectionTestText
.
setVisibility
(
View
.
VISIBLE
)
connectionTestText
.
setText
(
getString
(
R
.
string
.
connection_test_pending
))
}
private
def
changeSwitch
(
checked
:
Boolean
)
{
private
def
changeSwitch
(
checked
:
Boolean
)
{
serviceStarted
=
checked
serviceStarted
=
checked
...
@@ -198,21 +188,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -198,21 +188,6 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
}
}
}
}
private
def
showProgress
(
msg
:
Int
)
:
Handler
=
{
clearDialog
()
progressDialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
msg
),
true
,
false
)
new
Handler
{
override
def
handleMessage
(
msg
:
Message
)
{
clearDialog
()
}
}
}
def
cancelStart
()
{
clearDialog
()
changeSwitch
(
checked
=
false
)
}
def
prepareStartService
()
{
def
prepareStartService
()
{
Utils
.
ThrowableFuture
{
Utils
.
ThrowableFuture
{
if
(
app
.
isNatEnabled
)
serviceLoad
()
else
{
if
(
app
.
isNatEnabled
)
serviceLoad
()
else
{
...
@@ -227,27 +202,30 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -227,27 +202,30 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
}
}
override
def
onCreate
(
savedInstanceState
:
Bundle
)
{
override
def
onCreate
(
savedInstanceState
:
Bundle
)
{
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
layout_main
)
setContentView
(
R
.
layout
.
layout_main
)
// Initialize Toolbar
drawer
=
new
DrawerBuilder
()
val
toolbar
=
findViewById
(
R
.
id
.
toolbar
).
asInstanceOf
[
Toolbar
]
.
withActivity
(
this
)
toolbar
.
setTitle
(
"shadowsocks"
)
// non-translatable logo
// TODO: .withHeader(R.drawable.some_header)
toolbar
.
setTitleTextAppearance
(
toolbar
.
getContext
,
R
.
style
.
Toolbar_Logo
)
.
addDrawerItems
(
val
field
=
classOf
[
Toolbar
].
getDeclaredField
(
"mTitleTextView"
)
new
PrimaryDrawerItem
()
field
.
setAccessible
(
true
)
.
withIdentifier
(
DRAWER_PROFILES
)
val
title
=
field
.
get
(
toolbar
).
asInstanceOf
[
TextView
]
.
withName
(
R
.
string
.
profiles
)
title
.
setFocusable
(
true
)
// TODO: withIcons
title
.
setGravity
(
0x10
)
)
title
.
getLayoutParams
.
height
=
ViewGroup
.
LayoutParams
.
MATCH_PARENT
.
addStickyDrawerItems
(
title
.
setOnClickListener
(
_
=>
startActivity
(
new
Intent
(
this
,
classOf
[
ProfileManagerActivity
])))
new
SecondaryDrawerItem
()
val
typedArray
=
obtainStyledAttributes
(
Array
(
R
.
attr
.
selectableItemBackgroundBorderless
))
.
withIdentifier
(
DRAWER_GLOBAL_SETTINGS
)
title
.
setBackgroundResource
(
typedArray
.
getResourceId
(
0
,
0
))
.
withName
(
R
.
string
.
settings
),
typedArray
.
recycle
()
new
SecondaryDrawerItem
()
val
tf
=
Typefaces
.
get
(
this
,
"fonts/Iceland.ttf"
)
.
withIdentifier
(
DRAWER_ABOUT
)
if
(
tf
!=
null
)
title
.
setTypeface
(
tf
)
.
withName
(
R
.
string
.
about
)
title
.
setCompoundDrawablesWithIntrinsicBounds
(
0
,
0
,
R
.
drawable
.
ic_arrow_drop_down
,
0
)
.
withSelectable
(
false
)
)
.
withOnDrawerItemClickListener
(
this
)
.
withActionBarDrawerToggle
(
true
)
.
build
()
if
(
savedInstanceState
==
null
)
displayFragment
(
profilesFragment
)
stat
=
findViewById
(
R
.
id
.
stat
)
stat
=
findViewById
(
R
.
id
.
stat
)
connectionTestText
=
findViewById
(
R
.
id
.
connection_test
).
asInstanceOf
[
TextView
]
connectionTestText
=
findViewById
(
R
.
id
.
connection_test
).
asInstanceOf
[
TextView
]
txText
=
findViewById
(
R
.
id
.
tx
).
asInstanceOf
[
TextView
]
txText
=
findViewById
(
R
.
id
.
tx
).
asInstanceOf
[
TextView
]
...
@@ -307,6 +285,81 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -307,6 +285,81 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
updateTraffic
(
0
,
0
,
0
,
0
)
updateTraffic
(
0
,
0
,
0
,
0
)
handler
.
post
(
attachServiceCallback
)
handler
.
post
(
attachServiceCallback
)
app
.
settings
.
registerOnSharedPreferenceChangeListener
(
this
)
registerReceiver
(
connectedListener
,
new
IntentFilter
(
Action
.
CONNECTED
))
val
intent
=
getIntent
if
(
intent
!=
null
)
handleShareIntent
(
intent
)
}
override
def
onNewIntent
(
intent
:
Intent
)
{
super
.
onNewIntent
(
intent
)
handleShareIntent
(
intent
)
}
def
handleShareIntent
(
intent
:
Intent
)
{
val
sharedStr
=
intent
.
getAction
match
{
case
Intent
.
ACTION_VIEW
=>
intent
.
getData
.
toString
case
NfcAdapter
.
ACTION_NDEF_DISCOVERED
=>
val
rawMsgs
=
intent
.
getParcelableArrayExtra
(
NfcAdapter
.
EXTRA_NDEF_MESSAGES
)
if
(
rawMsgs
!=
null
&&
rawMsgs
.
nonEmpty
)
new
String
(
rawMsgs
(
0
).
asInstanceOf
[
NdefMessage
].
getRecords
()(
0
).
getPayload
)
else
null
case
_
=>
null
}
if
(
TextUtils
.
isEmpty
(
sharedStr
))
return
val
profiles
=
Parser
.
findAll
(
sharedStr
).
toList
if
(
profiles
.
isEmpty
)
{
// TODO: show error msg
return
}
val
dialog
=
new
AlertDialog
.
Builder
(
this
)
.
setTitle
(
R
.
string
.
add_profile_dialog
)
.
setPositiveButton
(
android
.
R
.
string
.
yes
,
((
_
,
_
)
=>
profiles
.
foreach
(
app
.
profileManager
.
createProfile
))
:
DialogInterface.OnClickListener
)
.
setNegativeButton
(
android
.
R
.
string
.
no
,
null
)
.
setMessage
(
profiles
.
mkString
(
"\n"
))
.
create
()
dialog
.
show
()
}
def
onSharedPreferenceChanged
(
pref
:
SharedPreferences
,
key
:
String
)
:
Unit
=
key
match
{
case
Key
.
isNAT
=>
handler
.
post
(()
=>
{
detachService
()
attachServiceCallback
()
})
case
_
=>
}
private
def
displayFragment
(
fragment
:
ToolbarFragment
)
{
getFragmentManager
.
beginTransaction
().
replace
(
R
.
id
.
content
,
fragment
).
commitAllowingStateLoss
()
drawer
.
closeDrawer
()
}
override
def
onItemClick
(
view
:
View
,
position
:
Int
,
drawerItem
:
IDrawerItem
[
_
,
_
<:
ViewHolder
])
:
Boolean
=
{
drawerItem
.
getIdentifier
match
{
case
DRAWER_PROFILES
=>
displayFragment
(
profilesFragment
)
case
DRAWER_GLOBAL_SETTINGS
=>
displayFragment
(
globalSettingsFragment
)
case
DRAWER_ABOUT
=>
app
.
track
(
TAG
,
"about"
)
val
web
=
new
WebView
(
this
)
web
.
loadUrl
(
"file:///android_asset/pages/about.html"
)
web
.
setWebViewClient
(
new
WebViewClient
()
{
override
def
shouldOverrideUrlLoading
(
view
:
WebView
,
url
:
String
)
:
Boolean
=
{
try
startActivity
(
new
Intent
(
Intent
.
ACTION_VIEW
,
Uri
.
parse
(
url
)))
catch
{
case
_:
android.content.ActivityNotFoundException
=>
// Ignore
}
true
}
})
new
AlertDialog
.
Builder
(
this
)
.
setTitle
(
getString
(
R
.
string
.
about_title
).
formatLocal
(
Locale
.
ENGLISH
,
BuildConfig
.
VERSION_NAME
))
.
setNegativeButton
(
getString
(
android
.
R
.
string
.
ok
),
null
)
.
setView
(
web
)
.
create
()
.
show
()
}
true
// unexpected cases will throw exception
}
}
private
def
hideCircle
()
{
private
def
hideCircle
()
{
...
@@ -317,97 +370,44 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -317,97 +370,44 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
}
}
}
}
private
def
updateState
(
resetConnectionTest
:
Boolean
=
true
)
{
private
def
updateState
()
{
if
(
bgService
!=
null
)
{
if
(
bgService
!=
null
)
{
bgService
.
getState
match
{
bgService
.
getState
match
{
case
State
.
CONNECTING
=>
case
State
.
CONNECTING
=>
fab
.
setBackgroundTintList
(
greyTint
)
fab
.
setBackgroundTintList
(
greyTint
)
serviceStarted
=
false
serviceStarted
=
false
fab
.
setImageResource
(
R
.
drawable
.
ic_start_busy
)
fab
.
setImageResource
(
R
.
drawable
.
ic_start_busy
)
preferences
.
setEnabled
(
false
)
fabProgressCircle
.
show
()
fabProgressCircle
.
show
()
stat
.
setVisibility
(
View
.
GONE
)
//
stat.setVisibility(View.GONE)
case
State
.
CONNECTED
=>
case
State
.
CONNECTED
=>
fab
.
setBackgroundTintList
(
greenTint
)
fab
.
setBackgroundTintList
(
greenTint
)
serviceStarted
=
true
serviceStarted
=
true
fab
.
setImageResource
(
R
.
drawable
.
ic_start_connected
)
fab
.
setImageResource
(
R
.
drawable
.
ic_start_connected
)
preferences
.
setEnabled
(
false
)
fabProgressCircle
.
postDelayed
(
hideCircle
,
100
)
fabProgressCircle
.
postDelayed
(
hideCircle
,
100
)
stat
.
setVisibility
(
View
.
VISIBLE
)
stat
.
setVisibility
(
View
.
VISIBLE
)
if
(
resetConnectionTest
||
state
!=
State
.
CONNECTED
)
if
(
app
.
isNatEnabled
)
connectionTestText
.
setVisibility
(
View
.
GONE
)
else
{
connectionTestText
.
setVisibility
(
View
.
VISIBLE
)
connectionTestText
.
setText
(
getString
(
R
.
string
.
connection_test_pending
))
}
case
State
.
STOPPING
=>
case
State
.
STOPPING
=>
fab
.
setBackgroundTintList
(
greyTint
)
fab
.
setBackgroundTintList
(
greyTint
)
serviceStarted
=
false
serviceStarted
=
false
fab
.
setImageResource
(
R
.
drawable
.
ic_start_busy
)
fab
.
setImageResource
(
R
.
drawable
.
ic_start_busy
)
preferences
.
setEnabled
(
false
)
fabProgressCircle
.
show
()
fabProgressCircle
.
show
()
stat
.
setVisibility
(
View
.
GONE
)
//
stat.setVisibility(View.GONE)
case
_
=>
case
_
=>
fab
.
setBackgroundTintList
(
greyTint
)
fab
.
setBackgroundTintList
(
greyTint
)
serviceStarted
=
false
serviceStarted
=
false
fab
.
setImageResource
(
R
.
drawable
.
ic_start_idle
)
fab
.
setImageResource
(
R
.
drawable
.
ic_start_idle
)
preferences
.
setEnabled
(
true
)
fabProgressCircle
.
postDelayed
(
hideCircle
,
100
)
fabProgressCircle
.
postDelayed
(
hideCircle
,
100
)
stat
.
setVisibility
(
View
.
GONE
)
//
stat.setVisibility(View.GONE)
}
}
state
=
bgService
.
getState
state
=
bgService
.
getState
}
}
}
}
private
def
updateCurrentProfile
()
=
{
// Check if current profile changed
if
(
preferences
.
profile
==
null
||
app
.
profileId
!=
preferences
.
profile
.
id
)
{
updatePreferenceScreen
(
app
.
currentProfile
match
{
case
Some
(
profile
)
=>
profile
// updated
case
None
=>
// removed
app
.
switchProfile
((
app
.
profileManager
.
getFirstProfile
match
{
case
Some
(
first
)
=>
first
case
None
=>
app
.
profileManager
.
createDefault
()
}).
id
)
})
if
(
serviceStarted
)
serviceLoad
()
true
}
else
{
preferences
.
refreshProfile
()
false
}
}
protected
override
def
onResume
()
{
protected
override
def
onResume
()
{
super
.
onResume
()
super
.
onResume
()
app
.
refreshContainerHolder
app
.
refreshContainerHolder
()
updateState
(
updateCurrentProfile
())
}
private
def
updatePreferenceScreen
(
profile
:
Profile
)
{
if
(
profile
.
host
==
"198.199.101.152"
)
if
(
adView
==
null
)
{
adView
=
new
AdView
(
this
)
adView
.
setAdUnitId
(
"ca-app-pub-9097031975646651/7760346322"
)
adView
.
setAdSize
(
AdSize
.
SMART_BANNER
)
preferences
.
getView
.
asInstanceOf
[
ViewGroup
].
addView
(
adView
,
1
)
// Demographics
val
random
=
new
Random
()
val
adBuilder
=
new
AdRequest
.
Builder
()
adBuilder
.
setGender
(
AdRequest
.
GENDER_MALE
)
val
year
=
1975
+
random
.
nextInt
(
40
)
val
month
=
1
+
random
.
nextInt
(
12
)
val
day
=
random
.
nextInt
(
28
)
adBuilder
.
setBirthday
(
new
GregorianCalendar
(
year
,
month
,
day
).
getTime
)
// Load Ad
updateState
()
adView
.
loadAd
(
adBuilder
.
build
())
}
else
adView
.
setVisibility
(
View
.
VISIBLE
)
else
if
(
adView
!=
null
)
adView
.
setVisibility
(
View
.
GONE
)
preferences
.
setProfile
(
profile
)
}
}
override
def
onStart
()
{
override
def
onStart
()
{
...
@@ -417,30 +417,22 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -417,30 +417,22 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
override
def
onStop
()
{
override
def
onStop
()
{
super
.
onStop
()
super
.
onStop
()
unregisterCallback
()
unregisterCallback
()
clearDialog
()
}
}
override
def
onDestroy
()
{
override
def
onDestroy
()
{
super
.
onDestroy
()
super
.
onDestroy
()
unregisterReceiver
(
connectedListener
)
app
.
settings
.
unregisterOnSharedPreferenceChangeListener
(
this
)
detachService
()
detachService
()
new
BackupManager
(
this
).
dataChanged
()
new
BackupManager
(
this
).
dataChanged
()
handler
.
removeCallbacksAndMessages
(
null
)
handler
.
removeCallbacksAndMessages
(
null
)
}
}
def
recovery
()
{
if
(
serviceStarted
)
serviceStop
()
val
h
=
showProgress
(
R
.
string
.
recovering
)
Utils
.
ThrowableFuture
{
app
.
copyAssets
()
h
.
sendEmptyMessage
(
0
)
}
}
override
def
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
)
:
Unit
=
resultCode
match
{
override
def
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
)
:
Unit
=
resultCode
match
{
case
Activity
.
RESULT_OK
=>
case
Activity
.
RESULT_OK
=>
serviceLoad
()
serviceLoad
()
case
_
=>
case
_
=>
c
ancelStart
(
)
c
hangeSwitch
(
checked
=
false
)
Log
.
e
(
TAG
,
"Failed to start VpnService"
)
Log
.
e
(
TAG
,
"Failed to start VpnService"
)
}
}
...
@@ -456,11 +448,4 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
...
@@ -456,11 +448,4 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
changeSwitch
(
checked
=
false
)
changeSwitch
(
checked
=
false
)
}
}
}
}
def
clearDialog
()
{
if
(
progressDialog
!=
null
&&
progressDialog
.
isShowing
)
{
if
(!
isDestroyed
)
progressDialog
.
dismiss
()
progressDialog
=
null
}
}
}
}
src/main/scala/com/github/shadowsocks/ProfileConfigActivity.scala
0 → 100644
View file @
dbb137c5
package
com.github.shadowsocks
import
android.app.Activity
import
android.os.Bundle
import
android.support.v7.widget.Toolbar
import
android.support.v7.widget.Toolbar.OnMenuItemClickListener
class
ProfileConfigActivity
extends
Activity
{
override
def
onCreate
(
savedInstanceState
:
Bundle
)
{
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
layout_profile_config
)
val
toolbar
=
findViewById
(
R
.
id
.
toolbar
).
asInstanceOf
[
Toolbar
]
toolbar
.
setTitle
(
"Profile config"
)
// TODO
toolbar
.
setNavigationIcon
(
R
.
drawable
.
ic_navigation_close
)
toolbar
.
setNavigationOnClickListener
(
_
=>
finish
())
toolbar
.
inflateMenu
(
R
.
menu
.
profile_config_menu
)
toolbar
.
setOnMenuItemClickListener
(
getFragmentManager
.
findFragmentById
(
R
.
id
.
content
)
.
asInstanceOf
[
OnMenuItemClickListener
])
}
}
src/main/scala/com/github/shadowsocks/
widget/FloatingActionMenuBehavior
.scala
→
src/main/scala/com/github/shadowsocks/
ProfileConfigFragment
.scala
View file @
dbb137c5
...
@@ -18,68 +18,86 @@
...
@@ -18,68 +18,86 @@
/* */
/* */
/*******************************************************************************/
/*******************************************************************************/
package
com.github.shadowsocks
.widget
package
com.github.shadowsocks
import
android.animation.ValueAnimator
import
android.content._
import
android.content.Context
import
android.os._
import
android.support.design.widget.CoordinatorLayout
import
android.support.v14.preference.SwitchPreference
import
android.support.design.widget.CoordinatorLayout.Behavior
import
android.support.v4.content.LocalBroadcastManager
import
android.support.design.widget.Snackbar.SnackbarLayout
import
android.support.v7.app.AlertDialog
import
android.support.v4.view.animation.FastOutSlowInInterpolator
import
android.support.v7.preference.Preference
import
android.util.AttributeSet
import
android.support.v7.widget.Toolbar.OnMenuItemClickListener
import
android.view.View
import
android.view.MenuItem
import
com.github.clans.fab.FloatingActionMenu
import
be.mygod.preference._
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.preferences.KcpCliPreferenceDialogFragment
import
com.github.shadowsocks.utils.
{
Action
,
Key
,
Utils
}
import
scala.collection.JavaConverters._
class
ProfileConfigFragment
extends
PreferenceFragment
with
OnMenuItemClickListener
{
private
lazy
val
clipboard
=
getActivity
.
getSystemService
(
Context
.
CLIPBOARD_SERVICE
).
asInstanceOf
[
ClipboardManager
]
private
var
profile
:
Profile
=
_
private
var
isProxyApps
:
SwitchPreference
=
_
/**
override
def
onCreatePreferences
(
bundle
:
Bundle
,
key
:
String
)
{
* Behavior for com.github.clans.fab.FloatingActionMenu that is aware of Snackbars and scrolling.
app
.
profileManager
.
getProfile
(
getActivity
.
getIntent
.
getIntExtra
(
Action
.
EXTRA_PROFILE_ID
,
-
1
))
match
{
*
case
Some
(
p
)
=>
* @author Mygod
profile
=
p
*/
profile
.
serialize
(
app
.
editor
).
apply
()
class
FloatingActionMenuBehavior
(
context
:
Context
,
attrs
:
AttributeSet
)
case
None
=>
getActivity
.
finish
()
extends
Behavior
[
FloatingActionMenu
](
context
,
attrs
)
{
private
var
fabTranslationYAnimator
:
ValueAnimator
=
_
private
var
fabTranslationY
:
Float
=
_
override
def
layoutDependsOn
(
parent
:
CoordinatorLayout
,
child
:
FloatingActionMenu
,
dependency
:
View
)
:
Boolean
=
dependency
.
isInstanceOf
[
SnackbarLayout
]
override
def
onDependentViewChanged
(
parent
:
CoordinatorLayout
,
child
:
FloatingActionMenu
,
dependency
:
View
)
:
Boolean
=
{
dependency
match
{
case
_:
SnackbarLayout
=>
var
targetTransY
=
parent
.
getDependencies
(
child
).
asScala
.
filter
(
view
=>
view
.
isInstanceOf
[
SnackbarLayout
]
&&
parent
.
doViewsOverlap
(
child
,
view
))
.
map
(
view
=>
view
.
getTranslationY
-
view
.
getHeight
).
reduceOption
(
_
min
_
).
getOrElse
(
0F
)
if
(
targetTransY
>
0
)
targetTransY
=
0
if
(
fabTranslationY
!=
targetTransY
)
{
val
currentTransY
=
child
.
getTranslationY
if
(
fabTranslationYAnimator
!=
null
&&
fabTranslationYAnimator
.
isRunning
)
fabTranslationYAnimator
.
cancel
()
if
(
child
.
isShown
&&
Math
.
abs
(
currentTransY
-
targetTransY
)
>
child
.
getHeight
*
0.667F
)
{
if
(
fabTranslationYAnimator
==
null
)
{
fabTranslationYAnimator
=
new
ValueAnimator
fabTranslationYAnimator
.
setInterpolator
(
new
FastOutSlowInInterpolator
)
fabTranslationYAnimator
.
addUpdateListener
(
animation
=>
child
.
setTranslationY
(
animation
.
getAnimatedValue
.
asInstanceOf
[
Float
]))
}
fabTranslationYAnimator
.
setFloatValues
(
currentTransY
,
targetTransY
)
fabTranslationYAnimator
.
start
()
}
else
child
.
setTranslationY
(
targetTransY
)
fabTranslationY
=
targetTransY
}
}
addPreferencesFromResource
(
R
.
xml
.
pref_profile
)
if
(
Build
.
VERSION
.
SDK_INT
>=
25
&&
getActivity
.
getSystemService
(
classOf
[
UserManager
]).
isDemoUser
)
{
findPreference
(
Key
.
host
).
setSummary
(
"shadowsocks.example.org"
)
findPreference
(
Key
.
remotePort
).
setSummary
(
"1337"
)
findPreference
(
Key
.
password
).
setSummary
(
"\u2022"
*
32
)
}
}
isProxyApps
=
findPreference
(
Key
.
proxyApps
).
asInstanceOf
[
SwitchPreference
]
isProxyApps
.
setEnabled
(
Utils
.
isLollipopOrAbove
||
app
.
isNatEnabled
)
isProxyApps
.
setOnPreferenceClickListener
(
_
=>
{
startActivity
(
new
Intent
(
getActivity
,
classOf
[
AppManager
]))
isProxyApps
.
setChecked
(
true
)
false
false
})
}
override
def
onResume
()
{
super
.
onResume
()
isProxyApps
.
setChecked
(
app
.
settings
.
getBoolean
(
Key
.
proxyApps
,
false
))
// fetch proxyApps updated by AppManager
}
override
def
onDisplayPreferenceDialog
(
preference
:
Preference
)
:
Unit
=
preference
.
getKey
match
{
case
Key
.
kcpcli
=>
displayPreferenceDialog
(
Key
.
kcpcli
,
new
KcpCliPreferenceDialogFragment
())
case
_
=>
super
.
onDisplayPreferenceDialog
(
preference
)
}
}
override
def
onStartNestedScroll
(
parent
:
CoordinatorLayout
,
child
:
FloatingActionMenu
,
directTargetChild
:
View
,
override
def
onMenuItemClick
(
item
:
MenuItem
)
:
Boolean
=
item
.
getItemId
match
{
target
:
View
,
nestedScrollAxes
:
Int
)
=
true
case
R
.
id
.
action_qr_code_nfc
=>
override
def
onNestedScroll
(
parent
:
CoordinatorLayout
,
child
:
FloatingActionMenu
,
target
:
View
,
dxConsumed
:
Int
,
getFragmentManager
.
beginTransaction
().
add
(
new
QRCodeDialog
(
profile
.
toString
),
""
).
commitAllowingStateLoss
()
dyConsumed
:
Int
,
dxUnconsumed
:
Int
,
dyUnconsumed
:
Int
)
{
true
super
.
onNestedScroll
(
parent
,
child
,
target
,
dxConsumed
,
dyConsumed
,
dxUnconsumed
,
dyUnconsumed
)
case
R
.
id
.
action_export
=>
val
dy
=
dyConsumed
+
dyUnconsumed
clipboard
.
setPrimaryClip
(
ClipData
.
newPlainText
(
null
,
profile
.
toString
))
if
(
child
.
isMenuButtonHidden
)
{
true
if
(
dy
<
0
)
child
.
showMenuButton
(
true
)
case
R
.
id
.
action_delete
=>
}
else
if
(
dy
>
0
)
child
.
hideMenuButton
(
true
)
new
AlertDialog
.
Builder
(
getActivity
)
.
setTitle
(
"Confirm?"
)
// TODO
.
setPositiveButton
(
android
.
R
.
string
.
yes
,
((
_
,
_
)
=>
{
app
.
profileManager
.
delProfile
(
profile
.
id
)
LocalBroadcastManager
.
getInstance
(
getActivity
)
.
sendBroadcast
(
new
Intent
(
Action
.
PROFILE_REMOVED
).
putExtra
(
Action
.
EXTRA_PROFILE_ID
,
profile
.
id
))
getActivity
.
finish
()
})
:
DialogInterface.OnClickListener
)
.
setNegativeButton
(
android
.
R
.
string
.
no
,
null
)
.
create
()
.
show
()
true
case
R
.
id
.
action_apply
=>
profile
.
deserialize
(
app
.
settings
)
app
.
profileManager
.
updateProfile
(
profile
)
LocalBroadcastManager
.
getInstance
(
getActivity
)
.
sendBroadcast
(
new
Intent
(
Action
.
PROFILE_CHANGED
).
putExtra
(
Action
.
EXTRA_PROFILE_ID
,
profile
.
id
))
getActivity
.
finish
()
true
case
_
=>
false
}
}
}
}
src/main/scala/com/github/shadowsocks/Profile
ManagerActivity
.scala
→
src/main/scala/com/github/shadowsocks/Profile
sFragment
.scala
View file @
dbb137c5
...
@@ -20,131 +20,78 @@
...
@@ -20,131 +20,78 @@
package
com.github.shadowsocks
package
com.github.shadowsocks
import
java.nio.charset.Charset
import
android.app.Activity
import
android.app.TaskStackBuilder
import
android.content._
import
android.content._
import
android.content.pm.PackageManager
import
android.graphics.Typeface
import
android.nfc.NfcAdapter.CreateNdefMessageCallback
import
android.nfc.
{
NdefMessage
,
NdefRecord
,
NfcAdapter
,
NfcEvent
}
import
android.os.
{
Build
,
Bundle
,
Handler
,
UserManager
}
import
android.os.
{
Build
,
Bundle
,
Handler
,
UserManager
}
import
android.provider.Settings
import
android.support.v4.content.LocalBroadcastManager
import
android.support.v7.app.
{
AlertDialog
,
AppCompatActivity
}
import
android.support.v7.widget.RecyclerView.ViewHolder
import
android.support.v7.widget.RecyclerView.ViewHolder
import
android.support.v7.widget.Toolbar.OnMenuItemClickListener
import
android.support.v7.widget.Toolbar.OnMenuItemClickListener
import
android.support.v7.widget._
import
android.support.v7.widget._
import
android.support.v7.widget.helper.ItemTouchHelper
import
android.support.v7.widget.helper.ItemTouchHelper
import
android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback
import
android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback
import
android.text.style.TextAppearanceSpan
import
android.text.
{
SpannableStringBuilder
,
Spanned
,
TextUtils
}
import
android.view._
import
android.view._
import
android.widget.
{
CheckedTextView
,
ImageView
,
LinearLayout
,
Toast
}
import
android.widget.
{
TextView
,
Toast
}
import
com.github.clans.fab.
{
FloatingActionButton
,
FloatingActionMenu
}
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import
com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.utils.
{
Key
,
Parser
,
TrafficMonitor
,
Utils
}
import
com.github.shadowsocks.utils.
{
Action
,
Parser
,
TrafficMonitor
,
Utils
}
import
com.github.shadowsocks.widget.UndoSnackbarManager
import
com.github.shadowsocks.widget.UndoSnackbarManager
import
net.glxn.qrgen.android.QRCode
import
scala.collection.mutable.ArrayBuffer
import
scala.collection.mutable.ArrayBuffer
final
class
ProfileManagerActivity
extends
AppCompatActivity
with
OnMenuItemClickListener
with
ServiceBoundContext
final
class
ProfilesFragment
extends
ToolbarFragment
with
OnMenuItemClickListener
{
with
View
.
OnClickListener
with
CreateNdefMessageCallback
{
private
final
class
ProfileViewHolder
(
val
view
:
View
)
extends
RecyclerView
.
ViewHolder
(
view
)
private
final
class
ProfileViewHolder
(
val
view
:
View
)
extends
RecyclerView
.
ViewHolder
(
view
)
with
View
.
OnClickListener
with
View
.
OnKeyListener
{
with
View
.
OnClickListener
{
var
item
:
Profile
=
_
var
item
:
Profile
=
_
private
val
text
=
itemView
.
findViewById
(
android
.
R
.
id
.
text1
).
asInstanceOf
[
CheckedTextView
]
private
val
text1
=
itemView
.
findViewById
(
android
.
R
.
id
.
text1
).
asInstanceOf
[
TextView
]
private
val
text2
=
itemView
.
findViewById
(
android
.
R
.
id
.
text2
).
asInstanceOf
[
TextView
]
itemView
.
setOnClickListener
(
this
)
itemView
.
setOnClickListener
(
this
)
itemView
.
setOnKeyListener
(
this
)
{
{
val
shareBtn
=
itemView
.
findViewById
(
R
.
id
.
share
)
val
edit
=
itemView
.
findViewById
(
R
.
id
.
edit
)
shareBtn
.
setOnClickListener
(
_
=>
{
edit
.
setOnClickListener
(
_
=>
startActivity
(
new
Intent
(
getActivity
,
classOf
[
ProfileConfigActivity
])
val
url
=
item
.
toString
.
putExtra
(
Action
.
EXTRA_PROFILE_ID
,
item
.
id
)))
if
(
isNfcBeamEnabled
)
{
edit
.
setOnLongClickListener
(
_
=>
{
nfcAdapter
.
setNdefPushMessageCallback
(
ProfileManagerActivity
.
this
,
ProfileManagerActivity
.
this
)
Utils
.
positionToast
(
Toast
.
makeText
(
getActivity
,
edit
.
getContentDescription
,
Toast
.
LENGTH_SHORT
),
edit
,
nfcShareItem
=
url
.
getBytes
(
Charset
.
forName
(
"UTF-8"
))
getActivity
.
getWindow
,
0
,
Utils
.
dpToPx
(
getActivity
,
8
)).
show
()
}
val
image
=
new
ImageView
(
ProfileManagerActivity
.
this
)
image
.
setLayoutParams
(
new
LinearLayout
.
LayoutParams
(-
1
,
-
1
))
val
qrcode
=
QRCode
.
from
(
url
)
.
withSize
(
Utils
.
dpToPx
(
ProfileManagerActivity
.
this
,
250
),
Utils
.
dpToPx
(
ProfileManagerActivity
.
this
,
250
))
.
asInstanceOf
[
QRCode
].
bitmap
()
image
.
setImageBitmap
(
qrcode
)
val
dialog
=
new
AlertDialog
.
Builder
(
ProfileManagerActivity
.
this
)
.
setCancelable
(
true
)
.
setPositiveButton
(
R
.
string
.
close
,
null
)
.
setNegativeButton
(
R
.
string
.
copy_url
,
((
_
,
_
)
=>
clipboard
.
setPrimaryClip
(
ClipData
.
newPlainText
(
null
,
url
)))
:
DialogInterface.OnClickListener
)
.
setView
(
image
)
.
setTitle
(
R
.
string
.
share
)
.
create
()
if
(!
isNfcAvailable
)
dialog
.
setMessage
(
getString
(
R
.
string
.
share_message_without_nfc
))
else
if
(!
isNfcBeamEnabled
)
{
dialog
.
setMessage
(
getString
(
R
.
string
.
share_message_nfc_disabled
))
dialog
.
setButton
(
DialogInterface
.
BUTTON_NEUTRAL
,
getString
(
R
.
string
.
turn_on_nfc
),
((
_
,
_
)
=>
startActivity
(
new
Intent
(
Settings
.
ACTION_NFC_SETTINGS
)))
:
DialogInterface.OnClickListener
)
}
else
{
dialog
.
setMessage
(
getString
(
R
.
string
.
share_message
))
dialog
.
setOnDismissListener
(
_
=>
nfcAdapter
.
setNdefPushMessageCallback
(
null
,
ProfileManagerActivity
.
this
))
}
dialog
.
show
()
})
shareBtn
.
setOnLongClickListener
(
_
=>
{
Utils
.
positionToast
(
Toast
.
makeText
(
ProfileManagerActivity
.
this
,
R
.
string
.
share
,
Toast
.
LENGTH_SHORT
),
shareBtn
,
getWindow
,
0
,
Utils
.
dpToPx
(
ProfileManagerActivity
.
this
,
8
)).
show
()
true
true
})
})
}
}
def
updateText
(
txTotal
:
Long
=
0
,
rxTotal
:
Long
=
0
)
{
def
updateText
(
txTotal
:
Long
=
0
,
rxTotal
:
Long
=
0
)
{
val
builder
=
new
SpannableStringBuilder
val
tx
=
item
.
tx
+
txTotal
val
tx
=
item
.
tx
+
txTotal
val
rx
=
item
.
rx
+
rxTotal
val
rx
=
item
.
rx
+
rxTotal
builder
.
append
(
if
(
isDemoMode
)
"Profile #"
+
item
.
id
else
item
.
name
)
var
title
=
if
(
isDemoMode
)
"Profile #"
+
item
.
id
else
item
.
name
val
builder
=
new
StringBuilder
()
val
address
=
(
if
(
item
.
host
.
contains
(
":"
))
"[%s]:%d"
else
"%s:%d"
).
format
(
item
.
host
,
item
.
remotePort
)
if
(
title
==
null
||
title
.
isEmpty
)
title
=
address
else
builder
.
append
(
address
)
if
(
tx
!=
0
||
rx
!=
0
)
{
if
(
tx
!=
0
||
rx
!=
0
)
{
val
start
=
builder
.
length
if
(
builder
.
nonEmpty
)
builder
.
append
(
'\n'
)
builder
.
append
(
getString
(
R
.
string
.
stat_profiles
,
builder
.
append
(
getString
(
R
.
string
.
stat_profiles
,
TrafficMonitor
.
formatTraffic
(
tx
),
TrafficMonitor
.
formatTraffic
(
rx
)))
TrafficMonitor
.
formatTraffic
(
tx
),
TrafficMonitor
.
formatTraffic
(
rx
)))
builder
.
setSpan
(
new
TextAppearanceSpan
(
ProfileManagerActivity
.
this
,
android
.
R
.
style
.
TextAppearance_Small
),
start
+
1
,
builder
.
length
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
handler
.
post
(()
=>
text
.
setText
(
builder
))
handler
.
post
(()
=>
{
text1
.
setText
(
title
)
text2
.
setText
(
builder
)
text2
.
setVisibility
(
if
(
builder
.
isEmpty
)
View
.
GONE
else
View
.
VISIBLE
)
})
}
}
def
bind
(
item
:
Profile
)
{
def
bind
(
item
:
Profile
)
{
this
.
item
=
item
this
.
item
=
item
updateText
()
updateText
()
if
(
item
.
id
==
app
.
profileId
)
{
if
(
item
.
id
==
app
.
profileId
)
{
text
.
setChecked
(
true
)
text
1
.
setTypeface
(
null
,
Typeface
.
BOLD
)
selectedItem
=
this
selectedItem
=
this
}
else
{
}
else
{
text
.
setChecked
(
false
)
text
1
.
setTypeface
(
null
,
Typeface
.
NORMAL
)
if
(
selectedItem
eq
this
)
selectedItem
=
null
if
(
selectedItem
eq
this
)
selectedItem
=
null
}
}
}
}
def
onClick
(
v
:
View
)
{
def
onClick
(
v
:
View
)
:
Unit
=
app
.
switchProfile
(
item
.
id
)
app
.
switchProfile
(
item
.
id
)
finish
()
}
def
onKey
(
v
:
View
,
keyCode
:
Int
,
event
:
KeyEvent
)
:
Boolean
=
if
(
event
.
getAction
==
KeyEvent
.
ACTION_DOWN
)
keyCode
match
{
case
KeyEvent
.
KEYCODE_DPAD_LEFT
=>
val
index
=
getAdapterPosition
if
(
index
>=
0
)
{
profilesAdapter
.
remove
(
index
)
undoManager
.
remove
(
index
,
item
)
true
}
else
false
case
_
=>
false
}
else
false
}
}
private
class
ProfilesAdapter
extends
RecyclerView
.
Adapter
[
ProfileViewHolder
]
{
private
class
ProfilesAdapter
extends
RecyclerView
.
Adapter
[
ProfileViewHolder
]
{
...
@@ -196,46 +143,48 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
...
@@ -196,46 +143,48 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
app
.
profileManager
.
delProfile
(
item
.
id
)
app
.
profileManager
.
delProfile
(
item
.
id
)
if
(
item
.
id
==
app
.
profileId
)
app
.
profileId
(-
1
)
if
(
item
.
id
==
app
.
profileId
)
app
.
profileId
(-
1
)
}
}
def
refreshId
(
id
:
Int
)
{
val
index
=
profiles
.
indexWhere
(
_
.
id
==
id
)
profiles
(
index
)
=
app
.
profileManager
.
getProfile
(
id
).
get
notifyItemChanged
(
index
)
}
def
removeId
(
id
:
Int
)
{
val
index
=
profiles
.
indexWhere
(
_
.
id
==
id
)
profiles
.
remove
(
index
)
notifyItemRemoved
(
index
)
}
}
}
private
var
selectedItem
:
ProfileViewHolder
=
_
private
var
selectedItem
:
ProfileViewHolder
=
_
private
val
handler
=
new
Handler
private
val
handler
=
new
Handler
private
val
profilesListener
:
BroadcastReceiver
=
(
_
,
intent
)
=>
{
private
var
menu
:
FloatingActionMenu
=
_
val
id
=
intent
.
getIntExtra
(
Action
.
EXTRA_PROFILE_ID
,
-
1
)
intent
.
getAction
match
{
case
Action
.
PROFILE_CHANGED
=>
profilesAdapter
.
refreshId
(
id
)
case
Action
.
PROFILE_REMOVED
=>
profilesAdapter
.
removeId
(
id
)
}
}
private
lazy
val
profilesAdapter
=
new
ProfilesAdapter
private
lazy
val
profilesAdapter
=
new
ProfilesAdapter
private
var
undoManager
:
UndoSnackbarManager
[
Profile
]
=
_
private
var
undoManager
:
UndoSnackbarManager
[
Profile
]
=
_
private
lazy
val
clipboard
=
getSystemService
(
Context
.
CLIPBOARD_SERVICE
).
asInstanceOf
[
ClipboardManager
]
private
lazy
val
clipboard
=
getActivity
.
getSystemService
(
Context
.
CLIPBOARD_SERVICE
).
asInstanceOf
[
ClipboardManager
]
private
lazy
val
isDemoMode
=
Build
.
VERSION
.
SDK_INT
>=
25
&&
getSystemService
(
classOf
[
UserManager
]).
isDemoUser
private
lazy
val
isDemoMode
=
Build
.
VERSION
.
SDK_INT
>=
25
&&
getActivity
.
getSystemService
(
classOf
[
UserManager
]).
isDemoUser
private
var
nfcAdapter
:
NfcAdapter
=
_
private
var
nfcShareItem
:
Array
[
Byte
]
=
_
private
var
isNfcAvailable
:
Boolean
=
_
private
var
isNfcEnabled
:
Boolean
=
_
private
var
isNfcBeamEnabled
:
Boolean
=
_
override
def
onCreate
(
savedInstanceState
:
Bundle
)
{
override
def
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
,
savedInstanceState
:
Bundle
)
:
View
=
super
.
onCreate
(
savedInstanceState
)
inflater
.
inflate
(
R
.
layout
.
layout_profiles
,
container
,
false
)
setContentView
(
R
.
layout
.
layout_profiles
)
val
toolbar
=
findViewById
(
R
.
id
.
toolbar
).
asInstanceOf
[
Toolbar
]
override
def
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
toolbar
.
setTitle
(
R
.
string
.
profiles
)
toolbar
.
setTitle
(
R
.
string
.
profiles
)
toolbar
.
setNavigationIcon
(
R
.
drawable
.
ic_navigation_close
)
toolbar
.
setNavigationOnClickListener
(
_
=>
{
val
intent
=
getParentActivityIntent
if
(
shouldUpRecreateTask
(
intent
)
||
isTaskRoot
)
TaskStackBuilder
.
create
(
this
).
addNextIntentWithParentStack
(
intent
).
startActivities
()
else
finish
()
})
toolbar
.
inflateMenu
(
R
.
menu
.
profile_manager_menu
)
toolbar
.
inflateMenu
(
R
.
menu
.
profile_manager_menu
)
toolbar
.
setOnMenuItemClickListener
(
this
)
toolbar
.
setOnMenuItemClickListener
(
this
)
initFab
()
app
.
profileManager
.
setProfileAddedListener
(
profilesAdapter
.
add
)
app
.
profileManager
.
setProfileAddedListener
(
profilesAdapter
.
add
)
val
profilesList
=
findViewById
(
R
.
id
.
profilesList
).
asInstanceOf
[
RecyclerView
]
val
profilesList
=
view
.
findViewById
(
R
.
id
.
profilesList
).
asInstanceOf
[
RecyclerView
]
val
layoutManager
=
new
LinearLayoutManager
(
this
)
val
layoutManager
=
new
LinearLayoutManager
(
getActivity
)
profilesList
.
setLayoutManager
(
layoutManager
)
profilesList
.
setLayoutManager
(
layoutManager
)
profilesList
.
setItemAnimator
(
new
DefaultItemAnimator
)
profilesList
.
setItemAnimator
(
new
DefaultItemAnimator
)
profilesList
.
setAdapter
(
profilesAdapter
)
profilesList
.
setAdapter
(
profilesAdapter
)
...
@@ -255,169 +204,65 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
...
@@ -255,169 +204,65 @@ final class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClic
true
true
}
}
}).
attachToRecyclerView
(
profilesList
)
}).
attachToRecyclerView
(
profilesList
)
val
filter
=
new
IntentFilter
(
Action
.
PROFILE_CHANGED
)
filter
.
addAction
(
Action
.
PROFILE_REMOVED
)
LocalBroadcastManager
.
getInstance
(
getActivity
).
registerReceiver
(
profilesListener
,
filter
)
}
attachService
(
new
IShadowsocksServiceCallback
.
Stub
{
override
def
onAttach
(
activity
:
Activity
)
{
super
.
onAttach
(
activity
)
activity
.
asInstanceOf
[
ServiceBoundContext
].
attachService
(
new
IShadowsocksServiceCallback
.
Stub
{
def
stateChanged
(
state
:
Int
,
profileName
:
String
,
msg
:
String
)
:
Unit
=
()
// ignore
def
stateChanged
(
state
:
Int
,
profileName
:
String
,
msg
:
String
)
:
Unit
=
()
// ignore
def
trafficUpdated
(
txRate
:
Long
,
rxRate
:
Long
,
txTotal
:
Long
,
rxTotal
:
Long
)
:
Unit
=
def
trafficUpdated
(
txRate
:
Long
,
rxRate
:
Long
,
txTotal
:
Long
,
rxTotal
:
Long
)
:
Unit
=
if
(
selectedItem
!=
null
)
selectedItem
.
updateText
(
txTotal
,
rxTotal
)
if
(
selectedItem
!=
null
)
selectedItem
.
updateText
(
txTotal
,
rxTotal
)
})
})
if
(
app
.
settings
.
getBoolean
(
Key
.
profileTip
,
true
))
{
app
.
editor
.
putBoolean
(
Key
.
profileTip
,
false
).
apply
()
new
AlertDialog
.
Builder
(
this
).
setTitle
(
R
.
string
.
profile_manager_dialog
)
.
setMessage
(
R
.
string
.
profile_manager_dialog_content
).
setPositiveButton
(
R
.
string
.
gotcha
,
null
).
create
().
show
()
}
}
val
intent
=
getIntent
override
def
onStart
()
{
if
(
intent
!=
null
)
handleShareIntent
(
intent
)
super
.
onStart
()
getActivity
.
asInstanceOf
[
ServiceBoundContext
].
registerCallback
()
}
}
def
initFab
()
{
override
def
onStop
()
{
menu
=
findViewById
(
R
.
id
.
menu
).
asInstanceOf
[
FloatingActionMenu
]
super
.
onStop
()
menu
.
setClosedOnTouchOutside
(
true
)
getActivity
.
asInstanceOf
[
ServiceBoundContext
].
unregisterCallback
()
val
dm
=
AppCompatDrawableManager
.
get
val
manualAddFAB
=
findViewById
(
R
.
id
.
fab_manual_add
).
asInstanceOf
[
FloatingActionButton
]
manualAddFAB
.
setImageDrawable
(
dm
.
getDrawable
(
this
,
R
.
drawable
.
ic_content_create
))
manualAddFAB
.
setOnClickListener
(
this
)
val
qrcodeAddFAB
=
findViewById
(
R
.
id
.
fab_qrcode_add
).
asInstanceOf
[
FloatingActionButton
]
qrcodeAddFAB
.
setImageDrawable
(
dm
.
getDrawable
(
this
,
R
.
drawable
.
ic_image_camera_alt
))
qrcodeAddFAB
.
setOnClickListener
(
this
)
val
nfcAddFAB
=
findViewById
(
R
.
id
.
fab_nfc_add
).
asInstanceOf
[
FloatingActionButton
]
nfcAddFAB
.
setImageDrawable
(
dm
.
getDrawable
(
this
,
R
.
drawable
.
ic_device_nfc
))
nfcAddFAB
.
setOnClickListener
(
this
)
val
importAddFAB
=
findViewById
(
R
.
id
.
fab_import_add
).
asInstanceOf
[
FloatingActionButton
]
importAddFAB
.
setImageDrawable
(
dm
.
getDrawable
(
this
,
R
.
drawable
.
ic_content_paste
))
importAddFAB
.
setOnClickListener
(
this
)
menu
.
setOnMenuToggleListener
(
opened
=>
if
(
opened
)
qrcodeAddFAB
.
setVisibility
(
if
(
getPackageManager
.
hasSystemFeature
(
PackageManager
.
FEATURE_CAMERA
))
View
.
VISIBLE
else
View
.
GONE
))
}
}
override
def
onDetach
()
{
override
def
onResume
()
{
getActivity
.
asInstanceOf
[
ServiceBoundContext
].
detachService
()
super
.
onResume
()
undoManager
.
flush
()
updateNfcState
()
super
.
onDetach
()
}
}
override
def
on
NewIntent
(
intent
:
Intent
)
{
override
def
on
Destroy
(
)
{
super
.
onNewIntent
(
intent
)
LocalBroadcastManager
.
getInstance
(
getActivity
).
unregisterReceiver
(
profilesListener
)
handleShareIntent
(
intent
)
super
.
onDestroy
(
)
}
}
override
def
onClick
(
v
:
View
){
def
onMenuItemClick
(
item
:
MenuItem
)
:
Boolean
=
item
.
getItemId
match
{
v
.
getId
match
{
case
R
.
id
.
action_scan_qr_code
=>
case
R
.
id
.
fab_manual_add
=>
startActivity
(
new
Intent
(
getActivity
,
classOf
[
ScannerActivity
]))
menu
.
toggle
(
true
)
true
val
profile
=
app
.
profileManager
.
createProfile
()
case
R
.
id
.
action_import
=>
app
.
profileManager
.
updateProfile
(
profile
)
app
.
switchProfile
(
profile
.
id
)
finish
()
case
R
.
id
.
fab_qrcode_add
=>
menu
.
toggle
(
false
)
val
intent
=
new
Intent
(
this
,
classOf
[
ScannerActivity
])
startActivity
(
intent
)
case
R
.
id
.
fab_nfc_add
=>
menu
.
toggle
(
true
)
val
dialog
=
new
AlertDialog
.
Builder
(
ProfileManagerActivity
.
this
)
.
setCancelable
(
true
)
.
setPositiveButton
(
R
.
string
.
gotcha
,
null
)
.
setTitle
(
R
.
string
.
add_profile_nfc_hint_title
)
.
create
()
if
(!
isNfcBeamEnabled
)
{
dialog
.
setMessage
(
getString
(
R
.
string
.
share_message_nfc_disabled
))
dialog
.
setButton
(
DialogInterface
.
BUTTON_NEUTRAL
,
getString
(
R
.
string
.
turn_on_nfc
),
((
_
,
_
)
=>
startActivity
(
new
Intent
(
Settings
.
ACTION_NFC_SETTINGS
))
)
:
DialogInterface.OnClickListener
)
}
else
{
dialog
.
setMessage
(
getString
(
R
.
string
.
add_profile_nfc_hint
))
}
dialog
.
show
()
case
R
.
id
.
fab_import_add
=>
menu
.
toggle
(
true
)
if
(
clipboard
.
hasPrimaryClip
)
{
if
(
clipboard
.
hasPrimaryClip
)
{
val
profiles
=
Parser
.
findAll
(
clipboard
.
getPrimaryClip
.
getItemAt
(
0
).
getText
)
val
profiles
=
Parser
.
findAll
(
clipboard
.
getPrimaryClip
.
getItemAt
(
0
).
getText
)
if
(
profiles
.
nonEmpty
)
{
if
(
profiles
.
nonEmpty
)
{
profiles
.
foreach
(
app
.
profileManager
.
createProfile
)
profiles
.
foreach
(
app
.
profileManager
.
createProfile
)
Toast
.
makeText
(
this
,
R
.
string
.
action_import_msg
,
Toast
.
LENGTH_SHORT
).
show
()
Toast
.
makeText
(
getActivity
,
R
.
string
.
action_import_msg
,
Toast
.
LENGTH_SHORT
).
show
()
return
return
true
}
}
Toast
.
makeText
(
this
,
R
.
string
.
action_import_err
,
Toast
.
LENGTH_SHORT
).
show
()
}
}
def
updateNfcState
()
{
isNfcAvailable
=
false
isNfcEnabled
=
false
isNfcBeamEnabled
=
false
nfcAdapter
=
NfcAdapter
.
getDefaultAdapter
(
this
)
if
(
nfcAdapter
!=
null
)
{
isNfcAvailable
=
true
if
(
nfcAdapter
.
isEnabled
)
{
isNfcEnabled
=
true
if
(
nfcAdapter
.
isNdefPushEnabled
)
{
isNfcBeamEnabled
=
true
nfcAdapter
.
setNdefPushMessageCallback
(
null
,
ProfileManagerActivity
.
this
)
}
}
}
}
def
handleShareIntent
(
intent
:
Intent
)
{
val
sharedStr
=
intent
.
getAction
match
{
case
Intent
.
ACTION_VIEW
=>
intent
.
getData
.
toString
case
NfcAdapter
.
ACTION_NDEF_DISCOVERED
=>
val
rawMsgs
=
intent
.
getParcelableArrayExtra
(
NfcAdapter
.
EXTRA_NDEF_MESSAGES
)
if
(
rawMsgs
!=
null
&&
rawMsgs
.
nonEmpty
)
new
String
(
rawMsgs
(
0
).
asInstanceOf
[
NdefMessage
].
getRecords
()(
0
).
getPayload
)
else
null
case
_
=>
null
}
if
(
TextUtils
.
isEmpty
(
sharedStr
))
return
val
profiles
=
Parser
.
findAll
(
sharedStr
).
toList
if
(
profiles
.
isEmpty
)
{
finish
()
return
}
val
dialog
=
new
AlertDialog
.
Builder
(
this
)
.
setTitle
(
R
.
string
.
add_profile_dialog
)
.
setPositiveButton
(
android
.
R
.
string
.
yes
,
((
_
,
_
)
=>
profiles
.
foreach
(
app
.
profileManager
.
createProfile
))
:
DialogInterface.OnClickListener
)
.
setNegativeButton
(
android
.
R
.
string
.
no
,
((
_
,
_
)
=>
finish
())
:
DialogInterface.OnClickListener
)
.
setMessage
(
profiles
.
mkString
(
"\n"
))
.
create
()
dialog
.
show
()
}
override
def
onStart
()
{
super
.
onStart
()
registerCallback
}
override
def
onStop
()
{
super
.
onStop
()
unregisterCallback
}
}
override
def
onDestroy
()
{
detachService
()
undoManager
.
flush
()
app
.
profileManager
.
setProfileAddedListener
(
null
)
super
.
onDestroy
()
}
}
Toast
.
makeText
(
getActivity
,
R
.
string
.
action_import_err
,
Toast
.
LENGTH_SHORT
).
show
()
override
def
onBackPressed
()
{
true
if
(
menu
.
isOpened
)
menu
.
close
(
true
)
else
super
.
onBackPressed
()
case
R
.
id
.
action_manual_settings
=>
}
val
profile
=
app
.
profileManager
.
createProfile
()
app
.
profileManager
.
updateProfile
(
profile
)
def
createNdefMessage
(
nfcEvent
:
NfcEvent
)
=
new
NdefMessage
(
Array
(
new
NdefRecord
(
NdefRecord
.
TNF_ABSOLUTE_URI
,
nfcShareItem
,
Array
[
Byte
](),
nfcShareItem
)))
def
onMenuItemClick
(
item
:
MenuItem
)
:
Boolean
=
item
.
getItemId
match
{
case
R
.
id
.
action_export
=>
case
R
.
id
.
action_export
=>
app
.
profileManager
.
getAllProfiles
match
{
app
.
profileManager
.
getAllProfiles
match
{
case
Some
(
profiles
)
=>
case
Some
(
profiles
)
=>
clipboard
.
setPrimaryClip
(
ClipData
.
newPlainText
(
null
,
profiles
.
mkString
(
"\n"
)))
clipboard
.
setPrimaryClip
(
ClipData
.
newPlainText
(
null
,
profiles
.
mkString
(
"\n"
)))
Toast
.
makeText
(
this
,
R
.
string
.
action_export_msg
,
Toast
.
LENGTH_SHORT
).
show
()
Toast
.
makeText
(
getActivity
,
R
.
string
.
action_export_msg
,
Toast
.
LENGTH_SHORT
).
show
()
case
_
=>
Toast
.
makeText
(
this
,
R
.
string
.
action_export_err
,
Toast
.
LENGTH_SHORT
).
show
()
case
_
=>
Toast
.
makeText
(
getActivity
,
R
.
string
.
action_export_err
,
Toast
.
LENGTH_SHORT
).
show
()
}
}
true
true
case
_
=>
false
case
_
=>
false
...
...
src/main/scala/com/github/shadowsocks/QRCodeDialog.scala
0 → 100644
View file @
dbb137c5
package
com.github.shadowsocks
import
java.nio.charset.Charset
import
android.app.Activity
import
android.nfc.
{
NdefMessage
,
NdefRecord
,
NfcAdapter
}
import
android.os.Bundle
import
android.view.
{
LayoutInflater
,
View
,
ViewGroup
}
import
android.widget.
{
ImageView
,
LinearLayout
}
import
com.github.shadowsocks.utils.Utils
import
net.glxn.qrgen.android.QRCode
final
class
QRCodeDialog
(
url
:
String
)
extends
DialogFragment
{
private
lazy
val
nfcShareItem
=
url
.
getBytes
(
Charset
.
forName
(
"UTF-8"
))
private
var
adapter
:
NfcAdapter
=
_
override
def
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
,
savedInstanceState
:
Bundle
)
:
View
=
{
val
image
=
new
ImageView
(
getActivity
)
image
.
setLayoutParams
(
new
LinearLayout
.
LayoutParams
(-
1
,
-
1
))
val
qrcode
=
QRCode
.
from
(
url
)
.
withSize
(
Utils
.
dpToPx
(
getActivity
,
250
),
Utils
.
dpToPx
(
getActivity
,
250
))
.
asInstanceOf
[
QRCode
].
bitmap
()
image
.
setImageBitmap
(
qrcode
)
image
}
override
def
onAttach
(
activity
:
Activity
)
{
superOnAttach
(
activity
)
adapter
=
NfcAdapter
.
getDefaultAdapter
(
getActivity
)
if
(
adapter
!=
null
)
adapter
.
setNdefPushMessage
(
new
NdefMessage
(
Array
(
new
NdefRecord
(
NdefRecord
.
TNF_ABSOLUTE_URI
,
nfcShareItem
,
Array
[
Byte
](),
nfcShareItem
))),
activity
)
}
override
def
onDetach
()
{
if
(
adapter
!=
null
)
{
adapter
.
setNdefPushMessage
(
null
,
getActivity
)
adapter
=
null
}
super
.
onDetach
()
}
}
src/main/scala/com/github/shadowsocks/ServiceBoundContext.scala
View file @
dbb137c5
...
@@ -46,14 +46,14 @@ trait ServiceBoundContext extends Context with IBinder.DeathRecipient {
...
@@ -46,14 +46,14 @@ trait ServiceBoundContext extends Context with IBinder.DeathRecipient {
}
}
}
}
protected
def
registerCallback
()
:
Unit
=
if
(
bgService
!=
null
&&
callback
!=
null
&&
!
callbackRegistered
)
try
{
def
registerCallback
()
:
Unit
=
if
(
bgService
!=
null
&&
callback
!=
null
&&
!
callbackRegistered
)
try
{
bgService
.
registerCallback
(
callback
)
bgService
.
registerCallback
(
callback
)
callbackRegistered
=
true
callbackRegistered
=
true
}
catch
{
}
catch
{
case
_:
RemoteException
=>
// Nothing
case
_:
RemoteException
=>
// Nothing
}
}
protected
def
unregisterCallback
()
{
def
unregisterCallback
()
{
if
(
bgService
!=
null
&&
callback
!=
null
&&
callbackRegistered
)
try
bgService
.
unregisterCallback
(
callback
)
catch
{
if
(
bgService
!=
null
&&
callback
!=
null
&&
callbackRegistered
)
try
bgService
.
unregisterCallback
(
callback
)
catch
{
case
_:
RemoteException
=>
case
_:
RemoteException
=>
}
}
...
...
src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala
View file @
dbb137c5
...
@@ -57,7 +57,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
...
@@ -57,7 +57,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
.
setColor
(
ContextCompat
.
getColor
(
service
,
R
.
color
.
material_primary_500
))
.
setColor
(
ContextCompat
.
getColor
(
service
,
R
.
color
.
material_primary_500
))
.
setTicker
(
service
.
getString
(
R
.
string
.
forward_success
))
.
setTicker
(
service
.
getString
(
R
.
string
.
forward_success
))
.
setContentTitle
(
profileName
)
.
setContentTitle
(
profileName
)
.
setContentIntent
(
PendingIntent
.
getActivity
(
service
,
0
,
new
Intent
(
service
,
classOf
[
Shadowsocks
])
.
setContentIntent
(
PendingIntent
.
getActivity
(
service
,
0
,
new
Intent
(
service
,
classOf
[
MainActivity
])
.
setFlags
(
Intent
.
FLAG_ACTIVITY_REORDER_TO_FRONT
),
0
))
.
setFlags
(
Intent
.
FLAG_ACTIVITY_REORDER_TO_FRONT
),
0
))
.
setSmallIcon
(
R
.
drawable
.
ic_stat_shadowsocks
)
.
setSmallIcon
(
R
.
drawable
.
ic_stat_shadowsocks
)
builder
.
addAction
(
R
.
drawable
.
ic_navigation_close
,
builder
.
addAction
(
R
.
drawable
.
ic_navigation_close
,
...
...
src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala
deleted
100644 → 0
View file @
7eddf8be
/*******************************************************************************/
/* */
/* Copyright (C) 2016 by Max Lv <max.c.lv@gmail.com> */
/* Copyright (C) 2016 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
/* */
/* This program is free software: you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation, either version 3 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* */
/*******************************************************************************/
package
com.github.shadowsocks
import
java.util.Locale
import
android.content.SharedPreferences.OnSharedPreferenceChangeListener
import
android.content.
{
Intent
,
SharedPreferences
}
import
android.net.Uri
import
android.os.
{
Build
,
Bundle
,
UserManager
}
import
android.support.design.widget.Snackbar
import
android.support.v14.preference.SwitchPreference
import
android.support.v7.app.AlertDialog
import
android.support.v7.preference.
{
DropDownPreference
,
Preference
}
import
android.webkit.
{
WebView
,
WebViewClient
}
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.preferences.KcpCliPreferenceDialogFragment
import
com.github.shadowsocks.utils.
{
Key
,
TcpFastOpen
,
Utils
}
import
be.mygod.preference._
object
ShadowsocksSettings
{
// Constants
private
final
val
TAG
=
"ShadowsocksSettings"
private
val
PROXY_PREFS
=
Array
(
Key
.
name
,
Key
.
host
,
Key
.
remotePort
,
Key
.
localPort
,
Key
.
password
,
Key
.
method
,
Key
.
auth
,
Key
.
kcp
,
Key
.
kcpPort
,
Key
.
kcpcli
)
private
val
FEATURE_PREFS
=
Array
(
Key
.
route
,
Key
.
remoteDns
,
Key
.
proxyApps
,
Key
.
udpdns
,
Key
.
ipv6
)
// Helper functions
def
updateDropDownPreference
(
pref
:
Preference
,
value
:
String
)
{
pref
.
asInstanceOf
[
DropDownPreference
].
setValue
(
value
)
}
def
updateEditTextPreference
(
pref
:
Preference
,
value
:
String
)
{
pref
.
asInstanceOf
[
EditTextPreference
].
setText
(
value
)
}
def
updateNumberPickerPreference
(
pref
:
Preference
,
value
:
Int
)
{
pref
.
asInstanceOf
[
NumberPickerPreference
].
setValue
(
value
)
}
def
updateSwitchPreference
(
pref
:
Preference
,
value
:
Boolean
)
{
pref
.
asInstanceOf
[
SwitchPreference
].
setChecked
(
value
)
}
def
updatePreference
(
pref
:
Preference
,
name
:
String
,
profile
:
Profile
,
demo
:
Boolean
=
false
)
{
name
match
{
case
Key
.
name
=>
updateEditTextPreference
(
pref
,
profile
.
name
)
pref
.
setSummary
(
if
(
demo
)
"Profile #"
+
profile
.
id
else
"%s"
)
case
Key
.
host
=>
updateEditTextPreference
(
pref
,
profile
.
host
)
pref
.
setSummary
(
if
(
demo
)
"shadowsocks.example.org"
else
"%s"
)
case
Key
.
remotePort
=>
updateNumberPickerPreference
(
pref
,
profile
.
remotePort
)
pref
.
setSummary
(
if
(
demo
)
"1337"
else
"%d"
)
case
Key
.
localPort
=>
updateNumberPickerPreference
(
pref
,
profile
.
localPort
)
case
Key
.
password
=>
updateEditTextPreference
(
pref
,
profile
.
password
)
pref
.
setSummary
(
if
(
demo
)
"\u2022"
*
32
else
"%s"
)
case
Key
.
remoteDns
=>
updateEditTextPreference
(
pref
,
profile
.
remoteDns
)
if
(
profile
.
remoteDns
!=
null
)
pref
.
setSummary
(
if
(
demo
)
"8.8.8.8"
else
"%s"
)
case
Key
.
method
=>
updateDropDownPreference
(
pref
,
profile
.
method
)
case
Key
.
route
=>
updateDropDownPreference
(
pref
,
profile
.
route
)
case
Key
.
proxyApps
=>
updateSwitchPreference
(
pref
,
profile
.
proxyApps
)
case
Key
.
udpdns
=>
updateSwitchPreference
(
pref
,
profile
.
udpdns
)
case
Key
.
auth
=>
updateSwitchPreference
(
pref
,
profile
.
auth
)
case
Key
.
ipv6
=>
updateSwitchPreference
(
pref
,
profile
.
ipv6
)
case
Key
.
kcp
=>
updateSwitchPreference
(
pref
,
profile
.
kcp
)
case
Key
.
kcpPort
=>
updateNumberPickerPreference
(
pref
,
profile
.
kcpPort
)
case
Key
.
kcpcli
=>
updateEditTextPreference
(
pref
,
profile
.
kcpcli
)
}
}
}
class
ShadowsocksSettings
extends
PreferenceFragment
with
OnSharedPreferenceChangeListener
{
import
ShadowsocksSettings._
private
def
activity
=
getActivity
.
asInstanceOf
[
Shadowsocks
]
lazy
val
natSwitch
:
SwitchPreference
=
findPreference
(
Key
.
isNAT
).
asInstanceOf
[
SwitchPreference
]
private
var
isProxyApps
:
SwitchPreference
=
_
override
def
onCreatePreferences
(
bundle
:
Bundle
,
key
:
String
)
{
addPreferencesFromResource
(
R
.
xml
.
pref_all
)
getPreferenceManager
.
getSharedPreferences
.
registerOnSharedPreferenceChangeListener
(
this
)
findPreference
(
Key
.
name
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
name
=
value
.
asInstanceOf
[
String
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
host
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
host
=
value
.
asInstanceOf
[
String
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
remotePort
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
remotePort
=
value
.
asInstanceOf
[
Int
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
localPort
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
localPort
=
value
.
asInstanceOf
[
Int
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
password
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
password
=
value
.
asInstanceOf
[
String
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
method
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
method
=
value
.
asInstanceOf
[
String
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
route
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
route
=
value
.
asInstanceOf
[
String
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
remoteDns
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
remoteDns
=
value
.
asInstanceOf
[
String
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
kcp
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
kcp
=
value
.
asInstanceOf
[
Boolean
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
kcpPort
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
kcpPort
=
value
.
asInstanceOf
[
Int
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
kcpcli
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
kcpcli
=
value
.
asInstanceOf
[
String
]
app
.
profileManager
.
updateProfile
(
profile
)
})
isProxyApps
=
findPreference
(
Key
.
proxyApps
).
asInstanceOf
[
SwitchPreference
]
isProxyApps
.
setOnPreferenceClickListener
(
_
=>
{
startActivity
(
new
Intent
(
activity
,
classOf
[
AppManager
]))
isProxyApps
.
setChecked
(
true
)
false
})
isProxyApps
.
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
proxyApps
=
value
.
asInstanceOf
[
Boolean
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
udpdns
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
udpdns
=
value
.
asInstanceOf
[
Boolean
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
auth
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
auth
=
value
.
asInstanceOf
[
Boolean
]
app
.
profileManager
.
updateProfile
(
profile
)
})
findPreference
(
Key
.
ipv6
).
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
profile
.
ipv6
=
value
.
asInstanceOf
[
Boolean
]
app
.
profileManager
.
updateProfile
(
profile
)
})
val
switch
=
findPreference
(
Key
.
isAutoConnect
).
asInstanceOf
[
SwitchPreference
]
switch
.
setOnPreferenceChangeListener
((
_
,
value
)
=>
{
BootReceiver
.
setEnabled
(
activity
,
value
.
asInstanceOf
[
Boolean
])
true
})
if
(
getPreferenceManager
.
getSharedPreferences
.
getBoolean
(
Key
.
isAutoConnect
,
false
))
{
BootReceiver
.
setEnabled
(
activity
,
true
)
getPreferenceManager
.
getSharedPreferences
.
edit
.
remove
(
Key
.
isAutoConnect
).
apply
()
}
switch
.
setChecked
(
BootReceiver
.
getEnabled
(
activity
))
val
tfo
=
findPreference
(
Key
.
tfo
).
asInstanceOf
[
SwitchPreference
]
tfo
.
setChecked
(
TcpFastOpen
.
sendEnabled
)
tfo
.
setOnPreferenceChangeListener
((
_
,
v
)
=>
{
val
value
=
v
.
asInstanceOf
[
Boolean
]
val
result
=
TcpFastOpen
.
enabled
(
value
)
if
(
result
!=
null
&&
result
!=
"Success."
)
Snackbar
.
make
(
activity
.
findViewById
(
android
.
R
.
id
.
content
),
result
,
Snackbar
.
LENGTH_LONG
).
show
()
value
==
TcpFastOpen
.
sendEnabled
})
if
(!
TcpFastOpen
.
supported
)
{
tfo
.
setEnabled
(
false
)
tfo
.
setSummary
(
getString
(
R
.
string
.
tcp_fastopen_summary_unsupported
,
java
.
lang
.
System
.
getProperty
(
"os.version"
)))
}
findPreference
(
"recovery"
).
setOnPreferenceClickListener
(
_
=>
{
app
.
track
(
TAG
,
"reset"
)
activity
.
recovery
()
true
})
findPreference
(
"about"
).
setOnPreferenceClickListener
(
_
=>
{
app
.
track
(
TAG
,
"about"
)
val
web
=
new
WebView
(
activity
)
web
.
loadUrl
(
"file:///android_asset/pages/about.html"
)
web
.
setWebViewClient
(
new
WebViewClient
()
{
override
def
shouldOverrideUrlLoading
(
view
:
WebView
,
url
:
String
)
:
Boolean
=
{
try
{
startActivity
(
new
Intent
(
Intent
.
ACTION_VIEW
,
Uri
.
parse
(
url
)))
}
catch
{
case
_:
android.content.ActivityNotFoundException
=>
// Ignore
}
true
}
})
new
AlertDialog
.
Builder
(
activity
)
.
setTitle
(
getString
(
R
.
string
.
about_title
).
formatLocal
(
Locale
.
ENGLISH
,
BuildConfig
.
VERSION_NAME
))
.
setNegativeButton
(
getString
(
android
.
R
.
string
.
ok
),
null
)
.
setView
(
web
)
.
create
()
.
show
()
true
})
}
override
def
onDisplayPreferenceDialog
(
preference
:
Preference
)
:
Unit
=
preference
.
getKey
match
{
case
Key
.
kcpcli
=>
displayPreferenceDialog
(
Key
.
kcpcli
,
new
KcpCliPreferenceDialogFragment
())
case
_
=>
super
.
onDisplayPreferenceDialog
(
preference
)
}
def
refreshProfile
()
{
profile
=
app
.
currentProfile
match
{
case
Some
(
p
)
=>
p
case
None
=>
app
.
profileManager
.
getFirstProfile
match
{
case
Some
(
p
)
=>
app
.
profileId
(
p
.
id
)
p
case
None
=>
val
default
=
app
.
profileManager
.
createDefault
()
app
.
profileId
(
default
.
id
)
default
}
}
isProxyApps
.
setChecked
(
profile
.
proxyApps
)
}
override
def
onDestroy
()
{
super
.
onDestroy
()
app
.
settings
.
unregisterOnSharedPreferenceChangeListener
(
this
)
}
def
onSharedPreferenceChanged
(
pref
:
SharedPreferences
,
key
:
String
)
:
Unit
=
key
match
{
case
Key
.
isNAT
=>
activity
.
handler
.
post
(()
=>
{
activity
.
detachService
()
activity
.
attachServiceCallback
()
})
case
_
=>
}
private
var
enabled
=
true
def
setEnabled
(
enabled
:
Boolean
)
{
this
.
enabled
=
enabled
for
(
name
<-
Key
.
isNAT
#::
PROXY_PREFS
.
toStream
#:::
FEATURE_PREFS
.
toStream
)
{
val
pref
=
findPreference
(
name
)
if
(
pref
!=
null
)
pref
.
setEnabled
(
enabled
&&
(
name
!=
Key
.
proxyApps
||
Utils
.
isLollipopOrAbove
||
app
.
isNatEnabled
))
}
}
var
profile
:
Profile
=
_
def
setProfile
(
profile
:
Profile
)
{
this
.
profile
=
profile
val
demo
=
Build
.
VERSION
.
SDK_INT
>=
25
&&
activity
.
getSystemService
(
classOf
[
UserManager
]).
isDemoUser
for
(
name
<-
Array
(
PROXY_PREFS
,
FEATURE_PREFS
).
flatten
)
updatePreference
(
findPreference
(
name
),
name
,
profile
,
demo
)
}
}
src/main/scala/com/github/shadowsocks/ToolbarFragment.scala
0 → 100644
View file @
dbb137c5
package
com.github.shadowsocks
import
android.app.Fragment
import
android.os.Bundle
import
android.support.v7.widget.Toolbar
import
android.view.View
class
ToolbarFragment
extends
Fragment
{
var
toolbar
:
Toolbar
=
_
override
def
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
toolbar
=
view
.
findViewById
(
R
.
id
.
toolbar
).
asInstanceOf
[
Toolbar
]
val
activity
=
getActivity
.
asInstanceOf
[
MainActivity
]
activity
.
drawer
.
setToolbar
(
activity
,
toolbar
,
true
)
}
}
src/main/scala/com/github/shadowsocks/database/Profile.scala
View file @
dbb137c5
...
@@ -23,8 +23,10 @@ package com.github.shadowsocks.database
...
@@ -23,8 +23,10 @@ package com.github.shadowsocks.database
import
java.net.URLEncoder
import
java.net.URLEncoder
import
java.util.Locale
import
java.util.Locale
import
android.content.SharedPreferences
import
android.os.Binder
import
android.os.Binder
import
android.util.Base64
import
android.util.Base64
import
com.github.shadowsocks.utils.Key
import
com.j256.ormlite.field.
{
DataType
,
DatabaseField
}
import
com.j256.ormlite.field.
{
DataType
,
DatabaseField
}
class
Profile
{
class
Profile
{
...
@@ -32,7 +34,7 @@ class Profile {
...
@@ -32,7 +34,7 @@ class Profile {
var
id
:
Int
=
0
var
id
:
Int
=
0
@DatabaseField
@DatabaseField
var
name
:
String
=
"
Untitled
"
var
name
:
String
=
""
@DatabaseField
@DatabaseField
var
host
:
String
=
""
var
host
:
String
=
""
...
@@ -100,4 +102,41 @@ class Profile {
...
@@ -100,4 +102,41 @@ class Profile {
'#'
+
URLEncoder
.
encode
(
name
,
"utf-8"
)
'#'
+
URLEncoder
.
encode
(
name
,
"utf-8"
)
def
isMethodUnsafe
:
Boolean
=
"table"
.
equalsIgnoreCase
(
method
)
||
"rc4"
.
equalsIgnoreCase
(
method
)
def
isMethodUnsafe
:
Boolean
=
"table"
.
equalsIgnoreCase
(
method
)
||
"rc4"
.
equalsIgnoreCase
(
method
)
def
serialize
(
editor
:
SharedPreferences.Editor
)
:
SharedPreferences.Editor
=
editor
.
putString
(
Key
.
name
,
name
)
.
putString
(
Key
.
host
,
host
)
.
putInt
(
Key
.
localPort
,
localPort
)
.
putInt
(
Key
.
remotePort
,
remotePort
)
.
putString
(
Key
.
password
,
password
)
.
putString
(
Key
.
route
,
route
)
.
putString
(
Key
.
remoteDns
,
remoteDns
)
.
putBoolean
(
Key
.
proxyApps
,
proxyApps
)
.
putBoolean
(
Key
.
bypass
,
bypass
)
.
putBoolean
(
Key
.
udpdns
,
udpdns
)
.
putBoolean
(
Key
.
auth
,
auth
)
.
putBoolean
(
Key
.
ipv6
,
ipv6
)
.
putString
(
Key
.
individual
,
individual
)
.
putBoolean
(
Key
.
kcp
,
kcp
)
.
putInt
(
Key
.
kcpPort
,
kcpPort
)
.
putString
(
Key
.
kcpcli
,
kcpcli
)
def
deserialize
(
pref
:
SharedPreferences
)
{
// It's assumed that default values are never used, so 0/false/null is always used even if that isn't the case
name
=
pref
.
getString
(
Key
.
name
,
null
)
host
=
pref
.
getString
(
Key
.
host
,
null
)
localPort
=
pref
.
getInt
(
Key
.
localPort
,
0
)
remotePort
=
pref
.
getInt
(
Key
.
remotePort
,
0
)
password
=
pref
.
getString
(
Key
.
password
,
null
)
route
=
pref
.
getString
(
Key
.
route
,
null
)
remoteDns
=
pref
.
getString
(
Key
.
remoteDns
,
null
)
proxyApps
=
pref
.
getBoolean
(
Key
.
proxyApps
,
false
)
bypass
=
pref
.
getBoolean
(
Key
.
bypass
,
false
)
udpdns
=
pref
.
getBoolean
(
Key
.
udpdns
,
false
)
auth
=
pref
.
getBoolean
(
Key
.
auth
,
false
)
ipv6
=
pref
.
getBoolean
(
Key
.
ipv6
,
false
)
individual
=
pref
.
getString
(
Key
.
individual
,
null
)
kcp
=
pref
.
getBoolean
(
Key
.
kcp
,
false
)
kcpPort
=
pref
.
getInt
(
Key
.
kcpPort
,
0
)
kcpcli
=
pref
.
getString
(
Key
.
kcpcli
,
null
)
}
}
}
src/main/scala/com/github/shadowsocks/utils/Constants.scala
View file @
dbb137c5
...
@@ -151,8 +151,6 @@ object Key {
...
@@ -151,8 +151,6 @@ object Key {
val
localPort
=
"localPortNum"
val
localPort
=
"localPortNum"
val
remoteDns
=
"remoteDns"
val
remoteDns
=
"remoteDns"
val
profileTip
=
"profileTip"
val
kcp
=
"kcp"
val
kcp
=
"kcp"
val
kcpPort
=
"kcpPort"
val
kcpPort
=
"kcpPort"
val
kcpcli
=
"kcpcli"
val
kcpcli
=
"kcpcli"
...
@@ -170,9 +168,14 @@ object State {
...
@@ -170,9 +168,14 @@ object State {
}
}
object
Action
{
object
Action
{
val
SERVICE
=
"com.github.shadowsocks.SERVICE"
final
val
SERVICE
=
"com.github.shadowsocks.SERVICE"
val
CLOSE
=
"com.github.shadowsocks.CLOSE"
final
val
CLOSE
=
"com.github.shadowsocks.CLOSE"
val
QUICK_SWITCH
=
"com.github.shadowsocks.QUICK_SWITCH"
final
val
CONNECTED
=
"com.github.shadowsocks.CONNECTED"
// TODO
final
val
PROFILE_CHANGED
=
"com.github.shadowsocks.PROFILE_CHANGED"
final
val
PROFILE_REMOVED
=
"com.github.shadowsocks.PROFILE_REMOVED"
final
val
QUICK_SWITCH
=
"com.github.shadowsocks.QUICK_SWITCH"
final
val
EXTRA_PROFILE_ID
=
"com.github.shadowsocks.EXTRA_PROFILE_ID"
}
}
object
Route
{
object
Route
{
...
...
src/main/scala/com/github/shadowsocks/utils/Typefaces.scala
0 → 100644
View file @
dbb137c5
package
com.github.shadowsocks.utils
import
java.util
import
android.content.Context
import
android.graphics.Typeface
import
android.util.Log
import
com.github.shadowsocks.ShadowsocksApplication.app
object
Typefaces
{
def
get
(
c
:
Context
,
assetPath
:
String
)
:
Typeface
=
{
cache
synchronized
{
if
(!
cache
.
containsKey
(
assetPath
))
{
try
{
cache
.
put
(
assetPath
,
Typeface
.
createFromAsset
(
c
.
getAssets
,
assetPath
))
}
catch
{
case
e
:
Exception
=>
Log
.
e
(
"Typefaces"
,
"Could not get typeface '"
+
assetPath
+
"' because "
+
e
.
getMessage
)
app
.
track
(
e
)
return
null
}
}
return
cache
.
get
(
assetPath
)
}
}
private
final
val
cache
=
new
util
.
Hashtable
[
String
,
Typeface
]
}
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