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() + } +} +