Skip to content

Commit

Permalink
Android sample application in Kotlin
Browse files Browse the repository at this point in the history
Android sample application in Kotlin with quickcapture document scanning sdk implementation.With full help doc and technical documentation for developers for easier implementation and try.
  • Loading branch information
Amalkarunakaran authored Jun 19, 2023
1 parent 0bb7081 commit ac021b8
Show file tree
Hide file tree
Showing 35 changed files with 1,081 additions and 0 deletions.
42 changes: 42 additions & 0 deletions kotlin_sample/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}

android {
namespace 'com.extrieve.quickcapture.docappkotlin'
compileSdk 33

defaultConfig {
applicationId "com.extrieve.quickcapture.docappkotlin"
minSdk 21
targetSdk 33
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}

dependencies {

implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.extrieve.quickcapture:QCv2:2.1.2'
}
21 changes: 21 additions & 0 deletions kotlin_sample/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.extrieve.quickcapture.docappkotlin

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.extrieve.quickcapture.docappkotlin", appContext.packageName)
}
}
26 changes: 26 additions & 0 deletions kotlin_sample/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.DocAppKotlin"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package com.extrieve.quickcapture.docappkotlin

import android.content.ContextWrapper
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
import android.widget.Toast
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import java.io.File
import java.io.IOException

import com.extrieve.quickcapture.sdk.*;

class MainActivity : AppCompatActivity() {
/*DEV_HELP : Declare variables for the classes from SDK*/
private var cameraHelper: CameraHelper? = null
private var imageHelper: ImgHelper? = null

/*DEV_HELP : Declare variables for ActivityResultLauncher to accept result from camera activity
* As CameraHelper is an activity based class*/
private var captureActivityResultLauncher: ActivityResultLauncher<Intent>? = null
private val REQUIREDPERMISSIONS = arrayOf("android.permission.CAMERA")
var fileCollection: ArrayList<String>? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
checkAndPromptPermissions()

/*DEV_HELP : Initialise object of ImgHelper class.Pass the current activity context*/
imageHelper = ImgHelper(this)
/*DEV_HELP : Initialise object CameraHelper*/
cameraHelper = CameraHelper()

/*DEV_HELP : assign registerForActivityResult for getting result from CameraHelper*/
captureActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
result: ActivityResult -> handleCaptureActivityResult(result)
}

/*DEV_HELP : Capture Document with SDK Button click handler*/
findViewById<View>(R.id.getPictureButton).setOnClickListener {
setConfig()
openCameraActivity()
}
}

/*DEV_HELP : Basic permission for App/SDK to work*/
private fun checkAndPromptPermissions() {
for (permission in REQUIREDPERMISSIONS) {
if (ContextCompat.checkSelfPermission(
this,
permission
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
REQUIREDPERMISSIONS,
REQUEST_CODE_PERMISSIONS
)
}
}
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
for (permission in REQUIREDPERMISSIONS) {
if (ContextCompat.checkSelfPermission(
this,
permission
) != PackageManager.PERMISSION_GRANTED
) {
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT)
.show()
finish()
}
//Got permission
}
}
}

/*DEV_HELP : SetUp SDKConfig - Refer tech. Doc. for further info.*/
private fun setConfig() {
imageHelper!!.SetPageLayout(4) //A1-A7(1-7),PHOTO,CUSTOM,ID(8,9,10)
imageHelper!!.SetImageQuality(1) //0,1,2 - Photo_Quality, Document_Quality, Compressed_Document
imageHelper!!.SetDPI(200) //int dpi_val = 100, 150, 200, 300, 500, 600;

//can set output file path
CameraSupport.CamConfigClass.OutputPath = buildStoragePath()
}

/*DEV_HELP : BuildStoragePath*/
private fun buildStoragePath(): String {
val c = ContextWrapper(this)
return c.getExternalFilesDir(".GoNoGoImages")!!.absolutePath
}

/*DEV_HELP : handleCaptureActivityResult definition*/
private fun handleCaptureActivityResult(result: ActivityResult) {
run {
val resultCode = result.resultCode
if (resultCode != RESULT_OK) {
return
}
val data = result.data
var status: Boolean? = null
if (data != null) {
status = data.extras!!["STATUS"] as Boolean?
}
val description = data!!.extras!!["DESCRIPTION"] as String?
if (!status!!) {
val imageCaptureLog = "Description : " + description +
".Exception: " + CameraSupport.CamConfigClass.LastLogInfo
Log.d("INFO", imageCaptureLog)
Toast.makeText(this, imageCaptureLog, Toast.LENGTH_LONG).show()
finishActivity(MainActivity.Companion.REQUEST_CODE_FILE_RETURN)
return
}
fileCollection = data.extras!!["fileCollection"] as ArrayList<String>?
if (fileCollection == null || fileCollection!!.isEmpty()) return
try {
showImages(fileCollection!!)
} catch (e: IOException) {
e.printStackTrace()
}
finishActivity(REQUEST_CODE_FILE_RETURN)
}
}

/*DEV_HELP : showImages*/
@Throws(IOException::class)
private fun showImages(FilesPath: ArrayList<String>) {
val fileCollectionLength = FilesPath.size
for (i in 0 until fileCollectionLength) {
val dir = FilesPath[i]
val imgFile = File(dir)
//notifyMediaStoreScanner(imgFile);
if (imgFile.exists()) {
val myBitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
val myImage = findViewById<ImageView>(R.id.displayImageView)
myImage.setImageBitmap(myBitmap)
}
Toast.makeText(this, "SDK captured $fileCollectionLength images.", Toast.LENGTH_SHORT)
.show()
}
}

/*DEV_HELP : OpenCameraActivity*/
private fun openCameraActivity() {

/*DEV_HELP : Check basic permissions for camera if needed*/
//if (!MainActivity.this.allPermissionsGranted()) {
//Toast.makeText(MainActivity.this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show();
/*DEV_HELP : TODO : handle invalid permission*/
// return;
// }
try {
/*DEV_HELP :redirecting to camera*/
val captureIntent = Intent(this, Class.forName("com.extrieve.quickcapture.sdk.CameraHelper"))
val photoURI = Uri.parse(CameraSupport.CamConfigClass.OutputPath)
grantUriPermission(
this.packageName, photoURI,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
captureActivityResultLauncher!!.launch(captureIntent)
} catch (ex: Exception) {
/*DEV_HELP : TODO : handle invalid Exception*/
Toast.makeText(this, "Failed to open camera -" + ex.message, Toast.LENGTH_LONG).show()
}
}

companion object {
private const val REQUEST_CODE_PERMISSIONS = 1001
private const val REQUEST_CODE_FILE_RETURN = 1004
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
Loading

0 comments on commit ac021b8

Please sign in to comment.