Skip to content

Commit ac021b8

Browse files
Android sample application in Kotlin
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.
1 parent 0bb7081 commit ac021b8

35 files changed

+1081
-0
lines changed

kotlin_sample/app/build.gradle

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
plugins {
2+
id 'com.android.application'
3+
id 'org.jetbrains.kotlin.android'
4+
}
5+
6+
android {
7+
namespace 'com.extrieve.quickcapture.docappkotlin'
8+
compileSdk 33
9+
10+
defaultConfig {
11+
applicationId "com.extrieve.quickcapture.docappkotlin"
12+
minSdk 21
13+
targetSdk 33
14+
versionCode 1
15+
versionName "1.0"
16+
17+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18+
}
19+
20+
buildTypes {
21+
release {
22+
minifyEnabled false
23+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24+
}
25+
}
26+
compileOptions {
27+
sourceCompatibility JavaVersion.VERSION_1_8
28+
targetCompatibility JavaVersion.VERSION_1_8
29+
}
30+
kotlinOptions {
31+
jvmTarget = '1.8'
32+
}
33+
}
34+
35+
dependencies {
36+
37+
implementation 'androidx.core:core-ktx:1.8.0'
38+
implementation 'androidx.appcompat:appcompat:1.6.1'
39+
implementation 'com.google.android.material:material:1.5.0'
40+
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
41+
implementation 'com.extrieve.quickcapture:QCv2:2.1.2'
42+
}

kotlin_sample/app/proguard-rules.pro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.extrieve.quickcapture.docappkotlin
2+
3+
import androidx.test.platform.app.InstrumentationRegistry
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
import org.junit.Assert.*
10+
11+
/**
12+
* Instrumented test, which will execute on an Android device.
13+
*
14+
* See [testing documentation](http://d.android.com/tools/testing).
15+
*/
16+
@RunWith(AndroidJUnit4::class)
17+
class ExampleInstrumentedTest {
18+
@Test
19+
fun useAppContext() {
20+
// Context of the app under test.
21+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22+
assertEquals("com.extrieve.quickcapture.docappkotlin", appContext.packageName)
23+
}
24+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools">
4+
5+
<application
6+
android:allowBackup="true"
7+
android:dataExtractionRules="@xml/data_extraction_rules"
8+
android:fullBackupContent="@xml/backup_rules"
9+
android:icon="@mipmap/ic_launcher"
10+
android:label="@string/app_name"
11+
android:roundIcon="@mipmap/ic_launcher_round"
12+
android:supportsRtl="true"
13+
android:theme="@style/Theme.DocAppKotlin"
14+
tools:targetApi="31">
15+
<activity
16+
android:name=".MainActivity"
17+
android:exported="true">
18+
<intent-filter>
19+
<action android:name="android.intent.action.MAIN" />
20+
21+
<category android:name="android.intent.category.LAUNCHER" />
22+
</intent-filter>
23+
</activity>
24+
</application>
25+
26+
</manifest>
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package com.extrieve.quickcapture.docappkotlin
2+
3+
import android.content.ContextWrapper
4+
import android.content.Intent
5+
import android.content.pm.PackageManager
6+
import android.graphics.BitmapFactory
7+
import android.net.Uri
8+
import android.os.Build
9+
import androidx.appcompat.app.AppCompatActivity
10+
import android.os.Bundle
11+
import android.util.Log
12+
import android.view.View
13+
import android.widget.ImageView
14+
import android.widget.Toast
15+
import androidx.activity.result.ActivityResult
16+
import androidx.activity.result.ActivityResultLauncher
17+
import androidx.activity.result.contract.ActivityResultContracts
18+
import androidx.core.app.ActivityCompat
19+
import androidx.core.content.ContextCompat
20+
import java.io.File
21+
import java.io.IOException
22+
23+
import com.extrieve.quickcapture.sdk.*;
24+
25+
class MainActivity : AppCompatActivity() {
26+
/*DEV_HELP : Declare variables for the classes from SDK*/
27+
private var cameraHelper: CameraHelper? = null
28+
private var imageHelper: ImgHelper? = null
29+
30+
/*DEV_HELP : Declare variables for ActivityResultLauncher to accept result from camera activity
31+
* As CameraHelper is an activity based class*/
32+
private var captureActivityResultLauncher: ActivityResultLauncher<Intent>? = null
33+
private val REQUIREDPERMISSIONS = arrayOf("android.permission.CAMERA")
34+
var fileCollection: ArrayList<String>? = null
35+
36+
override fun onCreate(savedInstanceState: Bundle?) {
37+
super.onCreate(savedInstanceState)
38+
setContentView(R.layout.activity_main)
39+
checkAndPromptPermissions()
40+
41+
/*DEV_HELP : Initialise object of ImgHelper class.Pass the current activity context*/
42+
imageHelper = ImgHelper(this)
43+
/*DEV_HELP : Initialise object CameraHelper*/
44+
cameraHelper = CameraHelper()
45+
46+
/*DEV_HELP : assign registerForActivityResult for getting result from CameraHelper*/
47+
captureActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
48+
result: ActivityResult -> handleCaptureActivityResult(result)
49+
}
50+
51+
/*DEV_HELP : Capture Document with SDK Button click handler*/
52+
findViewById<View>(R.id.getPictureButton).setOnClickListener {
53+
setConfig()
54+
openCameraActivity()
55+
}
56+
}
57+
58+
/*DEV_HELP : Basic permission for App/SDK to work*/
59+
private fun checkAndPromptPermissions() {
60+
for (permission in REQUIREDPERMISSIONS) {
61+
if (ContextCompat.checkSelfPermission(
62+
this,
63+
permission
64+
) != PackageManager.PERMISSION_GRANTED
65+
) {
66+
ActivityCompat.requestPermissions(
67+
this,
68+
REQUIREDPERMISSIONS,
69+
REQUEST_CODE_PERMISSIONS
70+
)
71+
}
72+
}
73+
}
74+
75+
override fun onRequestPermissionsResult(
76+
requestCode: Int,
77+
permissions: Array<String>,
78+
grantResults: IntArray
79+
) {
80+
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
81+
if (requestCode == REQUEST_CODE_PERMISSIONS) {
82+
for (permission in REQUIREDPERMISSIONS) {
83+
if (ContextCompat.checkSelfPermission(
84+
this,
85+
permission
86+
) != PackageManager.PERMISSION_GRANTED
87+
) {
88+
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT)
89+
.show()
90+
finish()
91+
}
92+
//Got permission
93+
}
94+
}
95+
}
96+
97+
/*DEV_HELP : SetUp SDKConfig - Refer tech. Doc. for further info.*/
98+
private fun setConfig() {
99+
imageHelper!!.SetPageLayout(4) //A1-A7(1-7),PHOTO,CUSTOM,ID(8,9,10)
100+
imageHelper!!.SetImageQuality(1) //0,1,2 - Photo_Quality, Document_Quality, Compressed_Document
101+
imageHelper!!.SetDPI(200) //int dpi_val = 100, 150, 200, 300, 500, 600;
102+
103+
//can set output file path
104+
CameraSupport.CamConfigClass.OutputPath = buildStoragePath()
105+
}
106+
107+
/*DEV_HELP : BuildStoragePath*/
108+
private fun buildStoragePath(): String {
109+
val c = ContextWrapper(this)
110+
return c.getExternalFilesDir(".GoNoGoImages")!!.absolutePath
111+
}
112+
113+
/*DEV_HELP : handleCaptureActivityResult definition*/
114+
private fun handleCaptureActivityResult(result: ActivityResult) {
115+
run {
116+
val resultCode = result.resultCode
117+
if (resultCode != RESULT_OK) {
118+
return
119+
}
120+
val data = result.data
121+
var status: Boolean? = null
122+
if (data != null) {
123+
status = data.extras!!["STATUS"] as Boolean?
124+
}
125+
val description = data!!.extras!!["DESCRIPTION"] as String?
126+
if (!status!!) {
127+
val imageCaptureLog = "Description : " + description +
128+
".Exception: " + CameraSupport.CamConfigClass.LastLogInfo
129+
Log.d("INFO", imageCaptureLog)
130+
Toast.makeText(this, imageCaptureLog, Toast.LENGTH_LONG).show()
131+
finishActivity(MainActivity.Companion.REQUEST_CODE_FILE_RETURN)
132+
return
133+
}
134+
fileCollection = data.extras!!["fileCollection"] as ArrayList<String>?
135+
if (fileCollection == null || fileCollection!!.isEmpty()) return
136+
try {
137+
showImages(fileCollection!!)
138+
} catch (e: IOException) {
139+
e.printStackTrace()
140+
}
141+
finishActivity(REQUEST_CODE_FILE_RETURN)
142+
}
143+
}
144+
145+
/*DEV_HELP : showImages*/
146+
@Throws(IOException::class)
147+
private fun showImages(FilesPath: ArrayList<String>) {
148+
val fileCollectionLength = FilesPath.size
149+
for (i in 0 until fileCollectionLength) {
150+
val dir = FilesPath[i]
151+
val imgFile = File(dir)
152+
//notifyMediaStoreScanner(imgFile);
153+
if (imgFile.exists()) {
154+
val myBitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
155+
val myImage = findViewById<ImageView>(R.id.displayImageView)
156+
myImage.setImageBitmap(myBitmap)
157+
}
158+
Toast.makeText(this, "SDK captured $fileCollectionLength images.", Toast.LENGTH_SHORT)
159+
.show()
160+
}
161+
}
162+
163+
/*DEV_HELP : OpenCameraActivity*/
164+
private fun openCameraActivity() {
165+
166+
/*DEV_HELP : Check basic permissions for camera if needed*/
167+
//if (!MainActivity.this.allPermissionsGranted()) {
168+
//Toast.makeText(MainActivity.this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show();
169+
/*DEV_HELP : TODO : handle invalid permission*/
170+
// return;
171+
// }
172+
try {
173+
/*DEV_HELP :redirecting to camera*/
174+
val captureIntent = Intent(this, Class.forName("com.extrieve.quickcapture.sdk.CameraHelper"))
175+
val photoURI = Uri.parse(CameraSupport.CamConfigClass.OutputPath)
176+
grantUriPermission(
177+
this.packageName, photoURI,
178+
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
179+
)
180+
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
181+
captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
182+
}
183+
captureActivityResultLauncher!!.launch(captureIntent)
184+
} catch (ex: Exception) {
185+
/*DEV_HELP : TODO : handle invalid Exception*/
186+
Toast.makeText(this, "Failed to open camera -" + ex.message, Toast.LENGTH_LONG).show()
187+
}
188+
}
189+
190+
companion object {
191+
private const val REQUEST_CODE_PERMISSIONS = 1001
192+
private const val REQUEST_CODE_FILE_RETURN = 1004
193+
}
194+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
xmlns:aapt="http://schemas.android.com/aapt"
3+
android:width="108dp"
4+
android:height="108dp"
5+
android:viewportWidth="108"
6+
android:viewportHeight="108">
7+
<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">
8+
<aapt:attr name="android:fillColor">
9+
<gradient
10+
android:endX="85.84757"
11+
android:endY="92.4963"
12+
android:startX="42.9492"
13+
android:startY="49.59793"
14+
android:type="linear">
15+
<item
16+
android:color="#44000000"
17+
android:offset="0.0" />
18+
<item
19+
android:color="#00000000"
20+
android:offset="1.0" />
21+
</gradient>
22+
</aapt:attr>
23+
</path>
24+
<path
25+
android:fillColor="#FFFFFF"
26+
android:fillType="nonZero"
27+
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"
28+
android:strokeWidth="1"
29+
android:strokeColor="#00000000" />
30+
</vector>

0 commit comments

Comments
 (0)