From 1907ac2fef5ea89dca93cba0a1a2df662a6abbad Mon Sep 17 00:00:00 2001 From: Max Lv Date: Sun, 1 Dec 2013 12:40:22 +0800 Subject: [PATCH] support scaning QR code --- build.sbt | 3 +- src/main/res/values-zh/strings.xml | 7 ++ src/main/res/values/strings.xml | 6 ++ .../com/github/shadowsocks/Shadowsocks.scala | 73 ++++++++++++++++--- .../shadowsocks/utils/ShadowParser.scala | 34 +++++++++ 5 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 src/main/scala/com/github/shadowsocks/utils/ShadowParser.scala diff --git a/build.sbt b/build.sbt index 2681f52e..22db1b05 100644 --- a/build.sbt +++ b/build.sbt @@ -4,7 +4,8 @@ libraryDependencies ++= Seq( "com.google.android" % "admob" % "6.4.1", "dnsjava" % "dnsjava" % "2.1.5", "org.scalaj" %% "scalaj-http" % "0.3.10", - "commons-net" % "commons-net" % "3.3" + "commons-net" % "commons-net" % "3.3", + "com.google.zxing" % "android-integration" % "2.2" ) libraryDependencies ++= Seq( diff --git a/src/main/res/values-zh/strings.xml b/src/main/res/values-zh/strings.xml index 3d597411..957104f5 100755 --- a/src/main/res/values-zh/strings.xml +++ b/src/main/res/values-zh/strings.xml @@ -66,4 +66,11 @@ 刷新 DNS 缓存 刷新中... 添加配置文件 + + + + 扫描二维码 + 手动设置 + + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 618c6d6d..ab72d692 100755 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -83,4 +83,10 @@ Flushing Add Profile + + + Scan QR code + Manually Setting + + diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index 2c373356..b98678d2 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -72,6 +72,7 @@ import com.github.shadowsocks.database.Category import com.github.shadowsocks.utils._ import com.github.shadowsocks.database.Item import com.github.shadowsocks.database.Category +import com.google.zxing.integration.android.IntentIntegrator class ProfileIconDownloader(context: Context, connectTimeout: Int, readTimeout: Int) extends BaseImageDownloader(context, connectTimeout, readTimeout) { @@ -569,6 +570,50 @@ class Shadowsocks drawer.setActiveView(v, pos) } + + def newProfile(id: Int) { + + val builder = new AlertDialog.Builder(this) + builder.setTitle(R.string.add_profile) + .setItems(R.array.add_profile_methods, new DialogInterface.OnClickListener() { + def onClick(dialog: DialogInterface, which: Int) { + which match { + case 0 => { + val integrator = new IntentIntegrator(Shadowsocks.this) + integrator.initiateScan() + } + case 1 => addProfile(id) + case _ => + } + } + }) + builder.create().show() + + } + + def addProfile(profile: Profile) { + drawer.closeMenu(true) + + val h = showProgress(getString(R.string.loading)) + + handler.postDelayed(new Runnable { + def run() { + currentProfile = profile + profileManager.createOrUpdateProfile(currentProfile) + profileManager.reload(currentProfile.id) + menuAdapter.updateList(getMenuList, currentProfile.id) + + if (!isSinglePane) { + sendBroadcast(new Intent(Action.UPDATE_FRAGMENT)) + } else { + updatePreferenceScreen() + } + + h.sendEmptyMessage(0) + } + }, 600) + } + def addProfile(id: Int) { drawer.closeMenu(true) @@ -665,7 +710,7 @@ class Shadowsocks buf ++= getProfileList buf += - new Item(-400, getString(R.string.add_profile), android.R.drawable.ic_menu_add, addProfile) + new Item(-400, getString(R.string.add_profile), android.R.drawable.ic_menu_add, newProfile) buf += new Category(getString(R.string.settings)) @@ -827,16 +872,24 @@ class Shadowsocks } override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { - resultCode match { - case Activity.RESULT_OK => { - prepared = true - if (!serviceStart) { - switchButton.setChecked(false) - } + val scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data) + if (scanResult != null) { + ShadowParser.parse(scanResult.getContents) match { + case Some(profile) => addProfile(profile) + case _ => // ignore } - case _ => { - clearDialog() - Log.e(Shadowsocks.TAG, "Failed to start VpnService") + } else { + resultCode match { + case Activity.RESULT_OK => { + prepared = true + if (!serviceStart) { + switchButton.setChecked(false) + } + } + case _ => { + clearDialog() + Log.e(Shadowsocks.TAG, "Failed to start VpnService") + } } } } diff --git a/src/main/scala/com/github/shadowsocks/utils/ShadowParser.scala b/src/main/scala/com/github/shadowsocks/utils/ShadowParser.scala new file mode 100644 index 00000000..338a9705 --- /dev/null +++ b/src/main/scala/com/github/shadowsocks/utils/ShadowParser.scala @@ -0,0 +1,34 @@ +package com.github.shadowsocks.utils + +import com.github.shadowsocks.database.Profile +import android.net.Uri +import android.util.{Log, Base64} + +object ShadowParser { + val TAG = "ShadowParser" + def parse (data: String): Option[Profile] = { + try { + Log.d(TAG, data) + val uri = Uri.parse(data.trim) + if (uri.isOpaque && uri.getScheme == "shadow") { + val encoded = data.replace("shadow:", "") + val content = new String(Base64.decode(encoded, Base64.NO_PADDING), "UTF-8") + val info = content.split('@') + val encinfo = info(0).split(':') + val serverinfo = info(1).split(':') + + val profile = new Profile + profile.name = serverinfo(0) + profile.host = serverinfo(0) + profile.remotePort = serverinfo(1).toInt + profile.localPort = 1080 + profile.method = encinfo(0) + profile.password = encinfo(1) + return Some(profile) + } + } catch { + case ex : Exception => Log.e(TAG, "parser error", ex)// Ignore + } + None + } +}