Commit 257b44f3 authored by Max Lv's avatar Max Lv

Merge pull request #417 from Mygod/master

Some final touches
parents 7d581508 63bc4a57
......@@ -64,7 +64,7 @@
<activity
android:name=".ProfileManagerActivity"
android:label="@string/app_name"
android:label="@string/profiles"
android:parentActivityName=".Shadowsocks"
android:exported="false">
<intent-filter>
......@@ -75,14 +75,8 @@
<activity
android:name=".AppManager"
android:label="@string/app_name"
android:parentActivityName=".Shadowsocks"
android:exported="false">
<intent-filter>
<action android:name="com.github.shadowsocks.AppManager"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
android:label="@string/proxied_apps"
android:parentActivityName=".Shadowsocks"/>
<service
android:name=".ShadowsocksRunnerService"
......
......@@ -5,10 +5,8 @@ import android.os.Parcelable;
public class Config implements Parcelable {
public boolean isGlobalProxy = true;
public boolean isGFWList = true;
public boolean isProxyApps = false;
public boolean isBypassApps = false;
public boolean isTrafficStat = false;
public boolean isUdpDns = false;
public boolean isAuth = false;
public boolean isIpv6 = false;
......@@ -34,13 +32,11 @@ public class Config implements Parcelable {
}
};
public Config(boolean isGlobalProxy, boolean isGFWList, boolean isBypassApps,
boolean isTrafficStat, boolean isUdpDns, boolean isAuth, boolean isIpv6, String profileName, String proxy, String sitekey,
public Config(boolean isProxyApps, boolean isBypassApps,
boolean isUdpDns, boolean isAuth, boolean isIpv6, String profileName, String proxy, String sitekey,
String encMethod, String proxiedAppString, String route, int remotePort, int localPort) {
this.isGlobalProxy = isGlobalProxy;
this.isGFWList = isGFWList;
this.isProxyApps = isProxyApps;
this.isBypassApps = isBypassApps;
this.isTrafficStat = isTrafficStat;
this.isUdpDns = isUdpDns;
this.isAuth = isAuth;
this.isIpv6 = isIpv6;
......@@ -59,10 +55,8 @@ public class Config implements Parcelable {
}
public void readFromParcel(Parcel in) {
isGlobalProxy = in.readInt() == 1;
isGFWList = in.readInt() == 1;
isProxyApps = in.readInt() == 1;
isBypassApps = in.readInt() == 1;
isTrafficStat = in.readInt() == 1;
isUdpDns = in.readInt() == 1;
isAuth = in.readInt() == 1;
isIpv6 = in.readInt() == 1;
......@@ -81,10 +75,8 @@ public class Config implements Parcelable {
}
@Override public void writeToParcel(Parcel out, int flags) {
out.writeInt(isGlobalProxy ? 1 : 0);
out.writeInt(isGFWList ? 1 : 0);
out.writeInt(isProxyApps ? 1 : 0);
out.writeInt(isBypassApps ? 1 : 0);
out.writeInt(isTrafficStat ? 1 : 0);
out.writeInt(isUdpDns ? 1 : 0);
out.writeInt(isAuth ? 1 : 0);
out.writeInt(isIpv6 ? 1 : 0);
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:duplicateParentState="false">
<include layout="@layout/toolbar_light_dark"/>
<LinearLayout
android:layout_width="fill_parent"
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:paddingLeft="48dp"
android:paddingStart="48dp"
android:paddingTop="8dp"
android:elevation="1dp"
android:background="@android:color/white"
android:elevation="1dp" tools:ignore="RtlSymmetry">
android:orientation="vertical"
tools:ignore="RtlSymmetry">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:padding="4dp"
android:gravity="center_vertical"
android:textSize="18sp"
android:text="@string/on"/>
<Switch android:checked="true"
android:id="@+id/onSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<TextView
android:id="@+id/bypassLabel"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:padding="3dp"
android:padding="4dp"
android:gravity="center_vertical"
android:textSize="18sp"
android:text="@string/bypass_apps"/>
......@@ -28,9 +49,8 @@
<Switch
android:id="@+id/bypassSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:layout_marginRight="6dp"/>
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
<FrameLayout android:layout_width="fill_parent"
android:layout_height="0dp"
......
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
<TableRow>
android:background="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/itemicon"
android:layout_width="48dp"
android:layout_width="60dp"
android:layout_height="48dp"
android:scaleType="fitCenter"
android:padding="3dp"/>
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"/>
<TextView
android:id="@+id/itemtext"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="fill_parent"
android:text="uid:packages"
android:gravity="center_vertical"
android:maxLength="25"
android:textSize="18sp"
android:padding="3dp"/>
android:ellipsize="end"
android:textSize="18sp"/>
<Switch
android:id="@+id/itemcheck"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="6dp"/>
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"/>
</TableRow>
</TableLayout>
</LinearLayout>
......@@ -2,9 +2,8 @@
<!--Generated by crowdin.net-->
<resources>
<string name="app_name">影梭</string>
<string name="screen_name">shadowsocks</string>
<string name="global_cat">全局设置</string>
<string name="misc_cat">杂项</string>
<string name="misc_cat">其他</string>
<string name="proxy_cat">服务器设置</string>
<string name="profile_name">配置名称</string>
<string name="profile">配置文件</string>
......@@ -12,9 +11,9 @@
<string name="proxy">服务器</string>
<string name="proxy_summary">远程服务器的地址</string>
<string name="remote_port">远程端口</string>
<string name="remote_port_summary">远程服务器的端口号</string>
<string name="remote_port_summary">%d(远程服务器的端口号)</string>
<string name="port">本地端口</string>
<string name="port_summary">本地客户端的端口号</string>
<string name="port_summary">%d(本地客户端的端口号)</string>
<string name="sitekey">密码</string>
<string name="sitekey_summary">远程服务器的密码</string>
<string name="enc_method">加密方法</string>
......@@ -26,11 +25,9 @@
<string name="auto_set_gfwlist">国内路由</string>
<string name="auto_set_gfwlist_summary">绕过所有位于中国的网站</string>
<string name="route_list">路由</string>
<string name="auto_set_proxy">全局代理</string>
<string name="auto_set_proxy_summary">设置系统代理</string>
<string name="proxied_apps">分应用代理</string>
<string name="proxied_apps_summary">为应用程序分别设置代理</string>
<string name="proxied_help">分应用代理</string>
<string name="on"></string>
<string name="bypass_apps">绕行模式</string>
<string name="bypass_apps_summary">启用该选项,以使所选应用程序的流量不经过代理</string>
<string name="auto_connect">自动连接</string>
......@@ -44,8 +41,6 @@
<string name="stopping">正在关闭……</string>
<string name="vpn_error">后台服务启动失败:%s</string>
<string name="close">关闭</string>
<string name="port_alert">本地端口号应大于1024</string>
<string name="port_empty">端口号不能为空</string>
<string name="proxy_empty">代理服务器地址不能为空</string>
<string name="connect">连接</string>
<string name="initializing">正在初始化……</string>
......@@ -59,20 +54,20 @@
<string name="about">关于</string>
<string name="about_title">影梭(shadowsocks)%s</string>
<string name="flush_dnscache">刷新 DNS 缓存</string>
<string name="flush_dnscache_summary">需要 ROOT 权限</string>
<string name="flush_dnscache_summary_disabled">需要 ROOT 权限</string>
<string name="flushing">刷新中……</string>
<string name="qrcode">二维码</string>
<string name="add_profile">添加配置文件</string>
<string name="nat">NAT 模式 (仅限调试)</string>
<string name="nat_summary">停用 VPN 模式,启用 NAT 模式</string>
<string name="nat_summary">从 VPN 模式切换为 NAT 模式</string>
<string name="nat_summary_no_root">NAT 模式已禁用,需要 ROOT 权限</string>
<string name="udp_dns">UDP 转发</string>
<string name="udp_dns_summary">由远程服务器转发 UDP 协议的数据包</string>
<string name="onetime_auth">一次性认证</string>
<string name="onetime_auth_summary">启用一次性认证(实验性)</string>
<string name="onetime_auth_summary">启用一次性认证(实验性)</string>
<string name="ipv6">IPv6 路由</string>
<string name="ipv6_summary">向远程服务器转发 IPv6 流量</string>
......
......@@ -7,4 +7,8 @@
<attr name="entries" format="reference" />
<attr name="entryValues" format="reference" />
</declare-styleable>
<declare-styleable name="NumberPickerPreference">
<attr name="max" format="integer" />
<attr name="min" format="integer" />
</declare-styleable>
</resources>
This diff is collapsed.
......@@ -2,16 +2,16 @@
<resources>
<string name="app_name">Shadowsocks</string>
<string name="screen_name">shadowsocks</string>
<string name="screen_name" translatable="false">shadowsocks</string>
<!-- global/misc category -->
<string name="global_cat">Global Settings</string>
<string name="misc_cat">Misc</string>
<string name="profile">Profile</string>
<string name="profile_summary">Switch to another profile</string>
<string name="profile_summary">Switch to another profile or add new profiles</string>
<string name="nat">NAT mode (deprecated)</string>
<string name="nat_summary">Use NAT mode instead of VPN mode.</string>
<string name="nat_summary_no_root">NAT mode is disabled without root.</string>
<string name="nat_summary">Use NAT mode instead of VPN mode</string>
<string name="nat_summary_no_root">NAT mode is disabled without root</string>
<!-- proxy category -->
<string name="proxy_cat">Server Settings</string>
......@@ -19,9 +19,9 @@
<string name="proxy">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_summary">Port number of the remote server</string>
<string name="remote_port_summary">%d (port number of the remote server)</string>
<string name="port">Local Port</string>
<string name="port_summary">Port number of the local server</string>
<string name="port_summary">%d (port number of the local server)</string>
<string name="sitekey">Password</string>
<string name="sitekey_summary">Password of the remote server</string>
<string name="enc_method">Encrypt Method</string>
......@@ -40,11 +40,9 @@
<string name="auto_set_gfwlist_summary">Bypass all sites located in China
</string>
<string name="route_list">Route</string>
<string name="auto_set_proxy">Global Proxy</string>
<string name="auto_set_proxy_summary">Set up system-wide proxy</string>
<string name="proxied_apps">Per-App Proxy</string>
<string name="proxied_apps_summary">Set proxy for selected apps</string>
<string name="proxied_help">Per-App Proxy</string>
<string name="on">On</string>
<string name="bypass_apps">Bypass Mode</string>
<string name="bypass_apps_summary">Enable this option to bypass selected apps</string>
<string name="auto_connect">Auto Connect</string>
......@@ -64,15 +62,11 @@
<!-- alert category -->
<string name="close">Close</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="proxy_empty">Proxy should not be empty</string>
<string name="connect">Connect</string>
<string name="initializing">Initializing…</string>
<string name="recovering">Resetting…</string>
<string name="loading">Loading…</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="remove_profile">Remove this profile "%s"?</string>
<!-- menu category -->
......@@ -84,7 +78,7 @@
<string name="about_title">Shadowsocks %s</string>
<string name="qrcode">QR Code</string>
<string name="flush_dnscache">Flush DNS cache</string>
<string name="flush_dnscache_summary">Require ROOT permission</string>
<string name="flush_dnscache_summary_disabled">Requires ROOT permission</string>
<string name="flushing">Flushing</string>
<string name="add_profile">Add Profile</string>
<string name="action_export">Export to Clipboard</string>
......
......@@ -26,17 +26,19 @@
android:key="proxy"
android:summary="@string/proxy_summary"
android:title="@string/proxy"/>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:inputType="number"
<com.github.shadowsocks.preferences.NumberPickerPreference
android:defaultValue="443"
app:min="1"
app:max="65535"
android:key="remotePort"
android:summary="@string/remote_port_summary"
app:summary="@string/remote_port_summary"
android:title="@string/remote_port"/>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:inputType="number"
<com.github.shadowsocks.preferences.NumberPickerPreference
android:defaultValue="1080"
app:min="1025"
app:max="65535"
android:key="port"
android:summary="@string/port_summary"
app:summary="@string/port_summary"
android:title="@string/port"/>
<com.github.shadowsocks.preferences.PasswordEditTextPreference
android:inputType="textPassword"
......@@ -75,18 +77,10 @@
android:summary="@string/ipv6_summary"
android:title="@string/ipv6"/>
<SwitchPreference
android:defaultValue="true"
android:key="isGlobalProxy"
android:disableDependentsState="true"
android:summary="@string/auto_set_proxy_summary"
android:title="@string/auto_set_proxy"/>
<Preference
android:key="proxyedApps"
android:dependency="isGlobalProxy"
android:key="isProxyApps"
android:defaultValue="false"
android:summary="@string/proxied_apps_summary"
android:title="@string/proxied_apps">
<intent android:action="com.github.shadowsocks.AppManager"/>
</Preference>
android:title="@string/proxied_apps"/>
<SwitchPreference
android:key="isUdpDns"
android:defaultValue="false"
......@@ -102,8 +96,7 @@
android:summary="@string/nat_summary"/>
<Preference android:key="recovery" android:title="@string/recovery"
android:summary="@string/recovery_summary"/>
<Preference android:key="flush_dnscache" android:title="@string/flush_dnscache"
android:summary="@string/flush_dnscache_summary"/>
<Preference android:key="flush_dnscache" android:title="@string/flush_dnscache"/>
<Preference android:key="about" android:title="@string/about"/>
</PreferenceCategory>
......
......@@ -104,11 +104,12 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
implicit def anyrefarray_tools[T <: AnyRef](a: Array[T]): ObjectArrayTools[T] = new ObjectArrayTools(a)
var apps: Array[ProxiedApp] = null
var appListView: ListView = null
var overlay: TextView = null
var adapter: ListAdapter = null
var appsLoaded: Boolean = false
var apps: Array[ProxiedApp] = _
var appListView: ListView = _
var loadingView: View = _
var overlay: TextView = _
var adapter: ListAdapter = _
@volatile var appsLoading: Boolean = _
def loadApps(context: Context): Array[ProxiedApp] = {
val proxiedAppString = ShadowsocksApplication.settings.getString(Key.proxied, "")
......@@ -133,6 +134,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
}
def loadApps() {
appsLoading = true
apps = loadApps(this).sortWith((a, b) => {
if (a == null || b == null || a.name == null || b.name == null) {
true
......@@ -154,8 +156,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
val switch = convertView.findViewById(R.id.itemcheck).asInstanceOf[Switch]
val text = convertView.findViewById(R.id.itemtext).asInstanceOf[TextView]
entry = new ListEntry(switch, text, icon)
entry.text.setOnClickListener(AppManager.this)
entry.text.setOnClickListener(AppManager.this)
convertView.setOnClickListener(AppManager.this)
convertView.setTag(entry)
entry.switch.setOnCheckedChangeListener(AppManager.this)
} else {
......@@ -173,7 +174,6 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
convertView
}
}
appsLoaded = true
}
/** Called an application is check/unchecked */
......@@ -186,7 +186,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
}
def onClick(v: View) {
val switch = v.getTag.asInstanceOf[Switch]
val switch = v.getTag.asInstanceOf[ListEntry].switch
val app: ProxiedApp = switch.getTag.asInstanceOf[ProxiedApp]
if (app != null) {
app.proxied = !app.proxied
......@@ -230,9 +230,9 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
prefs.edit.putString(Key.proxied, apps).apply()
Toast.makeText(this, R.string.action_import_msg, Toast.LENGTH_SHORT).show()
// Restart activity
val intent = getIntent
finish()
startActivity(intent)
appListView.setVisibility(View.GONE)
loadingView.setVisibility(View.VISIBLE)
if (appsLoading) appsLoading = false else loadAppsAsync()
return true
}
}
......@@ -251,7 +251,7 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
this.setContentView(R.layout.layout_apps)
val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
toolbar.setTitle(R.string.proxied_help)
toolbar.setTitle(R.string.proxied_apps)
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha)
toolbar.setNavigationOnClickListener((v: View) => {
val intent = getParentActivityIntent
......@@ -267,11 +267,18 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT))
findViewById(R.id.onSwitch).asInstanceOf[Switch]
.setOnCheckedChangeListener((button: CompoundButton, checked: Boolean) => {
ShadowsocksApplication.settings.edit().putBoolean(Key.isProxyApps, checked).apply()
finish()
})
val bypassSwitch = findViewById(R.id.bypassSwitch).asInstanceOf[Switch]
bypassSwitch.setOnCheckedChangeListener((button: CompoundButton, checked: Boolean) =>
ShadowsocksApplication.settings.edit().putBoolean(Key.isBypassApps, checked).apply())
bypassSwitch.setChecked(ShadowsocksApplication.settings.getBoolean(Key.isBypassApps, false))
loadingView = findViewById(R.id.loading)
appListView = findViewById(R.id.applistview).asInstanceOf[ListView]
appListView.setOnScrollListener(new AbsListView.OnScrollListener {
var visible = false
......@@ -294,15 +301,16 @@ class AppManager extends AppCompatActivity with OnCheckedChangeListener with OnC
}
}
})
loadAppsAsync()
}
protected override def onResume() {
super.onResume()
def loadAppsAsync() {
Future {
if (!appsLoaded) loadApps()
while (!appsLoading) loadApps()
appsLoading = false
handler.post(() => {
appListView.setAdapter(adapter)
Utils.crossFade(AppManager.this, findViewById(R.id.loading), appListView)
Utils.crossFade(AppManager.this, loadingView, appListView)
})
}
}
......
......@@ -60,7 +60,7 @@ 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, SummaryEditTextPreference}
import com.github.shadowsocks.preferences.{NumberPickerPreference, DropDownPreference, PasswordEditTextPreference, SummaryEditTextPreference}
import com.github.shadowsocks.utils._
import com.google.android.gms.ads.{AdRequest, AdSize, AdView}
......@@ -96,8 +96,9 @@ 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 FEATURE_PREFS = Array(Key.route, Key.isGlobalProxy, Key.proxyedApps, Key.isUdpDns, Key.isAuth, Key.isIpv6)
val PROXY_PREFS = Array(Key.profileName, Key.proxy, Key.remotePort, Key.localPort, Key.sitekey, Key.encMethod,
Key.isAuth)
val FEATURE_PREFS = Array(Key.route, Key.isProxyApps, Key.isUdpDns, Key.isIpv6)
val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL,
Executable.TUN2SOCKS)
......@@ -111,6 +112,10 @@ object Shadowsocks {
pref.asInstanceOf[PasswordEditTextPreference].setText(value)
}
def updateNumberPickerPreference(pref: Preference, value: Int): Unit = {
pref.asInstanceOf[NumberPickerPreference].setValue(value)
}
def updateSummaryEditTextPreference(pref: Preference, value: String) {
pref.setSummary(value)
pref.asInstanceOf[SummaryEditTextPreference].setText(value)
......@@ -124,12 +129,12 @@ object Shadowsocks {
name match {
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)
case Key.remotePort => updateNumberPickerPreference(pref, profile.remotePort)
case Key.localPort => updateNumberPickerPreference(pref, profile.localPort)
case Key.sitekey => updatePasswordEditTextPreference(pref, profile.password)
case Key.encMethod => updateDropDownPreference(pref, profile.method)
case Key.route => updateDropDownPreference(pref, profile.route)
case Key.isGlobalProxy => updateSwitchPreference(pref, profile.global)
case Key.isProxyApps => updateSwitchPreference(pref, profile.proxyApps)
case Key.isUdpDns => updateSwitchPreference(pref, profile.udpdns)
case Key.isAuth => updateSwitchPreference(pref, profile.auth)
case Key.isIpv6 => updateSwitchPreference(pref, profile.ipv6)
......@@ -315,8 +320,6 @@ class Shadowsocks
def isReady: Boolean = {
if (!checkText(Key.proxy)) return false
if (!checkText(Key.sitekey)) return false
if (!checkNumber(Key.localPort, low = false)) return false
if (!checkNumber(Key.remotePort, low = true)) return false
if (bgService == null) return false
true
}
......@@ -347,15 +350,6 @@ class Shadowsocks
super.onCreate(savedInstanceState)
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())
}
// Update the profile
if (!ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)) {
currentProfile = ShadowsocksApplication.profileManager.create()
......@@ -365,6 +359,16 @@ class Shadowsocks
currentProfile = {
ShadowsocksApplication.currentProfile getOrElse currentProfile
}
ShadowsocksApplication.profileManager.load(currentProfile.id)
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())
}
// Initialize action bar
val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
......@@ -501,8 +505,7 @@ class Shadowsocks
for (name <- Shadowsocks.FEATURE_PREFS) {
val pref = preferences.findPreference(name)
if (pref != null) {
if (Seq(Key.isGlobalProxy, Key.proxyedApps)
.contains(name)) {
if (name == Key.isProxyApps) {
pref.setEnabled(enabled && (Utils.isLollipopOrAbove || !ShadowsocksApplication.isVpnEnabled))
} else {
pref.setEnabled(enabled)
......@@ -602,25 +605,6 @@ class Shadowsocks
!isTextEmpty(text, getString(R.string.proxy_empty))
}
def checkNumber(key: String, low: Boolean): Boolean = {
val text = ShadowsocksApplication.settings.getString(key, "")
if (isTextEmpty(text, getString(R.string.port_empty))) return false
try {
val port: Int = Integer.valueOf(text)
if (!low && port <= 1024) {
Snackbar.make(getWindow.getDecorView.findViewById(android.R.id.content), R.string.port_alert,
Snackbar.LENGTH_LONG).show
return false
}
} catch {
case ex: Exception =>
Snackbar.make(getWindow.getDecorView.findViewById(android.R.id.content), R.string.port_alert,
Snackbar.LENGTH_LONG).show
return false
}
true
}
/** Called when connect button is clicked. */
def serviceStart() {
bgService.start(ConfigUtils.load(ShadowsocksApplication.settings))
......
......@@ -390,10 +390,10 @@ class ShadowsocksNatService extends Service with BaseService {
init_sb.append(Utils.getIptables
+ " -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:" + 8153)
if (config.isGlobalProxy || config.isBypassApps) {
if (!config.isProxyApps || config.isBypassApps) {
http_sb.append(Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS)
}
if (!config.isGlobalProxy) {
if (config.isProxyApps) {
if (apps == null || apps.length <= 0) {
apps = AppManager.getProxiedApps(this, config.proxiedAppString)
}
......
......@@ -5,8 +5,8 @@ import java.util.Locale
import android.app.AlertDialog
import android.content.{DialogInterface, Intent}
import android.net.Uri
import android.os.Bundle
import android.preference.{Preference, PreferenceFragment}
import android.os.{Build, Bundle}
import android.preference.{SwitchPreference, Preference, PreferenceFragment}
import android.webkit.{WebViewClient, WebView}
import com.github.shadowsocks.utils.Key
......@@ -14,10 +14,18 @@ import com.github.shadowsocks.utils.Key
class ShadowsocksSettings extends PreferenceFragment {
private lazy val activity = getActivity.asInstanceOf[Shadowsocks]
private var isProxyApps: SwitchPreference = _
override def onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.pref_all)
isProxyApps = findPreference(Key.isProxyApps).asInstanceOf[SwitchPreference]
isProxyApps.setOnPreferenceChangeListener((preference: Preference, newValue: Any) => {
startActivity(new Intent(activity, classOf[AppManager]))
newValue.asInstanceOf[Boolean] // keep it ON
})
findPreference(Key.isNAT).setOnPreferenceChangeListener((preference: Preference, newValue: Any) =>
if (ShadowsocksApplication.isRoot) activity.handler.post(() => {
activity.deattachService()
......@@ -30,7 +38,11 @@ class ShadowsocksSettings extends PreferenceFragment {
true
})
findPreference("flush_dnscache").setOnPreferenceClickListener((preference: Preference) => {
val flush = findPreference("flush_dnscache")
if (Build.VERSION.SDK_INT >= 17 && !ShadowsocksApplication.isRoot) {
flush.setEnabled(false)
flush.setSummary(R.string.flush_dnscache_summary_disabled)
} else flush.setOnPreferenceClickListener((preference: Preference) => {
ShadowsocksApplication.track(Shadowsocks.TAG, "flush_dnscache")
activity.flushDnsCache()
true
......@@ -58,4 +70,9 @@ class ShadowsocksSettings extends PreferenceFragment {
true
})
}
override def onResume = {
super.onResume
isProxyApps.setChecked(ShadowsocksApplication.settings.getBoolean(Key.isProxyApps, false)) // update
}
}
......@@ -394,7 +394,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (Utils.isLollipopOrAbove) {
if (!config.isGlobalProxy) {
if (config.isProxyApps) {
val apps = AppManager.getProxiedApps(this, config.proxiedAppString)
val pkgSet: mutable.HashSet[String] = new mutable.HashSet[String]
for (app <- apps) {
......
......@@ -39,19 +39,19 @@
package com.github.shadowsocks.database
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper
import com.j256.ormlite.dao.Dao
import com.j256.ormlite.support.ConnectionSource
import com.j256.ormlite.table.TableUtils
import com.j256.ormlite.dao.Dao
object DBHelper {
val PROFILE = "profile.db"
}
class DBHelper(val context: Context)
extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 11) {
extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 12) {
lazy val profileDao: Dao[Profile, Int] = getDao(classOf[Profile])
......@@ -63,21 +63,31 @@ class DBHelper(val context: Context)
newVersion: Int) {
if (oldVersion != newVersion) {
if (oldVersion < 7) {
profileDao.executeRaw("DROP TABLE IF EXISTS 'profile';")
profileDao.executeRawNoArgs("DROP TABLE IF EXISTS 'profile';")
onCreate(database, connectionSource)
} else {
if (oldVersion < 8) {
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN udpdns SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
}
if (oldVersion < 9) {
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN route VARCHAR;")
}
if (oldVersion < 10) {
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN auth SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN auth SMALLINT;")
}
if (oldVersion < 11) {
profileDao.executeRaw("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
}
if (oldVersion < 12) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
onCreate(database, connectionSource)
profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
" udpdns, auth, ipv6, individual) " +
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, auth," +
" ipv6, individual FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
}
}
}
......
......@@ -63,30 +63,15 @@ class Profile {
@DatabaseField
var method: String = "rc4"
@DatabaseField
var date: String = ""
@DatabaseField
var route: String = "all"
@DatabaseField
var upload: Int = 0
@DatabaseField
var download: Int = 0
@DatabaseField
var chnroute: Boolean = true
@DatabaseField
var global: Boolean = true
var proxyApps: Boolean = false
@DatabaseField
var bypass: Boolean = false
@DatabaseField
var traffic: Boolean = false
@DatabaseField
var udpdns: Boolean = false
......@@ -98,7 +83,4 @@ class Profile {
@DatabaseField(dataType = DataType.LONG_STRING)
var individual: String = ""
@DatabaseField(dataType = DataType.LONG_STRING)
var description: String = ""
}
......@@ -117,10 +117,8 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
}
val edit = settings.edit()
edit.putBoolean(Key.isGlobalProxy, profile.global)
edit.putBoolean(Key.isGFWList, profile.chnroute)
edit.putBoolean(Key.isProxyApps, profile.proxyApps)
edit.putBoolean(Key.isBypassApps, profile.bypass)
edit.putBoolean(Key.isTrafficStat, profile.traffic)
edit.putBoolean(Key.isUdpDns, profile.udpdns)
edit.putBoolean(Key.isAuth, profile.auth)
edit.putBoolean(Key.isIpv6, profile.ipv6)
......@@ -128,8 +126,8 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
edit.putString(Key.proxy, profile.host)
edit.putString(Key.sitekey, profile.password)
edit.putString(Key.encMethod, profile.method)
edit.putString(Key.remotePort, profile.remotePort.toString)
edit.putString(Key.localPort, profile.localPort.toString)
edit.putInt(Key.remotePort, profile.remotePort)
edit.putInt(Key.localPort, profile.localPort)
edit.putString(Key.proxied, profile.individual)
edit.putInt(Key.profileId, profile.id)
edit.putString(Key.route, profile.route)
......@@ -143,10 +141,8 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
profile.id = settings.getInt(Key.profileId, -1)
profile.global = settings.getBoolean(Key.isGlobalProxy, false)
profile.chnroute = settings.getBoolean(Key.isGFWList, false)
profile.proxyApps = settings.getBoolean(Key.isProxyApps, false)
profile.bypass = settings.getBoolean(Key.isBypassApps, false)
profile.traffic = settings.getBoolean(Key.isTrafficStat, false)
profile.udpdns = settings.getBoolean(Key.isUdpDns, false)
profile.auth = settings.getBoolean(Key.isAuth, false)
profile.ipv6 = settings.getBoolean(Key.isIpv6, false)
......@@ -155,18 +151,8 @@ class ProfileManager(settings: SharedPreferences, dbHelper: DBHelper) {
profile.password = settings.getString(Key.sitekey, "default")
profile.method = settings.getString(Key.encMethod, "table")
profile.route = settings.getString(Key.route, "all")
profile.remotePort = try {
Integer.valueOf(settings.getString(Key.remotePort, "1984"))
} catch {
case ex: NumberFormatException =>
1984
}
profile.localPort = try {
Integer.valueOf(settings.getString(Key.localPort, "1984"))
} catch {
case ex: NumberFormatException =>
1984
}
profile.remotePort = settings.getInt(Key.remotePort, 1984)
profile.localPort = settings.getInt(Key.localPort, 1984)
profile.individual = settings.getString(Key.proxied, "")
profile
......
......@@ -14,7 +14,6 @@ import android.widget.{AdapterView, ArrayAdapter}
import com.github.shadowsocks.R
/**
* Based on: https://github.com/Mygod/mygod-lib-android/blob/683c41cb86d10e99ab0f457d41a218c6915fccc8/lib/src/main/scala/tk/mygod/preference/DropDownPreference.scala
* @author Mygod
*/
final class DropDownPreference(private val mContext: Context, attrs: AttributeSet = null)
......
......@@ -14,14 +14,13 @@ final class NatSwitchPreference(context: Context, attrs: AttributeSet) extends S
private var disabled = false
override def isEnabled = super.isEnabled && !disabled
override def setEnabled(b: Boolean) = super.setEnabled(b && !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]
}
override def onGetDefaultValue(a: TypedArray, index: Int) = ShadowsocksApplication.isVpnEnabled.asInstanceOf[AnyRef]
}
package com.github.shadowsocks.preferences
import android.content.Context
import android.content.res.TypedArray
import android.preference.DialogPreference
import android.util.AttributeSet
import android.view.ViewGroup
import android.widget.NumberPicker
import com.github.shadowsocks.R
/**
* @author Mygod
*/
final class NumberPickerPreference(context: Context, attrs: AttributeSet = null)
extends DialogPreference(context, attrs) with SummaryPreference {
private val picker = new NumberPicker(context)
private var value: Int = _
{
val a: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.NumberPickerPreference)
setMin(a.getInt(R.styleable.NumberPickerPreference_min, 0))
setMax(a.getInt(R.styleable.NumberPickerPreference_max, Int.MaxValue - 1))
a.recycle
}
initSummary(context, attrs)
def getValue = value
def getMin = if (picker == null) 0 else picker.getMinValue
def getMax = picker.getMaxValue
def setValue(i: Int) {
if (i == getValue || !callChangeListener(i)) return
picker.setValue(i)
value = picker.getValue
persistInt(value)
notifyChanged
}
def setMin(value: Int) = picker.setMinValue(value)
def setMax(value: Int) = picker.setMaxValue(value)
override protected def onCreateDialogView = {
val parent = picker.getParent.asInstanceOf[ViewGroup]
if (parent != null) parent.removeView(picker)
picker
}
override protected def onDialogClosed(positiveResult: Boolean) {
picker.clearFocus // commit changes
super.onDialogClosed(positiveResult) // forward compatibility
if (positiveResult) setValue(picker.getValue) else picker.setValue(value)
}
override protected def onGetDefaultValue(a: TypedArray, index: Int) = a.getInt(index, getMin).asInstanceOf[AnyRef]
override protected def onSetInitialValue(restorePersistedValue: Boolean, defaultValue: Any) {
val default = defaultValue.asInstanceOf[Int]
setValue(if (restorePersistedValue) getPersistedInt(default) else default)
}
protected def getSummaryValue: AnyRef = getValue.asInstanceOf[AnyRef]
}
......@@ -7,7 +7,6 @@ import com.github.shadowsocks.R
/**
* Make your preference support %s in summary. Override getSummaryValue to customize what to put in.
* Based on: https://github.com/Mygod/mygod-lib-android/blob/eb20c57a0da70bb5dbc27104002f20fb1eb31d76/lib/src/main/scala/tk/mygod/preference/SummaryPreference.scala
* @author Mygod
*/
trait SummaryPreference extends Preference {
......
......@@ -241,16 +241,13 @@ object ConfigUtils {
val password = proxy(2).trim
val method = proxy(3).trim
new Config(config.isGlobalProxy, config.isGFWList, config.isBypassApps, config.isTrafficStat,
config.isUdpDns, config.isAuth, config.isIpv6, config.profileName, host, password, method, config.proxiedAppString, config.route, port,
config.localPort)
new Config(config.isProxyApps, config.isBypassApps, config.isUdpDns, config.isAuth, config.isIpv6,
config.profileName, host, password, method, config.proxiedAppString, config.route, port, config.localPort)
}
def load(settings: SharedPreferences): Config = {
val isGlobalProxy = settings.getBoolean(Key.isGlobalProxy, false)
val isGFWList = settings.getBoolean(Key.isGFWList, false)
val isProxyApps = settings.getBoolean(Key.isProxyApps, false)
val isBypassApps = settings.getBoolean(Key.isBypassApps, false)
val isTrafficStat = settings.getBoolean(Key.isTrafficStat, false)
val isUdpDns = settings.getBoolean(Key.isUdpDns, false)
val isAuth = settings.getBoolean(Key.isAuth, false)
val isIpv6 = settings.getBoolean(Key.isIpv6, false)
......@@ -261,21 +258,11 @@ object ConfigUtils {
val encMethod = settings.getString(Key.encMethod, "table")
val route = settings.getString(Key.route, "all")
val remotePort: Int = try {
settings.getString(Key.remotePort, "1984").toInt
} catch {
case ex: NumberFormatException =>
1984
}
val localPort: Int = try {
settings.getString(Key.localPort, "1984").toInt
} catch {
case ex: NumberFormatException =>
1984
}
val remotePort = settings.getInt(Key.remotePort, 1984)
val localPort = settings.getInt(Key.localPort, 1984)
val proxiedAppString = settings.getString(Key.proxied, "")
new Config(isGlobalProxy, isGFWList, isBypassApps, isTrafficStat, isUdpDns, isAuth, isIpv6, profileName, proxy,
sitekey, encMethod, proxiedAppString, route, remotePort, localPort)
new Config(isProxyApps, isBypassApps, isUdpDns, isAuth, isIpv6, profileName, proxy, sitekey, encMethod,
proxiedAppString, route, remotePort, localPort)
}
}
......@@ -67,16 +67,13 @@ object Key {
val profiles = "profiles"
val isNAT = "isNAT"
val proxyedApps = "proxyedApps"
val route = "route"
val isRunning = "isRunning"
val isAutoConnect = "isAutoConnect"
val isGlobalProxy = "isGlobalProxy"
val isGFWList = "isGFWList"
val isProxyApps = "isProxyApps"
val isBypassApps = "isBypassApps"
val isTrafficStat = "isTrafficStat"
val isUdpDns = "isUdpDns"
val isAuth= "isAuth"
val isIpv6= "isIpv6"
......
......@@ -55,7 +55,7 @@ import android.util.{DisplayMetrics, Base64, Log}
import android.view.View.MeasureSpec
import android.view.{Gravity, View, Window}
import android.widget.Toast
import com.github.shadowsocks.BuildConfig
import com.github.shadowsocks.{ShadowsocksApplication, BuildConfig}
import org.xbill.DNS._
......@@ -155,24 +155,13 @@ object Utils {
// Blocked > 3 seconds
def toggleAirplaneMode(context: Context) {
if (Build.VERSION.SDK_INT >= 17) {
toggleAboveApiLevel17()
} else {
if (ShadowsocksApplication.isRoot) {
Console.runRootCommand(Array("ndc resolver flushdefaultif", "ndc resolver flushif wlan0"))
} else if (Build.VERSION.SDK_INT < 17) {
toggleBelowApiLevel17(context)
}
}
private def toggleAboveApiLevel17() {
// Android 4.2 and above
Console.runRootCommand(Array("ndc resolver flushdefaultif", "ndc resolver flushif wlan0"))
//Utils.runRootCommand("settings put global airplane_mode_on 1\n"
// + "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true\n"
// + "settings put global airplane_mode_on 0\n"
// + "am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false\n")
}
//noinspection ScalaDeprecation
private def toggleBelowApiLevel17(context: Context) {
// Android 4.2 below
......
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