pull/32/head
GKF 6 years ago
parent 59f74e05d8
commit 2ba5ccc67d
  1. 3
      app/build.gradle
  2. 1
      app/src/main/AndroidManifest.xml
  3. 2
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  4. 112
      app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt
  5. 232
      app/src/main/java/io/legado/app/utils/FileUtils.kt
  6. 81
      app/src/main/res/layout/activity_qrcode_capture.xml
  7. 10
      app/src/main/res/menu/qr_code_scan.xml

@ -126,6 +126,9 @@ dependencies {
//Glide //Glide
implementation 'com.github.bumptech.glide:glide:4.9.0' implementation 'com.github.bumptech.glide:glide:4.9.0'
//
implementation 'cn.bingoogolapple:bga-qrcode-zxing:1.3.6'
// //
implementation 'com.jaredrummler:colorpicker:1.1.0' implementation 'com.jaredrummler:colorpicker:1.1.0'

@ -44,6 +44,7 @@
<activity android:name=".ui.config.ConfigActivity"/> <activity android:name=".ui.config.ConfigActivity"/>
<activity android:name=".ui.sourceedit.SourceEditActivity"/> <activity android:name=".ui.sourceedit.SourceEditActivity"/>
<activity android:name=".ui.bookshelf.BookshelfActivity" /> <activity android:name=".ui.bookshelf.BookshelfActivity" />
<activity android:name=".ui.qrcode.QrCodeActivity" />
</application> </application>
</manifest> </manifest>

@ -26,7 +26,7 @@ object Restore {
} }
fun importYueDuData(context: Context) { fun importYueDuData(context: Context) {
val yuedu = File(getSdPath(), "YueDu") val yuedu = File(FileUtils.getSdPath(), "YueDu")
val jsonPath = JsonPath.using( val jsonPath = JsonPath.using(
Configuration.builder() Configuration.builder()
.options(Option.SUPPRESS_EXCEPTIONS) .options(Option.SUPPRESS_EXCEPTIONS)

@ -0,0 +1,112 @@
package io.legado.app.ui.qrcode
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.lifecycle.AndroidViewModel
import cn.bingoogolapple.qrcode.core.QRCodeView
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.utils.FileUtils
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_qrcode_capture.*
import kotlinx.android.synthetic.main.view_title_bar.*
class QrCodeActivity : BaseActivity<AndroidViewModel>(), QRCodeView.Delegate {
override val viewModel: AndroidViewModel
get() = getViewModel(AndroidViewModel::class.java)
override val layoutID: Int
get() = R.layout.activity_qrcode_capture
private val requestQrImage = 202
private var flashlightIsOpen: Boolean = false
override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) {
setSupportActionBar(toolbar)
zxingview.setDelegate(this)
fab_flashlight.setOnClickListener {
if (flashlightIsOpen) {
flashlightIsOpen = false
zxingview.closeFlashlight()
} else {
flashlightIsOpen = true
zxingview.openFlashlight()
}
}
}
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.qr_code_scan, menu)
return super.onCompatCreateOptionsMenu(menu)
}
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_choose_from_gallery -> {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
startActivityForResult(intent, requestQrImage)
}
}
return super.onCompatOptionsItemSelected(item)
}
override fun onStart() {
super.onStart()
startCamera()
}
private fun startCamera() {
PermissionsCompat.Builder(this)
.addPermissions(*Permissions.Group.CAMERA)
.rationale(R.string.qr_per)
.onGranted {
zxingview.startCamera() // 打开后置摄像头开始预览,但是并未开始识别
zxingview.startSpotAndShowRect() // 显示扫描框,并开始识别
}.build()
}
override fun onStop() {
zxingview.stopCamera() // 关闭摄像头预览,并且隐藏扫描框
super.onStop()
}
override fun onDestroy() {
zxingview.onDestroy() // 销毁二维码扫描控件
super.onDestroy()
}
override fun onScanQRCodeSuccess(result: String) {
val intent = Intent()
intent.putExtra("result", result)
setResult(RESULT_OK, intent)
finish()
}
override fun onCameraAmbientBrightnessChanged(isDark: Boolean) {
}
override fun onScanQRCodeOpenCameraError() {
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
data?.let {
zxingview.startSpotAndShowRect() // 显示扫描框,并开始识别
if (resultCode == Activity.RESULT_OK && requestCode == requestQrImage) {
val picturePath = FileUtils.getPath(this, it.data)
// 本来就用到 QRCodeView 时可直接调 QRCodeView 的方法,走通用的回调
zxingview.decodeQRCode(picturePath)
}
}
}
}

@ -1,5 +1,235 @@
package io.legado.app.utils package io.legado.app.utils
import android.annotation.TargetApi
import android.content.ContentUris
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment import android.os.Environment
import android.os.storage.StorageManager
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.util.Log
import androidx.core.content.ContextCompat
import java.io.File
import java.io.IOException
import java.lang.reflect.Array
import java.util.*
fun getSdPath() = Environment.getExternalStorageDirectory().absolutePath
object FileUtils {
fun getSdPath() = Environment.getExternalStorageDirectory().absolutePath
fun getFileByPath(filePath: String): File? {
return if (isSpace(filePath)) null else File(filePath)
}
fun isSpace(s: String?): Boolean {
if (s == null) return true
var i = 0
val len = s.length
while (i < len) {
if (!Character.isWhitespace(s[i])) {
return false
}
++i
}
return true
}
fun getSdCardPath(): String {
var sdCardDirectory = Environment.getExternalStorageDirectory().absolutePath
try {
sdCardDirectory = File(sdCardDirectory).canonicalPath
} catch (ioe: IOException) {
}
return sdCardDirectory
}
fun getStorageData(pContext: Context): ArrayList<String>? {
val storageManager = pContext.getSystemService(Context.STORAGE_SERVICE) as StorageManager
try {
val getVolumeList = storageManager.javaClass.getMethod("getVolumeList")
val storageValumeClazz = Class.forName("android.os.storage.StorageVolume")
val getPath = storageValumeClazz.getMethod("getPath")
val invokeVolumeList = getVolumeList.invoke(storageManager)
val length = Array.getLength(invokeVolumeList)
val list = ArrayList<String>()
for (i in 0 until length) {
val storageValume = Array.get(invokeVolumeList, i)//得到StorageVolume对象
val path = getPath.invoke(storageValume) as String
list.add(path)
}
return list
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
fun getExtSdCardPaths(con: Context): ArrayList<String> {
val paths = ArrayList<String>()
val files = ContextCompat.getExternalFilesDirs(con, "external")
val firstFile = files[0]
for (file in files) {
if (file != null && file != firstFile) {
val index = file.absolutePath.lastIndexOf("/Android/data")
if (index < 0) {
Log.w("", "Unexpected external file dir: " + file.absolutePath)
} else {
var path = file.absolutePath.substring(0, index)
try {
path = File(path).canonicalPath
} catch (e: IOException) {
// Keep non-canonical path.
}
paths.add(path)
}
}
}
return paths
}
@TargetApi(Build.VERSION_CODES.KITKAT)
fun getPath(context: Context, uri: Uri): String? {
val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
if ("primary".equals(type, ignoreCase = true)) {
return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
}
} else if (isDownloadsDocument(uri)) {
val id = DocumentsContract.getDocumentId(uri)
val split = id.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
if ("raw".equals(
type,
ignoreCase = true
)
) { //处理某些机型(比如Goole Pixel )ID是raw:/storage/emulated/0/Download/c20f8664da05ab6b4644913048ea8c83.mp4
return split[1]
}
val contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)
)
return getDataColumn(context, contentUri, null, null)
} else if (isMediaDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
var contentUri: Uri? = null
if ("image" == type) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
} else if ("video" == type) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
} else if ("audio" == type) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
val selection = "_id=?"
val selectionArgs = arrayOf(split[1])
return getDataColumn(context, contentUri, selection, selectionArgs)
}// MediaProvider
// DownloadsProvider
} else if ("content".equals(uri.scheme!!, ignoreCase = true)) {
// Return the remote address
return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(
context,
uri,
null,
null
)
} else if ("file".equals(uri.scheme!!, ignoreCase = true)) {
return uri.path
}// File
// MediaStore (and general)
return null
}
fun getDataColumn(
context: Context, uri: Uri?, selection: String?,
selectionArgs: kotlin.Array<String>?
): String? {
val column = "_data"
val projection = arrayOf(column)
try {
context.contentResolver.query(
uri!!,
projection,
selection,
selectionArgs,
null
)!!.use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
val index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(index)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
fun isExternalStorageDocument(uri: Uri): Boolean {
return "com.android.externalstorage.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
fun isDownloadsDocument(uri: Uri): Boolean {
return "com.android.providers.downloads.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
fun isMediaDocument(uri: Uri): Boolean {
return "com.android.providers.media.documents" == uri.authority
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
fun isGooglePhotosUri(uri: Uri): Boolean {
return "com.google.android.apps.photos.content" == uri.authority
}
}

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<cn.bingoogolapple.qrcode.zxing.ZXingView
android:id="@+id/zxingview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:qrcv_animTime="1000"
app:qrcv_barCodeTipText="将条码放入框内,即可自动扫描"
app:qrcv_barcodeRectHeight="120dp"
app:qrcv_borderColor="@android:color/white"
app:qrcv_borderSize="1dp"
app:qrcv_cornerColor="@color/colorPrimaryDark"
app:qrcv_cornerDisplayType="center"
app:qrcv_cornerLength="20dp"
app:qrcv_cornerSize="3dp"
app:qrcv_isAutoZoom="true"
app:qrcv_isBarcode="false"
app:qrcv_isOnlyDecodeScanBoxArea="false"
app:qrcv_isScanLineReverse="true"
app:qrcv_isShowDefaultGridScanLineDrawable="false"
app:qrcv_isShowDefaultScanLineDrawable="true"
app:qrcv_isShowLocationPoint="true"
app:qrcv_isShowTipBackground="true"
app:qrcv_isShowTipTextAsSingleLine="false"
app:qrcv_isTipTextBelowRect="false"
app:qrcv_maskColor="#33FFFFFF"
app:qrcv_qrCodeTipText="将二维码放入框内,即可自动扫描"
app:qrcv_rectWidth="300dp"
app:qrcv_scanLineColor="@color/colorPrimaryDark"
app:qrcv_scanLineMargin="0dp"
app:qrcv_scanLineSize="0.5dp"
app:qrcv_tipTextColor="@android:color/white"
app:qrcv_tipTextSize="12sp"
app:qrcv_toolbarHeight="?attr/actionBarSize"
app:qrcv_topOffset="65dp"
app:qrcv_verticalBias="-1" />
<io.legado.app.ui.widget.TitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/scan_qr_code" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:padding="16dp">
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_flashlight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="@string/read_aloud"
android:src="@drawable/ic_daytime"
android:tint="@color/tv_text_default"
app:backgroundTint="@color/background_menu"
app:elevation="2dp"
app:fabSize="mini"
app:pressedTranslationZ="2dp" />
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
</RelativeLayout>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_choose_from_gallery"
android:title="@string/gallery"
app:showAsAction="always" />
</menu>
Loading…
Cancel
Save