From 05981fb11733e69c9696efb27a1c3209e7707dd3 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 8 Feb 2016 14:25:14 +0800 Subject: [PATCH 1/3] Add a prompt: per-app proxy requires NAT mode under Android 4.x --- src/main/res/values-v21/strings.xml | 4 ++++ src/main/res/values-zh-v21/strings.xml | 4 ++++ src/main/res/values-zh/strings.xml | 2 +- src/main/res/values/strings.xml | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 src/main/res/values-v21/strings.xml create mode 100644 src/main/res/values-zh-v21/strings.xml 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..81ac0089 100755 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -26,7 +26,7 @@ 绕过所有位于中国的网站 路由 分应用代理 - 为应用程序分别设置代理 + 为应用程序分别设置代理,在 4.x 下需要启用 NAT 模式 开 绕行模式 启用该选项,以使所选应用程序的流量不经过代理 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 3f5c51d4..bc378a90 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 From 10e3b211e3db49064b890696ed82c8377132f94a Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 8 Feb 2016 14:40:49 +0800 Subject: [PATCH 2/3] Ask user to refrain from further using NAT mode --- src/main/res/values-zh/strings.xml | 1 + src/main/res/values/strings.xml | 1 + src/main/scala/com/github/shadowsocks/Shadowsocks.scala | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index 81ac0089..ee30b407 100755 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -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 bc378a90..4c020eeb 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -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/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() From 3556eebfeb09b30d5bd6dccc0e0c005feb916163 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 8 Feb 2016 15:06:53 +0800 Subject: [PATCH 3/3] Tasker: scroll to selected profile --- .../github/shadowsocks/ProfileManagerActivity.scala | 11 ++++------- .../scala/com/github/shadowsocks/TaskerActivity.scala | 8 ++++++-- 2 files changed, 10 insertions(+), 9 deletions(-) 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/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)) } }