Skip to content

Commit

Permalink
- update to target SDK 33 (Android 13)
Browse files Browse the repository at this point in the history
- update libraries
- Crash on RxPermissions (#5). Fix: Change to kPermission
- Android app supported by 0 devices (#4) fixed
- remove Rx dependency
- remove deprecated startActivityForResult and use registerForActivityResult
- avoid crash if there is no point to add to image
- allow landscape
- capture images based on device orientation
  • Loading branch information
iulian-buzila committed Jun 16, 2023
1 parent 88ccd45 commit 74a10c9
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 127 deletions.
27 changes: 13 additions & 14 deletions DocumentScanner/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ apply plugin: 'com.github.dcendents.android-maven'

group="com.zynkware"

def libraryVersionCode = 5
def libraryVersionName = "1.0.1"
def libraryVersionCode = 6
def libraryVersionName = "1.1.0"

repositories {
mavenCentral()
Expand All @@ -15,12 +15,11 @@ repositories {
}

android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
compileSdkVersion 33

defaultConfig {
minSdkVersion 21
targetSdkVersion 29
targetSdkVersion 33
versionCode libraryVersionCode
versionName libraryVersionName

Expand Down Expand Up @@ -61,21 +60,21 @@ dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.0'

implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'

implementation 'com.github.zynkware:Tiny-OpenCV:4.4.0-4'

implementation "androidx.camera:camera-camera2:1.0.0"
implementation "androidx.camera:camera-lifecycle:1.0.0"
implementation "androidx.camera:camera-view:1.0.0-alpha24"

implementation 'com.github.tbruyelle:rxpermissions:0.12'
implementation "androidx.camera:camera-camera2:1.2.3"
implementation "androidx.camera:camera-lifecycle:1.2.3"
implementation "androidx.camera:camera-view:1.2.3"

implementation 'androidx.exifinterface:exifinterface:1.3.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3'
implementation 'com.github.fondesa:kpermissions:3.3.0'

implementation 'androidx.exifinterface:exifinterface:1.3.6'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
implementation 'id.zelory:compressor:3.0.1'
}

Expand Down
7 changes: 4 additions & 3 deletions DocumentScanner/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.zynksoftware.documentscanner">

<uses-feature android:name="android.hardware.camera2" />
<uses-feature android:name="android.hardware.camera2" android:required="false"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove"/>

<application
Expand All @@ -15,12 +16,12 @@

<activity
android:name=".ui.scan.InternalScanActivity"
android:screenOrientation="portrait"
android:exported="false"
android:theme="@style/ActivityTheme" />

<activity
android:name=".ScanActivity"
android:screenOrientation="portrait"
android:exported="false"
android:theme="@style/ActivityTheme" />

</application>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ package com.zynksoftware.documentscanner.ui.camerascreen
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.tbruyelle.rxpermissions3.RxPermissions
import androidx.activity.result.contract.ActivityResultContracts
import com.fondesa.kpermissions.allGranted
import com.fondesa.kpermissions.allShouldShowRationale
import com.fondesa.kpermissions.extension.permissionsBuilder
import com.fondesa.kpermissions.extension.send
import com.zynksoftware.documentscanner.R
import com.zynksoftware.documentscanner.common.extensions.hide
import com.zynksoftware.documentscanner.common.extensions.show
Expand All @@ -44,14 +49,41 @@ import java.io.FileNotFoundException
internal class CameraScreenFragment: BaseFragment(), ScanSurfaceListener {

companion object {
private const val GALLERY_REQUEST_CODE = 878
private val TAG = CameraScreenFragment::class.simpleName

fun newInstance(): CameraScreenFragment {
return CameraScreenFragment()
}
}

private var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
try {
val imageUri = result?.data?.data
if (imageUri != null) {
val realPath = FileUriUtils.getRealPath(getScanActivity(), imageUri)
if (realPath != null) {
getScanActivity().reInitOriginalImageFile()
getScanActivity().originalImageFile = File(realPath)
startCroppingProcess()
} else {
Log.e(TAG, DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR.error)
onError(DocumentScannerErrorModel(
DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR, null))
}
} else {
Log.e(TAG, DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR.error)
onError(DocumentScannerErrorModel(
DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR, null))
}
} catch (e: FileNotFoundException) {
Log.e(TAG, "FileNotFoundException", e)
onError(DocumentScannerErrorModel(
DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR, e))
}
}
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_camera_screen, container, false)
}
Expand Down Expand Up @@ -108,41 +140,41 @@ internal class CameraScreenFragment: BaseFragment(), ScanSurfaceListener {
}

private fun checkForCameraPermissions() {
RxPermissions(this)
.requestEach(Manifest.permission.CAMERA)
.subscribe { permission ->
when {
permission.granted -> {
startCamera()
}
permission.shouldShowRequestPermissionRationale -> {
onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.CAMERA_PERMISSION_REFUSED_WITHOUT_NEVER_ASK_AGAIN))
}
else -> {
onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.CAMERA_PERMISSION_REFUSED_GO_TO_SETTINGS))
}
permissionsBuilder(Manifest.permission.CAMERA)
.build()
.send { result ->
if (result.allGranted()) {
startCamera()
} else if(result.allShouldShowRationale()) {
onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.CAMERA_PERMISSION_REFUSED_WITHOUT_NEVER_ASK_AGAIN))
} else {
onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.CAMERA_PERMISSION_REFUSED_GO_TO_SETTINGS))
}
}
}

private fun checkForStoragePermissions() {
RxPermissions(this)
.requestEach(Manifest.permission.READ_EXTERNAL_STORAGE)
.subscribe { permission ->
when {
permission.granted -> {
selectImageFromGallery()
}
permission.shouldShowRequestPermissionRationale -> {
onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.STORAGE_PERMISSION_REFUSED_WITHOUT_NEVER_ASK_AGAIN))
}
else -> {
onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.STORAGE_PERMISSION_REFUSED_GO_TO_SETTINGS))
}
permissionsBuilder(getStoragePermission())
.build()
.send { result ->
if (result.allGranted()) {
selectImageFromGallery()
} else if (result.allShouldShowRationale()) {
onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.STORAGE_PERMISSION_REFUSED_WITHOUT_NEVER_ASK_AGAIN))
} else {
onError(DocumentScannerErrorModel(DocumentScannerErrorModel.ErrorMessage.STORAGE_PERMISSION_REFUSED_GO_TO_SETTINGS))
}
}
}

private fun getStoragePermission(): String {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
}

private fun startCamera() {
scanSurfaceView.start()
}
Expand Down Expand Up @@ -175,36 +207,7 @@ internal class CameraScreenFragment: BaseFragment(), ScanSurfaceListener {
val photoPickerIntent = Intent(Intent.ACTION_OPEN_DOCUMENT)
photoPickerIntent.addCategory(Intent.CATEGORY_OPENABLE)
photoPickerIntent.type = "image/*"
startActivityForResult(photoPickerIntent, GALLERY_REQUEST_CODE)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && requestCode == GALLERY_REQUEST_CODE) {
try {
val imageUri = data?.data
if (imageUri != null) {
val realPath = FileUriUtils.getRealPath(getScanActivity(), imageUri)
if (realPath != null) {
getScanActivity().reInitOriginalImageFile()
getScanActivity().originalImageFile = File(realPath)
startCroppingProcess()
} else {
Log.e(TAG, DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR.error)
onError(DocumentScannerErrorModel(
DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR, null))
}
} else {
Log.e(TAG, DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR.error)
onError(DocumentScannerErrorModel(
DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR, null))
}
} catch (e: FileNotFoundException) {
Log.e(TAG, "FileNotFoundException", e)
onError(DocumentScannerErrorModel(
DocumentScannerErrorModel.ErrorMessage.TAKE_IMAGE_FROM_GALLERY_ERROR, e))
}
}
resultLauncher.launch(photoPickerIntent)
}

override fun scanSurfacePictureTaken() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ internal class ScanCanvasView : FrameLayout {

private var shouldAnimate = true

var pointer1: View = View(context)
var pointer2: View = View(context)
var pointer3: View = View(context)
var pointer4: View = View(context)
private var pointer1: View = View(context)
private var pointer2: View = View(context)
private var pointer3: View = View(context)
private var pointer4: View = View(context)

init {
paint.color = ContextCompat.getColor(context, R.color.zdc_white_transparent)
Expand Down Expand Up @@ -114,8 +114,8 @@ internal class ScanCanvasView : FrameLayout {
}
}

var previewWidth: Float? = null
var previewHeight: Float? = null
private var previewWidth: Float? = null
private var previewHeight: Float? = null

fun showShape(previewWidth: Float, previewHeight: Float, points: Array<Point>) {
this.previewWidth = previewWidth
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PointF
import android.util.AttributeSet
import android.util.Log
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
Expand Down Expand Up @@ -133,17 +134,22 @@ internal class PolygonView @JvmOverloads constructor(
}

private fun setPointsCoordinates(pointFMap: Map<Int, PointF>) {
pointer1.x = pointFMap.getValue(0).x - pointPadding
pointer1.y = pointFMap.getValue(0).y - pointPadding
try {
pointer1.x = pointFMap.getValue(0).x - pointPadding
pointer1.y = pointFMap.getValue(0).y - pointPadding

pointer2.x = pointFMap.getValue(1).x - pointPadding
pointer2.y = pointFMap.getValue(1).y - pointPadding
pointer2.x = pointFMap.getValue(1).x - pointPadding
pointer2.y = pointFMap.getValue(1).y - pointPadding

pointer3.x = pointFMap.getValue(2).x - pointPadding
pointer3.y = pointFMap.getValue(2).y - pointPadding
pointer3.x = pointFMap.getValue(2).x - pointPadding
pointer3.y = pointFMap.getValue(2).y - pointPadding

pointer4.x = pointFMap.getValue(3).x - pointPadding
pointer4.y = pointFMap.getValue(3).y - pointPadding
pointer4.x = pointFMap.getValue(3).x - pointPadding
pointer4.y = pointFMap.getValue(3).y - pointPadding
} catch (exception: NoSuchElementException) {
// avoid crash if there is no point to add to image
Log.e(TAG, "", exception)
}
}

override fun dispatchDraw(canvas: Canvas) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import android.os.CountDownTimer
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.Surface
import android.widget.FrameLayout
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
Expand Down Expand Up @@ -100,7 +99,7 @@ internal class ScanSurfaceView : FrameLayout {
private fun openCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)

cameraProviderFuture.addListener(Runnable {
cameraProviderFuture.addListener({
cameraProvider = cameraProviderFuture.get()

try {
Expand All @@ -126,7 +125,6 @@ internal class ScanSurfaceView : FrameLayout {

imageCapture = null
imageCapture = ImageCapture.Builder()
.setTargetRotation(Surface.ROTATION_0)
.setFlashMode(flashMode)
.build()
}
Expand All @@ -138,7 +136,6 @@ internal class ScanSurfaceView : FrameLayout {
private fun setUseCases() {
preview = Preview.Builder()
.setTargetResolution(previewSize)
.setTargetRotation(Surface.ROTATION_0)
.build()
.also {
it.setSurfaceProvider(viewFinder.surfaceProvider)
Expand All @@ -153,10 +150,9 @@ internal class ScanSurfaceView : FrameLayout {
imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setTargetResolution(android.util.Size(width, height))
.setTargetRotation(Surface.ROTATION_0)
.build()

imageAnalysis?.setAnalyzer(ContextCompat.getMainExecutor(context), { image ->
imageAnalysis?.setAnalyzer(ContextCompat.getMainExecutor(context)) { image ->
if (isAutoCaptureOn) {
try {
val mat = image.yuvToRgba()
Expand All @@ -177,7 +173,7 @@ internal class ScanSurfaceView : FrameLayout {
clearAndInvalidateCanvas()
}
image.close()
})
}

camera = cameraProvider!!.bindToLifecycle(lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageAnalysis, imageCapture)
}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ All errors are returned to app using `fun onError(error: DocumentScannerErrorMod
## Thanks
* [OpenCV](https://opencv.org)
* [Compressor](https://github.com/zetbaitsu/Compressor)
* [RxPermissions](https://github.com/tbruyelle/RxPermissions)
* [kPermissions](https://github.com/fondesa/kpermissions)
* Inspiration from [mayuce](https://github.com/mayuce/AndroidDocumentScanner), [adityaarora1](https://github.com/adityaarora1/LiveEdgeDetection) and [hannesa2](https://github.com/hannesa2/LiveEdgeDetection)


Expand Down
Loading

0 comments on commit 74a10c9

Please sign in to comment.