Commit 51e155c9 authored by Mygod's avatar Mygod

Add more material design (3)

parent 1f695b9c
...@@ -29,13 +29,10 @@ libraryDependencies ++= Seq( ...@@ -29,13 +29,10 @@ libraryDependencies ++= Seq(
"com.github.kevinsawicki" % "http-request" % "6.0", "com.github.kevinsawicki" % "http-request" % "6.0",
"commons-net" % "commons-net" % "3.3", "commons-net" % "commons-net" % "3.3",
"com.google.zxing" % "android-integration" % "3.2.1", "com.google.zxing" % "android-integration" % "3.2.1",
"com.joanzapata.android" % "android-iconify" % "1.0.11",
"net.glxn.qrgen" % "android" % "2.0", "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-base" % "8.1.0",
"com.google.android.gms" % "play-services-ads" % "8.1.0", "com.google.android.gms" % "play-services-ads" % "8.1.0",
"com.google.android.gms" % "play-services-analytics" % "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.android.support" % "design" % "23.1.0",
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01", "com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01",
"com.nostra13.universalimageloader" % "universal-image-loader" % "1.9.4", "com.nostra13.universalimageloader" % "universal-image-loader" % "1.9.4",
...@@ -46,13 +43,9 @@ libraryDependencies ++= Seq( ...@@ -46,13 +43,9 @@ libraryDependencies ++= Seq(
proguardOptions in Android ++= Seq("-keep class android.support.v4.app.** { *; }", proguardOptions in Android ++= Seq("-keep class android.support.v4.app.** { *; }",
"-keep interface android.support.v4.app.** { *; }", "-keep interface android.support.v4.app.** { *; }",
"-keep class android.support.v7.widget.Toolbar { <init>(...); }", "-keep class android.support.v7.widget.Toolbar { <init>(...); }",
"-keep class com.actionbarsherlock.** { *; }",
"-keep interface com.actionbarsherlock.** { *; }",
"-keep class org.jraf.android.backport.** { *; }", "-keep class org.jraf.android.backport.** { *; }",
"-keep class com.github.shadowsocks.** { *; }", "-keep class com.github.shadowsocks.** { *; }",
"-keep class * extends com.j256.ormlite.** { *; }", "-keep class * extends com.j256.ormlite.** { *; }",
"-keep class com.joanzapata.** { *; }",
"-keepattributes *Annotation*", "-keepattributes *Annotation*",
"-dontwarn com.google.android.gms.internal.zzhu", "-dontwarn com.google.android.gms.internal.zzhu",
"-dontwarn org.xbill.**", "-dontwarn org.xbill.**")
"-dontwarn com.actionbarsherlock.**")
...@@ -22,7 +22,8 @@ ...@@ -22,7 +22,8 @@
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:backupAgent=".ShadowsocksBackupAgent" android:backupAgent=".ShadowsocksBackupAgent"
android:label="@string/app_name"> android:label="@string/app_name"
android:theme="@style/Theme.Material">
<meta-data android:name="com.google.android.gms.version" <meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/> android:value="@integer/google_play_services_version"/>
...@@ -36,7 +37,6 @@ ...@@ -36,7 +37,6 @@
<activity <activity
android:name=".Shadowsocks" android:name=".Shadowsocks"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/Theme.Shadowsocks"
android:launchMode="singleTask"> android:launchMode="singleTask">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
...@@ -62,11 +62,22 @@ ...@@ -62,11 +62,22 @@
android:launchMode="singleTask"> android:launchMode="singleTask">
</activity> </activity>
<activity
android:name=".ProfileManagerActivity"
android:label="@string/app_name"
android:parentActivityName=".Shadowsocks"
android:exported="false">
<intent-filter>
<action android:name="com.github.shadowsocks.ProfileManagerActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity <activity
android:name=".AppManager" android:name=".AppManager"
android:theme="@style/Theme.Material"
android:label="@string/app_name" android:label="@string/app_name"
android:parentActivityName=".Shadowsocks"> android:parentActivityName=".Shadowsocks"
android:exported="false">
<intent-filter> <intent-filter>
<action android:name="com.github.shadowsocks.AppManager"/> <action android:name="com.github.shadowsocks.AppManager"/>
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
......
Subproject commit 095aa7490cb39d513180d1f756e6751107511f98 Subproject commit 890e880dc61ad4f2e9a340a78c797c4aabee6429
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar_light_dark" />
<android.support.v7.widget.RecyclerView android:id="@+id/profilesList"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- Based on: https://github.com/android/platform_frameworks_base/blob/master/core/res/res/layout/select_dialog_singlechoice_material.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:paddingStart="20dp"
android:paddingEnd="?attr/dialogPreferredPadding"
android:paddingLeft="20dp"
android:paddingRight="?attr/dialogPreferredPadding">
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorAlertDialogListItem"
android:gravity="center_vertical"
android:drawableStart="?android:attr/listChoiceIndicatorSingle"
android:drawableLeft="?android:attr/listChoiceIndicatorSingle"
android:drawablePadding="20dp"
android:maxLines="4"
android:scrollHorizontally="false" />
<!-- TODO: Replace this view with an ImageView -->
<ImageView android:id="@+id/qrcode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@android:drawable/ic_menu_add"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/qrcode"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/MenuDrawer.Widget.Category" />
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/MenuDrawer.Widget.Title" />
\ No newline at end of file
...@@ -7,6 +7,5 @@ ...@@ -7,6 +7,5 @@
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:elevation="4dp" android:elevation="4dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:titleTextAppearance="@style/Toolbar.TitleText"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:id="@+id/toolbar" /> android:id="@+id/toolbar" />
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- Todo: replace this placeholder icon with a material one -->
<item android:title="@string/add_profile"
android:icon="@android:drawable/ic_menu_add"
android:alphabeticShortcut="a"
android:numericShortcut="0"
app:showAsAction="always">
<menu>
<item android:id="@+id/scan_qr_code"
android:title="@string/add_profile_methods_scan_qr_code"
android:alphabeticShortcut="s"
android:numericShortcut="1"/>
<item android:id="@+id/manual_settings"
android:title="@string/add_profile_methods_manual_settings"
android:alphabeticShortcut="m"
android:numericShortcut="2"/>
</menu>
</item>
</menu>
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
<resources> <resources>
<string name="app_name">影梭</string> <string name="app_name">影梭</string>
<string name="screen_name">shadowsocks</string> <string name="screen_name">shadowsocks</string>
<string name="function_cat">服务控制</string>
<string name="service_controller">代理开关</string>
<string name="service_summary">启动/关闭后台服务</string>
<string name="proxy_cat">服务器设置</string> <string name="proxy_cat">服务器设置</string>
<string name="profile">配置文件</string> <string name="profile">配置文件</string>
<string name="profile_summary">配置文件名称</string> <string name="profile_summary">配置文件名称</string>
...@@ -39,26 +36,18 @@ ...@@ -39,26 +36,18 @@
<string name="traffic_stat_summary">在通知栏上显示当前上下行速率</string> <string name="traffic_stat_summary">在通知栏上显示当前上下行速率</string>
<string name="forward_success">后台服务已开始运行。</string> <string name="forward_success">后台服务已开始运行。</string>
<string name="service_running">已连接到「%s」</string> <string name="service_running">已连接到「%s」</string>
<string name="service_status" formatted="false">当前速率:%d KB/s</string>
<string name="forward_fail">后台服务启动失败。</string>
<string name="service_stopped">后台服务已停止。</string>
<string name="forward_stop">后台服务已停止。</string>
<string name="service_failed">无法连接远程服务器</string> <string name="service_failed">无法连接远程服务器</string>
<string name="stop">停止服务</string> <string name="stop">停止服务</string>
<string name="stopping">正在关闭...</string> <string name="stopping">正在关闭...</string>
<string name="vpn_error">后台服务启动失败:%s</string> <string name="vpn_error">后台服务启动失败:%s</string>
<string name="ok_iknow">确定</string>
<string name="close">关闭</string> <string name="close">关闭</string>
<string name="port_alert">本地端口号应大于1024</string> <string name="port_alert">本地端口号应大于1024</string>
<string name="port_empty">端口号不能为空</string> <string name="port_empty">端口号不能为空</string>
<string name="proxy_empty">代理服务器地址不能为空</string> <string name="proxy_empty">代理服务器地址不能为空</string>
<string name="crash_alert">检测到意外退出,状态已被重置。</string>
<string name="connect">连接</string> <string name="connect">连接</string>
<string name="initializing">正在初始化...</string> <string name="initializing">正在初始化...</string>
<string name="recovering">重置中...</string> <string name="recovering">重置中...</string>
<string name="loading">正在加载...</string> <string name="loading">正在加载...</string>
<string name="yes"></string>
<string name="no"></string>
<string name="profiles">配置文件</string> <string name="profiles">配置文件</string>
<string name="settings">设置选项</string> <string name="settings">设置选项</string>
<string name="remove_profile">删除此配置文件「%s」?</string> <string name="remove_profile">删除此配置文件「%s」?</string>
...@@ -84,10 +73,8 @@ ...@@ -84,10 +73,8 @@
<!-- profile --> <!-- profile -->
<string name="add_profile_dialog">为影梭添加此配置文件?</string> <string name="add_profile_dialog">为影梭添加此配置文件?</string>
<string-array name="add_profile_methods"> <string name="add_profile_methods_scan_qr_code">扫描二维码</string>
<item>扫描二维码</item> <string name="add_profile_methods_manual_settings">手动设置</string>
<item>手动设置</item>
</string-array>
<string-array name="route_entry"> <string-array name="route_entry">
<item>全局</item> <item>全局</item>
...@@ -101,6 +88,8 @@ ...@@ -101,6 +88,8 @@
<string name="action_export_err">导出失败</string> <string name="action_export_err">导出失败</string>
<string name="action_import_msg">导入成功</string> <string name="action_import_msg">导入成功</string>
<string name="action_import_err">导入失败</string> <string name="action_import_err">导入失败</string>
<string name="error">错误</string>
<string name="removed">已删除</string>
<string name="undo">撤销</string>
</resources> </resources>
...@@ -4,15 +4,14 @@ ...@@ -4,15 +4,14 @@
<string name="app_name">Shadowsocks</string> <string name="app_name">Shadowsocks</string>
<string name="screen_name">shadowsocks</string> <string name="screen_name">shadowsocks</string>
<!-- function category --> <!-- global category -->
<string name="function_cat">Service Controller</string> <string name="global_cat">Global Settings</string>
<string name="service_controller">Proxy Switch</string> <string name="profile">Profile</string>
<string name="service_summary">Enable / Disable Proxy</string> <string name="profile_summary">Switch to another profile</string>
<!-- proxy category --> <!-- proxy category -->
<string name="proxy_cat">Server Settings</string> <string name="proxy_cat">Server Settings</string>
<string name="profile">Profile</string> <string name="profile_name">Profile name</string>
<string name="profile_summary">Profile name</string>
<string name="proxy">Server</string> <string name="proxy">Server</string>
<string name="proxy_summary">Hostname or IP of the remote server</string> <string name="proxy_summary">Hostname or IP of the remote server</string>
<string name="remote_port">Remote Port</string> <string name="remote_port">Remote Port</string>
...@@ -54,26 +53,18 @@ ...@@ -54,26 +53,18 @@
<!-- notification category --> <!-- notification category -->
<string name="forward_success">Shadowsocks started.</string> <string name="forward_success">Shadowsocks started.</string>
<string name="service_running">Connected to "%s"</string> <string name="service_running">Connected to "%s"</string>
<string name="service_status" formatted="false">Current Rate: %d KB/s</string>
<string name="forward_fail">Shadowsocks failed to start.</string>
<string name="service_stopped">Shadowsocks stopped.</string>
<string name="forward_stop">Shadowsocks stopped.</string>
<string name="service_failed">Failed to connect the remote server</string> <string name="service_failed">Failed to connect the remote server</string>
<string name="stop">Stop the service</string> <string name="stop">Stop the service</string>
<string name="stopping">Shutting down...</string> <string name="stopping">Shutting down...</string>
<string name="vpn_error">%s</string> <string name="vpn_error">%s</string>
<string name="enable_nat">Turn on NAT mode.</string> <string name="enable_nat">Turn on NAT mode.</string>
<string name="disable_nat">Turn off NAT mode.</string> <string name="disable_nat">Turn off NAT mode.</string>
<string name="error">ERROR</string>
<!-- alert category --> <!-- alert category -->
<string name="ok_iknow">OK</string>
<string name="close">Close</string> <string name="close">Close</string>
<string name="port_alert">The local port number should be greater than 1024</string> <string name="port_alert">The local port number should be greater than 1024</string>
<string name="port_empty">Port should not be empty</string> <string name="port_empty">Port should not be empty</string>
<string name="proxy_empty">Proxy should not be empty</string> <string name="proxy_empty">Proxy should not be empty</string>
<string name="crash_alert">An unexpected exit detected, the context has been reset.
</string>
<string name="connect">Connect</string> <string name="connect">Connect</string>
<string name="initializing">Initializing…</string> <string name="initializing">Initializing…</string>
<string name="recovering">Resetting…</string> <string name="recovering">Resetting…</string>
...@@ -101,9 +92,9 @@ ...@@ -101,9 +92,9 @@
<!-- profile --> <!-- profile -->
<string name="add_profile_dialog">Add this Shadowsocks Profile?</string> <string name="add_profile_dialog">Add this Shadowsocks Profile?</string>
<string-array name="add_profile_methods"> <string name="add_profile_methods_scan_qr_code">Scan QR code</string>
<item>Scan QR code</item> <string name="add_profile_methods_manual_settings">Manual Settings</string>
<item>Manual Settings</item> <string name="removed">Removed</string>
</string-array> <string name="undo">Undo</string>
</resources> </resources>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--
Zirco Browser for Android
Copyright (C) 2010 - 2011 J. Devauchelle and contributors.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 3 as published by the Free Software Foundation.
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.
-->
<resources> <resources>
<style name="Toolbar.TitleText" parent="TextAppearance.Widget.AppCompat.Toolbar.Title"> <style name="Toolbar.Logo" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
<item name="android:textSize">24sp</item> <item name="android:textSize">24sp</item>
</style> </style>
<style name="Theme.Material" parent="Theme.AppCompat.Light.NoActionBar"> <style name="Theme.Material" parent="Theme.AppCompat.Light.NoActionBar">
...@@ -51,45 +37,4 @@ ...@@ -51,45 +37,4 @@
<item name="android:windowIsTranslucent">true</item> <item name="android:windowIsTranslucent">true</item>
</style> </style>
<!-- Variation on the Light theme that turns off the title -->
<style name="Theme.Shadowsocks" parent="Theme.Material">
<item name="menuDrawerStyle">@style/MenuDrawerStyle</item>
</style>
<style name="MenuDrawer"/>
<style name="MenuDrawer.Widget"/>
<style name="MenuDrawer.Widget.Category">
<item name="android:textStyle">bold</item>
<item name="android:textColor">?android:attr/textColorSecondaryInverse</item>
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">true</item>
<item name="android:gravity">center_vertical</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:background">@drawable/md__category_background</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
</style>
<style name="MenuDrawer.Widget.Title">
<item name="android:background">?android:attr/selectableItemBackground</item>
<item name="android:textAppearance">?android:attr/textAppearance</item>
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:textSize">18sp</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:paddingRight">32dp</item>
<item name="android:paddingTop">8dp</item>
<item name="android:paddingBottom">8dp</item>
<item name="android:drawablePadding">16dp</item>
<item name="android:gravity">center_vertical</item>
</style>
<style name="MenuDrawerStyle" parent="Widget.MenuDrawer">
<item name="mdActiveIndicator">@drawable/menu_arrow</item>
<item name="mdMenuSize">250dp</item>
</style>
</resources> </resources>
...@@ -2,50 +2,58 @@ ...@@ -2,50 +2,58 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks"> xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks">
<PreferenceCategory android:title="@string/global_cat">
<Preference android:summary="@string/profile_summary" android:title="@string/profile">
<intent android:action="com.github.shadowsocks.ProfileManagerActivity"/>
</Preference>
<!-- TODO: VPN/NAT SwitchPreference -->
<SwitchPreference
android:key="isAutoConnect"
android:summary="@string/auto_connect_summary"
android:title="@string/auto_connect"/>
<Preference android:key="recovery" android:title="@string/recovery"/>
<Preference android:key="flush_dnscache" android:title="@string/flush_dnscache"/>
</PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/proxy_cat"> android:title="@string/proxy_cat">
<com.github.shadowsocks.preferences.ProfileEditTextPreference <com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue="Default" android:defaultValue="Default"
android:key="profileName" android:key="profileName"
android:summary="@string/profile_summary" android:title="@string/profile_name"/>
android:title="@string/profile">
</com.github.shadowsocks.preferences.ProfileEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference <com.github.shadowsocks.preferences.SummaryEditTextPreference
android:defaultValue="198.199.101.152" android:defaultValue="198.199.101.152"
android:key="proxy" android:key="proxy"
android:summary="@string/proxy_summary" android:summary="@string/proxy_summary"
android:title="@string/proxy"> android:title="@string/proxy"/>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference <com.github.shadowsocks.preferences.SummaryEditTextPreference
android:inputType="number" android:inputType="number"
android:defaultValue="443" android:defaultValue="443"
android:key="remotePort" android:key="remotePort"
android:summary="@string/remote_port_summary" android:summary="@string/remote_port_summary"
android:title="@string/remote_port"> android:title="@string/remote_port"/>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.SummaryEditTextPreference <com.github.shadowsocks.preferences.SummaryEditTextPreference
android:inputType="number" android:inputType="number"
android:defaultValue="1080" android:defaultValue="1080"
android:key="port" android:key="port"
android:summary="@string/port_summary" android:summary="@string/port_summary"
android:title="@string/port"> android:title="@string/port"/>
</com.github.shadowsocks.preferences.SummaryEditTextPreference>
<com.github.shadowsocks.preferences.PasswordEditTextPreference <com.github.shadowsocks.preferences.PasswordEditTextPreference
android:inputType="textPassword" android:inputType="textPassword"
android:defaultValue="u1rRWTssNv0p" android:defaultValue="u1rRWTssNv0p"
android:key="sitekey" android:key="sitekey"
android:summary="@string/sitekey_summary" android:summary="@string/sitekey_summary"
android:title="@string/sitekey"> android:title="@string/sitekey"/>
</com.github.shadowsocks.preferences.PasswordEditTextPreference>
<com.github.shadowsocks.preferences.DropDownPreference <com.github.shadowsocks.preferences.DropDownPreference
android:defaultValue="rc4" android:defaultValue="rc4"
android:key="encMethod" android:key="encMethod"
app:entries="@array/enc_method_entry" app:entries="@array/enc_method_entry"
app:entryValues="@array/enc_method_value" app:entryValues="@array/enc_method_value"
app:summary="%s" app:summary="%s"
android:title="@string/enc_method"> android:title="@string/enc_method"/>
</com.github.shadowsocks.preferences.DropDownPreference>
</PreferenceCategory> </PreferenceCategory>
...@@ -58,21 +66,18 @@ ...@@ -58,21 +66,18 @@
app:entries="@array/route_entry" app:entries="@array/route_entry"
app:entryValues="@array/route_value" app:entryValues="@array/route_value"
app:summary="%s" app:summary="%s"
android:title="@string/route_list"> android:title="@string/route_list"/>
</com.github.shadowsocks.preferences.DropDownPreference>
<SwitchPreference <SwitchPreference
android:key="isIpv6" android:key="isIpv6"
android:defaultValue="false" android:defaultValue="false"
android:summary="@string/ipv6_summary" android:summary="@string/ipv6_summary"
android:title="@string/ipv6"> android:title="@string/ipv6"/>
</SwitchPreference>
<SwitchPreference <SwitchPreference
android:defaultValue="true" android:defaultValue="true"
android:key="isGlobalProxy" android:key="isGlobalProxy"
android:disableDependentsState="true" android:disableDependentsState="true"
android:summary="@string/auto_set_proxy_summary" android:summary="@string/auto_set_proxy_summary"
android:title="@string/auto_set_proxy"> android:title="@string/auto_set_proxy"/>
</SwitchPreference>
<Preference <Preference
android:key="proxyedApps" android:key="proxyedApps"
android:dependency="isGlobalProxy" android:dependency="isGlobalProxy"
...@@ -84,19 +89,12 @@ ...@@ -84,19 +89,12 @@
android:key="isUdpDns" android:key="isUdpDns"
android:defaultValue="false" android:defaultValue="false"
android:summary="@string/udp_dns_summary" android:summary="@string/udp_dns_summary"
android:title="@string/udp_dns"> android:title="@string/udp_dns"/>
</SwitchPreference>
<SwitchPreference <SwitchPreference
android:key="isAuth" android:key="isAuth"
android:defaultValue="false" android:defaultValue="false"
android:summary="@string/onetime_auth_summary" android:summary="@string/onetime_auth_summary"
android:title="@string/onetime_auth"> android:title="@string/onetime_auth"/>
</SwitchPreference>
<SwitchPreference
android:key="isAutoConnect"
android:summary="@string/auto_connect_summary"
android:title="@string/auto_connect">
</SwitchPreference>
</PreferenceCategory> </PreferenceCategory>
......
...@@ -39,32 +39,26 @@ ...@@ -39,32 +39,26 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.app.{Activity, ProgressDialog} import java.io.{ByteArrayInputStream, ByteArrayOutputStream, InputStream}
import android.content.ClipboardManager
import android.content.ClipData import android.app.ProgressDialog
import android.content.Context import android.content.{ClipData, ClipboardManager, Context, SharedPreferences}
import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.{Bitmap, PixelFormat} import android.graphics.{Bitmap, PixelFormat}
import android.os.Bundle import android.os.{Bundle, Handler}
import android.os.Handler
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar import android.support.v7.widget.Toolbar
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.View.OnClickListener import android.view.View.OnClickListener
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams import android.view.ViewGroup.LayoutParams
import android.view.WindowManager import android.view.{Menu, MenuItem, View, ViewGroup, WindowManager}
import android.widget._
import android.widget.AbsListView.OnScrollListener import android.widget.AbsListView.OnScrollListener
import android.widget.CompoundButton.OnCheckedChangeListener import android.widget.CompoundButton.OnCheckedChangeListener
import android.widget._
import com.github.shadowsocks.utils.{Key, Scheme, Utils}
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer
import com.nostra13.universalimageloader.core.download.BaseImageDownloader 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.{DisplayImageOptions, ImageLoader, ImageLoaderConfiguration}
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer
import com.github.shadowsocks.utils.{Utils, Scheme, Key}
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future import scala.concurrent.Future
...@@ -105,7 +99,7 @@ object AppManager { ...@@ -105,7 +99,7 @@ object AppManager {
} }
} }
class AppManager extends Activity with OnCheckedChangeListener with OnClickListener { class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnClickListener {
val MSG_LOAD_START = 1 val MSG_LOAD_START = 1
val MSG_LOAD_FINISH = 2 val MSG_LOAD_FINISH = 2
......
...@@ -57,14 +57,14 @@ class ParserActivity extends Activity { ...@@ -57,14 +57,14 @@ class ParserActivity extends Activity {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(R.string.add_profile_dialog) .setTitle(R.string.add_profile_dialog)
.setCancelable(false) .setCancelable(false)
.setPositiveButton(R.string.yes, ((dialog: DialogInterface, id: Int) => { .setPositiveButton(android.R.string.yes, ((dialog: DialogInterface, id: Int) => {
Parser.parse(data) match { Parser.parse(data) match {
case Some(profile) => addProfile(profile) case Some(profile) => addProfile(profile)
case _ => // ignore case _ => // ignore
} }
dialog.dismiss() dialog.dismiss()
}): DialogInterface.OnClickListener) }): DialogInterface.OnClickListener)
.setNegativeButton(R.string.no, ((dialog: DialogInterface, id: Int) => { .setNegativeButton(android.R.string.no, ((dialog: DialogInterface, id: Int) => {
dialog.dismiss() dialog.dismiss()
finish() finish()
}): DialogInterface.OnClickListener) }): DialogInterface.OnClickListener)
...@@ -90,7 +90,7 @@ class ParserActivity extends Activity { ...@@ -90,7 +90,7 @@ class ParserActivity extends Activity {
h.postDelayed(() => { h.postDelayed(() => {
val profileManager = val profileManager =
new ProfileManager(PreferenceManager.getDefaultSharedPreferences(getBaseContext), new ProfileManager(PreferenceManager.getDefaultSharedPreferences(getBaseContext),
getApplication.asInstanceOf[ShadowsocksApplication].dbHelper) ShadowsocksApplication.dbHelper)
profileManager.createOrUpdateProfile(profile) profileManager.createOrUpdateProfile(profile)
profileManager.reload(profile.id) profileManager.reload(profile.id)
h.sendEmptyMessage(0) h.sendEmptyMessage(0)
......
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
}
}
...@@ -43,19 +43,47 @@ import java.util ...@@ -43,19 +43,47 @@ import java.util
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import android.app.Application import android.app.Application
import com.github.shadowsocks.database.DBHelper import android.content.pm.PackageManager
import com.github.shadowsocks.utils.Utils import android.preference.PreferenceManager
import com.google.android.gms.analytics.GoogleAnalytics import com.github.shadowsocks.database.{DBHelper, ProfileManager}
import com.github.shadowsocks.utils.{Key, Utils}
import com.google.android.gms.analytics.{HitBuilders, GoogleAnalytics}
import com.google.android.gms.common.api.ResultCallback import com.google.android.gms.common.api.ResultCallback
import com.google.android.gms.tagmanager.{ContainerHolder, TagManager} 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)
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
def proxy = settings.getString(Key.proxy, "")
def currentProfile = profileManager.getProfile(profileId)
}
class ShadowsocksApplication extends Application { class ShadowsocksApplication extends Application {
lazy val dbHelper = new DBHelper(this) import ShadowsocksApplication._
lazy val SIG_FUNC = "getSignature"
var containerHolder: ContainerHolder = null
lazy val tracker = GoogleAnalytics.getInstance(this).newTracker(R.xml.tracker)
override def onCreate() { override def onCreate() {
ShadowsocksApplication.instance = this
val tm = TagManager.getInstance(this) val tm = TagManager.getInstance(this)
val pending = tm.loadContainerPreferNonDefault("GTM-NT8WS8", R.raw.gtm_default_container) val pending = tm.loadContainerPreferNonDefault("GTM-NT8WS8", R.raw.gtm_default_container)
val callback = new ResultCallback[ContainerHolder] { val callback = new ResultCallback[ContainerHolder] {
......
...@@ -40,22 +40,20 @@ ...@@ -40,22 +40,20 @@
package com.github.shadowsocks package com.github.shadowsocks
import java.io.File import java.io.File
import java.lang.reflect.{InvocationTargetException, Method}
import java.net.{Inet6Address, InetAddress} import java.net.{Inet6Address, InetAddress}
import java.util.Locale import java.util.Locale
import android.app._ import android.app._
import android.content._ import android.content._
import android.content.pm.{PackageInfo, PackageManager} import android.content.pm.{PackageInfo, PackageManager}
import android.net.{Network, ConnectivityManager} import android.net.{ConnectivityManager, Network}
import android.os._ import android.os._
import android.support.v4.app.NotificationCompat import android.support.v4.app.NotificationCompat
import android.support.v4.content.ContextCompat import android.support.v4.content.ContextCompat
import android.util.{SparseArray, Log} import android.util.{Log, SparseArray}
import android.widget.Toast import android.widget.Toast
import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import com.google.android.gms.analytics.HitBuilders
import scala.collection._ import scala.collection._
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
...@@ -77,8 +75,6 @@ class ShadowsocksNatService extends Service with BaseService { ...@@ -77,8 +75,6 @@ class ShadowsocksNatService extends Service with BaseService {
var apps: Array[ProxiedApp] = null var apps: Array[ProxiedApp] = null
val myUid = Process.myUid() val myUid = Process.myUid()
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
private val dnsAddressCache = new SparseArray[String] private val dnsAddressCache = new SparseArray[String]
def getNetId(network: Network): Int = { def getNetId(network: Network): Int = {
...@@ -254,8 +250,8 @@ class ShadowsocksNatService extends Service with BaseService { ...@@ -254,8 +250,8 @@ class ShadowsocksNatService extends Service with BaseService {
def startDnsDaemon() { def startDnsDaemon() {
val conf = if (config.route == Route.BYPASS_CHN) { val conf = if (config.route == Route.BYPASS_CHN) {
val reject = ConfigUtils.getRejectList(getContext, application) val reject = ConfigUtils.getRejectList(getContext)
val blackList = ConfigUtils.getBlackList(getContext, application) val blackList = ConfigUtils.getBlackList(getContext)
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "127.0.0.1", 8153, ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "127.0.0.1", 8153,
Path.BASE + "pdnsd-nat.pid", reject, blackList, 8163, "") Path.BASE + "pdnsd-nat.pid", reject, blackList, 8163, "")
} else { } else {
...@@ -457,19 +453,14 @@ class ShadowsocksNatService extends Service with BaseService { ...@@ -457,19 +453,14 @@ class ShadowsocksNatService extends Service with BaseService {
registerReceiver(lockReceiver, screenFilter) registerReceiver(lockReceiver, screenFilter)
} }
// send event ShadowsocksApplication.track(TAG, "start")
application.tracker.send(new HitBuilders.EventBuilder()
.setCategory(TAG)
.setAction("start")
.setLabel(getVersionName)
.build())
changeState(State.CONNECTING) changeState(State.CONNECTING)
Future { Future {
if (config.proxy == "198.199.101.152") { if (config.proxy == "198.199.101.152") {
val holder = application.containerHolder val holder = ShadowsocksApplication.containerHolder
try { try {
config = ConfigUtils.getPublicConfig(getBaseContext, holder.getContainer, config) config = ConfigUtils.getPublicConfig(getBaseContext, holder.getContainer, config)
} catch { } catch {
...@@ -531,12 +522,7 @@ class ShadowsocksNatService extends Service with BaseService { ...@@ -531,12 +522,7 @@ class ShadowsocksNatService extends Service with BaseService {
} }
} }
// send event ShadowsocksApplication.track(TAG, "stop")
application.tracker.send(new HitBuilders.EventBuilder()
.setCategory(TAG)
.setAction("stop")
.setLabel(getVersionName)
.build())
// reset NAT // reset NAT
killProcesses() killProcesses()
......
package com.github.shadowsocks package com.github.shadowsocks
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceFragment import android.preference.{Preference, PreferenceFragment}
// TODO: Move related logic here // TODO: Move related logic here
class ShadowsocksSettings extends PreferenceFragment { class ShadowsocksSettings extends PreferenceFragment {
private lazy val activity = getActivity.asInstanceOf[Shadowsocks]
override def onCreate(savedInstanceState: Bundle) { override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.pref_all) addPreferencesFromResource(R.xml.pref_all)
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
})
} }
} }
...@@ -53,7 +53,6 @@ import android.util.Log ...@@ -53,7 +53,6 @@ import android.util.Log
import android.widget.Toast import android.widget.Toast
import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.aidl.Config
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import com.google.android.gms.analytics.HitBuilders
import org.apache.commons.net.util.SubnetUtils import org.apache.commons.net.util.SubnetUtils
import scala.collection.mutable import scala.collection.mutable
...@@ -62,8 +61,6 @@ import scala.concurrent.ExecutionContext.Implicits.global ...@@ -62,8 +61,6 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future import scala.concurrent.Future
class ShadowsocksVpnService extends VpnService with BaseService { class ShadowsocksVpnService extends VpnService with BaseService {
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
val TAG = "ShadowsocksVpnService" val TAG = "ShadowsocksVpnService"
val VPN_MTU = 1500 val VPN_MTU = 1500
val PRIVATE_VLAN = "26.26.26.%s" val PRIVATE_VLAN = "26.26.26.%s"
...@@ -152,12 +149,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { ...@@ -152,12 +149,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
// channge the state // channge the state
changeState(State.STOPPING) changeState(State.STOPPING)
// send event ShadowsocksApplication.track(TAG, "stop")
application.tracker.send(new HitBuilders.EventBuilder()
.setCategory(TAG)
.setAction("stop")
.setLabel(getVersionName)
.build())
// reset VPN // reset VPN
killProcesses() killProcesses()
...@@ -231,18 +223,13 @@ class ShadowsocksVpnService extends VpnService with BaseService { ...@@ -231,18 +223,13 @@ class ShadowsocksVpnService extends VpnService with BaseService {
return return
} }
// send event ShadowsocksApplication.track(TAG, "start")
application.tracker.send(new HitBuilders.EventBuilder()
.setCategory(TAG)
.setAction("start")
.setLabel(getVersionName)
.build())
changeState(State.CONNECTING) changeState(State.CONNECTING)
Future { Future {
if (config.proxy == "198.199.101.152") { if (config.proxy == "198.199.101.152") {
val holder = getApplication.asInstanceOf[ShadowsocksApplication].containerHolder val holder = ShadowsocksApplication.containerHolder
try { try {
config = ConfigUtils.getPublicConfig(getBaseContext, holder.getContainer, config) config = ConfigUtils.getPublicConfig(getBaseContext, holder.getContainer, config)
} catch { } catch {
...@@ -371,8 +358,8 @@ class ShadowsocksVpnService extends VpnService with BaseService { ...@@ -371,8 +358,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val ipv6 = if (config.isIpv6) "" else "reject = ::/0;" val ipv6 = if (config.isIpv6) "" else "reject = ::/0;"
val conf = { val conf = {
if (config.route == Route.BYPASS_CHN) { if (config.route == Route.BYPASS_CHN) {
val reject = ConfigUtils.getRejectList(getContext, application) val reject = ConfigUtils.getRejectList(getContext)
val blackList = ConfigUtils.getBlackList(getContext, application) val blackList = ConfigUtils.getBlackList(getContext)
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153, ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
Path.BASE + "pdnsd-vpn.pid", reject, blackList, 8163, ipv6) Path.BASE + "pdnsd-vpn.pid", reject, blackList, 8163, ipv6)
} else { } else {
......
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2014 <max.c.lv@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (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
}
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2013 <max.c.lv@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* ___====-_ _-====___
* _--^^^#####// \\#####^^^--_
* _-^##########// ( ) \\##########^-_
* -############// |\^^/| \\############-
* _/############// (@::@) \\############\_
* /#############(( \\// ))#############\
* -###############\\ (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
}
...@@ -198,14 +198,14 @@ object ConfigUtils { ...@@ -198,14 +198,14 @@ object ConfigUtils {
} }
def refresh(context: Context) { def refresh(context: Context) {
val holder = context.getApplicationContext.asInstanceOf[ShadowsocksApplication].containerHolder val holder = ShadowsocksApplication.containerHolder
if (holder != null) holder.refresh() if (holder != null) holder.refresh()
} }
def getRejectList(context: Context, app: ShadowsocksApplication): String = { def getRejectList(context: Context): String = {
val default = context.getString(R.string.reject) val default = context.getString(R.string.reject)
try { try {
val container = app.containerHolder.getContainer val container = ShadowsocksApplication.containerHolder.getContainer
val update = container.getString("reject") val update = container.getString("reject")
if (update == null || update.isEmpty) default else update if (update == null || update.isEmpty) default else update
} catch { } catch {
...@@ -213,10 +213,10 @@ object ConfigUtils { ...@@ -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) val default = context.getString(R.string.black_list)
try { try {
val container = app.containerHolder.getContainer val container = ShadowsocksApplication.containerHolder.getContainer
val update = container.getString("black_list") val update = container.getString("black_list")
if (update == null || update.isEmpty) default else update if (update == null || update.isEmpty) default else update
} catch { } catch {
......
...@@ -111,8 +111,6 @@ object State { ...@@ -111,8 +111,6 @@ object State {
object Action { object Action {
val SERVICE = "com.github.shadowsocks.SERVICE" val SERVICE = "com.github.shadowsocks.SERVICE"
val CLOSE = "com.github.shadowsocks.CLOSE" 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 { object Route {
......
...@@ -92,6 +92,9 @@ object Utils { ...@@ -92,6 +92,9 @@ object Utils {
new String(Base64.encode(mdg.digest, 0)) 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 * round or floor depending on whether you are using offsets(floor) or
* widths(round) * widths(round)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment