diff --git a/build.sbt b/build.sbt index 3abae4dd..d2c8a1a9 100644 --- a/build.sbt +++ b/build.sbt @@ -29,16 +29,12 @@ libraryDependencies ++= Seq( "com.github.kevinsawicki" % "http-request" % "6.0", "commons-net" % "commons-net" % "3.3", "com.google.zxing" % "android-integration" % "3.2.1", - "com.joanzapata.android" % "android-iconify" % "1.0.11", "net.glxn.qrgen" % "android" % "2.0", - "net.simonvt.menudrawer" % "menudrawer" % "3.0.6", "com.google.android.gms" % "play-services-base" % "8.1.0", "com.google.android.gms" % "play-services-ads" % "8.1.0", "com.google.android.gms" % "play-services-analytics" % "8.1.0", - "com.android.support" % "support-v4" % "23.1.0", "com.android.support" % "design" % "23.1.0", "com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01", - "com.nostra13.universalimageloader" % "universal-image-loader" % "1.9.4", "com.j256.ormlite" % "ormlite-core" % "4.48", "com.j256.ormlite" % "ormlite-android" % "4.48" ) @@ -46,13 +42,9 @@ libraryDependencies ++= Seq( proguardOptions in Android ++= Seq("-keep class android.support.v4.app.** { *; }", "-keep interface android.support.v4.app.** { *; }", "-keep class android.support.v7.widget.Toolbar { (...); }", - "-keep class com.actionbarsherlock.** { *; }", - "-keep interface com.actionbarsherlock.** { *; }", "-keep class org.jraf.android.backport.** { *; }", "-keep class com.github.shadowsocks.** { *; }", "-keep class * extends com.j256.ormlite.** { *; }", - "-keep class com.joanzapata.** { *; }", "-keepattributes *Annotation*", "-dontwarn com.google.android.gms.internal.zzhu", - "-dontwarn org.xbill.**", - "-dontwarn com.actionbarsherlock.**") + "-dontwarn org.xbill.**") diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 787cd862..e03bc12c 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -22,7 +22,8 @@ android:hardwareAccelerated="true" android:icon="@drawable/ic_launcher" android:backupAgent=".ShadowsocksBackupAgent" - android:label="@string/app_name"> + android:label="@string/app_name" + android:theme="@style/Theme.Material"> @@ -36,7 +37,6 @@ @@ -63,10 +63,21 @@ + android:parentActivityName=".Shadowsocks" + android:exported="false"> + + + + + + + diff --git a/src/main/jni/shadowsocks-libev b/src/main/jni/shadowsocks-libev index 095aa749..890e880d 160000 --- a/src/main/jni/shadowsocks-libev +++ b/src/main/jni/shadowsocks-libev @@ -1 +1 @@ -Subproject commit 095aa7490cb39d513180d1f756e6751107511f98 +Subproject commit 890e880dc61ad4f2e9a340a78c797c4aabee6429 diff --git a/src/main/res/drawable-anydpi-v21/ic_content_add.xml b/src/main/res/drawable-anydpi-v21/ic_content_add.xml new file mode 100644 index 00000000..b9b8eca8 --- /dev/null +++ b/src/main/res/drawable-anydpi-v21/ic_content_add.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/drawable-anydpi-v21/ic_social_share.xml b/src/main/res/drawable-anydpi-v21/ic_social_share.xml new file mode 100644 index 00000000..e3fe874d --- /dev/null +++ b/src/main/res/drawable-anydpi-v21/ic_social_share.xml @@ -0,0 +1,9 @@ + + + diff --git a/src/main/res/drawable-hdpi/ab_bottom_solid_shadow.9.png b/src/main/res/drawable-hdpi/ab_bottom_solid_shadow.9.png deleted file mode 100755 index 758468cf..00000000 Binary files a/src/main/res/drawable-hdpi/ab_bottom_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/ab_solid_shadow.9.png b/src/main/res/drawable-hdpi/ab_solid_shadow.9.png deleted file mode 100755 index da77e95c..00000000 Binary files a/src/main/res/drawable-hdpi/ab_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/ab_stacked_solid_shadow.9.png b/src/main/res/drawable-hdpi/ab_stacked_solid_shadow.9.png deleted file mode 100755 index fae720ab..00000000 Binary files a/src/main/res/drawable-hdpi/ab_stacked_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/ab_texture_tile_shadow.png b/src/main/res/drawable-hdpi/ab_texture_tile_shadow.png deleted file mode 100755 index 45512c35..00000000 Binary files a/src/main/res/drawable-hdpi/ab_texture_tile_shadow.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/ab_transparent_shadow.9.png b/src/main/res/drawable-hdpi/ab_transparent_shadow.9.png deleted file mode 100755 index c58997f1..00000000 Binary files a/src/main/res/drawable-hdpi/ab_transparent_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/btn_cab_done_default_shadow.9.png b/src/main/res/drawable-hdpi/btn_cab_done_default_shadow.9.png deleted file mode 100755 index c8d351ed..00000000 Binary files a/src/main/res/drawable-hdpi/btn_cab_done_default_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/btn_cab_done_focused_shadow.9.png b/src/main/res/drawable-hdpi/btn_cab_done_focused_shadow.9.png deleted file mode 100755 index d3c46282..00000000 Binary files a/src/main/res/drawable-hdpi/btn_cab_done_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/btn_cab_done_pressed_shadow.9.png b/src/main/res/drawable-hdpi/btn_cab_done_pressed_shadow.9.png deleted file mode 100755 index 8d332ef2..00000000 Binary files a/src/main/res/drawable-hdpi/btn_cab_done_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/cab_background_bottom_shadow.9.png b/src/main/res/drawable-hdpi/cab_background_bottom_shadow.9.png deleted file mode 100755 index f7adb436..00000000 Binary files a/src/main/res/drawable-hdpi/cab_background_bottom_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/cab_background_top_shadow.9.png b/src/main/res/drawable-hdpi/cab_background_top_shadow.9.png deleted file mode 100755 index 73242850..00000000 Binary files a/src/main/res/drawable-hdpi/cab_background_top_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/ic_content_add.png b/src/main/res/drawable-hdpi/ic_content_add.png new file mode 100644 index 00000000..694179bd Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_content_add.png differ diff --git a/src/main/res/drawable-hdpi/ic_drawer.png b/src/main/res/drawable-hdpi/ic_drawer.png deleted file mode 100644 index 5c2c7f1e..00000000 Binary files a/src/main/res/drawable-hdpi/ic_drawer.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/ic_social_share.png b/src/main/res/drawable-hdpi/ic_social_share.png new file mode 100644 index 00000000..20ba4806 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_social_share.png differ diff --git a/src/main/res/drawable-hdpi/list_focused_shadow.9.png b/src/main/res/drawable-hdpi/list_focused_shadow.9.png deleted file mode 100755 index 426d73fb..00000000 Binary files a/src/main/res/drawable-hdpi/list_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/list_pressed_shadow.9.png b/src/main/res/drawable-hdpi/list_pressed_shadow.9.png deleted file mode 100755 index 74e43594..00000000 Binary files a/src/main/res/drawable-hdpi/list_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/md__category_background.9.png b/src/main/res/drawable-hdpi/md__category_background.9.png deleted file mode 100644 index 43a20adc..00000000 Binary files a/src/main/res/drawable-hdpi/md__category_background.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/menu_arrow.png b/src/main/res/drawable-hdpi/menu_arrow.png deleted file mode 100644 index c28d98f3..00000000 Binary files a/src/main/res/drawable-hdpi/menu_arrow.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/menu_dropdown_panel_shadow.9.png b/src/main/res/drawable-hdpi/menu_dropdown_panel_shadow.9.png deleted file mode 100755 index 9e12ec08..00000000 Binary files a/src/main/res/drawable-hdpi/menu_dropdown_panel_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/progress_bg_shadow.9.png b/src/main/res/drawable-hdpi/progress_bg_shadow.9.png deleted file mode 100755 index 3d5c707d..00000000 Binary files a/src/main/res/drawable-hdpi/progress_bg_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/progress_primary_shadow.9.png b/src/main/res/drawable-hdpi/progress_primary_shadow.9.png deleted file mode 100755 index 3bc3b3a2..00000000 Binary files a/src/main/res/drawable-hdpi/progress_primary_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/progress_secondary_shadow.9.png b/src/main/res/drawable-hdpi/progress_secondary_shadow.9.png deleted file mode 100755 index 6d50e090..00000000 Binary files a/src/main/res/drawable-hdpi/progress_secondary_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/spinner_ab_default_shadow.9.png b/src/main/res/drawable-hdpi/spinner_ab_default_shadow.9.png deleted file mode 100755 index 4fd4aeba..00000000 Binary files a/src/main/res/drawable-hdpi/spinner_ab_default_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/spinner_ab_disabled_shadow.9.png b/src/main/res/drawable-hdpi/spinner_ab_disabled_shadow.9.png deleted file mode 100755 index d42c97b8..00000000 Binary files a/src/main/res/drawable-hdpi/spinner_ab_disabled_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/spinner_ab_focused_shadow.9.png b/src/main/res/drawable-hdpi/spinner_ab_focused_shadow.9.png deleted file mode 100755 index c4b4f8c3..00000000 Binary files a/src/main/res/drawable-hdpi/spinner_ab_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/spinner_ab_pressed_shadow.9.png b/src/main/res/drawable-hdpi/spinner_ab_pressed_shadow.9.png deleted file mode 100755 index dda5bd51..00000000 Binary files a/src/main/res/drawable-hdpi/spinner_ab_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/tab_selected_focused_shadow.9.png b/src/main/res/drawable-hdpi/tab_selected_focused_shadow.9.png deleted file mode 100755 index 4d49b21e..00000000 Binary files a/src/main/res/drawable-hdpi/tab_selected_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/tab_selected_pressed_shadow.9.png b/src/main/res/drawable-hdpi/tab_selected_pressed_shadow.9.png deleted file mode 100755 index 545e1b76..00000000 Binary files a/src/main/res/drawable-hdpi/tab_selected_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/tab_selected_shadow.9.png b/src/main/res/drawable-hdpi/tab_selected_shadow.9.png deleted file mode 100755 index 2d782626..00000000 Binary files a/src/main/res/drawable-hdpi/tab_selected_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/tab_unselected_focused_shadow.9.png b/src/main/res/drawable-hdpi/tab_unselected_focused_shadow.9.png deleted file mode 100755 index 57c5f182..00000000 Binary files a/src/main/res/drawable-hdpi/tab_unselected_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/tab_unselected_pressed_shadow.9.png b/src/main/res/drawable-hdpi/tab_unselected_pressed_shadow.9.png deleted file mode 100755 index 4f18a95a..00000000 Binary files a/src/main/res/drawable-hdpi/tab_unselected_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-hdpi/tab_unselected_shadow.9.png b/src/main/res/drawable-hdpi/tab_unselected_shadow.9.png deleted file mode 100755 index 396b5dd3..00000000 Binary files a/src/main/res/drawable-hdpi/tab_unselected_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/ab_bottom_solid_shadow.9.png b/src/main/res/drawable-mdpi/ab_bottom_solid_shadow.9.png deleted file mode 100755 index 370f5fe2..00000000 Binary files a/src/main/res/drawable-mdpi/ab_bottom_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/ab_solid_shadow.9.png b/src/main/res/drawable-mdpi/ab_solid_shadow.9.png deleted file mode 100755 index cd8df3d2..00000000 Binary files a/src/main/res/drawable-mdpi/ab_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/ab_stacked_solid_shadow.9.png b/src/main/res/drawable-mdpi/ab_stacked_solid_shadow.9.png deleted file mode 100755 index f72f540e..00000000 Binary files a/src/main/res/drawable-mdpi/ab_stacked_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/ab_texture_tile_shadow.png b/src/main/res/drawable-mdpi/ab_texture_tile_shadow.png deleted file mode 100755 index 47838474..00000000 Binary files a/src/main/res/drawable-mdpi/ab_texture_tile_shadow.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/ab_transparent_shadow.9.png b/src/main/res/drawable-mdpi/ab_transparent_shadow.9.png deleted file mode 100755 index d101b074..00000000 Binary files a/src/main/res/drawable-mdpi/ab_transparent_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/btn_cab_done_default_shadow.9.png b/src/main/res/drawable-mdpi/btn_cab_done_default_shadow.9.png deleted file mode 100755 index 70f1520f..00000000 Binary files a/src/main/res/drawable-mdpi/btn_cab_done_default_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/btn_cab_done_focused_shadow.9.png b/src/main/res/drawable-mdpi/btn_cab_done_focused_shadow.9.png deleted file mode 100755 index be794842..00000000 Binary files a/src/main/res/drawable-mdpi/btn_cab_done_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/btn_cab_done_pressed_shadow.9.png b/src/main/res/drawable-mdpi/btn_cab_done_pressed_shadow.9.png deleted file mode 100755 index 4acbbf62..00000000 Binary files a/src/main/res/drawable-mdpi/btn_cab_done_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/cab_background_bottom_shadow.9.png b/src/main/res/drawable-mdpi/cab_background_bottom_shadow.9.png deleted file mode 100755 index 1c49ba0b..00000000 Binary files a/src/main/res/drawable-mdpi/cab_background_bottom_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/cab_background_top_shadow.9.png b/src/main/res/drawable-mdpi/cab_background_top_shadow.9.png deleted file mode 100755 index d19d5b3f..00000000 Binary files a/src/main/res/drawable-mdpi/cab_background_top_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/ic_content_add.png b/src/main/res/drawable-mdpi/ic_content_add.png new file mode 100644 index 00000000..3856041d Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_content_add.png differ diff --git a/src/main/res/drawable-mdpi/ic_drawer.png b/src/main/res/drawable-mdpi/ic_drawer.png deleted file mode 100644 index c9121257..00000000 Binary files a/src/main/res/drawable-mdpi/ic_drawer.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/ic_social_share.png b/src/main/res/drawable-mdpi/ic_social_share.png new file mode 100644 index 00000000..f02d360a Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_social_share.png differ diff --git a/src/main/res/drawable-mdpi/list_focused_shadow.9.png b/src/main/res/drawable-mdpi/list_focused_shadow.9.png deleted file mode 100755 index 4d3bcbfe..00000000 Binary files a/src/main/res/drawable-mdpi/list_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/list_pressed_shadow.9.png b/src/main/res/drawable-mdpi/list_pressed_shadow.9.png deleted file mode 100755 index a58996d4..00000000 Binary files a/src/main/res/drawable-mdpi/list_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/md__category_background.9.png b/src/main/res/drawable-mdpi/md__category_background.9.png deleted file mode 100644 index af0bc168..00000000 Binary files a/src/main/res/drawable-mdpi/md__category_background.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/menu_arrow.png b/src/main/res/drawable-mdpi/menu_arrow.png deleted file mode 100644 index ecacda71..00000000 Binary files a/src/main/res/drawable-mdpi/menu_arrow.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/menu_dropdown_panel_shadow.9.png b/src/main/res/drawable-mdpi/menu_dropdown_panel_shadow.9.png deleted file mode 100755 index f1999201..00000000 Binary files a/src/main/res/drawable-mdpi/menu_dropdown_panel_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/progress_bg_shadow.9.png b/src/main/res/drawable-mdpi/progress_bg_shadow.9.png deleted file mode 100755 index 9372a60f..00000000 Binary files a/src/main/res/drawable-mdpi/progress_bg_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/progress_primary_shadow.9.png b/src/main/res/drawable-mdpi/progress_primary_shadow.9.png deleted file mode 100755 index 18a10d9d..00000000 Binary files a/src/main/res/drawable-mdpi/progress_primary_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/progress_secondary_shadow.9.png b/src/main/res/drawable-mdpi/progress_secondary_shadow.9.png deleted file mode 100755 index 6196d8b8..00000000 Binary files a/src/main/res/drawable-mdpi/progress_secondary_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/spinner_ab_default_shadow.9.png b/src/main/res/drawable-mdpi/spinner_ab_default_shadow.9.png deleted file mode 100755 index 9aeafee2..00000000 Binary files a/src/main/res/drawable-mdpi/spinner_ab_default_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/spinner_ab_disabled_shadow.9.png b/src/main/res/drawable-mdpi/spinner_ab_disabled_shadow.9.png deleted file mode 100755 index 88dd4415..00000000 Binary files a/src/main/res/drawable-mdpi/spinner_ab_disabled_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/spinner_ab_focused_shadow.9.png b/src/main/res/drawable-mdpi/spinner_ab_focused_shadow.9.png deleted file mode 100755 index 757f15b7..00000000 Binary files a/src/main/res/drawable-mdpi/spinner_ab_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/spinner_ab_pressed_shadow.9.png b/src/main/res/drawable-mdpi/spinner_ab_pressed_shadow.9.png deleted file mode 100755 index 3580ff27..00000000 Binary files a/src/main/res/drawable-mdpi/spinner_ab_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/tab_selected_focused_shadow.9.png b/src/main/res/drawable-mdpi/tab_selected_focused_shadow.9.png deleted file mode 100755 index a6523c2e..00000000 Binary files a/src/main/res/drawable-mdpi/tab_selected_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/tab_selected_pressed_shadow.9.png b/src/main/res/drawable-mdpi/tab_selected_pressed_shadow.9.png deleted file mode 100755 index a52c51d2..00000000 Binary files a/src/main/res/drawable-mdpi/tab_selected_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/tab_selected_shadow.9.png b/src/main/res/drawable-mdpi/tab_selected_shadow.9.png deleted file mode 100755 index 2660743a..00000000 Binary files a/src/main/res/drawable-mdpi/tab_selected_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/tab_unselected_focused_shadow.9.png b/src/main/res/drawable-mdpi/tab_unselected_focused_shadow.9.png deleted file mode 100755 index 0fa12dbf..00000000 Binary files a/src/main/res/drawable-mdpi/tab_unselected_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/tab_unselected_pressed_shadow.9.png b/src/main/res/drawable-mdpi/tab_unselected_pressed_shadow.9.png deleted file mode 100755 index 4365d178..00000000 Binary files a/src/main/res/drawable-mdpi/tab_unselected_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-mdpi/tab_unselected_shadow.9.png b/src/main/res/drawable-mdpi/tab_unselected_shadow.9.png deleted file mode 100755 index 1cf30026..00000000 Binary files a/src/main/res/drawable-mdpi/tab_unselected_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/ab_bottom_solid_shadow.9.png b/src/main/res/drawable-xhdpi/ab_bottom_solid_shadow.9.png deleted file mode 100755 index fc0b308e..00000000 Binary files a/src/main/res/drawable-xhdpi/ab_bottom_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/ab_solid_shadow.9.png b/src/main/res/drawable-xhdpi/ab_solid_shadow.9.png deleted file mode 100755 index 17f16075..00000000 Binary files a/src/main/res/drawable-xhdpi/ab_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/ab_stacked_solid_shadow.9.png b/src/main/res/drawable-xhdpi/ab_stacked_solid_shadow.9.png deleted file mode 100755 index b62ee13c..00000000 Binary files a/src/main/res/drawable-xhdpi/ab_stacked_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/ab_texture_tile_shadow.png b/src/main/res/drawable-xhdpi/ab_texture_tile_shadow.png deleted file mode 100755 index 0c1174bc..00000000 Binary files a/src/main/res/drawable-xhdpi/ab_texture_tile_shadow.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/ab_transparent_shadow.9.png b/src/main/res/drawable-xhdpi/ab_transparent_shadow.9.png deleted file mode 100755 index 671fa845..00000000 Binary files a/src/main/res/drawable-xhdpi/ab_transparent_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/btn_cab_done_default_shadow.9.png b/src/main/res/drawable-xhdpi/btn_cab_done_default_shadow.9.png deleted file mode 100755 index 3521ae38..00000000 Binary files a/src/main/res/drawable-xhdpi/btn_cab_done_default_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/btn_cab_done_focused_shadow.9.png b/src/main/res/drawable-xhdpi/btn_cab_done_focused_shadow.9.png deleted file mode 100755 index 2ae6ea3f..00000000 Binary files a/src/main/res/drawable-xhdpi/btn_cab_done_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/btn_cab_done_pressed_shadow.9.png b/src/main/res/drawable-xhdpi/btn_cab_done_pressed_shadow.9.png deleted file mode 100755 index 772bde13..00000000 Binary files a/src/main/res/drawable-xhdpi/btn_cab_done_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/cab_background_bottom_shadow.9.png b/src/main/res/drawable-xhdpi/cab_background_bottom_shadow.9.png deleted file mode 100755 index 19da0c6b..00000000 Binary files a/src/main/res/drawable-xhdpi/cab_background_bottom_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/cab_background_top_shadow.9.png b/src/main/res/drawable-xhdpi/cab_background_top_shadow.9.png deleted file mode 100755 index f469c994..00000000 Binary files a/src/main/res/drawable-xhdpi/cab_background_top_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/ic_content_add.png b/src/main/res/drawable-xhdpi/ic_content_add.png new file mode 100644 index 00000000..67bb598e Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_content_add.png differ diff --git a/src/main/res/drawable-xhdpi/ic_drawer.png b/src/main/res/drawable-xhdpi/ic_drawer.png deleted file mode 100644 index 56f01f3e..00000000 Binary files a/src/main/res/drawable-xhdpi/ic_drawer.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/ic_social_share.png b/src/main/res/drawable-xhdpi/ic_social_share.png new file mode 100644 index 00000000..81c80b70 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_social_share.png differ diff --git a/src/main/res/drawable-xhdpi/list_focused_shadow.9.png b/src/main/res/drawable-xhdpi/list_focused_shadow.9.png deleted file mode 100755 index 011da445..00000000 Binary files a/src/main/res/drawable-xhdpi/list_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/list_pressed_shadow.9.png b/src/main/res/drawable-xhdpi/list_pressed_shadow.9.png deleted file mode 100755 index 4f6f689b..00000000 Binary files a/src/main/res/drawable-xhdpi/list_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/md__category_background.9.png b/src/main/res/drawable-xhdpi/md__category_background.9.png deleted file mode 100644 index 942d72e6..00000000 Binary files a/src/main/res/drawable-xhdpi/md__category_background.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/menu_arrow.png b/src/main/res/drawable-xhdpi/menu_arrow.png deleted file mode 100644 index b1599372..00000000 Binary files a/src/main/res/drawable-xhdpi/menu_arrow.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/menu_dropdown_panel_shadow.9.png b/src/main/res/drawable-xhdpi/menu_dropdown_panel_shadow.9.png deleted file mode 100755 index 3a04b510..00000000 Binary files a/src/main/res/drawable-xhdpi/menu_dropdown_panel_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/progress_bg_shadow.9.png b/src/main/res/drawable-xhdpi/progress_bg_shadow.9.png deleted file mode 100755 index 8b4853aa..00000000 Binary files a/src/main/res/drawable-xhdpi/progress_bg_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/progress_primary_shadow.9.png b/src/main/res/drawable-xhdpi/progress_primary_shadow.9.png deleted file mode 100755 index bdca8165..00000000 Binary files a/src/main/res/drawable-xhdpi/progress_primary_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/progress_secondary_shadow.9.png b/src/main/res/drawable-xhdpi/progress_secondary_shadow.9.png deleted file mode 100755 index a424a145..00000000 Binary files a/src/main/res/drawable-xhdpi/progress_secondary_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/spinner_ab_default_shadow.9.png b/src/main/res/drawable-xhdpi/spinner_ab_default_shadow.9.png deleted file mode 100755 index 14b1401d..00000000 Binary files a/src/main/res/drawable-xhdpi/spinner_ab_default_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/spinner_ab_disabled_shadow.9.png b/src/main/res/drawable-xhdpi/spinner_ab_disabled_shadow.9.png deleted file mode 100755 index c9dfbd60..00000000 Binary files a/src/main/res/drawable-xhdpi/spinner_ab_disabled_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/spinner_ab_focused_shadow.9.png b/src/main/res/drawable-xhdpi/spinner_ab_focused_shadow.9.png deleted file mode 100755 index 1c8d33d0..00000000 Binary files a/src/main/res/drawable-xhdpi/spinner_ab_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/spinner_ab_pressed_shadow.9.png b/src/main/res/drawable-xhdpi/spinner_ab_pressed_shadow.9.png deleted file mode 100755 index cfc8b5e1..00000000 Binary files a/src/main/res/drawable-xhdpi/spinner_ab_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/tab_selected_focused_shadow.9.png b/src/main/res/drawable-xhdpi/tab_selected_focused_shadow.9.png deleted file mode 100755 index 0b8b3e9e..00000000 Binary files a/src/main/res/drawable-xhdpi/tab_selected_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/tab_selected_pressed_shadow.9.png b/src/main/res/drawable-xhdpi/tab_selected_pressed_shadow.9.png deleted file mode 100755 index f6d8ee5c..00000000 Binary files a/src/main/res/drawable-xhdpi/tab_selected_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/tab_selected_shadow.9.png b/src/main/res/drawable-xhdpi/tab_selected_shadow.9.png deleted file mode 100755 index aec4e87a..00000000 Binary files a/src/main/res/drawable-xhdpi/tab_selected_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/tab_unselected_focused_shadow.9.png b/src/main/res/drawable-xhdpi/tab_unselected_focused_shadow.9.png deleted file mode 100755 index 3c87dd9c..00000000 Binary files a/src/main/res/drawable-xhdpi/tab_unselected_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/tab_unselected_pressed_shadow.9.png b/src/main/res/drawable-xhdpi/tab_unselected_pressed_shadow.9.png deleted file mode 100755 index a5a2c25e..00000000 Binary files a/src/main/res/drawable-xhdpi/tab_unselected_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xhdpi/tab_unselected_shadow.9.png b/src/main/res/drawable-xhdpi/tab_unselected_shadow.9.png deleted file mode 100755 index d211e8a8..00000000 Binary files a/src/main/res/drawable-xhdpi/tab_unselected_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/ab_bottom_solid_shadow.9.png b/src/main/res/drawable-xxhdpi/ab_bottom_solid_shadow.9.png deleted file mode 100755 index d767dd92..00000000 Binary files a/src/main/res/drawable-xxhdpi/ab_bottom_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/ab_solid_shadow.9.png b/src/main/res/drawable-xxhdpi/ab_solid_shadow.9.png deleted file mode 100755 index 04314abf..00000000 Binary files a/src/main/res/drawable-xxhdpi/ab_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/ab_stacked_solid_shadow.9.png b/src/main/res/drawable-xxhdpi/ab_stacked_solid_shadow.9.png deleted file mode 100755 index 47555f2f..00000000 Binary files a/src/main/res/drawable-xxhdpi/ab_stacked_solid_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/ab_texture_tile_shadow.png b/src/main/res/drawable-xxhdpi/ab_texture_tile_shadow.png deleted file mode 100755 index 5f5299ca..00000000 Binary files a/src/main/res/drawable-xxhdpi/ab_texture_tile_shadow.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/ab_transparent_shadow.9.png b/src/main/res/drawable-xxhdpi/ab_transparent_shadow.9.png deleted file mode 100755 index 0e9ba38d..00000000 Binary files a/src/main/res/drawable-xxhdpi/ab_transparent_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/btn_cab_done_default_shadow.9.png b/src/main/res/drawable-xxhdpi/btn_cab_done_default_shadow.9.png deleted file mode 100755 index 0c0079c7..00000000 Binary files a/src/main/res/drawable-xxhdpi/btn_cab_done_default_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/btn_cab_done_focused_shadow.9.png b/src/main/res/drawable-xxhdpi/btn_cab_done_focused_shadow.9.png deleted file mode 100755 index ddf7531b..00000000 Binary files a/src/main/res/drawable-xxhdpi/btn_cab_done_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/btn_cab_done_pressed_shadow.9.png b/src/main/res/drawable-xxhdpi/btn_cab_done_pressed_shadow.9.png deleted file mode 100755 index 2ef2a7be..00000000 Binary files a/src/main/res/drawable-xxhdpi/btn_cab_done_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/cab_background_bottom_shadow.9.png b/src/main/res/drawable-xxhdpi/cab_background_bottom_shadow.9.png deleted file mode 100755 index 858de47e..00000000 Binary files a/src/main/res/drawable-xxhdpi/cab_background_bottom_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/cab_background_top_shadow.9.png b/src/main/res/drawable-xxhdpi/cab_background_top_shadow.9.png deleted file mode 100755 index 3480efff..00000000 Binary files a/src/main/res/drawable-xxhdpi/cab_background_top_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/ic_content_add.png b/src/main/res/drawable-xxhdpi/ic_content_add.png new file mode 100644 index 00000000..0fdced8f Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_content_add.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_drawer.png b/src/main/res/drawable-xxhdpi/ic_drawer.png deleted file mode 100644 index 1fc1d9c0..00000000 Binary files a/src/main/res/drawable-xxhdpi/ic_drawer.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/ic_social_share.png b/src/main/res/drawable-xxhdpi/ic_social_share.png new file mode 100644 index 00000000..784933ad Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_social_share.png differ diff --git a/src/main/res/drawable-xxhdpi/list_focused_shadow.9.png b/src/main/res/drawable-xxhdpi/list_focused_shadow.9.png deleted file mode 100755 index cc17ef5b..00000000 Binary files a/src/main/res/drawable-xxhdpi/list_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/list_pressed_shadow.9.png b/src/main/res/drawable-xxhdpi/list_pressed_shadow.9.png deleted file mode 100755 index 3af5169d..00000000 Binary files a/src/main/res/drawable-xxhdpi/list_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/menu_dropdown_panel_shadow.9.png b/src/main/res/drawable-xxhdpi/menu_dropdown_panel_shadow.9.png deleted file mode 100755 index c2ff4a09..00000000 Binary files a/src/main/res/drawable-xxhdpi/menu_dropdown_panel_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/progress_bg_shadow.9.png b/src/main/res/drawable-xxhdpi/progress_bg_shadow.9.png deleted file mode 100755 index 7c221af9..00000000 Binary files a/src/main/res/drawable-xxhdpi/progress_bg_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/progress_primary_shadow.9.png b/src/main/res/drawable-xxhdpi/progress_primary_shadow.9.png deleted file mode 100755 index 3a2d7056..00000000 Binary files a/src/main/res/drawable-xxhdpi/progress_primary_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/progress_secondary_shadow.9.png b/src/main/res/drawable-xxhdpi/progress_secondary_shadow.9.png deleted file mode 100755 index 1a7fb589..00000000 Binary files a/src/main/res/drawable-xxhdpi/progress_secondary_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/spinner_ab_default_shadow.9.png b/src/main/res/drawable-xxhdpi/spinner_ab_default_shadow.9.png deleted file mode 100755 index f178f195..00000000 Binary files a/src/main/res/drawable-xxhdpi/spinner_ab_default_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/spinner_ab_disabled_shadow.9.png b/src/main/res/drawable-xxhdpi/spinner_ab_disabled_shadow.9.png deleted file mode 100755 index 9ec5a1ef..00000000 Binary files a/src/main/res/drawable-xxhdpi/spinner_ab_disabled_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/spinner_ab_focused_shadow.9.png b/src/main/res/drawable-xxhdpi/spinner_ab_focused_shadow.9.png deleted file mode 100755 index 125bbda7..00000000 Binary files a/src/main/res/drawable-xxhdpi/spinner_ab_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/spinner_ab_pressed_shadow.9.png b/src/main/res/drawable-xxhdpi/spinner_ab_pressed_shadow.9.png deleted file mode 100755 index 5770c069..00000000 Binary files a/src/main/res/drawable-xxhdpi/spinner_ab_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/tab_selected_focused_shadow.9.png b/src/main/res/drawable-xxhdpi/tab_selected_focused_shadow.9.png deleted file mode 100755 index 4cc29080..00000000 Binary files a/src/main/res/drawable-xxhdpi/tab_selected_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/tab_selected_pressed_shadow.9.png b/src/main/res/drawable-xxhdpi/tab_selected_pressed_shadow.9.png deleted file mode 100755 index 539f850d..00000000 Binary files a/src/main/res/drawable-xxhdpi/tab_selected_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/tab_selected_shadow.9.png b/src/main/res/drawable-xxhdpi/tab_selected_shadow.9.png deleted file mode 100755 index f9b28d0f..00000000 Binary files a/src/main/res/drawable-xxhdpi/tab_selected_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/tab_unselected_focused_shadow.9.png b/src/main/res/drawable-xxhdpi/tab_unselected_focused_shadow.9.png deleted file mode 100755 index cf9c97a2..00000000 Binary files a/src/main/res/drawable-xxhdpi/tab_unselected_focused_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/tab_unselected_pressed_shadow.9.png b/src/main/res/drawable-xxhdpi/tab_unselected_pressed_shadow.9.png deleted file mode 100755 index 8dd01d5c..00000000 Binary files a/src/main/res/drawable-xxhdpi/tab_unselected_pressed_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxhdpi/tab_unselected_shadow.9.png b/src/main/res/drawable-xxhdpi/tab_unselected_shadow.9.png deleted file mode 100755 index 7b5c16da..00000000 Binary files a/src/main/res/drawable-xxhdpi/tab_unselected_shadow.9.png and /dev/null differ diff --git a/src/main/res/drawable-xxxhdpi/ic_content_add.png b/src/main/res/drawable-xxxhdpi/ic_content_add.png new file mode 100644 index 00000000..d64c22e9 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_content_add.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_social_share.png b/src/main/res/drawable-xxxhdpi/ic_social_share.png new file mode 100644 index 00000000..5a8544ce Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_social_share.png differ diff --git a/src/main/res/drawable/ab_background_textured_shadow.xml b/src/main/res/drawable/ab_background_textured_shadow.xml deleted file mode 100755 index d4101f6b..00000000 --- a/src/main/res/drawable/ab_background_textured_shadow.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/main/res/drawable/btn_cab_done_shadow.xml b/src/main/res/drawable/btn_cab_done_shadow.xml deleted file mode 100755 index 4dd67cf6..00000000 --- a/src/main/res/drawable/btn_cab_done_shadow.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - diff --git a/src/main/res/drawable/progress_horizontal_shadow.xml b/src/main/res/drawable/progress_horizontal_shadow.xml deleted file mode 100755 index 0e2ecfa1..00000000 --- a/src/main/res/drawable/progress_horizontal_shadow.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/main/res/drawable/selectable_background_shadow.xml b/src/main/res/drawable/selectable_background_shadow.xml deleted file mode 100755 index 6feb6539..00000000 --- a/src/main/res/drawable/selectable_background_shadow.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/main/res/drawable/spinner_background_ab_shadow.xml b/src/main/res/drawable/spinner_background_ab_shadow.xml deleted file mode 100755 index 5ddd9ec0..00000000 --- a/src/main/res/drawable/spinner_background_ab_shadow.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - diff --git a/src/main/res/drawable/tab_indicator_ab_shadow.xml b/src/main/res/drawable/tab_indicator_ab_shadow.xml deleted file mode 100755 index e58ed862..00000000 --- a/src/main/res/drawable/tab_indicator_ab_shadow.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/res/layout/layout_apps.xml b/src/main/res/layout/layout_apps.xml index f84d4bbd..0d75f23d 100644 --- a/src/main/res/layout/layout_apps.xml +++ b/src/main/res/layout/layout_apps.xml @@ -32,8 +32,17 @@ android:layout_marginEnd="6dp" android:layout_marginRight="6dp"/> - + + + + \ No newline at end of file diff --git a/src/main/res/layout/layout_main_ad.xml b/src/main/res/layout/layout_main_ad.xml deleted file mode 100644 index 067e6416..00000000 --- a/src/main/res/layout/layout_main_ad.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - diff --git a/src/main/res/layout/layout_profiles.xml b/src/main/res/layout/layout_profiles.xml new file mode 100644 index 00000000..a98f4674 --- /dev/null +++ b/src/main/res/layout/layout_profiles.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/src/main/res/layout/layout_profiles_item.xml b/src/main/res/layout/layout_profiles_item.xml new file mode 100644 index 00000000..d19eeb02 --- /dev/null +++ b/src/main/res/layout/layout_profiles_item.xml @@ -0,0 +1,33 @@ + + + + + + diff --git a/src/main/res/layout/main.xml b/src/main/res/layout/main.xml deleted file mode 100644 index a4e6a136..00000000 --- a/src/main/res/layout/main.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/src/main/res/layout/menu_row_category.xml b/src/main/res/layout/menu_row_category.xml deleted file mode 100644 index c89e5d96..00000000 --- a/src/main/res/layout/menu_row_category.xml +++ /dev/null @@ -1,5 +0,0 @@ - - \ No newline at end of file diff --git a/src/main/res/layout/menu_row_item.xml b/src/main/res/layout/menu_row_item.xml deleted file mode 100644 index af2c4b38..00000000 --- a/src/main/res/layout/menu_row_item.xml +++ /dev/null @@ -1,5 +0,0 @@ - - \ No newline at end of file diff --git a/src/main/res/layout/toolbar_light_dark.xml b/src/main/res/layout/toolbar_light_dark.xml index b3b7a02b..ae61afe4 100644 --- a/src/main/res/layout/toolbar_light_dark.xml +++ b/src/main/res/layout/toolbar_light_dark.xml @@ -7,6 +7,5 @@ android:background="?attr/colorPrimary" android:elevation="4dp" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" - app:titleTextAppearance="@style/Toolbar.TitleText" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" android:id="@+id/toolbar" /> diff --git a/src/main/res/menu/add_profile_methods.xml b/src/main/res/menu/add_profile_methods.xml new file mode 100644 index 00000000..6d080c8f --- /dev/null +++ b/src/main/res/menu/add_profile_methods.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index 195090d5..54343ba6 100755 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -3,9 +3,6 @@ 影梭 shadowsocks - 服务控制 - 代理开关 - 启动/关闭后台服务 服务器设置 配置文件 配置文件名称 @@ -39,26 +36,18 @@ 在通知栏上显示当前上下行速率 后台服务已开始运行。 已连接到「%s」 - 当前速率:%d KB/s - 后台服务启动失败。 - 后台服务已停止。 - 后台服务已停止。 无法连接远程服务器 停止服务 - 正在关闭... + 正在关闭…… 后台服务启动失败:%s - 确定 关闭 本地端口号应大于1024 端口号不能为空 代理服务器地址不能为空 - 检测到意外退出,状态已被重置。 连接 - 正在初始化... - 重置中... - 正在加载... - 是 - 否 + 正在初始化…… + 重置中…… + 正在加载…… 配置文件 设置选项 删除此配置文件「%s」? @@ -66,13 +55,10 @@ 关于 影梭(shadowsocks)%s 刷新 DNS 缓存 - 刷新中... + 刷新中…… 二维码 添加配置文件 - 启用 NAT 模式. - 禁用 NAT 模式. - UDP 转发 由远程服务器转发 UDP 协议的数据包 @@ -84,10 +70,8 @@ 为影梭添加此配置文件? - - 扫描二维码 - 手动设置 - + 扫描二维码 + 手动设置 全局 @@ -101,6 +85,8 @@ 导出失败 导入成功 导入失败 - 错误 + + 已删除 + 撤销 diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index 157d3c4a..311f4003 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -2,17 +2,7 @@ - #ff5e5e5e - #17170443 - #FFFFFF - #000000 - #646464 - #c8c8c8 - #323232 - #00000000 - #FF555555 - - #43A047 + #388E3C #90A4AE #607D8B #455A64 diff --git a/src/main/res/values/ids.xml b/src/main/res/values/ids.xml deleted file mode 100644 index 925ac058..00000000 --- a/src/main/res/values/ids.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 0fa48668..73ccaec1 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -4,15 +4,17 @@ Shadowsocks shadowsocks - - Service Controller - Proxy Switch - Enable / Disable Proxy + + Global Settings + Profile + Switch to another profile + NAT mode + Use NAT/VPN mode. + NAT mode is disabled without root. Server Settings - Profile - Profile name + Profile name Server Hostname or IP of the remote server Remote Port @@ -54,26 +56,16 @@ Shadowsocks started. Connected to "%s" - Current Rate: %d KB/s - Shadowsocks failed to start. - Shadowsocks stopped. - Shadowsocks stopped. Failed to connect the remote server Stop the service - Shutting down... + Shutting down… %s - Turn on NAT mode. - Turn off NAT mode. - ERROR - OK Close The local port number should be greater than 1024 Port should not be empty Proxy should not be empty - An unexpected exit detected, the context has been reset. - Connect Initializing… Resetting… @@ -101,9 +93,9 @@ Add this Shadowsocks Profile? - - Scan QR code - Manual Settings - + Scan QR code + Manual Settings + Removed + Undo diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 7cc7ff33..efa07058 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -1,22 +1,8 @@ - - - - - - - - - - - diff --git a/src/main/res/xml/pref_all.xml b/src/main/res/xml/pref_all.xml index c4df91c0..29e54dd2 100644 --- a/src/main/res/xml/pref_all.xml +++ b/src/main/res/xml/pref_all.xml @@ -2,50 +2,60 @@ + + + + + + + + + + + + - - + android:title="@string/profile_name"/> - + android:title="@string/proxy"/> - + android:title="@string/remote_port"/> - + android:title="@string/port"/> - + android:title="@string/sitekey"/> - + android:title="@string/enc_method"/> @@ -58,21 +68,18 @@ app:entries="@array/route_entry" app:entryValues="@array/route_value" app:summary="%s" - android:title="@string/route_list"> - + android:title="@string/route_list"/> - + android:title="@string/ipv6"/> - + android:title="@string/auto_set_proxy"/> - + android:title="@string/udp_dns"/> - - - + android:title="@string/onetime_auth"/> diff --git a/src/main/scala/com/github/shadowsocks/AppManager.scala b/src/main/scala/com/github/shadowsocks/AppManager.scala index 88004783..4ad4bfe6 100644 --- a/src/main/scala/com/github/shadowsocks/AppManager.scala +++ b/src/main/scala/com/github/shadowsocks/AppManager.scala @@ -39,38 +39,28 @@ package com.github.shadowsocks -import android.app.{Activity, ProgressDialog} -import android.content.ClipboardManager -import android.content.ClipData -import android.content.Context -import android.content.SharedPreferences import android.content.pm.PackageManager -import android.graphics.{Bitmap, PixelFormat} -import android.os.Bundle -import android.os.Handler +import android.content.{ClipData, ClipboardManager, Context, SharedPreferences} +import android.graphics.PixelFormat +import android.graphics.drawable.Drawable +import android.os.{Bundle, Handler} import android.preference.PreferenceManager +import android.support.v7.app.AppCompatActivity import android.support.v7.widget.Toolbar -import android.view.Menu -import android.view.MenuItem -import android.view.View +import android.support.v7.widget.Toolbar.OnMenuItemClickListener import android.view.View.OnClickListener -import android.view.ViewGroup import android.view.ViewGroup.LayoutParams -import android.view.WindowManager -import android.widget._ +import android.view.{MenuItem, View, ViewGroup, WindowManager} import android.widget.AbsListView.OnScrollListener import android.widget.CompoundButton.OnCheckedChangeListener -import com.nostra13.universalimageloader.core.download.BaseImageDownloader -import java.io.{ByteArrayOutputStream, ByteArrayInputStream, InputStream} -import com.nostra13.universalimageloader.core.{DisplayImageOptions, ImageLoader, ImageLoaderConfiguration} -import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer -import com.github.shadowsocks.utils.{Utils, Scheme, Key} +import android.widget._ +import com.github.shadowsocks.utils.{Key, Utils} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.language.implicitConversions -case class ProxiedApp(uid: Int, name: String, packageName: String, var proxied: Boolean) +case class ProxiedApp(uid: Int, name: String, packageName: String, icon: Drawable, var proxied: Boolean) class ObjectArrayTools[T <: AnyRef](a: Array[T]) { def binarySearch(key: T) = { @@ -100,12 +90,13 @@ object AppManager { val name = packageManager.getApplicationLabel(a).toString val packageName = a.packageName val proxied = proxiedApps.binarySearch(userName) >= 0 - new ProxiedApp(uid, name, packageName, proxied) + new ProxiedApp(uid, name, packageName, null, proxied) }.toArray } } -class AppManager extends Activity with OnCheckedChangeListener with OnClickListener { +class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnClickListener + with OnMenuItemClickListener { val MSG_LOAD_START = 1 val MSG_LOAD_FINISH = 2 @@ -116,13 +107,11 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe var apps: Array[ProxiedApp] = null var appListView: ListView = null var overlay: TextView = null - var progressDialog: ProgressDialog = null var adapter: ListAdapter = null var appsLoaded: Boolean = false def loadApps(context: Context): Array[ProxiedApp] = { - val prefs = PreferenceManager.getDefaultSharedPreferences(context) - val proxiedAppString = prefs.getString(Key.proxied, "") + val proxiedAppString = ShadowsocksApplication.settings.getString(Key.proxied, "") val proxiedApps = proxiedAppString.split('|').sortWith(_ < _) import scala.collection.JavaConversions._ @@ -139,7 +128,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe val name = packageManager.getApplicationLabel(a).toString val packageName = a.packageName val proxied = (proxiedApps binarySearch userName) >= 0 - new ProxiedApp(uid, name, packageName, proxied) + new ProxiedApp(uid, name, packageName, a.loadIcon(packageManager), proxied) }.toArray } @@ -174,19 +163,9 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe } val app: ProxiedApp = apps(position) - val options = - new DisplayImageOptions.Builder() - .showStubImage(STUB) - .showImageForEmptyUri(STUB) - .showImageOnFail(STUB) - .resetViewBeforeLoading() - .cacheInMemory() - .cacheOnDisc() - .displayer(new FadeInBitmapDisplayer(300)) - .build() - ImageLoader.getInstance().displayImage(Scheme.APP + app.packageName, entry.icon, options) entry.text.setText(app.name) + entry.icon.setImageDrawable(app.icon) val switch = entry.switch switch.setTag(app) switch.setChecked(app.proxied) @@ -224,7 +203,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe } } - protected override def onOptionsItemSelected(item: MenuItem): Boolean = { + def onMenuItemClick(item: MenuItem): Boolean = { val clipboard = getSystemService(Context.CLIPBOARD_SERVICE).asInstanceOf[ClipboardManager] val prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext) item.getItemId match { @@ -262,12 +241,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe Toast.makeText(this, R.string.action_import_err, Toast.LENGTH_SHORT).show() return false } - super.onOptionsItemSelected(item) - } - - protected override def onCreateOptionsMenu(menu: Menu): Boolean = { - getMenuInflater.inflate(R.menu.app_manager_menu, menu) - super.onCreateOptionsMenu(menu) + false } protected override def onCreate(savedInstanceState: Bundle) { @@ -283,6 +257,9 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe val intent = getParentActivityIntent if (intent == null) finish else navigateUpTo(intent) }) + toolbar.inflateMenu(R.menu.app_manager_menu) + toolbar.setOnMenuItemClickListener(this) + this.overlay = View.inflate(this, R.layout.overlay, null).asInstanceOf[TextView] getWindowManager.addView(overlay, new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, @@ -290,100 +267,59 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT)) - val config = - new ImageLoaderConfiguration.Builder(this) - .imageDownloader(new AppIconDownloader(this)) - .build() - ImageLoader.getInstance().init(config) - val bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch] - val prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext) bypassSwitch.setOnCheckedChangeListener((button: CompoundButton, checked: Boolean) => - prefs.edit().putBoolean(Key.isBypassApps, checked).apply()) - bypassSwitch.setChecked(prefs.getBoolean(Key.isBypassApps, false)) + ShadowsocksApplication.settings.edit().putBoolean(Key.isBypassApps, checked).apply()) + bypassSwitch.setChecked(ShadowsocksApplication.settings.getBoolean(Key.isBypassApps, false)) appListView = findViewById(R.id.applistview).asInstanceOf[ListView] + appListView.setOnScrollListener(new AbsListView.OnScrollListener { + var visible = false + def onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, + totalItemCount: Int) { + if (visible) { + val name: String = apps(firstVisibleItem).name + if (name != null && name.length > 1) { + overlay.setText(apps(firstVisibleItem).name.substring(0, 1)) + } else { + overlay.setText("*") + } + overlay.setVisibility(View.VISIBLE) + } + } + def onScrollStateChanged(view: AbsListView, scrollState: Int) { + visible = true + if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) { + overlay.setVisibility(View.INVISIBLE) + } + } + }) } protected override def onResume() { super.onResume() Future { - handler.post(loadStartRunnable) if (!appsLoaded) loadApps() - handler.post(loadFinishRunnable) + handler.post(() => { + appListView.setAdapter(adapter) + Utils.crossFade(AppManager.this, findViewById(R.id.loading), appListView) + }) } } def saveAppSettings(context: Context) { if (apps == null) return - val prefs = PreferenceManager.getDefaultSharedPreferences(this) val proxiedApps = new StringBuilder apps.foreach(app => if (app.proxied) { proxiedApps ++= app.uid.toString proxiedApps += '|' }) - val edit: SharedPreferences.Editor = prefs.edit + val edit: SharedPreferences.Editor = ShadowsocksApplication.settings.edit edit.putString(Key.proxied, proxiedApps.toString()) edit.apply } - val loadStartRunnable = new Runnable { - override def run() { - progressDialog = ProgressDialog - .show(AppManager.this, "", getString(R.string.loading), true, true) - } - } - - val loadFinishRunnable = new Runnable { - override def run() = { - appListView.setAdapter(adapter) - appListView.setOnScrollListener(new AbsListView.OnScrollListener { - def onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, - totalItemCount: Int) { - if (visible) { - val name: String = apps(firstVisibleItem).name - if (name != null && name.length > 1) { - overlay.setText(apps(firstVisibleItem).name.substring(0, 1)) - } else { - overlay.setText("*") - } - overlay.setVisibility(View.VISIBLE) - } - } - def onScrollStateChanged(view: AbsListView, scrollState: Int) { - visible = true - if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) { - overlay.setVisibility(View.INVISIBLE) - } - } - var visible = false - }) - if (progressDialog != null) { - progressDialog.dismiss() - progressDialog = null - } - } - } - var handler: Handler = null - class AppIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int) - extends BaseImageDownloader(context, connectTimeout, readTimeout) { - - def this(context: Context) { - this(context, 0, 0) - } - - override def getStreamFromOtherSource(imageUri: String, extra: AnyRef): InputStream = { - val packageName = imageUri.substring(Scheme.APP.length) - val drawable = Utils.getAppIcon(getBaseContext, packageName) - val bitmap = Utils.drawableToBitmap(drawable) - - val os = new ByteArrayOutputStream() - bitmap.compress(Bitmap.CompressFormat.PNG, 100, os) - new ByteArrayInputStream(os.toByteArray) - } - } - } diff --git a/src/main/scala/com/github/shadowsocks/ParserActivity.scala b/src/main/scala/com/github/shadowsocks/ParserActivity.scala index 59911e27..ba102adc 100644 --- a/src/main/scala/com/github/shadowsocks/ParserActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ParserActivity.scala @@ -57,14 +57,14 @@ class ParserActivity extends Activity { new AlertDialog.Builder(this) .setTitle(R.string.add_profile_dialog) .setCancelable(false) - .setPositiveButton(R.string.yes, ((dialog: DialogInterface, id: Int) => { + .setPositiveButton(android.R.string.yes, ((dialog: DialogInterface, id: Int) => { Parser.parse(data) match { case Some(profile) => addProfile(profile) case _ => // ignore } dialog.dismiss() }): DialogInterface.OnClickListener) - .setNegativeButton(R.string.no, ((dialog: DialogInterface, id: Int) => { + .setNegativeButton(android.R.string.no, ((dialog: DialogInterface, id: Int) => { dialog.dismiss() finish() }): DialogInterface.OnClickListener) @@ -90,7 +90,7 @@ class ParserActivity extends Activity { h.postDelayed(() => { val profileManager = new ProfileManager(PreferenceManager.getDefaultSharedPreferences(getBaseContext), - getApplication.asInstanceOf[ShadowsocksApplication].dbHelper) + ShadowsocksApplication.dbHelper) profileManager.createOrUpdateProfile(profile) profileManager.reload(profile.id) h.sendEmptyMessage(0) diff --git a/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala b/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala new file mode 100644 index 00000000..1f2d9583 --- /dev/null +++ b/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala @@ -0,0 +1,176 @@ +package com.github.shadowsocks + +import android.app.AlertDialog +import android.content.{DialogInterface, Intent} +import android.os.Bundle +import android.support.design.widget.Snackbar +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.RecyclerView.ViewHolder +import android.support.v7.widget.Toolbar.OnMenuItemClickListener +import android.support.v7.widget.helper.ItemTouchHelper +import android.support.v7.widget.helper.ItemTouchHelper.SimpleCallback +import android.support.v7.widget.{DefaultItemAnimator, LinearLayoutManager, RecyclerView, Toolbar} +import android.view.View.{OnAttachStateChangeListener, OnClickListener} +import android.view.{LayoutInflater, MenuItem, View, ViewGroup} +import android.widget.{LinearLayout, ImageView, CheckedTextView, Toast} +import com.github.shadowsocks.database.Profile +import com.github.shadowsocks.utils.{Parser, Utils} +import com.google.zxing.integration.android.IntentIntegrator +import net.glxn.qrgen.android.QRCode + +import scala.collection.mutable.ArrayBuffer + +/** + * @author Mygod + */ +class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListener { + private class ProfileViewHolder(val view: View) extends RecyclerView.ViewHolder(view) with View.OnClickListener { + private var item: Profile = _ + private val text = itemView.findViewById(android.R.id.text1).asInstanceOf[CheckedTextView] + itemView.setOnClickListener(this) + + { + val qrcode = itemView.findViewById(R.id.qrcode) + qrcode.setOnClickListener((v: View) => { + val image = new ImageView(ProfileManagerActivity.this) + image.setLayoutParams(new LinearLayout.LayoutParams(-1, -1)) + val qrcode = QRCode.from(Parser.generate(item)) + .withSize(Utils.dpToPx(ProfileManagerActivity.this, 250), Utils.dpToPx(ProfileManagerActivity.this, 250)) + .asInstanceOf[QRCode] + image.setImageBitmap(qrcode.bitmap()) + + new AlertDialog.Builder(ProfileManagerActivity.this) + .setCancelable(true) + .setNegativeButton(getString(R.string.close), + ((dialog: DialogInterface, id: Int) => dialog.cancel()): DialogInterface.OnClickListener) + .setView(image) + .create() + .show() + }) + qrcode.setOnLongClickListener((v: View) => { + Utils.positionToast(Toast.makeText(ProfileManagerActivity.this, R.string.qrcode, Toast.LENGTH_SHORT), qrcode, + getWindow, 0, Utils.dpToPx(ProfileManagerActivity.this, 8)).show + true + }) + } + + def bind(item: Profile) { + text.setText(item.name) + text.setChecked(item.id == ShadowsocksApplication.profileId) + this.item = item + } + + def onClick(v: View) = { + ShadowsocksApplication.profileId(item.id) + finish + } + } + + private class ProfilesAdapter extends RecyclerView.Adapter[ProfileViewHolder] { + private val recycleBin = new ArrayBuffer[(Int, Profile)] + private var profiles = new ArrayBuffer[Profile] + profiles ++= ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List[Profile]()) + + def getItemCount = profiles.length + + def onBindViewHolder(vh: ProfileViewHolder, i: Int) = vh.bind(profiles(i)) + + def onCreateViewHolder(vg: ViewGroup, i: Int) = + new ProfileViewHolder(LayoutInflater.from(vg.getContext).inflate(R.layout.layout_profiles_item, vg, false)) + + def add(item: Profile) { + removedSnackbar.dismiss + commitRemoves + val pos = getItemCount + if (ShadowsocksApplication.profileManager.createOrUpdateProfile(item)) { + profiles += item + notifyItemInserted(pos) + } + } + + def remove(pos: Int) { + recycleBin.append((pos, profiles(pos))) + profiles.remove(pos) + notifyItemRemoved(pos) + } + def undoRemoves { + for ((index, item) <- recycleBin.reverseIterator) { + profiles.insert(index, item) + notifyItemInserted(index) + } + recycleBin.clear + } + def commitRemoves { + for ((index, item) <- recycleBin) { + ShadowsocksApplication.profileManager.delProfile(item.id) + if (item.id == ShadowsocksApplication.profileId) ShadowsocksApplication.profileId(-1) + } + recycleBin.clear + } + } + + private lazy val profilesAdapter = new ProfilesAdapter + private var removedSnackbar: Snackbar = _ + + override def onCreate(savedInstanceState: Bundle) { + super.onCreate(savedInstanceState) + setContentView(R.layout.layout_profiles) + + val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar] + toolbar.setTitle(R.string.profiles) + toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha) + toolbar.setNavigationOnClickListener((v: View) => { + val intent = getParentActivityIntent + if (intent == null) finish else navigateUpTo(intent) + }) + toolbar.inflateMenu(R.menu.add_profile_methods) + toolbar.setOnMenuItemClickListener(this) + + val profilesList = findViewById(R.id.profilesList).asInstanceOf[RecyclerView] + profilesList.setLayoutManager(new LinearLayoutManager(this)) + profilesList.setItemAnimator(new DefaultItemAnimator) + profilesList.setAdapter(profilesAdapter) + removedSnackbar = Snackbar.make(findViewById(android.R.id.content), R.string.removed, Snackbar.LENGTH_LONG) + .setAction(R.string.undo, ((v: View) => profilesAdapter.undoRemoves): OnClickListener) + removedSnackbar.getView.addOnAttachStateChangeListener(new OnAttachStateChangeListener { + def onViewDetachedFromWindow(v: View) = profilesAdapter.commitRemoves + def onViewAttachedToWindow(v: View) = () + }) + new ItemTouchHelper(new SimpleCallback(0, ItemTouchHelper.START | ItemTouchHelper.END) { + def onSwiped(viewHolder: ViewHolder, direction: Int) = { + profilesAdapter.remove(viewHolder.getAdapterPosition) + removedSnackbar.show + } + def onMove(recyclerView: RecyclerView, viewHolder: ViewHolder, target: ViewHolder) = false // TODO? + }).attachToRecyclerView(profilesList) + } + + override def onDestroy { + super.onDestroy + profilesAdapter.commitRemoves + } + + override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + val scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data) + if (scanResult != null) Parser.parse(scanResult.getContents) match { + case Some(profile) => profilesAdapter.add(profile) + case _ => // ignore + } + } + + def onMenuItemClick(item: MenuItem) = item.getItemId match { + case R.id.scan_qr_code => + val integrator = new IntentIntegrator(this) + val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN) + list.add("tw.com.quickmark") + integrator.setTargetApplications(list) + integrator.initiateScan() + true + case R.id.manual_settings => + ShadowsocksApplication.profileManager.reload(-1) + ShadowsocksApplication.profileId(ShadowsocksApplication.profileManager.save.id) + finish + true + case _ => false + } +} diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index 64c167aa..6c290b9d 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -38,66 +38,37 @@ */ package com.github.shadowsocks -import java.io.{ByteArrayInputStream, ByteArrayOutputStream, FileOutputStream, IOException, InputStream, OutputStream} +import java.io.{FileOutputStream, IOException, InputStream, OutputStream} import java.util import java.util.Locale import android.app.backup.BackupManager import android.app.{Activity, AlertDialog, ProgressDialog} import android.content._ -import android.content.pm.{PackageInfo, PackageManager} import android.content.res.AssetManager -import android.graphics.{Bitmap, Color, Typeface} +import android.graphics.Typeface import android.net.{Uri, VpnService} import android.os._ -import android.preference.{Preference, PreferenceManager, SwitchPreference} -import android.support.v4.content.ContextCompat +import android.preference.{Preference, SwitchPreference} import android.support.design.widget.{FloatingActionButton, Snackbar} +import android.support.v4.content.ContextCompat import android.support.v7.app.AppCompatActivity import android.support.v7.widget.Toolbar -import android.util.{DisplayMetrics, Log} +import android.util.Log import android.view.{View, ViewGroup, ViewParent} import android.webkit.{WebView, WebViewClient} import android.widget._ import com.github.jorgecastilloprz.FABProgressCircle import com.github.shadowsocks.aidl.{IShadowsocksService, IShadowsocksServiceCallback} import com.github.shadowsocks.database._ -import com.github.shadowsocks.preferences.{DropDownPreference, PasswordEditTextPreference, ProfileEditTextPreference, SummaryEditTextPreference} +import com.github.shadowsocks.preferences.{DropDownPreference, PasswordEditTextPreference, SummaryEditTextPreference} import com.github.shadowsocks.utils._ import com.google.android.gms.ads.{AdRequest, AdSize, AdView} -import com.google.android.gms.analytics.HitBuilders -import com.google.zxing.integration.android.IntentIntegrator -import com.joanzapata.android.iconify.IconDrawable -import com.joanzapata.android.iconify.Iconify.IconValue -import com.nostra13.universalimageloader.core.download.BaseImageDownloader -import net.glxn.qrgen.android.QRCode -import net.simonvt.menudrawer.MenuDrawer -import scala.collection.mutable.{ArrayBuffer, ListBuffer} +import scala.collection.mutable.ArrayBuffer import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future -class ProfileIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int) - extends BaseImageDownloader(context, connectTimeout, readTimeout) { - - def this(context: Context) { - this(context, 0, 0) - } - - override def getStreamFromOtherSource(imageUri: String, extra: AnyRef): InputStream = { - val text = imageUri.substring(Scheme.PROFILE.length) - val size = Utils.dpToPx(context, 16).toInt - val idx = text.getBytes.last % 6 - val color = Seq(Color.MAGENTA, Color.GREEN, Color.YELLOW, Color.BLUE, Color.DKGRAY, Color.CYAN)( - idx) - val bitmap = Utils.getBitmap(text, size, size, color) - - val os = new ByteArrayOutputStream() - bitmap.compress(Bitmap.CompressFormat.PNG, 100, os) - new ByteArrayInputStream(os.toByteArray) - } -} - object Typefaces { def get(c: Context, assetPath: String): Typeface = { cache synchronized { @@ -126,12 +97,10 @@ object Shadowsocks { val REQUEST_CONNECT = 1 val PREFS_NAME = "Shadowsocks" - val PROXY_PREFS = Array(Key.profileName, Key.proxy, Key.remotePort, Key.localPort, Key.sitekey, - Key.encMethod) - val FEATRUE_PREFS = Array(Key.route, Key.isGlobalProxy, Key.proxyedApps, - Key.isUdpDns, Key.isAuth, Key.isIpv6, Key.isAutoConnect) - - val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL, Executable.TUN2SOCKS) + val PROXY_PREFS = Array(Key.proxy, Key.remotePort, Key.localPort, Key.sitekey, Key.encMethod) + val FEATURE_PREFS = Array(Key.route, Key.isGlobalProxy, Key.proxyedApps, Key.isUdpDns, Key.isAuth, Key.isIpv6) + val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL, + Executable.TUN2SOCKS) // Helper functions def updateDropDownPreference(pref: Preference, value: String) { @@ -148,18 +117,13 @@ object Shadowsocks { pref.asInstanceOf[SummaryEditTextPreference].setText(value) } - def updateProfileEditTextPreference(pref: Preference, value: String) { - pref.asInstanceOf[ProfileEditTextPreference].resetSummary(value) - pref.asInstanceOf[ProfileEditTextPreference].setText(value) - } - def updateSwitchPreference(pref: Preference, value: Boolean) { pref.asInstanceOf[SwitchPreference].setChecked(value) } def updatePreference(pref: Preference, name: String, profile: Profile) { name match { - case Key.profileName => updateProfileEditTextPreference(pref, profile.name) + case Key.profileName => updateSummaryEditTextPreference(pref, profile.name) case Key.proxy => updateSummaryEditTextPreference(pref, profile.host) case Key.remotePort => updateSummaryEditTextPreference(pref, profile.remotePort.toString) case Key.localPort => updateSummaryEditTextPreference(pref, profile.localPort.toString) @@ -176,14 +140,8 @@ object Shadowsocks { } class Shadowsocks - extends AppCompatActivity - with MenuAdapter.MenuListener { - - // Flags - val MSG_CRASH_RECOVER: Int = 1 - val STATE_MENUDRAWER = "com.github.shadowsocks.menuDrawer" - val STATE_ACTIVE_VIEW_ID = "com.github.shadowsocks.activeViewId" - var singlePane: Int = -1 + extends AppCompatActivity { + import Shadowsocks._ // Variables private var serviceStarted = false @@ -217,8 +175,8 @@ class Shadowsocks if (fab != null) fab.setEnabled(true) stateUpdate() - if (!status.getBoolean(getVersionName, false)) { - status.edit.putBoolean(getVersionName, true).apply() + if (!ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)) { + ShadowsocksApplication.settings.edit.putBoolean(ShadowsocksApplication.getVersionName, true).apply() recovery() } } @@ -236,33 +194,11 @@ class Shadowsocks private lazy val preferences = getFragmentManager.findFragmentById(android.R.id.content).asInstanceOf[ShadowsocksSettings] - lazy val settings = PreferenceManager.getDefaultSharedPreferences(this) - lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE) - private lazy val preferenceReceiver = new PreferenceBroadcastReceiver - private lazy val drawer = MenuDrawer.attach(this) - private lazy val menuAdapter = new MenuAdapter(this, getMenuList) - private lazy val listView = new ListView(this) - private lazy val profileManager = - new ProfileManager(settings, getApplication.asInstanceOf[ShadowsocksApplication].dbHelper) - private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication] private lazy val greyTint = ContextCompat.getColorStateList(this, R.color.material_blue_grey_700) - private lazy val greenTint = ContextCompat.getColorStateList(this, R.color.material_green_600) + private lazy val greenTint = ContextCompat.getColorStateList(this, R.color.material_green_700) var handler = new Handler() - def isSinglePane: Boolean = { - if (singlePane == -1) { - val metrics = new DisplayMetrics() - getWindowManager.getDefaultDisplay.getMetrics(metrics) - val widthPixels = metrics.widthPixels - val scaleFactor = metrics.density - val widthDp = widthPixels / scaleFactor - - singlePane = if (widthDp <= 720) 1 else 0 - } - singlePane == 1 - } - private def changeSwitch(checked: Boolean) { serviceStarted = checked fab.setImageResource(if (checked) R.drawable.ic_cloud else R.drawable.ic_cloud_off) @@ -359,18 +295,6 @@ class Shadowsocks Console.runRootCommand(Utils.getIptables + " -t nat -F OUTPUT") } - private def getVersionName: String = { - var version: String = null - try { - val pi: PackageInfo = getPackageManager.getPackageInfo(getPackageName, 0) - version = pi.versionName - } catch { - case e: PackageManager.NameNotFoundException => - version = "Package name not found" - } - version - } - def isTextEmpty(s: String, msg: String): Boolean = { if (s != null && s.length > 0) return false Snackbar.make(getWindow.getDecorView.findViewById(android.R.id.content), msg, Snackbar.LENGTH_LONG).show @@ -393,7 +317,7 @@ class Shadowsocks def prepareStartService() { Future { - if (isVpnEnabled) { + if (ShadowsocksApplication.isVpnEnabled) { val intent = VpnService.prepare(this) if (intent != null) { startActivityForResult(intent, Shadowsocks.REQUEST_CONNECT) @@ -413,60 +337,43 @@ class Shadowsocks } } - override def setContentView(layoutResId: Int) { - drawer.setContentView(layoutResId) - onContentChanged() - } - override def onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) - if (settings.getString(Key.proxy, "") == "198.199.101.152") { - setContentView(R.layout.layout_main_ad) - val adView = findViewById(R.id.adView).asInstanceOf[AdView] + setContentView(R.layout.layout_main) + if (ShadowsocksApplication.proxy == "198.199.101.152") { + val adView = new AdView(this) + adView.setAdUnitId("ca-app-pub-9097031975646651/7760346322") + adView.setAdSize(AdSize.SMART_BANNER) + preferences.getView.asInstanceOf[ViewGroup].addView(adView, 0) adView.loadAd(new AdRequest.Builder().build()) - } else { - setContentView(R.layout.layout_main) } // Update the profile - if (!status.getBoolean(getVersionName, false)) { - currentProfile = profileManager.create() + if (!ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)) { + currentProfile = ShadowsocksApplication.profileManager.create() } // Initialize the profile currentProfile = { - profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile - } - - // Initialize drawer - menuAdapter.setActiveId(settings.getInt(Key.profileId, -1)) - menuAdapter.setListener(this) - listView.setAdapter(menuAdapter) - drawer.setMenuView(listView) - - if (Utils.isLollipopOrAbove) { - drawer.setDrawerIndicatorEnabled(false) - } else { - // The drawable that replaces the up indicator in the action bar - drawer.setSlideDrawable(R.drawable.ic_drawer) - // Whether the previous drawable should be shown - drawer.setDrawerIndicatorEnabled(true) - } - - if (!isSinglePane) { - drawer.openMenu(false) + ShadowsocksApplication.currentProfile getOrElse currentProfile } // Initialize action bar val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar] - toolbar.setTitle(getTitle) + toolbar.setTitle(getString(R.string.screen_name)) + toolbar.setTitleTextAppearance(toolbar.getContext, R.style.Toolbar_Logo) val field = classOf[Toolbar].getDeclaredField("mTitleTextView") field.setAccessible(true) val title: TextView = field.get(toolbar).asInstanceOf[TextView] val tf: Typeface = Typefaces.get(this, "fonts/Iceland.ttf") if (tf != null) title.setTypeface(tf) + title.setOnClickListener((view: View) => { + ShadowsocksApplication.track(TAG, "about") + showAbout() + }) + fab = findViewById(R.id.fab).asInstanceOf[FloatingActionButton] fabProgressCircle = findViewById(R.id.fabProgressCircle).asInstanceOf[FABProgressCircle] fab.setOnClickListener((v: View) => { @@ -489,29 +396,9 @@ class Shadowsocks }) fab.setOnLongClickListener((v: View) => { Utils.positionToast(Toast.makeText(this, if (serviceStarted) R.string.stop else R.string.connect, - Toast.LENGTH_SHORT), fab, getWindow, 0, Utils.dpToPx(this, 8).toInt).show + Toast.LENGTH_SHORT), fab, getWindow, 0, Utils.dpToPx(this, 8)).show true }) - toolbar.setNavigationIcon(R.drawable.ic_drawer) - toolbar.setNavigationOnClickListener((v: View) => drawer.toggleMenu()) - title.setOnLongClickListener((v: View) => { - if (Utils.isLollipopOrAbove && bgService != null - && (bgService.getState == State.INIT || bgService.getState == State.STOPPED)) { - val natEnabled = !status.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove) - status.edit().putBoolean(Key.isNAT, natEnabled).apply() - deattachService() - attachService() - if (!Utils.isLollipopOrAbove) setPreferenceEnabled(State.isAvailable(bgService.getState)) - Toast.makeText(getBaseContext, if (natEnabled) R.string.enable_nat else R.string.disable_nat, Toast.LENGTH_LONG) - .show() - true - } else { - false - } - }) - - // Register broadcast receiver - registerReceiver(preferenceReceiver, new IntentFilter(Action.UPDATE_PREFS)) // Bind to the service handler.post(() => { @@ -521,7 +408,8 @@ class Shadowsocks def attachService() { if (bgService == null) { - val s = if (!isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService] + val s = if (ShadowsocksApplication.isVpnEnabled) classOf[ShadowsocksVpnService] + else classOf[ShadowsocksNatService] val intent = new Intent(this, s) intent.setAction(Action.SERVICE) bindService(intent, connection, Context.BIND_AUTO_CREATE) @@ -541,222 +429,28 @@ class Shadowsocks } } - override def onRestoreInstanceState(inState: Bundle) { - super.onRestoreInstanceState(inState) - drawer.restoreState(inState.getParcelable(STATE_MENUDRAWER)) - } - - override def onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putParcelable(STATE_MENUDRAWER, drawer.saveState()) - outState.putInt(STATE_ACTIVE_VIEW_ID, currentProfile.id) - } - - override def onBackPressed() { - val drawerState = drawer.getDrawerState - if (drawerState == MenuDrawer.STATE_OPEN || drawerState == MenuDrawer.STATE_OPENING) { - drawer.closeMenu() - return - } - super.onBackPressed() - } - - override def onActiveViewChanged(v: View, pos: Int) { - drawer.setActiveView(v, pos) - } - - def newProfile(id: Int) { - - val builder = new AlertDialog.Builder(this) - builder - .setTitle(R.string.add_profile) - .setItems(R.array.add_profile_methods, ((dialog: DialogInterface, which: Int) => which match { - case 0 => - dialog.dismiss() - val h = showProgress(R.string.loading) - h.postDelayed(() => { - val integrator = new IntentIntegrator(Shadowsocks.this) - val list = new java.util.ArrayList(IntentIntegrator.TARGET_ALL_KNOWN) - list.add("tw.com.quickmark") - integrator.setTargetApplications(list) - integrator.initiateScan() - h.sendEmptyMessage(0) - }, 600) - case 1 => - dialog.dismiss() - addProfile(id) - case _ => - }): DialogInterface.OnClickListener) - builder.create().show() - } - def reloadProfile() { - drawer.closeMenu(true) + currentProfile = ShadowsocksApplication.currentProfile match { + case Some(profile) => profile // updated + case None => // removed + val profiles = ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List[Profile]()) + ShadowsocksApplication.profileId(if (profiles.isEmpty) -1 else profiles.head.id) + } - val h = showProgress(R.string.loading) - - handler.postDelayed(() => { - currentProfile = { - profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile - } - menuAdapter.updateList(getMenuList, currentProfile.id) - - updatePreferenceScreen() - - h.sendEmptyMessage(0) - }, 600) + updatePreferenceScreen() } def addProfile(profile: Profile) { - drawer.closeMenu(true) + currentProfile = profile + ShadowsocksApplication.profileManager.createOrUpdateProfile(currentProfile) + ShadowsocksApplication.profileManager.reload(currentProfile.id) - val h = showProgress(R.string.loading) - - handler.postDelayed(() => { - currentProfile = profile - profileManager.createOrUpdateProfile(currentProfile) - profileManager.reload(currentProfile.id) - menuAdapter.updateList(getMenuList, currentProfile.id) - - updatePreferenceScreen() - - h.sendEmptyMessage(0) - }, 600) - } - - def addProfile(id: Int) { - drawer.closeMenu(true) - - val h = showProgress(R.string.loading) - - handler.postDelayed(() => { - currentProfile = profileManager.reload(id) - profileManager.save() - menuAdapter.updateList(getMenuList, currentProfile.id) - - updatePreferenceScreen() - - h.sendEmptyMessage(0) - }, 600) - } - - def updateProfile(id: Int) { - drawer.closeMenu(true) - - val h = showProgress(R.string.loading) - - handler.postDelayed(() => { - currentProfile = profileManager.reload(id) - menuAdapter.setActiveId(id) - menuAdapter.notifyDataSetChanged() - - updatePreferenceScreen() - - h.sendEmptyMessage(0) - }, 600) - } - - def delProfile(id: Int): Boolean = { - drawer.closeMenu(true) - - val profile = profileManager.getProfile(id) - - if (profile.isEmpty) return false - - new AlertDialog.Builder(this) - .setMessage(String.format(Locale.ENGLISH, getString(R.string.remove_profile), profile.get.name)) - .setCancelable(false) - .setNegativeButton(R.string.no, - ((dialog: DialogInterface, i: Int) => dialog.cancel()): DialogInterface.OnClickListener) - .setPositiveButton(R.string.yes, ((dialog: DialogInterface, i: Int) => { - profileManager.delProfile(id) - val profileId = { - val profiles = profileManager.getAllProfiles.getOrElse(List[Profile]()) - if (profiles.isEmpty) -1 else profiles.head.id - } - currentProfile = profileManager.load(profileId) - menuAdapter.updateList(getMenuList, currentProfile.id) - - updatePreferenceScreen() - - dialog.dismiss() - }): DialogInterface.OnClickListener) - .create() - .show() - - true - } - - def getProfileList: List[Item] = { - val list = profileManager.getAllProfiles getOrElse List[Profile]() - list.map(p => new IconItem(p.id, p.name, -1, updateProfile, delProfile)) - } - - def getMenuList: List[Any] = { - - val buf = new ListBuffer[Any]() - - buf += new Category(getString(R.string.profiles)) - - buf ++= getProfileList - - buf += - new DrawableItem(-400, getString(R.string.add_profile), new IconDrawable(this, IconValue.fa_plus_circle) - .colorRes(android.R.color.darker_gray).sizeDp(26), newProfile) - - buf += new Category(getString(R.string.settings)) - - buf += new DrawableItem(-100, getString(R.string.recovery), new IconDrawable(this, IconValue.fa_recycle) - .colorRes(android.R.color.darker_gray).sizeDp(26), _ => { - // send event - application.tracker.send(new HitBuilders.EventBuilder() - .setCategory(Shadowsocks.TAG) - .setAction("reset") - .setLabel(getVersionName) - .build()) - recovery() - }) - - buf += - new DrawableItem(-200, getString(R.string.flush_dnscache), new IconDrawable(this, IconValue.fa_refresh) - .colorRes(android.R.color.darker_gray).sizeDp(26), _ => { - // send event - application.tracker.send(new HitBuilders.EventBuilder() - .setCategory(Shadowsocks.TAG) - .setAction("flush_dnscache") - .setLabel(getVersionName) - .build()) - flushDnsCache() - }) - - buf += - new DrawableItem(-300, getString(R.string.qrcode), new IconDrawable(this, IconValue.fa_qrcode) - .colorRes(android.R.color.darker_gray).sizeDp(26), _ => { - // send event - application.tracker.send(new HitBuilders.EventBuilder() - .setCategory(Shadowsocks.TAG) - .setAction("qrcode") - .setLabel(getVersionName) - .build()) - showQrCode() - }) - - buf += new DrawableItem(-400, getString(R.string.about), new IconDrawable(this, IconValue.fa_info_circle) - .colorRes(android.R.color.darker_gray).sizeDp(26), _ => { - // send event - application.tracker.send(new HitBuilders.EventBuilder() - .setCategory(Shadowsocks.TAG) - .setAction("about") - .setLabel(getVersionName) - .build()) - showAbout() - }) - - buf.toList + updatePreferenceScreen() } protected override def onPause() { super.onPause() + ShadowsocksApplication.profileManager.save prepared = false } @@ -764,19 +458,19 @@ class Shadowsocks if (bgService != null) { bgService.getState match { case State.CONNECTING => - fab.setBackgroundTintList(greyTint); + fab.setBackgroundTintList(greyTint) fabProgressCircle.show() changeSwitch(checked = true) case State.CONNECTED => - fab.setBackgroundTintList(greenTint); + fab.setBackgroundTintList(greenTint) fabProgressCircle.hide() changeSwitch(checked = true) case State.STOPPING => - fab.setBackgroundTintList(greyTint); + fab.setBackgroundTintList(greyTint) fabProgressCircle.show() changeSwitch(checked = false) case _ => - fab.setBackgroundTintList(greyTint); + fab.setBackgroundTintList(greyTint) fabProgressCircle.hide() changeSwitch(checked = false) } @@ -789,25 +483,24 @@ class Shadowsocks stateUpdate() ConfigUtils.refresh(this) - // Check if profile list changed - val id = settings.getInt(Key.profileId, -1) - if (id != -1 && id != currentProfile.id) - reloadProfile() + // Check if current profile changed + if (ShadowsocksApplication.profileId != currentProfile.id) reloadProfile() } private def setPreferenceEnabled(enabled: Boolean) { + preferences.findPreference(Key.isNAT).setEnabled(enabled) for (name <- Shadowsocks.PROXY_PREFS) { val pref = preferences.findPreference(name) if (pref != null) { pref.setEnabled(enabled) } } - for (name <- Shadowsocks.FEATRUE_PREFS) { + for (name <- Shadowsocks.FEATURE_PREFS) { val pref = preferences.findPreference(name) if (pref != null) { if (Seq(Key.isGlobalProxy, Key.proxyedApps) .contains(name)) { - pref.setEnabled(enabled && (Utils.isLollipopOrAbove || !isVpnEnabled)) + pref.setEnabled(enabled && (Utils.isLollipopOrAbove || !ShadowsocksApplication.isVpnEnabled)) } else { pref.setEnabled(enabled) } @@ -817,11 +510,12 @@ class Shadowsocks private def updatePreferenceScreen() { val profile = currentProfile + Shadowsocks.updatePreference(preferences.findPreference(Key.profileName), Key.profileName, profile) for (name <- Shadowsocks.PROXY_PREFS) { val pref = preferences.findPreference(name) Shadowsocks.updatePreference(pref, name, profile) } - for (name <- Shadowsocks.FEATRUE_PREFS) { + for (name <- Shadowsocks.FEATURE_PREFS) { val pref = preferences.findPreference(name) Shadowsocks.updatePreference(pref, name, profile) } @@ -839,7 +533,6 @@ class Shadowsocks override def onDestroy() { super.onDestroy() deattachService() - unregisterReceiver(preferenceReceiver) new BackupManager(this).dataChanged() handler.removeCallbacksAndMessages(null) } @@ -867,13 +560,12 @@ class Shadowsocks } def reset() { - crashRecovery() install() } - private def recovery() { + def recovery() { serviceStop() val h = showProgress(R.string.recovering) Future { @@ -882,26 +574,7 @@ class Shadowsocks } } - private def dp2px(dp: Int): Int = - Math.round(dp * (getBaseContext.getResources.getDisplayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)) - - private def showQrCode() { - val image = new ImageView(this) - image.setLayoutParams(new LinearLayout.LayoutParams(-1, -1)) - val qrcode = QRCode.from(Parser.generate(currentProfile)) - .withSize(dp2px(250), dp2px(250)).asInstanceOf[QRCode] - image.setImageBitmap(qrcode.bitmap()) - - new AlertDialog.Builder(this) - .setCancelable(true) - .setNegativeButton(getString(R.string.close), - ((dialog: DialogInterface, id: Int) => dialog.cancel()): DialogInterface.OnClickListener) - .setView(image) - .create() - .show() - } - - private def flushDnsCache() { + def flushDnsCache() { val h = showProgress(R.string.flushing) Future { Utils.toggleAirplaneMode(getBaseContext) @@ -909,29 +582,13 @@ class Shadowsocks } } - override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { - val scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data) - if (scanResult != null) { - Parser.parse(scanResult.getContents) match { - case Some(profile) => addProfile(profile) - case _ => // ignore - } - } else { - resultCode match { - case Activity.RESULT_OK => - prepared = true - serviceStart() - case _ => - cancelStart() - Log.e(Shadowsocks.TAG, "Failed to start VpnService") - } - } - } - - var isRoot: Option[Boolean] = None - def isVpnEnabled: Boolean = { - if (isRoot.isEmpty) isRoot = Some(Console.isRoot) - !(isRoot.get && status.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove)) + override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) = resultCode match { + case Activity.RESULT_OK => + prepared = true + serviceStart() + case _ => + cancelStart() + Log.e(Shadowsocks.TAG, "Failed to start VpnService") } def serviceStop() { @@ -939,12 +596,12 @@ class Shadowsocks } def checkText(key: String): Boolean = { - val text = settings.getString(key, "") + val text = ShadowsocksApplication.settings.getString(key, "") !isTextEmpty(text, getString(R.string.proxy_empty)) } def checkNumber(key: String, low: Boolean): Boolean = { - val text = settings.getString(key, "") + val text = ShadowsocksApplication.settings.getString(key, "") if (isTextEmpty(text, getString(R.string.port_empty))) return false try { val port: Int = Integer.valueOf(text) @@ -964,15 +621,14 @@ class Shadowsocks /** Called when connect button is clicked. */ def serviceStart() { - bgService.start(ConfigUtils.load(settings)) + bgService.start(ConfigUtils.load(ShadowsocksApplication.settings)) - if (isVpnEnabled) { + if (ShadowsocksApplication.isVpnEnabled) { changeSwitch(checked = false) } } private def showAbout() { - val web = new WebView(this) web.loadUrl("file:///android_asset/pages/about.html") web.setWebViewClient(new WebViewClient() { @@ -982,18 +638,10 @@ class Shadowsocks } }) - var versionName = "" - try { - versionName = getPackageManager.getPackageInfo(getPackageName, 0).versionName - } catch { - case ex: PackageManager.NameNotFoundException => - versionName = "" - } - new AlertDialog.Builder(this) - .setTitle(getString(R.string.about_title).formatLocal(Locale.ENGLISH, versionName)) + .setTitle(getString(R.string.about_title).formatLocal(Locale.ENGLISH, ShadowsocksApplication.getVersionName)) .setCancelable(false) - .setNegativeButton(getString(R.string.ok_iknow), + .setNegativeButton(getString(android.R.string.ok), ((dialog: DialogInterface, id: Int) => dialog.cancel()): DialogInterface.OnClickListener) .setView(web) .create() @@ -1012,13 +660,13 @@ class Shadowsocks handler.post(() => if (state != s) { s match { case State.CONNECTING => - fab.setBackgroundTintList(greyTint); + fab.setBackgroundTintList(greyTint) fab.setImageResource(R.drawable.ic_cloud_queue) fab.setEnabled(false) fabProgressCircle.show() setPreferenceEnabled(enabled = false) case State.CONNECTED => - fab.setBackgroundTintList(greenTint); + fab.setBackgroundTintList(greenTint) if (state == State.CONNECTING) { fabProgressCircle.beginFinalAnimation() } else { @@ -1028,7 +676,7 @@ class Shadowsocks changeSwitch(checked = true) setPreferenceEnabled(enabled = false) case State.STOPPED => - fab.setBackgroundTintList(greyTint); + fab.setBackgroundTintList(greyTint) handler.postDelayed(() => fabProgressCircle.hide(), 1000) fab.setEnabled(true) changeSwitch(checked = false) @@ -1036,21 +684,13 @@ class Shadowsocks getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m), Snackbar.LENGTH_LONG).show setPreferenceEnabled(enabled = true) case State.STOPPING => - fab.setBackgroundTintList(greyTint); + fab.setBackgroundTintList(greyTint) fab.setImageResource(R.drawable.ic_cloud_queue) fab.setEnabled(false) - fabProgressCircle.show() + if (state == State.CONNECTED) fabProgressCircle.show() // ignore for stopped setPreferenceEnabled(enabled = false) } state = s }) } - - class PreferenceBroadcastReceiver extends BroadcastReceiver { - override def onReceive(context: Context, intent: Intent) { - currentProfile = profileManager.save() - menuAdapter.updateList(getMenuList, currentProfile.id) - } - } - } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala index ebb22de3..f63114e0 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala @@ -43,19 +43,53 @@ import java.util import java.util.concurrent.TimeUnit import android.app.Application -import com.github.shadowsocks.database.DBHelper -import com.github.shadowsocks.utils.Utils -import com.google.android.gms.analytics.GoogleAnalytics +import android.content.pm.PackageManager +import android.preference.PreferenceManager +import com.github.shadowsocks.database.{DBHelper, ProfileManager} +import com.github.shadowsocks.utils.{Console, Key, Utils} +import com.google.android.gms.analytics.{GoogleAnalytics, HitBuilders} import com.google.android.gms.common.api.ResultCallback import com.google.android.gms.tagmanager.{ContainerHolder, TagManager} +object ShadowsocksApplication { + var instance: ShadowsocksApplication = _ + lazy val dbHelper = new DBHelper(instance) + final val SIG_FUNC = "getSignature" + var containerHolder: ContainerHolder = _ + lazy val tracker = GoogleAnalytics.getInstance(instance).newTracker(R.xml.tracker) + lazy val settings = PreferenceManager.getDefaultSharedPreferences(instance) + lazy val profileManager = new ProfileManager(settings, dbHelper) + + val isRoot = Console.isRoot + def isVpnEnabled = !(isRoot && settings.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove)) + + def getVersionName = try { + instance.getPackageManager.getPackageInfo(instance.getPackageName, 0).versionName + } catch { + case _: PackageManager.NameNotFoundException => "Package name not found" + case _: Throwable => null + } + + // send event + def track(category: String, action: String) = tracker.send(new HitBuilders.EventBuilder() + .setAction(action) + .setLabel(getVersionName) + .build()) + + def profileId = settings.getInt(Key.profileId, -1) + def profileId(i: Int) = { + settings.edit.putInt(Key.profileId, i).apply + profileManager.load(i) + } + def proxy = settings.getString(Key.proxy, "") + def currentProfile = profileManager.getProfile(profileId) +} + class ShadowsocksApplication extends Application { - lazy val dbHelper = new DBHelper(this) - lazy val SIG_FUNC = "getSignature" - var containerHolder: ContainerHolder = null - lazy val tracker = GoogleAnalytics.getInstance(this).newTracker(R.xml.tracker) + import ShadowsocksApplication._ override def onCreate() { + ShadowsocksApplication.instance = this val tm = TagManager.getInstance(this) val pending = tm.loadContainerPreferNonDefault("GTM-NT8WS8", R.raw.gtm_default_container) val callback = new ResultCallback[ContainerHolder] { diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala index 6029337c..1ea60a5f 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala @@ -40,22 +40,20 @@ package com.github.shadowsocks import java.io.File -import java.lang.reflect.{InvocationTargetException, Method} import java.net.{Inet6Address, InetAddress} import java.util.Locale import android.app._ import android.content._ import android.content.pm.{PackageInfo, PackageManager} -import android.net.{Network, ConnectivityManager} +import android.net.{ConnectivityManager, Network} import android.os._ import android.support.v4.app.NotificationCompat import android.support.v4.content.ContextCompat -import android.util.{SparseArray, Log} +import android.util.{Log, SparseArray} import android.widget.Toast import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.utils._ -import com.google.android.gms.analytics.HitBuilders import scala.collection._ import scala.collection.mutable.ArrayBuffer @@ -77,8 +75,6 @@ class ShadowsocksNatService extends Service with BaseService { var apps: Array[ProxiedApp] = null val myUid = Process.myUid() - private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication] - private val dnsAddressCache = new SparseArray[String] def getNetId(network: Network): Int = { @@ -254,8 +250,8 @@ class ShadowsocksNatService extends Service with BaseService { def startDnsDaemon() { val conf = if (config.route == Route.BYPASS_CHN) { - val reject = ConfigUtils.getRejectList(getContext, application) - val blackList = ConfigUtils.getBlackList(getContext, application) + val reject = ConfigUtils.getRejectList(getContext) + val blackList = ConfigUtils.getBlackList(getContext) ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "127.0.0.1", 8153, Path.BASE + "pdnsd-nat.pid", reject, blackList, 8163, "") } else { @@ -457,19 +453,14 @@ class ShadowsocksNatService extends Service with BaseService { registerReceiver(lockReceiver, screenFilter) } - // send event - application.tracker.send(new HitBuilders.EventBuilder() - .setCategory(TAG) - .setAction("start") - .setLabel(getVersionName) - .build()) + ShadowsocksApplication.track(TAG, "start") changeState(State.CONNECTING) Future { if (config.proxy == "198.199.101.152") { - val holder = application.containerHolder + val holder = ShadowsocksApplication.containerHolder try { config = ConfigUtils.getPublicConfig(getBaseContext, holder.getContainer, config) } catch { @@ -531,12 +522,7 @@ class ShadowsocksNatService extends Service with BaseService { } } - // send event - application.tracker.send(new HitBuilders.EventBuilder() - .setCategory(TAG) - .setAction("stop") - .setLabel(getVersionName) - .build()) + ShadowsocksApplication.track(TAG, "stop") // reset NAT killProcesses() diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala index 6c2a3532..8d1643ca 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala @@ -39,12 +39,7 @@ package com.github.shadowsocks -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.SharedPreferences -import android.content.pm.PackageManager -import android.preference.PreferenceManager +import android.content.{BroadcastReceiver, Context, Intent} import com.github.shadowsocks.utils._ class ShadowsocksReceiver extends BroadcastReceiver { @@ -52,18 +47,8 @@ class ShadowsocksReceiver extends BroadcastReceiver { val TAG = "Shadowsocks" def onReceive(context: Context, intent: Intent) { - val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - val status = context.getSharedPreferences(Key.status, Context.MODE_PRIVATE) - - var versionName: String = null - try { - versionName = context.getPackageManager.getPackageInfo(context.getPackageName, 0).versionName - } catch { - case e: PackageManager.NameNotFoundException => - versionName = "NONE" - } - val isAutoConnect: Boolean = settings.getBoolean(Key.isAutoConnect, false) - val isInstalled: Boolean = status.getBoolean(versionName, false) + val isAutoConnect: Boolean = ShadowsocksApplication.settings.getBoolean(Key.isAutoConnect, false) + val isInstalled: Boolean = ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false) if (isAutoConnect && isInstalled) { if (Utils.isLollipopOrAbove) { val intent = new Intent(context, classOf[ShadowsocksRunnerService]) diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala index 7b2d3817..137912f7 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala @@ -40,19 +40,15 @@ package com.github.shadowsocks import android.app.{Activity, KeyguardManager} -import android.os._ -import android.net.VpnService import android.content._ +import android.net.VpnService +import android.os._ import android.util.Log -import android.preference.PreferenceManager -import com.github.shadowsocks.utils._ import com.github.shadowsocks.aidl.IShadowsocksService +import com.github.shadowsocks.utils._ class ShadowsocksRunnerActivity extends Activity { - private lazy val settings = PreferenceManager.getDefaultSharedPreferences(this) - private lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE) - val handler = new Handler() val connection = new ServiceConnection { override def onServiceConnected(name: ComponentName, service: IBinder) { @@ -65,18 +61,12 @@ class ShadowsocksRunnerActivity extends Activity { } // Variables - var isRoot: Option[Boolean] = None var bgService: IShadowsocksService = _ var receiver:BroadcastReceiver = _ - def isVpnEnabled: Boolean = { - if (isRoot.isEmpty) isRoot = Some(Console.isRoot) - !(isRoot.get && status.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove)) - } - def startBackgroundService() { - if (isVpnEnabled) { + if (ShadowsocksApplication.isVpnEnabled) { val intent = VpnService.prepare(ShadowsocksRunnerActivity.this) if (intent != null) { startActivityForResult(intent, Shadowsocks.REQUEST_CONNECT) @@ -84,14 +74,15 @@ class ShadowsocksRunnerActivity extends Activity { onActivityResult(Shadowsocks.REQUEST_CONNECT, Activity.RESULT_OK, null) } } else { - bgService.start(ConfigUtils.load(settings)) + bgService.start(ConfigUtils.load(ShadowsocksApplication.settings)) finish() } } def attachService() { if (bgService == null) { - val s = if (!isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService] + val s = if (ShadowsocksApplication.isVpnEnabled) classOf[ShadowsocksVpnService] + else classOf[ShadowsocksNatService] val intent = new Intent(this, s) intent.setAction(Action.SERVICE) bindService(intent, connection, Context.BIND_AUTO_CREATE) @@ -137,7 +128,7 @@ class ShadowsocksRunnerActivity extends Activity { resultCode match { case Activity.RESULT_OK => if (bgService != null) { - bgService.start(ConfigUtils.load(settings)) + bgService.start(ConfigUtils.load(ShadowsocksApplication.settings)) } case _ => Log.e(Shadowsocks.TAG, "Failed to start VpnService") diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala index e27168f2..8a271746 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerService.scala @@ -43,15 +43,10 @@ import android.app.Service import android.content._ import android.net.VpnService import android.os._ -import android.preference.PreferenceManager import com.github.shadowsocks.aidl.IShadowsocksService import com.github.shadowsocks.utils._ class ShadowsocksRunnerService extends Service { - - private lazy val settings = PreferenceManager.getDefaultSharedPreferences(this) - private lazy val status = getSharedPreferences(Key.status, Context.MODE_PRIVATE) - val handler = new Handler() val connection = new ServiceConnection { override def onServiceConnected(name: ComponentName, service: IBinder) { @@ -64,35 +59,29 @@ class ShadowsocksRunnerService extends Service { } // Variables - var isRoot: Option[Boolean] = None var bgService: IShadowsocksService = _ - def isVpnEnabled: Boolean = { - if (isRoot.isEmpty) isRoot = Some(Console.isRoot) - !(isRoot.get && status.getBoolean(Key.isNAT, !Utils.isLollipopOrAbove)) - } - override def onBind(intent: Intent): IBinder = { null } def startBackgroundService() { - if (isVpnEnabled) { + if (ShadowsocksApplication.isVpnEnabled) { val intent = VpnService.prepare(ShadowsocksRunnerService.this) if (intent == null) { if (bgService != null) { - bgService.start(ConfigUtils.load(settings)) + bgService.start(ConfigUtils.load(ShadowsocksApplication.settings)) } } } else { - bgService.start(ConfigUtils.load(settings)) + bgService.start(ConfigUtils.load(ShadowsocksApplication.settings)) } stopSelf() } def attachService() { if (bgService == null) { - val s = if (!isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService] + val s = if (!ShadowsocksApplication.isVpnEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService] val intent = new Intent(this, s) intent.setAction(Action.SERVICE) bindService(intent, connection, Context.BIND_AUTO_CREATE) diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala index 30ce662e..6869bfa1 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksSettings.scala @@ -1,12 +1,30 @@ package com.github.shadowsocks import android.os.Bundle -import android.preference.PreferenceFragment +import android.preference.{Preference, PreferenceFragment} +import com.github.shadowsocks.utils.Key // TODO: Move related logic here class ShadowsocksSettings extends PreferenceFragment { + private lazy val activity = getActivity.asInstanceOf[Shadowsocks] + override def onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) addPreferencesFromResource(R.xml.pref_all) + findPreference(Key.isNAT).setOnPreferenceChangeListener((preference: Preference, newValue: Any) => + if (ShadowsocksApplication.isRoot) activity.handler.post(() => { + activity.deattachService() + activity.attachService() + }) else false) + findPreference("recovery").setOnPreferenceClickListener((preference: Preference) => { + ShadowsocksApplication.track(Shadowsocks.TAG, "reset") + activity.recovery() + true + }) + findPreference("flush_dnscache").setOnPreferenceClickListener((preference: Preference) => { + ShadowsocksApplication.track(Shadowsocks.TAG, "flush_dnscache") + activity.flushDnsCache() + true + }) } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index 72e9449d..ba14253f 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -53,7 +53,6 @@ import android.util.Log import android.widget.Toast import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.utils._ -import com.google.android.gms.analytics.HitBuilders import org.apache.commons.net.util.SubnetUtils import scala.collection.mutable @@ -62,8 +61,6 @@ import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future class ShadowsocksVpnService extends VpnService with BaseService { - - private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication] val TAG = "ShadowsocksVpnService" val VPN_MTU = 1500 val PRIVATE_VLAN = "26.26.26.%s" @@ -152,12 +149,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { // channge the state changeState(State.STOPPING) - // send event - application.tracker.send(new HitBuilders.EventBuilder() - .setCategory(TAG) - .setAction("stop") - .setLabel(getVersionName) - .build()) + ShadowsocksApplication.track(TAG, "stop") // reset VPN killProcesses() @@ -231,18 +223,13 @@ class ShadowsocksVpnService extends VpnService with BaseService { return } - // send event - application.tracker.send(new HitBuilders.EventBuilder() - .setCategory(TAG) - .setAction("start") - .setLabel(getVersionName) - .build()) + ShadowsocksApplication.track(TAG, "start") changeState(State.CONNECTING) Future { if (config.proxy == "198.199.101.152") { - val holder = getApplication.asInstanceOf[ShadowsocksApplication].containerHolder + val holder = ShadowsocksApplication.containerHolder try { config = ConfigUtils.getPublicConfig(getBaseContext, holder.getContainer, config) } catch { @@ -371,8 +358,8 @@ class ShadowsocksVpnService extends VpnService with BaseService { val ipv6 = if (config.isIpv6) "" else "reject = ::/0;" val conf = { if (config.route == Route.BYPASS_CHN) { - val reject = ConfigUtils.getRejectList(getContext, application) - val blackList = ConfigUtils.getBlackList(getContext, application) + val reject = ConfigUtils.getRejectList(getContext) + val blackList = ConfigUtils.getBlackList(getContext) ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153, Path.BASE + "pdnsd-vpn.pid", reject, blackList, 8163, ipv6) } else { diff --git a/src/main/scala/com/github/shadowsocks/database/MenuAdapter.scala b/src/main/scala/com/github/shadowsocks/database/MenuAdapter.scala deleted file mode 100644 index e7d7463d..00000000 --- a/src/main/scala/com/github/shadowsocks/database/MenuAdapter.scala +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Shadowsocks - A shadowsocks client for Android - * Copyright (C) 2014 - * - * 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 . - * - * - * ___====-_ _-====___ - * _--^^^#####// \\#####^^^--_ - * _-^##########// ( ) \\##########^-_ - * -############// |\^^/| \\############- - * _/############// (@::@) \\############\_ - * /#############(( \\// ))#############\ - * -###############\\ (oo) //###############- - * -#################\\ / VV \ //#################- - * -###################\\/ \//###################- - * _#/|##########/\######( /\ )######/\##########|\#_ - * |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \| - * ` |/ V V ` V \#\| | | |/#/ V ' V V \| ' - * ` ` ` ` / | | | | \ ' ' ' ' - * ( | | | | ) - * __\ | | | | /__ - * (vvv(VVV)(VVV)vvv) - * - * HERE BE DRAGONS - * - */ - -package com.github.shadowsocks.database - -import android.content.Context -import android.graphics.drawable.Drawable -import android.view.{LayoutInflater, View, ViewGroup} -import android.widget.{BaseAdapter, TextView} -import com.github.shadowsocks.R - -abstract class Item { - def id: Int - def title: String - def click: Int => Unit - def longClick: Int => Boolean -} - -case class IconItem(id: Int, title: String, iconRes: Int, - click: Int => Unit, longClick: Int => Boolean = _ => false) - extends Item - -case class DrawableItem(id: Int, title: String, iconRes: Drawable, - click: Int => Unit, longClick: Int => Boolean = _ => false) - extends Item - -case class Category(title: String) - -object MenuAdapter { - - trait MenuListener { - def onActiveViewChanged(v: View, pos: Int) - } - - val UNDEFINED = -1 - val CATEGORY = 0 - val ITEM = 1 -} - -class MenuAdapter(context: Context, var items: List[Any]) extends BaseAdapter { - - def setListener(listener: MenuAdapter.MenuListener) { - this.listener = listener - } - - def setActiveId(activeId: Int) { - this.activeId = activeId - } - - @Override def getCount: Int = { - items.size - } - - @Override def getItem(position: Int): Object = { - items(position).asInstanceOf[Object] - } - - @Override def getItemId(position: Int): Long = { - position - } - - @Override override def getItemViewType(position: Int): Int = { - getItem(position) match { - case Category => MenuAdapter.CATEGORY - case IconItem => MenuAdapter.ITEM - case DrawableItem => MenuAdapter.ITEM - case _ => MenuAdapter.UNDEFINED - } - } - - @Override override def getViewTypeCount: Int = { - 3 - } - - @Override override def isEnabled(position: Int): Boolean = { - getItemViewType(position) != MenuAdapter.UNDEFINED - } - - @Override override def areAllItemsEnabled: Boolean = { - false - } - - @Override def getView(position: Int, convertView: View, parent: ViewGroup): View = { - var v: View = convertView - val item = getItem(position) - item match { - case value: Category => - if (v == null) { - v = LayoutInflater.from(context).inflate(R.layout.menu_row_category, parent, false) - } - v.asInstanceOf[TextView].setText(value.title) - case value: Item => - if (v == null) { - v = LayoutInflater.from(context).inflate(R.layout.menu_row_item, parent, false) - } - val tv: TextView = v.asInstanceOf[TextView] - tv.setText(value.title) - - value match { - case iconItem: IconItem => - if (iconItem.iconRes != -1) { - tv.setCompoundDrawablesWithIntrinsicBounds(iconItem.iconRes, 0, 0, 0) - } - case drawableItem: DrawableItem => - if (drawableItem.iconRes != null) { - tv.setCompoundDrawablesWithIntrinsicBounds(drawableItem.iconRes, null, null, null) - } - } - - tv.setOnClickListener((view: View) => { - val item = view.getTag(R.id.mdItem).asInstanceOf[Item] - item.click(item.id) - }) - tv.setOnLongClickListener((view: View) => { - val item = view.getTag(R.id.mdItem).asInstanceOf[Item] - item.longClick(item.id) - }) - - v.setTag(R.id.mdItem, value) - - if (value.id == activeId) { - if (listener != null) listener.onActiveViewChanged(v, position) - } - - case _ => - } - v.setTag(R.id.mdActiveViewPosition, position) - - - v - } - - def updateList(list: List[Any], activeId: Int) { - items = list - this.activeId = activeId - notifyDataSetChanged() - } - - private var listener: MenuAdapter.MenuListener = null - private var activeId: Int = -1 -} - - diff --git a/src/main/scala/com/github/shadowsocks/preferences/NatSwitchPreference.scala b/src/main/scala/com/github/shadowsocks/preferences/NatSwitchPreference.scala new file mode 100644 index 00000000..2bd38d27 --- /dev/null +++ b/src/main/scala/com/github/shadowsocks/preferences/NatSwitchPreference.scala @@ -0,0 +1,27 @@ +package com.github.shadowsocks.preferences + +import android.content.Context +import android.content.res.TypedArray +import android.preference.SwitchPreference +import android.util.AttributeSet +import com.github.shadowsocks.{R, ShadowsocksApplication} + +/** + * TODO: No animations? + * @author Mygod + */ +final class NatSwitchPreference(context: Context, attrs: AttributeSet) extends SwitchPreference(context, attrs) { + private var disabled = false + + override def isEnabled = super.isEnabled && !disabled + + override def onGetDefaultValue(a: TypedArray, index: Int) = { + val result = ShadowsocksApplication.isVpnEnabled + if (!ShadowsocksApplication.isRoot) { + disabled = true + setEnabled(false) + setSummary(R.string.nat_summary_no_root) + } + result.asInstanceOf[AnyRef] + } +} diff --git a/src/main/scala/com/github/shadowsocks/preferences/ProfileEditTextPreference.scala b/src/main/scala/com/github/shadowsocks/preferences/ProfileEditTextPreference.scala deleted file mode 100644 index 1ca8ecc3..00000000 --- a/src/main/scala/com/github/shadowsocks/preferences/ProfileEditTextPreference.scala +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Shadowsocks - A shadowsocks client for Android - * Copyright (C) 2013 - * - * 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 . - * - * - * ___====-_ _-====___ - * _--^^^#####// \\#####^^^--_ - * _-^##########// ( ) \\##########^-_ - * -############// |\^^/| \\############- - * _/############// (@::@) \\############\_ - * /#############(( \\// ))#############\ - * -###############\\ (oo) //###############- - * -#################\\ / VV \ //#################- - * -###################\\/ \//###################- - * _#/|##########/\######( /\ )######/\##########|\#_ - * |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \| - * ` |/ V V ` V \#\| | | |/#/ V ' V V \| ' - * ` ` ` ` / | | | | \ ' ' ' ' - * ( | | | | ) - * __\ | | | | /__ - * (vvv(VVV)(VVV)vvv) - * - * HERE BE DRAGONS - * - */ - -package com.github.shadowsocks.preferences - -import android.content.{Intent, Context} -import android.preference.EditTextPreference -import android.util.AttributeSet -import com.github.shadowsocks.utils.Action - -class ProfileEditTextPreference(context: Context, attrs: AttributeSet, defStyle: Int) - extends EditTextPreference(context, attrs, defStyle) { - - def this(context: Context, attrs: AttributeSet) = { - this(context, attrs, android.R.attr.editTextPreferenceStyle) - mDefaultSummary = getSummary - } - - override def setText(text: String) { - super.setText(text) - setSummary(text) - } - - override def setSummary(summary: CharSequence) { - if (summary.toString.isEmpty) { - super.setSummary(mDefaultSummary) - } else { - super.setSummary(summary) - } - context.sendBroadcast(new Intent(Action.UPDATE_PREFS)) - } - - def resetSummary(summary: CharSequence) { - if (summary.toString.isEmpty) { - super.setSummary(mDefaultSummary) - } else { - super.setSummary(summary) - } - } - - private var mDefaultSummary: CharSequence = getSummary -} diff --git a/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala b/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala index 8f57476d..774c355b 100644 --- a/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala +++ b/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala @@ -198,14 +198,14 @@ object ConfigUtils { } def refresh(context: Context) { - val holder = context.getApplicationContext.asInstanceOf[ShadowsocksApplication].containerHolder + val holder = ShadowsocksApplication.containerHolder if (holder != null) holder.refresh() } - def getRejectList(context: Context, app: ShadowsocksApplication): String = { + def getRejectList(context: Context): String = { val default = context.getString(R.string.reject) try { - val container = app.containerHolder.getContainer + val container = ShadowsocksApplication.containerHolder.getContainer val update = container.getString("reject") if (update == null || update.isEmpty) default else update } catch { @@ -213,10 +213,10 @@ object ConfigUtils { } } - def getBlackList(context: Context, app: ShadowsocksApplication): String = { + def getBlackList(context: Context): String = { val default = context.getString(R.string.black_list) try { - val container = app.containerHolder.getContainer + val container = ShadowsocksApplication.containerHolder.getContainer val update = container.getString("black_list") if (update == null || update.isEmpty) default else update } catch { diff --git a/src/main/scala/com/github/shadowsocks/utils/Constants.scala b/src/main/scala/com/github/shadowsocks/utils/Constants.scala index 3d812e9b..1b1f621e 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Constants.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Constants.scala @@ -66,7 +66,6 @@ object Key { val proxied = "Proxyed" val isNAT = "isNAT" - val status = "status" val proxyedApps = "proxyedApps" val route = "route" @@ -111,8 +110,6 @@ object State { object Action { val SERVICE = "com.github.shadowsocks.SERVICE" val CLOSE = "com.github.shadowsocks.CLOSE" - val UPDATE_FRAGMENT = "com.github.shadowsocks.ACTION_UPDATE_FRAGMENT" - val UPDATE_PREFS = "com.github.shadowsocks.ACTION_UPDATE_PREFS" } object Route { diff --git a/src/main/scala/com/github/shadowsocks/utils/Utils.scala b/src/main/scala/com/github/shadowsocks/utils/Utils.scala index 85b14a47..f2d5c981 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Utils.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Utils.scala @@ -42,6 +42,7 @@ import java.io._ import java.net._ import java.security.MessageDigest +import android.animation.{AnimatorListenerAdapter, Animator} import android.app.ActivityManager import android.content.pm.{ApplicationInfo, PackageManager} import android.content.{Context, Intent} @@ -92,6 +93,9 @@ object Utils { new String(Base64.encode(mdg.digest, 0)) } + def dpToPx(context: Context, dp: Int): Int = + Math.round(dp * (context.getResources.getDisplayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)) + /* * round or floor depending on whether you are using offsets(floor) or * widths(round) @@ -139,6 +143,16 @@ object Utils { toast } + def crossFade(context: Context, from: View, to: View) { + def shortAnimTime = context.getResources.getInteger(android.R.integer.config_shortAnimTime) + to.setAlpha(0) + to.setVisibility(View.VISIBLE) + to.animate().alpha(1).setDuration(shortAnimTime) + from.animate().alpha(0).setDuration(shortAnimTime).setListener(new AnimatorListenerAdapter { + override def onAnimationEnd(animation: Animator) = from.setVisibility(View.GONE) + }) + } + // Blocked > 3 seconds def toggleAirplaneMode(context: Context) { if (Build.VERSION.SDK_INT >= 17) {