diff --git a/mobile/build.sbt b/mobile/build.sbt
index bdb07c66..44b43590 100644
--- a/mobile/build.sbt
+++ b/mobile/build.sbt
@@ -36,6 +36,7 @@ libraryDependencies ++=
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" ::
"dnsjava" % "dnsjava" % "2.1.8" ::
"eu.chainfire" % "libsuperuser" % "1.0.0.201704021214" ::
+ "me.dm7.barcodescanner" % "zxing" % "1.9.3" ::
"net.glxn.qrgen" % "android" % "2.0" ::
Nil
diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml
index f6072dd4..b39c6259 100644
--- a/mobile/src/main/AndroidManifest.xml
+++ b/mobile/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
+
@@ -11,6 +12,8 @@
android:required="false"/>
+
+
+
diff --git a/mobile/src/main/res/layout/layout_scanner.xml b/mobile/src/main/res/layout/layout_scanner.xml
new file mode 100644
index 00000000..ae575a39
--- /dev/null
+++ b/mobile/src/main/res/layout/layout_scanner.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/mobile/src/main/res/values-ja/strings.xml b/mobile/src/main/res/values-ja/strings.xml
index 46db6437..366833f1 100644
--- a/mobile/src/main/res/values-ja/strings.xml
+++ b/mobile/src/main/res/values-ja/strings.xml
@@ -91,6 +91,7 @@
"QR コードを読み取る"
"手動設定"
"Zxingに準拠したQRコードスキャンアプリをインストールして下さい"
+QRコードを読み取るにはカメラの使用権限が必要です。
- "削除済み"
@@ -132,4 +133,4 @@
"不明なプラグイン"
"警告:このプラグインは信頼されていないソースからの可能性があります"
"プラグイン"
-
\ No newline at end of file
+
diff --git a/mobile/src/main/res/values-ru/strings.xml b/mobile/src/main/res/values-ru/strings.xml
index 30d47daf..a36dbe08 100644
--- a/mobile/src/main/res/values-ru/strings.xml
+++ b/mobile/src/main/res/values-ru/strings.xml
@@ -96,6 +96,7 @@
"Сканировать QR-код"
"Ручные настройки"
"Пожалуйста, установите любое ZXing-совместимое приложение для сканирования QR-кодов."
+Разрешение камеры требуется для сканирования QR код.
- "Удалено"
- "Удалено %d элемента"
@@ -140,4 +141,4 @@
"Неизвестный плагин %s"
"Предупреждение: этот плагин получен из недоверенного источника."
"Плагин: %s"
-
\ No newline at end of file
+
diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml
index 14dcab6c..ed29ac07 100644
--- a/mobile/src/main/res/values-zh-rCN/strings.xml
+++ b/mobile/src/main/res/values-zh-rCN/strings.xml
@@ -92,6 +92,7 @@
"扫描二维码"
"手动设置"
"请安装任意兼容 ZXing 的二维码扫描应用。"
+扫描二维码需要相机权限。
- "已删除 %d 项"
@@ -133,4 +134,4 @@
"未知插件 %s"
"警告:该插件似乎并非来自已知的可信源。"
"插件:%s"
-
\ No newline at end of file
+
diff --git a/mobile/src/main/res/values-zh-rTW/strings.xml b/mobile/src/main/res/values-zh-rTW/strings.xml
index 9e01852e..431d1830 100644
--- a/mobile/src/main/res/values-zh-rTW/strings.xml
+++ b/mobile/src/main/res/values-zh-rTW/strings.xml
@@ -90,6 +90,7 @@
"掃描 QR 碼"
"手動設定"
"請安裝任何相容 ZXing 的 QR 碼掃描應用程式。"
+掃描 QR 碼需要相機權限。
- "已移除 %d 項"
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
index 0d937680..b6fa8ab3 100644
--- a/mobile/src/main/res/values/strings.xml
+++ b/mobile/src/main/res/values/strings.xml
@@ -106,6 +106,7 @@
Scan QR code
Manual Settings
Please install any ZXing-compliant QR code scanning app.
+ Camera permission is required for scanning QR code.
- Removed
- %d items removed
diff --git a/mobile/src/main/scala/com/github/shadowsocks/ProfilesFragment.scala b/mobile/src/main/scala/com/github/shadowsocks/ProfilesFragment.scala
index 02ca811e..b9f67338 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/ProfilesFragment.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/ProfilesFragment.scala
@@ -352,21 +352,16 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
def onMenuItemClick(item: MenuItem): Boolean = item.getItemId match {
case R.id.action_scan_qr_code =>
- def installScanner() = {
- Toast.makeText(getActivity, R.string.add_profile_scanner_not_installed, Toast.LENGTH_LONG).show()
- try startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.zxing.client.android"))) catch {
- case exc: ActivityNotFoundException => exc.printStackTrace()
- }
- }
try startActivityForResult(new Intent("com.google.zxing.client.android.SCAN")
.addCategory(Intent.CATEGORY_DEFAULT)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_DOCUMENT),
REQUEST_SCAN_QR_CODE) catch {
- case _: ActivityNotFoundException => installScanner()
+ case _: ActivityNotFoundException =>
+ startActivity(new Intent(getActivity, classOf[ScannerActivity]))
case e: SecurityException =>
e.printStackTrace()
app.track(e)
- installScanner()
+ startActivity(new Intent(getActivity, classOf[ScannerActivity]))
}
true
case R.id.action_import =>
diff --git a/mobile/src/main/scala/com/github/shadowsocks/ScannerActivity.scala b/mobile/src/main/scala/com/github/shadowsocks/ScannerActivity.scala
new file mode 100644
index 00000000..b1dd4de6
--- /dev/null
+++ b/mobile/src/main/scala/com/github/shadowsocks/ScannerActivity.scala
@@ -0,0 +1,105 @@
+/*******************************************************************************/
+/* */
+/* Copyright (C) 2016 by Max Lv */
+/* Copyright (C) 2016 by Mygod Studio */
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, either version 3 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see . */
+/* */
+/*******************************************************************************/
+
+package com.github.shadowsocks
+
+import android.app.{Activity, TaskStackBuilder}
+import android.content.Intent
+import android.content.pm.{PackageManager, ShortcutManager}
+import android.os.{Build, Bundle}
+import android.support.v4.app.ActivityCompat
+import android.support.v4.content.ContextCompat
+import android.support.v7.app.AppCompatActivity
+import android.support.v7.widget.Toolbar
+import android.text.TextUtils
+import android.widget.Toast
+import com.google.zxing.Result
+import com.github.shadowsocks.ShadowsocksApplication.app
+import com.github.shadowsocks.utils.Parser
+import me.dm7.barcodescanner.zxing.ZXingScannerView
+
+object ScannerActivity {
+ private final val MY_PERMISSIONS_REQUEST_CAMERA = 1
+}
+
+class ScannerActivity extends AppCompatActivity with ZXingScannerView.ResultHandler {
+ import ScannerActivity._
+
+ private var scannerView: ZXingScannerView = _
+
+ override def onRequestPermissionsResult(requestCode: Int, permissions: Array[String],
+ grantResults: Array[Int]) {
+ if (requestCode == MY_PERMISSIONS_REQUEST_CAMERA) {
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0
+ && grantResults(0) == PackageManager.PERMISSION_GRANTED) {
+ scannerView.setResultHandler(this)
+ scannerView.startCamera()
+ } else {
+ Toast.makeText(this, R.string.add_profile_scanner_permission_required, Toast.LENGTH_SHORT).show()
+ finish()
+ }
+ }
+ }
+
+ def navigateUp() {
+ val intent = getParentActivityIntent
+ if (shouldUpRecreateTask(intent) || isTaskRoot)
+ TaskStackBuilder.create(this).addNextIntentWithParentStack(intent).startActivities()
+ else finish()
+ }
+
+ override def onCreate(state: Bundle) {
+ super.onCreate(state)
+ setContentView(R.layout.layout_scanner)
+ val toolbar = findViewById(R.id.toolbar).asInstanceOf[Toolbar]
+ toolbar.setTitle(getTitle)
+ toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_material)
+ toolbar.setNavigationOnClickListener(_ => navigateUp())
+ scannerView = findViewById(R.id.scanner).asInstanceOf[ZXingScannerView]
+ if (Build.VERSION.SDK_INT >= 25) getSystemService(classOf[ShortcutManager]).reportShortcutUsed("scan")
+ }
+
+ override def onResume() {
+ super.onResume()
+ val permissionCheck = ContextCompat.checkSelfPermission(this,
+ android.Manifest.permission.CAMERA)
+ if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
+ scannerView.setResultHandler(this) // Register ourselves as a handler for scan results.
+ scannerView.startCamera() // Start camera on resume
+ } else {
+ ActivityCompat.requestPermissions(this,
+ Array(android.Manifest.permission.CAMERA), MY_PERMISSIONS_REQUEST_CAMERA)
+ }
+ }
+
+ override def onPause() {
+ super.onPause()
+ scannerView.stopCamera() // Stop camera on pause
+ }
+
+ override def handleResult(rawResult: Result) = {
+ val uri = rawResult.getText
+ if (!TextUtils.isEmpty(uri))
+ Parser.findAll(uri).foreach(app.profileManager.createProfile)
+ navigateUp()
+ }
+}
+