diff --git a/build.sbt b/build.sbt index fcd92b70..10646322 100644 --- a/build.sbt +++ b/build.sbt @@ -38,6 +38,7 @@ libraryDependencies ++= Seq( "com.google.android.gms" % "play-services-analytics" % "8.4.0", "com.android.support" % "design" % "23.1.1", "com.android.support" % "gridlayout-v7" % "23.1.1", + "com.android.support" % "cardview-v7" % "23.2.0", "com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01", "com.j256.ormlite" % "ormlite-core" % "4.48", "com.j256.ormlite" % "ormlite-android" % "4.48", diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 80f7f67d..82c629f0 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -86,6 +86,17 @@ + + + + + + + diff --git a/src/main/res/layout/layout_quick_switch.xml b/src/main/res/layout/layout_quick_switch.xml new file mode 100644 index 00000000..b461f18d --- /dev/null +++ b/src/main/res/layout/layout_quick_switch.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index 31bf0e56..bd08a6ef 100755 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -38,6 +38,7 @@ NAT 模式需要 ROOT 权限 切换到 VPN 模式 停止服务 + 快速切换 正在关闭…… 后台服务启动失败:%s VPN 服务启动失败。你可能需要重启设备。 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 7ef92294..1c6f63e8 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -73,6 +73,7 @@ Shutting down… %s Failed to start VPN service. You might need to reboot your device. + Quick Switch Close diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index 7463b6bb..3b6dd526 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -5,6 +5,7 @@ + + + + + @@ -39,4 +44,10 @@ @android:color/transparent + + diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala index 1b29cbc5..09962ff0 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNotification.scala @@ -41,6 +41,8 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str .setSmallIcon(R.drawable.ic_stat_shadowsocks) .addAction(android.R.drawable.ic_menu_close_clear_cancel, service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, new Intent(Action.CLOSE), 0)) + .addAction(android.R.drawable.ic_menu_manage, service.getString(R.string.quick_switch), + PendingIntent.getActivity(service, 0, new Intent(Action.QUICK_SWITCH), 0)) private lazy val style = new BigTextStyle(builder) private val showOnUnlock = visible && Utils.isLollipopOrAbove private var isVisible = true diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksQuickSwitchActivity.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksQuickSwitchActivity.scala new file mode 100644 index 00000000..90519940 --- /dev/null +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksQuickSwitchActivity.scala @@ -0,0 +1,78 @@ +package com.github.shadowsocks + +import android.content.res.Resources +import android.os.{Handler, Build, Bundle} +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.{Toolbar, DefaultItemAnimator, LinearLayoutManager, RecyclerView} +import android.view.{LayoutInflater, View, ViewGroup} +import android.widget.CheckedTextView +import com.github.shadowsocks.database.Profile +import com.github.shadowsocks.utils.Utils + +/** + * Created by Lucas on 3/10/16. + */ +class ShadowsocksQuickSwitchActivity extends AppCompatActivity { + + private class ProfileViewHolder(val view: View) extends RecyclerView.ViewHolder(view) with View.OnClickListener { + { + val typedArray = obtainStyledAttributes(Array(android.R.attr.selectableItemBackground)) + view.setBackgroundResource(typedArray.getResourceId(0, 0)) + typedArray.recycle + } + private var item: Profile = _ + private val text = itemView.findViewById(android.R.id.text1).asInstanceOf[CheckedTextView] + itemView.setOnClickListener(this) + + def bind(item: Profile) { + this.item = item + text.setText(item.name) + if (item.id == ShadowsocksApplication.profileId) { + text.setChecked(true) + } else { + text.setChecked(false) + } + } + + def onClick(v: View) { + Utils.stopSsService(ShadowsocksQuickSwitchActivity.this) + ShadowsocksApplication.switchProfile(item.id) + val handler = new Handler(ShadowsocksQuickSwitchActivity.this.getMainLooper) + handler.postDelayed(() => { + Utils.startSsService(ShadowsocksQuickSwitchActivity.this) + }, 3000) + finish + } + } + + private class ProfilesAdapter extends RecyclerView.Adapter[ProfileViewHolder] { + val profiles = ShadowsocksApplication.profileManager.getAllProfiles.getOrElse(List.empty[Profile]) + + def getItemCount = profiles.length + + def onBindViewHolder(vh: ProfileViewHolder, i: Int) = i match { + case _ => vh.bind(profiles(i)) + } + + private val name = "select_dialog_singlechoice_" + (if (Build.VERSION.SDK_INT >= 21) "material" else "holo") + + def onCreateViewHolder(vg: ViewGroup, i: Int) = new ProfileViewHolder(LayoutInflater.from(vg.getContext) + .inflate(Resources.getSystem.getIdentifier(name, "layout", "android"), vg, false)) + } + + private val profilesAdapter = new ProfilesAdapter + + override def onCreate(savedInstanceState: Bundle) { + super.onCreate(savedInstanceState) + setContentView(R.layout.layout_quick_switch) + + val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar] + toolbar.setTitle(R.string.quick_switch) + + val profilesList = findViewById(R.id.profilesList).asInstanceOf[RecyclerView] + val lm = new LinearLayoutManager(this) + profilesList.setLayoutManager(lm) + profilesList.setItemAnimator(new DefaultItemAnimator) + profilesList.setAdapter(profilesAdapter) + } +} diff --git a/src/main/scala/com/github/shadowsocks/utils/Constants.scala b/src/main/scala/com/github/shadowsocks/utils/Constants.scala index 92e4f2ac..ee4bccd4 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Constants.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Constants.scala @@ -104,6 +104,7 @@ object State { object Action { val SERVICE = "com.github.shadowsocks.SERVICE" val CLOSE = "com.github.shadowsocks.CLOSE" + val QUICK_SWITCH = "com.github.shadowsocks.QUICK_SWITCH" } object Route {