@@ -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);
|
||||
|
||||
@@ -1,36 +1,56 @@
|
||||
<?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"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingLeft="48dp"
|
||||
android:paddingStart="48dp"
|
||||
android:paddingTop="8dp"
|
||||
android:background="@android:color/white"
|
||||
android:elevation="1dp" tools:ignore="RtlSymmetry">
|
||||
<TextView
|
||||
android:id="@+id/bypassLabel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="3dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/bypass_apps"/>
|
||||
<LinearLayout android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="48dp"
|
||||
android:paddingStart="48dp"
|
||||
android:elevation="1dp"
|
||||
android:background="@android:color/white"
|
||||
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="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="18sp"
|
||||
android:text="@string/bypass_apps"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/bypassSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_marginRight="6dp"/>
|
||||
<Switch
|
||||
android:id="@+id/bypassSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<FrameLayout android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
<?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>
|
||||
</resources>
|
||||
<declare-styleable name="NumberPickerPreference">
|
||||
<attr name="max" format="integer" />
|
||||
<attr name="min" format="integer" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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]
|
||||
if (!ShadowsocksApplication.isRoot) {
|
||||
disabled = true
|
||||
setEnabled(false)
|
||||
setSummary(R.string.nat_summary_no_root)
|
||||
}
|
||||
|
||||
override def onGetDefaultValue(a: TypedArray, index: Int) = ShadowsocksApplication.isVpnEnabled.asInstanceOf[AnyRef]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user