diff --git a/src/main/res/values-v21/strings.xml b/src/main/res/values-v21/strings.xml
new file mode 100644
index 00000000..14c389ed
--- /dev/null
+++ b/src/main/res/values-v21/strings.xml
@@ -0,0 +1,4 @@
+
+
+ Set proxy for selected apps
+
diff --git a/src/main/res/values-zh-v21/strings.xml b/src/main/res/values-zh-v21/strings.xml
new file mode 100644
index 00000000..e16254e2
--- /dev/null
+++ b/src/main/res/values-zh-v21/strings.xml
@@ -0,0 +1,4 @@
+
+
+ 为应用程序分别设置代理
+
diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml
index 46db437b..ee30b407 100755
--- a/src/main/res/values-zh/strings.xml
+++ b/src/main/res/values-zh/strings.xml
@@ -26,7 +26,7 @@
绕过所有位于中国的网站
路由
分应用代理
- 为应用程序分别设置代理
+ 为应用程序分别设置代理,在 4.x 下需要启用 NAT 模式
开
绕行模式
启用该选项,以使所选应用程序的流量不经过代理
@@ -34,6 +34,7 @@
随系统启动后台服务
后台服务已开始运行。
无法连接远程服务器
+ 警告:在 Android 5.0 及更高版本中不建议使用 NAT 模式
NAT 模式需要 ROOT 权限
切换到 VPN 模式
停止服务
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 3f5c51d4..4c020eeb 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -54,7 +54,7 @@
Route
Per-App Proxy
- Set proxy for selected apps
+ Set proxy for selected apps, requires NAT mode under Android 4.x
On
Bypass Mode
Enable this option to bypass selected apps
@@ -66,6 +66,7 @@
Shadowsocks started.
Failed to connect the remote server
+ WARNING: NAT mode has been deprecated since Android 5.0
NAT mode requires ROOT permission
Switch to VPN mode
Stop the service
diff --git a/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala b/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala
index 20e5e882..55423e0a 100644
--- a/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala
+++ b/src/main/scala/com/github/shadowsocks/ProfileManagerActivity.scala
@@ -76,12 +76,11 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
handler.post(() => text.setText(builder))
}
- def bind(item: Profile, i: Int) {
+ def bind(item: Profile) {
this.item = item
updateText()
if (item.id == ShadowsocksApplication.profileId) {
text.setChecked(true)
- selectedIndex = i
selectedItem = this
} else {
text.setChecked(false)
@@ -102,7 +101,7 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
def getItemCount = profiles.length
- def onBindViewHolder(vh: ProfileViewHolder, i: Int) = vh.bind(profiles(i), i)
+ 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))
@@ -154,7 +153,6 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
}
}
- private var selectedIndex = -1
private var selectedItem: ProfileViewHolder = _
private val handler = new Handler
@@ -181,10 +179,9 @@ class ProfileManagerActivity extends AppCompatActivity with OnMenuItemClickListe
profilesList.setLayoutManager(layoutManager)
profilesList.setItemAnimator(new DefaultItemAnimator)
profilesList.setAdapter(profilesAdapter)
- if (selectedIndex < 0) selectedIndex = profilesAdapter.profiles.zipWithIndex.collectFirst {
+ layoutManager.scrollToPosition(profilesAdapter.profiles.zipWithIndex.collectFirst {
case (profile, i) if profile.id == ShadowsocksApplication.profileId => i
- }.getOrElse(-1)
- layoutManager.scrollToPosition(selectedIndex)
+ }.getOrElse(-1))
removedSnackbar = Snackbar.make(profilesList, R.string.removed, Snackbar.LENGTH_LONG)
.setAction(R.string.undo, ((v: View) => profilesAdapter.undoRemoves): OnClickListener)
removedSnackbar.getView.addOnAttachStateChangeListener(new OnAttachStateChangeListener {
diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala
index 6eb2ff04..84a8602f 100644
--- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala
+++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala
@@ -168,6 +168,11 @@ class Shadowsocks
// Update the UI
if (fab != null) fab.setEnabled(true)
updateState()
+ if (Build.VERSION.SDK_INT >= 21 && !ShadowsocksApplication.isVpnEnabled) {
+ val snackbar = Snackbar.make(findViewById(android.R.id.content), R.string.nat_deprecated, Snackbar.LENGTH_LONG)
+ snackbar.setAction(R.string.switch_to_vpn, (_ => preferences.natSwitch.setChecked(false)): View.OnClickListener)
+ snackbar.show
+ }
if (!ShadowsocksApplication.settings.getBoolean(ShadowsocksApplication.getVersionName, false)) {
ShadowsocksApplication.settings.edit.putBoolean(ShadowsocksApplication.getVersionName, true).apply()
diff --git a/src/main/scala/com/github/shadowsocks/TaskerActivity.scala b/src/main/scala/com/github/shadowsocks/TaskerActivity.scala
index 8f0c40f5..e40426d8 100644
--- a/src/main/scala/com/github/shadowsocks/TaskerActivity.scala
+++ b/src/main/scala/com/github/shadowsocks/TaskerActivity.scala
@@ -82,7 +82,7 @@ class TaskerActivity extends AppCompatActivity {
}
private class ProfilesAdapter extends RecyclerView.Adapter[ProfileViewHolder] {
- private val profiles = ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List.empty[Profile])
+ val profiles = ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List.empty[Profile])
def getItemCount = 1 + profiles.length
def onBindViewHolder(vh: ProfileViewHolder, i: Int) = i match {
case 0 => vh.bindDefault
@@ -110,9 +110,13 @@ class TaskerActivity extends AppCompatActivity {
switch = findViewById(R.id.serviceSwitch).asInstanceOf[Switch]
switch.setChecked(taskerOption.switchOn)
val profilesList = findViewById(R.id.profilesList).asInstanceOf[RecyclerView]
- profilesList.setLayoutManager(new LinearLayoutManager(this))
+ val lm = new LinearLayoutManager(this)
+ profilesList.setLayoutManager(lm)
profilesList.setItemAnimator(new DefaultItemAnimator)
profilesList.setAdapter(profilesAdapter)
+ if (taskerOption.profileId >= 0) lm.scrollToPosition(profilesAdapter.profiles.zipWithIndex.collectFirst {
+ case (profile, i) if profile.id == taskerOption.profileId => i + 1
+ }.getOrElse(0))
}
}