├── README.md
├── SmartIDReader-Android-SDK.iml
├── app
├── app.iml
├── build.gradle
├── libs
│ ├── jniSmartIdEngineJar.jar
│ └── native-libs.jar
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ └── data
│ │ └── bundle_mock_smart_idreader.zip
│ ├── java
│ └── biz
│ │ └── smartengines
│ │ └── smartid
│ │ └── demo
│ │ ├── CameraView.java
│ │ ├── ElementsView.java
│ │ ├── FieldData.java
│ │ ├── FieldsSingleton.java
│ │ └── ResultView.java
│ └── res
│ ├── layout
│ ├── activity_camera_view.xml
│ ├── activity_result_view.xml
│ └── result_row_layout.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── doc
├── DocumentsReference.html
├── DocumentsReference.md
├── README.md
├── SmartIDReaderSDK.html
├── SmartIDReaderSDK.md
└── smartIdEngine.pdf
├── gradle.properties
└── settings.gradle
/README.md:
--------------------------------------------------------------------------------
1 | # Smart IDReader Android SDK - Demo version
2 |
3 | This is a **DEMO** version of [Smart IDReader](https://smartengines.com/smart-id-reader/) Android SDK by [Smart Engines](https://smartengines.com) which demonstrates the usage of Smart IDReader library/SDK without actually providing any recognition functionality.
4 | Instead, it outputs fake results for document search, field segmentation, recognition and photo image extraction.
5 |
6 | Simply open `SmartIDReader-Android-SDK.iml` with Android Studio and run it to see the working example.
7 | You are free to change the code however you want.
8 |
9 | More documentation is available in [doc](doc) directory.
10 |
11 | Free demonstrational applications with full functionality are available at [Google Play](https://play.google.com/store/apps/details?id=biz.smartengines.smartid) and [App Store](https://itunes.apple.com/app/smart-idreader/id1157877082).
12 |
13 | If you'd like to obtain a trial or full version of Smart IDReader please contact us via:
14 | * support@smartengines.com
15 | * https://smartengines.com/contacts
16 | * https://smartengines.ru/contacts
17 |
18 | Test and trial/full versions are only different in static library + configuration files so you wouldn't have to rewrite any code after you're finished integrating Smart IDReader SDK into your application.
19 |
20 | ## Smart IDReader overview
21 |
22 | Smart IDReader technology allows you to recognize identity and property rights documents while using video/photo cameras and scanners in mobile, desktop, server and terminal solutions. With this tecnhology you only need to present the document to the camera to let Smart IDReader recognize all required data in 1-3 seconds and then fill them in any file, form or a work sheet.
23 |
24 | Key features:
25 | * Real time document recognition in video stream on mobile devices
26 | * Recognition of documents in various lighting conditions
27 | * White label license
28 | * Security: only device RAM is used, no personal data is being copied or sent over the internet (e.g. for processing on servers)
29 |
30 | Supported platforms: iOS, Android, Windows, Linux, MacOS, Solaris and others
31 | Supported programming languages: C++, C, C#, Objective-C, Java, Visual Basic and others
32 | Supported architectures: armv7-v8, aarch64, x86, x86_64, SPARC, E2K and others
33 |
--------------------------------------------------------------------------------
/SmartIDReader-Android-SDK.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion '25.0.0'
6 | defaultConfig {
7 | applicationId 'biz.smartengines.smartid.demo'
8 | minSdkVersion 14
9 | targetSdkVersion 23
10 | versionCode 7
11 | versionName '1.1.0'
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | productFlavors {
20 | }
21 | }
22 |
23 | dependencies {
24 | compile fileTree(include: ['*.jar'], dir: 'libs')
25 | compile 'com.android.support:appcompat-v7:23.4.0'
26 | }
27 |
--------------------------------------------------------------------------------
/app/libs/jniSmartIdEngineJar.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartEngines/SmartIDReader-Android-SDK/f423e039a9b0358721fea1b8a36961bee7a6a727/app/libs/jniSmartIdEngineJar.jar
--------------------------------------------------------------------------------
/app/libs/native-libs.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartEngines/SmartIDReader-Android-SDK/f423e039a9b0358721fea1b8a36961bee7a6a727/app/libs/native-libs.jar
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/assets/data/bundle_mock_smart_idreader.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartEngines/SmartIDReader-Android-SDK/f423e039a9b0358721fea1b8a36961bee7a6a727/app/src/main/assets/data/bundle_mock_smart_idreader.zip
--------------------------------------------------------------------------------
/app/src/main/java/biz/smartengines/smartid/demo/CameraView.java:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (c) 2012-2017, Smart Engines Ltd
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without modification,
6 | are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice,
9 | this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the Smart Engines Ltd nor the names of its
14 | contributors may be used to endorse or promote products derived from this
15 | software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package biz.smartengines.smartid.demo;
30 |
31 | import android.Manifest;
32 | import android.content.Intent;
33 | import android.content.SharedPreferences;
34 | import android.content.pm.PackageManager;
35 | import android.content.res.AssetManager;
36 | import android.graphics.Bitmap;
37 | import android.graphics.Color;
38 | import android.hardware.Camera;
39 | import android.os.AsyncTask;
40 | import android.os.Build;
41 | import android.os.Bundle;
42 | import android.preference.PreferenceManager;
43 | import android.support.v4.app.ActivityCompat;
44 | import android.support.v4.content.ContextCompat;
45 | import android.support.v7.app.AppCompatActivity;
46 | import android.util.DisplayMetrics;
47 | import android.util.Log;
48 | import android.view.SurfaceHolder;
49 | import android.view.SurfaceView;
50 | import android.view.View;
51 | import android.widget.ImageButton;
52 | import android.widget.RelativeLayout;
53 | import android.widget.Toast;
54 | import java.io.File;
55 | import java.io.FileOutputStream;
56 | import java.io.IOException;
57 | import java.io.InputStream;
58 | import java.io.OutputStream;
59 | import java.util.ArrayList;
60 | import java.util.List;
61 | import java.util.Timer;
62 | import java.util.TimerTask;
63 | import java.util.concurrent.Semaphore;
64 |
65 | import biz.smartengines.smartid.swig.Image;
66 | import biz.smartengines.smartid.swig.ImageField;
67 | import biz.smartengines.smartid.swig.ImageOrientation;
68 | import biz.smartengines.smartid.swig.MatchResultVector;
69 | import biz.smartengines.smartid.swig.Quadrangle;
70 | import biz.smartengines.smartid.swig.RecognitionEngine;
71 | import biz.smartengines.smartid.swig.RecognitionResult;
72 | import biz.smartengines.smartid.swig.RecognitionSession;
73 | import biz.smartengines.smartid.swig.SegmentationResult;
74 | import biz.smartengines.smartid.swig.SegmentationResultVector;
75 | import biz.smartengines.smartid.swig.SessionSettings;
76 | import biz.smartengines.smartid.swig.StringField;
77 | import biz.smartengines.smartid.swig.StringVector;
78 |
79 |
80 | public class CameraView extends AppCompatActivity implements SurfaceHolder.Callback, Camera.PreviewCallback, View.OnClickListener {
81 |
82 | private static final String module_log = "SMARTID_DEMO_APP"; // log name
83 |
84 | private static final int REQUEST_CAMERA = 1; // id for request camera permission
85 | private static boolean camera_ready = false; // is camera ready to work
86 | private android.hardware.Camera camera = null; // camera class
87 | private SurfaceHolder holder; // holder for camera preview
88 |
89 | private Timer timer;
90 | private boolean use_timer = false;
91 | private final int timer_delay = 1000; // focusing timer start delay
92 | private final int timer_period = 3000; // focusing timer period
93 |
94 | private ElementsView view; // visualisation class during wokr process
95 |
96 | private static RecognitionEngine engine; // engine class
97 | private static boolean is_configured = false; // is engine configured
98 | private static boolean need_copy_assets = false; // need to extract working files from assets
99 |
100 | private static String bundle_name = "bundle_mock_smart_idreader.zip"; // name of bundle
101 | private static String document_mask = "*"; // document mask, for example card.* mrz.* rus.passport.*
102 |
103 | private static SessionSettings sessionSettings; // settings for recognition
104 | private static RecognitionSession session; // recognition session
105 | private static boolean session_working = false; // is session working for the moment
106 |
107 | private boolean is_nexus_5x = Build.MODEL.contains("Nexus 5X"); // the phone is Nexus 5X
108 |
109 | private static volatile byte[] data = null; // current frame buffer
110 |
111 | private Semaphore frame_waiting; // semaphore waiting for the frame
112 | private Semaphore frame_ready; // semaphore the frame is ready
113 |
114 | //----------------------------------------------------------------------------------------------
115 |
116 | private class Focus extends TimerTask { // focusing timer
117 |
118 | public void run() {
119 |
120 | focusing();
121 | }
122 | }
123 |
124 | public void focusing() {
125 |
126 | try{
127 | Camera.Parameters cparams = camera.getParameters();
128 |
129 | if( cparams.getMaxNumFocusAreas() > 0) // focus if at least one focus area exists
130 | {
131 | camera.cancelAutoFocus();
132 | cparams.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
133 | camera.setParameters(cparams);
134 | }
135 | }catch(RuntimeException e)
136 | {
137 | Log.d(module_log, "Cannot focus: " + e.getMessage());
138 | }
139 | }
140 |
141 | private View.OnClickListener onFocus = new View.OnClickListener() {
142 |
143 | public void onClick(View v) {
144 |
145 | if (use_timer == false) // focus on tap while not using timer focusing
146 | focusing();
147 | }
148 | };
149 |
150 | //----------------------------------------------------------------------------------------------
151 |
152 | class InitCore extends AsyncTask {
153 |
154 | @Override
155 | protected Void doInBackground(Void... unused) {
156 |
157 | if (need_copy_assets) // extract bundle file from assets
158 | copyAssets("data");
159 |
160 | configureEngine(); // configure engine
161 | return null;
162 | }
163 |
164 | @Override
165 | protected void onPostExecute(Void aVoid) {
166 | super.onPostExecute(aVoid);
167 |
168 | if(is_configured)
169 | {
170 | setDocumentsList(); // set document list
171 | view.invalidate(); // update view
172 | }
173 | }
174 | }
175 |
176 | private void configureEngine() {
177 |
178 | try {
179 | System.loadLibrary("jniSmartIdEngine"); // load library
180 | String bundle_path = getFilesDir().getAbsolutePath() + File.separator + bundle_name; // full path to bundle
181 |
182 | engine = new RecognitionEngine(bundle_path); // create engine
183 | sessionSettings = engine.CreateSessionSettings(); // create setting for engine
184 | is_configured = true;
185 |
186 | } catch (RuntimeException e) {
187 | Log.d(module_log, "Cannot init engine: " + e.getMessage());
188 | }
189 | catch(UnsatisfiedLinkError e) {
190 | Log.d(module_log, "Cannot load library: " + e.getMessage());
191 | }
192 | }
193 |
194 | void copyAssets(String bundle_dir) { // copy file from start directory bundle_dir
195 |
196 | try {
197 | AssetManager assetManager = getAssets();
198 | final String input_bundle_path = bundle_dir + File.separator + bundle_name;
199 | final String output_bundle_dir = getFilesDir().getAbsolutePath() + File.separator;
200 |
201 | InputStream input_stream = assetManager.open(input_bundle_path);
202 | File output_bundle_file = new File(output_bundle_dir, bundle_name);
203 | OutputStream output_stream = new FileOutputStream(output_bundle_file);
204 |
205 | int length = 0;
206 | byte[] buffer = new byte[1024];
207 |
208 | while ((length = input_stream.read(buffer)) > 0) {
209 |
210 | output_stream.write(buffer, 0, length);
211 | }
212 |
213 | input_stream.close();
214 | output_stream.close();
215 |
216 | } catch (IOException e) {
217 | Log.d(module_log, "Cannot copy bundle: " + e.getMessage());
218 | }
219 | }
220 |
221 | void setDocumentsList()
222 | {
223 | ArrayList doc_types = new ArrayList<>();
224 |
225 | try {
226 | sessionSettings.RemoveEnabledDocumentTypes("*"); // disable all documents
227 | sessionSettings.AddEnabledDocumentTypes(document_mask); // enable document list by mask
228 |
229 | StringVector document_types = sessionSettings.GetEnabledDocumentTypes(); // get full names of enabled documents
230 |
231 | for (int i = 0; i < document_types.size(); i++)
232 | doc_types.add(document_types.get(i));
233 |
234 | } catch (RuntimeException e) {
235 | Log.d(module_log, "Cannot set document types: " + e.getMessage());
236 | }
237 |
238 | view.SetDocumentsTypes(doc_types); // show document list
239 | }
240 |
241 | //----------------------------------------------------------------------------------------------
242 |
243 | @Override
244 | protected void onCreate(Bundle savedInstanceState) {
245 | super.onCreate(savedInstanceState);
246 | setContentView(R.layout.activity_camera_view);
247 |
248 | checkParameters(); // check parameters before start
249 |
250 | SurfaceView surface = (SurfaceView) findViewById(R.id.preview);
251 | surface.setOnClickListener(onFocus);
252 |
253 | holder = surface.getHolder();
254 | holder.addCallback(this);
255 | holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // set for compatibility with old android versions
256 |
257 | view = new ElementsView(this); // layout to show document frames during process
258 |
259 | RelativeLayout layout = (RelativeLayout) findViewById(R.id.layout);
260 | layout.addView(view);
261 |
262 | ImageButton start = (ImageButton) findViewById(R.id.start);
263 | start.setOnClickListener(this);
264 |
265 | if (!is_configured) {
266 | InitCore init = new InitCore(); // init core in different thread
267 | init.execute();
268 | }
269 | }
270 |
271 | @Override
272 | public void surfaceCreated(SurfaceHolder holder_) {
273 |
274 | if( camera_ready == false && needPermission(Manifest.permission.CAMERA) == true ) // request camera access permission if needed
275 | requestPermission(Manifest.permission.CAMERA, REQUEST_CAMERA);
276 | else
277 | initCamera();
278 | }
279 |
280 | @Override
281 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
282 |
283 | }
284 |
285 | @Override
286 | public void surfaceDestroyed(SurfaceHolder holder) {
287 |
288 | if(session_working == true)
289 | {
290 | stop_session(); // stop session
291 | }
292 |
293 | if(use_timer == true) // stop focus timer
294 | {
295 | timer.cancel();
296 | use_timer = false;
297 | }
298 |
299 | if(camera_ready == true) // stop preview
300 | {
301 | camera.stopPreview();
302 | camera.release();
303 |
304 | camera = null;
305 | camera_ready = false;
306 | }
307 | }
308 |
309 | @Override
310 | public void onClick(View v)
311 | {
312 | if (v.getId() == R.id.start) {
313 | start_session();
314 | }
315 | }
316 |
317 | protected void checkParameters()
318 | {
319 | int version_code = BuildConfig.VERSION_CODE; // application code version
320 |
321 | SharedPreferences sPref = PreferenceManager.getDefaultSharedPreferences(this);
322 | int version_current = sPref.getInt("version_code", -1); // code version from previous start
323 |
324 | need_copy_assets = version_code != version_current; // update bundle if needed
325 |
326 | SharedPreferences.Editor ed = sPref.edit(); // update version in preferences
327 | ed.putInt("version_code", version_code);
328 | ed.commit();
329 | }
330 |
331 | //----------------------------------------------------------------------------------------------
332 |
333 | public boolean needPermission(String permission)
334 | {
335 | int result = ContextCompat.checkSelfPermission(this, permission); // check if permission is granted
336 | return result != PackageManager.PERMISSION_GRANTED;
337 | }
338 |
339 | public void requestPermission(String permission, int request_code)
340 | {
341 | ActivityCompat.requestPermissions(this, new String[]{permission}, request_code); // ask for permission
342 | }
343 |
344 | @Override
345 | public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)
346 | {
347 | switch (requestCode) {
348 | case REQUEST_CAMERA: {
349 |
350 | boolean is_granted = false;
351 |
352 | for(int grantResult : grantResults)
353 | {
354 | if(grantResult == PackageManager.PERMISSION_GRANTED) // permission granted
355 | is_granted = true;
356 | }
357 |
358 | if (is_granted)
359 | initCamera();
360 | else
361 | toast("To continue please enable CAMERA permission in App Settings");
362 | }
363 | default:
364 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
365 | }
366 | }
367 |
368 | public void initCamera()
369 | {
370 | try {
371 | if(camera == null)
372 | camera = Camera.open(); // open camera
373 |
374 | if (camera != null) {
375 |
376 | setCameraParams(); // set camera parameters
377 | camera.setPreviewDisplay(holder); // set preview surface holder
378 | camera.startPreview(); // start preview
379 | camera_ready = true;
380 | }
381 |
382 | } catch (IOException | RuntimeException e)
383 | {
384 | Log.d(module_log, "Cannot init camera: " + e.getMessage());
385 | }
386 | }
387 |
388 | private void setCameraParams()
389 | {
390 | Camera.Parameters params = camera.getParameters();
391 |
392 | List focus_modes = params.getSupportedFocusModes(); // supported focus modes
393 | String focus_mode = Camera.Parameters.FOCUS_MODE_AUTO;
394 | boolean isAutoFocus = focus_modes.contains(focus_mode);
395 |
396 | if (isAutoFocus) { // camera can autofocus
397 |
398 | if (focus_modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
399 | focus_mode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
400 | else if (focus_modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
401 | focus_mode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
402 | } else {
403 | focus_mode = focus_modes.get(0); // camera doesn't support autofocus so select the first mode
404 | }
405 |
406 | DisplayMetrics metrics = new DisplayMetrics();
407 | getWindowManager().getDefaultDisplay().getMetrics(metrics);
408 |
409 | float display_ratio = (float)metrics.heightPixels / (float)metrics.widthPixels; // display ratio
410 |
411 | List sizes = params.getSupportedPreviewSizes();
412 | Camera.Size preview_size = sizes.get(0);
413 |
414 | final float tolerance = 0.1f;
415 | float preview_ratio_diff = Math.abs( (float) preview_size.width / (float) preview_size.height - display_ratio);
416 |
417 | for (int i = 1; i < sizes.size() ; i++) // select the best preview size that fits to screen
418 | {
419 | Camera.Size tmp_size = sizes.get(i);
420 | float tmp_ratio_diff = Math.abs( (float) tmp_size.width / (float) tmp_size.height - display_ratio);
421 |
422 | if( Math.abs(tmp_ratio_diff - preview_ratio_diff) < tolerance && tmp_size.width > preview_size.width || tmp_ratio_diff < preview_ratio_diff)
423 | {
424 | preview_size = tmp_size;
425 | preview_ratio_diff = tmp_ratio_diff;
426 | }
427 | }
428 |
429 | params.setFocusMode(focus_mode); // set focus mode
430 | params.setPreviewSize(preview_size.width, preview_size.height); // set preview size
431 | camera.setParameters(params);
432 | camera.setDisplayOrientation(!is_nexus_5x ? 90 : 270); // set portrait orientation
433 |
434 | if (focus_mode == Camera.Parameters.FOCUS_MODE_AUTO) // start timer focusing only if no hardware continuous auto focus and device supports auto focus
435 | {
436 | timer = new Timer();
437 | timer.schedule(new Focus(), timer_delay, timer_period);
438 | use_timer = true;
439 | }
440 |
441 | view.SetPreviewSize(preview_size.height, preview_size.width);
442 | }
443 |
444 | //----------------------------------------------------------------------------------------------
445 |
446 | @Override
447 | public void onPreviewFrame(byte[] data_, Camera camera) // get current camera frame
448 | {
449 | if(frame_waiting.tryAcquire() && session_working) // if frame waiting status - get current frame
450 | {
451 | data = data_;
452 | frame_ready.release(); // frame is ready
453 | }
454 | }
455 |
456 |
457 | void start_session()
458 | {
459 | if (is_configured == true && camera_ready == true) {
460 |
461 | try {
462 |
463 | sessionSettings.SetOption("common.sessionTimeout", "5.0"); // set session timeout
464 | session = engine.SpawnSession(sessionSettings);
465 |
466 | camera.setPreviewCallback(this); // set preview callback onPreviewFrame(...)
467 | session_working = true;
468 |
469 | frame_waiting = new Semaphore(1, true); // create semaphores
470 | frame_ready = new Semaphore(0, true);
471 |
472 | view.Start();
473 |
474 | new EngineTask().execute(); // start recognition thread
475 | view.invalidate();
476 |
477 |
478 | } catch (RuntimeException e) {
479 | Log.d(module_log, "Cannot init session with: " + document_mask);
480 | return;
481 | }
482 |
483 | ImageButton start = (ImageButton) findViewById(R.id.start); // make start button invisible
484 | start.setVisibility(View.INVISIBLE);
485 | }
486 | }
487 |
488 | void stop_session()
489 | {
490 | session_working = false;
491 | data = null;
492 |
493 | frame_waiting.release(); // release semaphores
494 | frame_ready.release();
495 |
496 | camera.setPreviewCallback(null); // stop preview
497 |
498 | view.Stop();
499 | view.invalidate();
500 |
501 | ImageButton start = (ImageButton) findViewById(R.id.start); // make start button visible
502 | start.setVisibility(View.VISIBLE);
503 | }
504 |
505 | //----------------------------------------------------------------------------------------------
506 |
507 | void show_result(RecognitionResult result)
508 | {
509 | Intent intent = new Intent(CameraView.this, ResultView.class);
510 | ArrayList fields = new ArrayList();
511 |
512 | StringVector texts = result.GetStringFieldNames(); // get all string results
513 | StringVector images = result.GetImageFieldNames(); // get all image results
514 |
515 | for (int i = 0; i < texts.size(); i++) // get text fields
516 | {
517 | StringField field = result.GetStringField(texts.get(i)); // field class
518 | String value = field.GetUtf8Value(); // value
519 | boolean is_accepted = field.IsAccepted(); // is accepted
520 |
521 | fields.add(new FieldData(field.GetName(), value, is_accepted));
522 | }
523 |
524 | for (int i = 0; i < images.size(); i++) // get image fields
525 | {
526 | ImageField field = result.GetImageField(images.get(i)); // image class
527 |
528 | Bitmap image = getBitmap(field.GetValue()); // convert to bitmap
529 | if (image != null)
530 | fields.add(new FieldData(field.GetName(), image, field.IsAccepted()));
531 | }
532 |
533 | FieldsSingleton.get().setFieldData(fields); // store all data to singleton
534 | startActivity(intent); // show result activity
535 | }
536 |
537 | public Bitmap getBitmap(Image image) { // convert Image to Bitmap
538 |
539 | Bitmap bitmap = null;
540 |
541 | try{
542 | int nChannels = image.getChannels();
543 | int sizeBytes = image.GetRequiredBufferLength();
544 | byte[] bytes = new byte[sizeBytes];
545 | image.CopyToBuffer(bytes);
546 |
547 | if (bytes != null) {
548 |
549 | int sizePixels = image.getHeight() * image.getWidth();
550 | int[] pixels = new int[sizePixels];
551 |
552 | int r = 0, g = 0, b = 0;
553 |
554 | for (int y = 0; y < image.getHeight(); y++) {
555 | for (int x = 0; x < image.getWidth(); x++) {
556 | if (nChannels == 1)
557 | r = g = b = (bytes[x + y * image.getStride()] & 0xFF);
558 |
559 | if (nChannels == 3) {
560 | b = bytes[3 * x + y * image.getStride() + 0] & 0xFF;
561 | g = bytes[3 * x + y * image.getStride() + 1] & 0xFF;
562 | r = bytes[3 * x + y * image.getStride() + 2] & 0xFF;
563 | }
564 |
565 | pixels[x + y * image.getWidth()] = Color.rgb(b, g, r);
566 | }
567 | }
568 |
569 | bitmap = Bitmap.createBitmap(pixels, image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
570 |
571 |
572 | }
573 | }catch(RuntimeException e)
574 | {
575 | Log.d(module_log, "Cannot process bitmap: " + e.toString());
576 | }
577 |
578 | return bitmap;
579 | }
580 |
581 | @Override
582 | public void onBackPressed()
583 | {
584 | Intent intent = new Intent(Intent.ACTION_MAIN);
585 | intent.addCategory(Intent.CATEGORY_HOME);
586 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
587 | startActivity(intent);
588 | }
589 |
590 | class EngineTask extends AsyncTask
591 | {
592 | @Override
593 | protected Void doInBackground(Void... unused) {
594 |
595 | while (true) {
596 |
597 | try {
598 | frame_ready.acquire(); // waiting for the frame
599 |
600 | if(session_working == false)
601 | break;
602 |
603 | Camera.Size size = camera.getParameters().getPreviewSize();
604 | RecognitionResult result = session.ProcessYUVSnapshot(data, size.width, size.height, !is_nexus_5x? ImageOrientation.Portrait : ImageOrientation.InvertedPortrait); // process frame
605 | publishProgress(result); // show current result
606 |
607 | }catch(RuntimeException e)
608 | {
609 | Log.d(module_log, "Cannot process snapshot: " + e.toString());
610 | }
611 | catch(InterruptedException e)
612 | {
613 | Log.d(module_log, "Problem with frame_ready semaphore: " + e.toString());
614 | }
615 | }
616 | return null;
617 | }
618 |
619 | @Override
620 | protected void onProgressUpdate(RecognitionResult... res)
621 | {
622 | RecognitionResult result = res[0];
623 |
624 | if (result.IsTerminal() == true) // recognition process is stopped by getting all fields correctly or timeout
625 | {
626 | stop_session();
627 | show_result(result);
628 |
629 | } else {
630 |
631 | MatchResultVector match = result.GetMatchResults();
632 |
633 | if (match.size() > 0)
634 | {
635 | view.Clear();
636 |
637 | for (int i = 0; i < match.size(); i++)
638 | view.AddQuad(match.get(i).getQuadrangle()); // show current document zones
639 |
640 | SegmentationResultVector segmentationResultVector = result.GetSegmentationResults();
641 |
642 | for (int j = 0; j < segmentationResultVector.size(); j++) {
643 |
644 | SegmentationResult segResult = segmentationResultVector.get(j);
645 | StringVector zoneNames = segResult.GetZoneNames();
646 |
647 | for (int k = 0; k < zoneNames.size(); k++) {
648 | Quadrangle quad = segResult.GetZoneQuadrangle(zoneNames.get(k)); // show current fields zones
649 | view.AddQuad(quad);
650 | }
651 | }
652 | }
653 |
654 | view.invalidate();
655 | frame_waiting.release(); //
656 | }
657 | }
658 | }
659 |
660 | void toast(String msg)
661 | {
662 | Toast error_message = Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG);
663 | error_message.show();
664 | }
665 | }
666 |
667 |
668 |
--------------------------------------------------------------------------------
/app/src/main/java/biz/smartengines/smartid/demo/ElementsView.java:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (c) 2012-2017, Smart Engines Ltd
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without modification,
6 | are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice,
9 | this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the Smart Engines Ltd nor the names of its
14 | contributors may be used to endorse or promote products derived from this
15 | software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package biz.smartengines.smartid.demo;
30 |
31 | import android.content.Context;
32 | import android.graphics.Canvas;
33 | import android.graphics.Color;
34 | import android.graphics.Paint;
35 | import android.graphics.PointF;
36 | import android.view.View;
37 |
38 | import java.util.ArrayList;
39 | import java.util.List;
40 |
41 | import biz.smartengines.smartid.swig.Quadrangle;
42 |
43 | public class ElementsView extends View {
44 |
45 | private static class QuadRectangle { // quad store class
46 |
47 | private PointF lt;
48 | private PointF rt;
49 | private PointF lb;
50 | private PointF rb;
51 |
52 | private QuadRectangle(PointF lt, PointF rt, PointF lb, PointF rb)
53 | {
54 | this.lt = lt;
55 | this.rt = rt;
56 | this.lb = lb;
57 | this.rb = rb;
58 | }
59 |
60 | public void drawRectangle(Canvas canvas, Paint paint) {
61 |
62 | canvas.drawLine(lt.x, lt.y, rt.x, rt.y, paint);
63 | canvas.drawLine(rt.x, rt.y, rb.x, rb.y, paint);
64 | canvas.drawLine(rb.x, rb.y, lb.x, lb.y, paint);
65 | canvas.drawLine(lb.x, lb.y, lt.x, lt.y, paint);
66 | }
67 | }
68 |
69 |
70 | private List quadRectangles; // current quad to show
71 | private List doc_types = null; // current document list
72 |
73 | private Paint paintYellow;
74 |
75 | private int preview_w = 0;
76 | private int preview_h = 0;
77 |
78 | private int canvas_w = 0;
79 | private int canvas_h = 0;
80 |
81 | private int line_width = (int) ( getContext().getResources().getDisplayMetrics().density * 2);
82 | int text_size = (int) (getContext().getResources().getDisplayMetrics().density * 20 );
83 | int text_padding = (int) (getContext().getResources().getDisplayMetrics().density * 10) ;
84 |
85 | public boolean is_started = false;
86 |
87 | public ElementsView(Context context) {
88 | super(context);
89 |
90 | paintYellow = new Paint();
91 | paintYellow.setColor(Color.YELLOW);
92 | paintYellow.setStrokeWidth(line_width);
93 | paintYellow.setTextSize(text_size);
94 | paintYellow.setTextAlign(Paint.Align.CENTER);
95 |
96 | quadRectangles = new ArrayList<>();
97 | }
98 |
99 | void AddQuad(Quadrangle quad) {
100 |
101 | PointF lt = new PointF();
102 | PointF rt = new PointF();
103 | PointF lb = new PointF();
104 | PointF rb = new PointF();
105 |
106 | // calculate quad position using canvas and preview sizes
107 |
108 | lt.x = (float)canvas_w * (float)quad.GetPoint(0).getX() / (float)preview_w ;
109 | lt.y = (float)canvas_h * (float)quad.GetPoint(0).getY() / (float)preview_h ;
110 |
111 | rt.x = (float)canvas_w * (float)quad.GetPoint(1).getX() / (float)preview_w ;
112 | rt.y = (float)canvas_h * (float)quad.GetPoint(1).getY() / (float)preview_h ;
113 |
114 | rb.x = (float)canvas_w * (float)quad.GetPoint(2).getX() / (float)preview_w ;
115 | rb.y = (float)canvas_h * (float)quad.GetPoint(2).getY() / (float)preview_h ;
116 |
117 | lb.x = (float)canvas_w * (float)quad.GetPoint(3).getX() / (float)preview_w ;
118 | lb.y = (float)canvas_h * (float)quad.GetPoint(3).getY() / (float)preview_h ;
119 |
120 | quadRectangles.add(new QuadRectangle(lt, rt, lb, rb));
121 | }
122 |
123 | void SetPreviewSize(int preview_w, int preview_h) {
124 | this.preview_w = preview_w;
125 | this.preview_h = preview_h;
126 | }
127 |
128 | void Clear() {
129 | quadRectangles.clear();
130 | }
131 |
132 | void Start() {
133 | is_started = true;
134 | }
135 |
136 | void Stop() {
137 | is_started = false;
138 | }
139 |
140 | public void SetDocumentsTypes(ArrayList doc_types_)
141 | {
142 | doc_types = doc_types_;
143 | }
144 |
145 | @Override
146 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
147 | super.onSizeChanged(w, h, oldw, oldh);
148 |
149 | this.canvas_w = w;
150 | this.canvas_h = h;
151 | }
152 |
153 | @Override
154 | protected void onDraw(Canvas canvas) {
155 | super.onDraw(canvas);
156 |
157 | if(is_started == true)
158 | {
159 | for (QuadRectangle quad : quadRectangles)
160 | quad.drawRectangle(canvas, paintYellow);
161 | }
162 | else {
163 | if (doc_types != null)
164 | {
165 | float height = canvas_h/2 - ( (text_padding + text_size) * (doc_types.size() - 1))/2;
166 |
167 | for (String doc_type : doc_types)
168 | {
169 | canvas.drawText(doc_type, canvas_w / 2, height, paintYellow);
170 | height += text_padding + text_size;
171 | }
172 | }
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/app/src/main/java/biz/smartengines/smartid/demo/FieldData.java:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (c) 2012-2017, Smart Engines Ltd
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without modification,
6 | are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice,
9 | this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the Smart Engines Ltd nor the names of its
14 | contributors may be used to endorse or promote products derived from this
15 | software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package biz.smartengines.smartid.demo;
30 |
31 | import android.graphics.Bitmap;
32 |
33 | public class FieldData { // stored field data
34 |
35 | private String name;
36 | private String value;
37 |
38 | private Bitmap image = null;
39 |
40 | private boolean isImage = false;
41 | private boolean accepted = false;
42 |
43 | public FieldData(String name, Bitmap photo, boolean accepted) {
44 | this.name = name;
45 | this.image = photo;
46 | this.accepted = accepted;
47 | this.isImage = true;
48 | }
49 |
50 | public FieldData(String name, String value, boolean accepted) {
51 | this.name = name;
52 | this.value = value;
53 | this.accepted = accepted;
54 | this.isImage = false;
55 | }
56 |
57 | public String name() {
58 | return name;
59 | }
60 | public String value() {
61 | return value;
62 | }
63 | public Bitmap image() { return image; }
64 | public boolean accepted() {
65 | return accepted;
66 | }
67 | public boolean isImage() {
68 | return isImage;
69 | }
70 |
71 | }
--------------------------------------------------------------------------------
/app/src/main/java/biz/smartengines/smartid/demo/FieldsSingleton.java:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (c) 2012-2017, Smart Engines Ltd
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without modification,
6 | are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice,
9 | this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the Smart Engines Ltd nor the names of its
14 | contributors may be used to endorse or promote products derived from this
15 | software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package biz.smartengines.smartid.demo;
30 |
31 | import java.util.List;
32 |
33 | public class FieldsSingleton { // singleton contains all fields
34 | private static FieldsSingleton instance;
35 |
36 | public static synchronized FieldsSingleton get()
37 | {
38 | if (instance == null) {
39 | instance = new FieldsSingleton();
40 | }
41 | return instance;
42 | }
43 |
44 | private List data; // list of result fields
45 | protected void setFieldData(List data) {
46 | this.data = data;
47 | }
48 |
49 | protected List getFieldData() {
50 | return data;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/biz/smartengines/smartid/demo/ResultView.java:
--------------------------------------------------------------------------------
1 | /**
2 | Copyright (c) 2012-2017, Smart Engines Ltd
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without modification,
6 | are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright notice,
9 | this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 | * Neither the name of the Smart Engines Ltd nor the names of its
14 | contributors may be used to endorse or promote products derived from this
15 | software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package biz.smartengines.smartid.demo;
30 |
31 | import android.app.Activity;
32 | import android.graphics.Color;
33 | import android.os.Bundle;
34 | import android.support.v7.app.AppCompatActivity;
35 | import android.view.Display;
36 | import android.view.LayoutInflater;
37 | import android.view.View;
38 | import android.view.ViewGroup;
39 | import android.widget.BaseAdapter;
40 | import android.widget.Button;
41 | import android.widget.ImageView;
42 | import android.widget.ListView;
43 | import android.widget.RelativeLayout;
44 | import android.widget.TextView;
45 |
46 | import java.util.List;
47 |
48 | public class ResultView extends AppCompatActivity implements View.OnClickListener{
49 |
50 | @Override
51 | protected void onCreate(Bundle savedInstanceState) {
52 | super.onCreate(savedInstanceState);
53 | setContentView(R.layout.activity_result_view);
54 |
55 | TextView tv = (TextView) findViewById(R.id.noResult);
56 | Button retryButton = (Button) findViewById(R.id.retryButton);
57 | ListView lv = (ListView) findViewById(R.id.listLoad);
58 | retryButton.setOnClickListener(this);
59 |
60 | if (FieldsSingleton.get().getFieldData().isEmpty()) // show message if there is no result
61 | {
62 | lv.setVisibility(View.GONE);
63 | tv.setVisibility(View.VISIBLE);
64 | retryButton.setVisibility(View.VISIBLE);
65 | }
66 |
67 | lv.setAdapter(new FieldAdapter(this, FieldsSingleton.get().getFieldData()));
68 | }
69 |
70 | @Override
71 | public void onClick(View v) {
72 | finish();
73 | } // clicked on retry
74 |
75 | private class FieldAdapter extends BaseAdapter {
76 |
77 | private List list;
78 | private Activity context;
79 |
80 | public FieldAdapter(Activity context, List list) {
81 | this.list = list;
82 | this.context = context;
83 | }
84 |
85 | @Override
86 | public int getCount() {
87 | return list.size();
88 | }
89 |
90 | @Override
91 | public FieldData getItem(int position) {
92 | return list.get(position);
93 | }
94 |
95 | @Override
96 | public View getView(int position, View convertView, ViewGroup parent)
97 | {
98 | FieldData field = getItem(position);
99 | LayoutInflater inflater = context.getLayoutInflater();
100 | View itemView = inflater.inflate(R.layout.result_row_layout, null, false);
101 |
102 | TextView text = (TextView) itemView.findViewById(R.id.label);
103 |
104 | if(field.isImage() == true) {
105 | text.setText(getItem(position).name());
106 | ImageView image = (ImageView)itemView.findViewById(R.id.image);
107 | Display display = getWindowManager().getDefaultDisplay();
108 | image.getLayoutParams().width = (int) (0.6 * display.getWidth());
109 | image.setImageBitmap(getItem(position).image());
110 | } else {
111 | text.setText(getItem(position).name() + ": " + getItem(position).value());
112 | ImageView image = (ImageView)itemView.findViewById(R.id.image);
113 | image.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
114 | image.setImageBitmap(null);
115 | }
116 |
117 | if(field.accepted() == false) // set text color depends on field status
118 | text.setTextColor(Color.RED);
119 | else
120 | text.setTextColor(Color.BLACK);
121 |
122 | return itemView;
123 | }
124 |
125 | @Override
126 | public long getItemId(int position) {
127 | return position;
128 | }
129 | }
130 |
131 | }
132 |
133 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_camera_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_result_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
28 |
29 |
41 |
50 |
51 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/result_row_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
17 |
18 |
19 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartEngines/SmartIDReader-Android-SDK/f423e039a9b0358721fea1b8a36961bee7a6a727/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartEngines/SmartIDReader-Android-SDK/f423e039a9b0358721fea1b8a36961bee7a6a727/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartEngines/SmartIDReader-Android-SDK/f423e039a9b0358721fea1b8a36961bee7a6a727/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartEngines/SmartIDReader-Android-SDK/f423e039a9b0358721fea1b8a36961bee7a6a727/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartEngines/SmartIDReader-Android-SDK/f423e039a9b0358721fea1b8a36961bee7a6a727/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 | 16dp
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Smart IDReader Demo
3 | No recognized data found
4 | Retry
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/doc/DocumentsReference.md:
--------------------------------------------------------------------------------
1 | # Smart IDReader Documents Reference
2 |
3 | * [Document types](#document-types)
4 | * [Smart CardReader fields](#smart-cardreader-fields)
5 | * [Smart 3D OCR MRZ fields](#smart-3d-ocr-mrz-fields)
6 | * [Smart PassportReader fields](#smart-passportreader-fields)
7 | * [Smart IDReader fields](#smart-idreader-fields)
8 | - [Driver's licence of Russian Federation fields](#drivers-licence-of-russian-federation-fields)
9 | - [Vehicle registration certificate of Russian Federation fields](#vehicle-registration-certificate-of-russian-federation-fields)
10 | - [Insurance individual account number of Russian Federation fields](#insurance-individual-account-number-of-russian-federation-fields)
11 | - [Russian international biometric passport fields](#russian-international-biometric-passport-fields)
12 | - [Kazakhstan ID cards fields](#kazakhstan-id-cards-fields)
13 | - [Chinese passport fields](#chinese-passport-fields)
14 | - [USA passport fields](#usa-passport-fields)
15 | - [Philippine passport fields](#philippine-passport-fields)
16 | - [UK Driving licence fields](#uk-driving-licence-fields)
17 | - [UK Passport fields](#uk-passport-fields)
18 | - [Austrian Driving Licence fields](#austrian-driving-licence-fields)
19 | - [Austrian ID card fields](#austrian-id-card-fields)
20 | - [Austrian passport fields](#austrian-passport-fields)
21 | - [German Driving Licence fields](#german-driving-licence-fields)
22 | - [German ID card fields](#german-id-card-fields)
23 | - [German passport fields](#german-passport-fields)
24 | - [Spanish ID card fields](#spanish-id-card-fields)
25 | - [Malaysian ID card fields](#malaysian-id-card-fields)
26 | - [Japanese Driving licence fields](#japanese-driving-licence-fields)
27 | - [Japanese Health Insurance card fields](#japanese-health-insurance-card-fields)
28 | - [Japanese passport fields](#japanese-passport-fields)
29 | - [Indian passport fields](#indian-passport-fields)
30 | - [Syrian passport fields](#syrian-passport-fields)
31 | - [Belarusian passport fields](#belarusian-passport-fields)
32 | - [Belarusian driving licence fields](#belarusian-driving-licence-fields)
33 | - [Russian visa fields](#russian-visa-fields)
34 |
35 | Smart IDReader, being in itself a framework for recognition and automatic entry of various identification documents, also provides interface for separate modules developed by [Smart Engines Ltd.](http://smartengines.biz) and designed for recognition of specific documents, such as bank cards or MRZ documents, which are implemented as a separate recognition engines inside Smart IDReader core. This reference lists the complete set of supported document types across all the modules of Smart IDReader framework, with the list of extracted fields.
36 |
37 | ## Document types
38 |
39 | *Please keep in mind that not all of these document types may be available in your build of the Smart IDReader SDK.*
40 |
41 | | Internal Engine | Document type | Description |
42 | |:--------------------:|:------------- |:----- |
43 | | Smart CardReader | `card.embossed` | Bank cards with embossed fields, front side |
44 | | Smart CardReader | `card.indent` | Bank cards with indent-printed fields, front side |
45 | | Smart 3D OCR MRZ | `mrz.mrp` | [ICAO Doc 9303](http://www.icao.int/publications/pages/publication.aspx?docnum=9303) Machine-readable passports
(2 lines, 44 characters each) |
46 | | Smart 3D OCR MRZ | `mrz.td1` | [ICAO Doc 9303](http://www.icao.int/publications/pages/publication.aspx?docnum=9303) Machine-readable travel document TD-1
(3 lines, 30 characters each) |
47 | | Smart 3D OCR MRZ | `mrz.td2` | [ICAO Doc 9303](http://www.icao.int/publications/pages/publication.aspx?docnum=9303) Machine-readable travel document TD-2
(2 lines, 36 characters each) |
48 | | Smart 3D OCR MRZ | `mrz.mrva` | [ICAO Doc 9303](http://www.icao.int/publications/pages/publication.aspx?docnum=9303) Machine-readable visa MRV-A
(2 lines, 44 characters each) |
49 | | Smart 3D OCR MRZ | `mrz.mrvb` | [ICAO Doc 9303](http://www.icao.int/publications/pages/publication.aspx?docnum=9303) Machine-readable visa MRV-B
(2 lines, 36 characters each)|
50 | | Smart 3D OCR MRZ | `mrz.mrvrus` | MRZ-like zone on visa of Russian Federation
(2 lines, 44 characters each) |
51 | | Smart 3D OCR MRZ | `mrz.cnis` | MRZ-like zone on French national identity card
(2 lines, 36 characters each) |
52 | | Smart 3D OCR MRZ | `mrz.m3z` | MRZ-like zone on citizen passport of Russian Federation
(2 lines, 44 characters each) |
53 | | Smart 3D OCR MRZ | `mrz.chedl` | MRZ-like zone on Swiss driving licence
(3 lines, 9, 30 and 30 characters) |
54 | | Smart PassportReader | `rus.passport.national` | Citizen passport of Russian Federation,
pages 2 and 3 |
55 | | Smart IDReader | `rus.drvlic.type1` | Driver's licence of Russian Federation
('pink and cyan' cards, front side) |
56 | | Smart IDReader | `rus.drvlic.type2` | Driver's licence of Russian Federation
('yellow' cards, front side) |
57 | | Smart IDReader | `rus.drvlic.type2` | Driver's licence of Russian Federation
(vertical laminated cards, front side) |
58 | | Smart IDReader | `rus.sts.new` | Vehicle registration certificate of Russian Federation
(new 'pink' cards, front and back) |
59 | | Smart IDReader | `rus.sts.old` | Vehicle registration certificate of Russian Federation
(old 'yellow' cards, front and back) |
60 | | Smart IDReader | `rus.snils.type1` | Insurance individual account number (SNILS) of
Russian Federation (laminated cards, front side) |
61 | | Smart IDReader | `rus.snils.type2` | Insurance individual account number (SNILS) of
Russian Federation (card-size, front side) |
62 | | Smart IDReader | `rus.passport.biometric` | Russian international passport (biometric, main page) |
63 | | Smart IDReader | `kaz.id.type1` | Kazakhstan ID card (old, with 2-line MRZ, front and back) |
64 | | Smart IDReader | `kaz.id.type2` | Kazakhstan ID card (new, with 3-line MRZ, front and back) |
65 | | Smart IDReader | `chn.passport.old` | Chinese passport (old type, main page) |
66 | | Smart IDReader | `chn.passport.new` | Chinese passport (new type, main page) |
67 | | Smart IDReader | `usa.passport.old` | USA passport (old type - hexagonal shapes on
the backgound, main page) |
68 | | Smart IDReader | `usa.passport.new` | USA passport (new type, main page) |
69 | | Smart IDReader | `phl.passport.type1` | Philippine passport (non-biometric,
birth place and gender in the same line, main page) |
70 | | Smart IDReader | `phl.passport.type2` | Philippine passport (non-biometric,
birth place and gender in separate lines, main page) |
71 | | Smart IDReader | `gbr.drvlic.type1` | UK Driving Licence (common type, front) |
72 | | Smart IDReader | `gbr.drvlic.type2` | UK Driving Licence (flag or tire on the right, front) |
73 | | Smart IDReader | `gbr.drvlic.provisional` | UK Provisional Driving Licence (front) |
74 | | Smart IDReader | `gbr.passport.type1`| UK passport (non-biometric, main page) |
75 | | Smart IDReader | `gbr.passport.type2`| UK passport (biometric, main page) |
76 | | Smart IDReader | `aut.drvlic.type1` | Austrian Driving Licence (title at the top, front) |
77 | | Smart IDReader | `aut.drvlic.type2` | Austrian Driving Licence (title in the top-right corner, front) |
78 | | Smart IDReader | `aut.id.common` | Austrian ID card (front) |
79 | | Smart IDReader | `aut.passport.type1` | Austrian passport (eagle in the top-left corner, main page) |
80 | | Smart IDReader | `deu.drvlic.common` | German Driving Licence (front) |
81 | | Smart IDReader | `deu.id.type1` | German ID card (card-size, front) |
82 | | Smart IDReader | `deu.id.type2` | German ID card (TD-1 MRZ on the front, front) |
83 | | Smart IDReader | `deu.passport.type1` | German passport (yellow, eagle in the background, main page) |
84 | | Smart IDReader | `esp.id.type1` | Spanish ID card (front) |
85 | | Smart IDReader | `mys.id.type1` | Malaysian ID card (MyKad, front) |
86 | | Smart IDReader | `jpn.drvlic.type1` | Japanese Driving licence (front) |
87 | | Smart IDReader | `jpn.insurance.type1` | Japanese Health Insurance card (front) |
88 | | Smart IDReader | `jpn.passport.type1` | Japanese passport (main page) |
89 | | Smart IDReader | `ind.passport.type1` | Indian passport (main page) |
90 | | Smart IDReader | `syr.passport.type1` | Syrian passport (main page) |
91 | | Smart IDReader | `blr.passport.type1` | Belarusian passport (main page) |
92 | | Smart IDReader | `blr.drvlic.type1` | Belarusian driving licence (front) |
93 | | Smart IDReader | `rus.visa.type1` | Russian visa (main page) |
94 |
95 |
96 | ## Smart CardReader fields
97 |
98 | The following table lists the fields extracted by Smart CardReader engine (for documents `card.embossed` and `card.indent`).
99 |
100 | | Field name | Format | Example | Description |
101 | |:-----------|:-------|:--------|:------------|
102 | |`number`|String of digits
separated by spaces|`5123 4567 8901 2345`|Number of the bank card|
103 | |`expiry_date`|`MMSYY` where `MM` stands
for month, `YY` stands for year
and `S` stands for separator character|`01/22`|Expiry date of the bank card|
104 | |`name`|String with latin characters,
spaces and dots|`MR. SAM CARDHOLDER`|Name of the cardholder
as printed on the card|
105 |
106 | ## Smart 3D OCR MRZ fields
107 |
108 | The following table lists the fields extracted by Smart 3D OCR MRZ engine (for MRZ documents `mrz.mrp`, `mrz.td1`, `mrz.td2`, `mrz.mrva`, `mrz.mrvb`, `mrz.mrvrus`, `mrz.cnis`, `mrz.m3z`).
109 |
110 | | Field name | Document types | Format | Example | Description |
111 | |:-----------|:---------------|:-------|:--------|:------------|
112 | |`doc_type_code` |`mrz.*`|2 symbols (first two characters of MRZ)|`P<`|Code representation of the MRZ document subtype|
113 | |`first_name` |`mrz.*`|String of latin characters separated by spaces|`SAM JOHN`|First name of the document holder|
114 | |`second_name` |`mrz.*`|String of latin characters separated by spaces|`TRAVELLER`|Last name of the document holder|
115 | |`gender` |`mrz.*`|Single character: `M`, `F` or `<`|`M`|Gender code of the document holder|
116 | |`nationality` |`mrz.*`|[ISO 3166-1](http://www.iso.org/iso/home/standards/country_codes.htm) three-letter|`UTO`|Nationality of the document holder|
117 | |`country` |`mrz.*`|[ISO 3166-1](http://www.iso.org/iso/home/standards/country_codes.htm) three-letter|`UTO`|Issuing authority code|
118 | |`number` |`mrz.*`|9-character string|`BF12345<<`|Document number as appears in the MRZ|
119 | |`number_formatted` |`mrz.*`|String of characters|`BF12345`|Formatted document number string|
120 | |`birth_date` |`mrz.*`|`YYMMDD`|`990102`|Date of birth as appears in the MRZ|
121 | |`birth_date_formatted` |`mrz.*`|`DD.MM.YYYY`|`02.01.1999`|Formatted date of birth|
122 | |`expiry_date` |`mrz.*` except `mrz.m3z` and `mrz.cnis`|`YYMMDD`|`200102`|Expiry date of the MRZ document|
123 | |`expiry_date_formatted` |`mrz.*` except `mrz.m3z` and `mrz.cnis`|`DD.MM.YYYY`|`02.01.2020`|Formatted expiry date of the MRZ document|
124 | |`opt_data_1` |`mrz.*`|String of characters|`88393 000`|Formatted optional data on the first MRZ line (at the discretion of issuer)|
125 | |`opt_data_2` |`mrz.*`|String of characters|`88393 000`|Formatted optional data on the second MRZ line (at the discretion of issuer)|
126 | |`department_code` |`mrz.m3z`|`DDD-DDD` string|`100-100`|Russian citizen passport authority code|
127 | |`issue_date` |`mrz.m3z`|`YYMMDD`|`000102`|Issue date of the Russian citizen passport|
128 | |`issue_date_formatted` |`mrz.m3z`|`DD.MM.YYYY`|`02.01.2000`|Formatted issue date of the Russian citizen passport|
129 |
130 | ## Smart PassportReader fields
131 |
132 | The following table lists the fields extracted by Smart PassportReader (for Russian citizen passport `rus.passport.national`).
133 |
134 | | Field name | Format | Example | Description |
135 | |:-----------|:-------|:--------|:------------|
136 | |`authority`|UTF-8 cyrillic string|`УВД Г. МОСКВЫ`|Issuing authority|
137 | |`authority_code`|`DDD-DDD`|`100-100`|Issuing authority code|
138 | |`birthdate`|`DD.MM.YYYY`|`02.01.1980`|Date of birth|
139 | |`birthplace`|UTF-8 cyrillic string|`Г. МОСКВА`|Place of birth|
140 | |`gender`|UTF-8 cyrillic string|`МУЖ.`|Gender|
141 | |`issue_date`|`DD.MM.YYYY`|`02.01.2000`|Passport issue date|
142 | |`mrz_line1`|44-character string|`PNRUSIM8REK< where `D` is a digit
and `X` is either
digit or cyrillic letter|`11 22 333000`| Formatted document number|
161 | |`birth_date`|`rus.drvlic.*`|`DD.MM.YYYY`|`02.01.1980`|Date of birth of
the document holder|
162 | |`issue_date`|`rus.drvlic.*`|`DD.MM.YYYY`|`01.02.1999`|Document issue date|
163 | |`expiration_date`|`rus.drvlic.*`|`DD.MM.YYYY`|`01.02.2009`|Document expiry date|
164 | |`name_rus`|`rus.drvlic.*`|UTF-8 cyrillic string|`ИВАН`|Document holder
first name|
165 | |`patronymic_rus`|`rus.drvlic.*`|UTF-8 cyrillic string|`ИВАНОВИЧ`|Document holder
patronymic (second name)|
166 | |`surname_rus`|`rus.drvlic.*`|UTF-8 cyrillic string|`ИВАНОВ`|Document holder
last name|
167 | |`name_and_patronymic_rus`|`rus.drvlic.type1`|UTF-8 cyrillic string|`ИВАН ИВАНОВИЧ`|Document holder
first name and patronymic,
as appears on the document|
168 |
169 | #### Vehicle registration certificate of Russian Federation fields
170 |
171 | The following table lists the fields extracted by Smart IDReader from documents `rus.sts.new` and `rus.sts.old`.
172 |
173 | | Field name | Document types | Format | Example | Description |
174 | |:-----------|:---------------|:-------|:--------|:------------|
175 | |`number`|`rus.sts.*`|UTF-8 string `DD XX DDDDDD`,
where `D` is a digit
and `X` is either
digit or cyrillic letter|`11 22 333000`| Formatted document number|
176 | |`plate`|`rus.sts.new` (back side)|UTF-8 string `ADDDAADD[D]` where `A` is a cyrillic letter and `D` is a digit|`А123BC177`|Vehicle licence plate number|
177 | |`vin`|`rus.sts.new` (back side)|17-character string|`1FTLP62W4XH128703`|Vehicle identification number|
178 |
179 | #### Insurance individual account number of Russian Federation fields
180 |
181 | The following table lists the fields extracted by Smart IDReader from document `rus.snils.type1` and `rus.snils.type2`.
182 |
183 | | Field name | Document types | Format | Example | Description |
184 | |:-----------|:---------------|:-------|:--------|:------------|
185 | |`number`|`rus.snils.*`|`DDD-DDD-DDD DD`|`123-456-789 64`|Document number|
186 | |`name`|`rus.snils.*`|UTF-8 cyrillic string|`ИВАН`|Document holder first name|
187 | |`patronymic`|`rus.snils.*`|UTF-8 cyrillic string|`ИВАНОВИЧ`|Document holder patronymic
(second name)|
188 | |`surname`|`rus.snils.*`|UTF-8 cyrillic string|`ИВАНОВ`|Document holder last name|
189 |
190 | #### Russian international biometric passport fields
191 |
192 | The following table lists the fields extracted by Smart IDReader from document `rus.passport.biometric`.
193 |
194 | | Field name | Document types | Format | Example | Description |
195 | |:-----------|:---------------|:-------|:--------|:------------|
196 | |`authority`|`rus.passport.biometric`|UTF-8 string|`ФМС 10001`|Issuing authority|
197 | |`birth_date`|`rus.passport.biometric`|`DD.MM.YYYY`|`01.01.1990`|Birth date (VIS)|
198 | |`birth_date_mrz`|`rus.passport.biometric`|`YYMMDD`|`900101`|Date of birth as appears in MRZ|
199 | |`birth_date_mrz_formatted`|`rus.passport.biometric`|`DD.MM.YYYY`|`01.01.1990`|Formatted date of birth from MRZ|
200 | |`birth_place`|`rus.passport.biometric`|UTF-8 cyrillic string|`Г.МОСКВА`|Birth place|
201 | |`expiry_date`|`rus.passport.biometric`|`DD.MM.YYYY`|`01.01.2020`|Expiry date (VIS)|
202 | |`expiry_date_mrz`|`rus.passport.biometric`|`YYMMDD`|`200101`|Expiry date as appears in MRZ|
203 | |`expiry_date_mrz_formatted`|`rus.passport.biometric`|`DD.MM.YYYY`|`01.01.2020`|Formatted expiry date from MRZ|
204 | |`full_mrz`|`rus.passport.biometric`|88 characters string|`P`|`01.01.1990 UNITED KINGDOM`|Birth date and birth place as appears on the document|
335 | |`expiry_date`|`gbr.drvlic.*`|`DD.MM.YYYY`|`01.01.2020`|Expiry date|
336 | |`issue_date`|`gbr.drvlic.*`|`DD.MM.YYYY`|`01.01.2000`|Issue date|
337 | |`name`|`gbr.drvlic.*`|String of characters|`JOHN`|First name|
338 | |`number`|`gbr.drvlic.*`|String of characters|`SMITH00000000000 00`|Document number|
339 | |`surname`|`gbr.drvlic.*`|String of characters|`SMITH`|Last name|
340 |
341 |
342 | #### UK Passport fields
343 |
344 | The following table lists the fields extracted by Smart IDReader from documents `gbr.passport.type1` and `gbr.passport.type2`.
345 |
346 | | Field name | Document types | Format | Example | Description |
347 | |:-----------|:---------------|:-------|:--------|:------------|
348 | |`authority`|`gbr.passport.*`|String of characters|`IPS`|Issuing authority|
349 | |`birth_date`|`gbr.passport.*`|`DD.MM.YYYY`|`01.01.1990`|Birth date (VIS)|
350 | |`birth_date_mrz`|`gbr.passport.*`|`YYMMDD`|`900101`|Date of birth as appears in MRZ|
351 | |`birth_date_mrz_formatted`|`gbr.passport.*`|`DD.MM.YYYY`|`01.01.1990`|Formatted date of birth from MRZ|
352 | |`birth_place`|`gbr.passport.*`|String of characters|`LONDON`|Birth place|
353 | |`expiry_date`|`gbr.passport.*`|`01.01.2020`|Expiry date (VIS)|
354 | |`expiry_date_mrz`|`gbr.passport.*`|`200101`|Expiry date as appears in MRZ|
355 | |`expiry_date_mrz_formatted`|`gbr.passport.*`|`DD.MM.YYYY`|`01.01.2020`|Formatted expiry date from MRZ|
356 | |`full_mrz`|`gbr.passport.*`|88 character string|`P`|`01.01.1990 WIEN`|Birth date and birth place as appears on the document|
377 | |`expiry_date`|`aut.drvlic.type1`|`DD.MM.YYYY`|`01.01.2020`|Expiry date|
378 | |`issue_date`|`aut.drvlic.*`|`DD.MM.YYYY`|`01.01.2000`|Issue date|
379 | |`issue_place`|`aut.drvlic.*`|String of characters|`WIEN`|Place of issue|
380 | |`name`|`aut.drvlic.*`|String of characters|`JOHN`|First name|
381 | |`number`|`aut.drvlic.*`|`DDDDDDDD`|`01234567`|Document number|
382 | |`surname`|`aut.drvlic.*`|String of characters|`SMITH`|Last name|
383 |
384 |
385 | #### Austrian ID card fields
386 |
387 | The following table lists the fields extracted by Smart IDReader from the document `aut.id.common`.
388 |
389 | | Field name | Document types | Format | Example | Description |
390 | |:-----------|:---------------|:-------|:--------|:------------|
391 | |`birth_date`|`aut.id.*`|`DD.MM.YYYY`|`01.01.1990`|Birth date|
392 | |`gender`|`aut.id.*`|Single character|`M`|Gender|
393 | |`name`|`aut.id.*`|String of characters|`JOHN`|First name|
394 | |`number`|`aut.id.*`|`DDDDDDDD`|`01234567`|Document number|
395 | |`surname`|`aut.id.*`|String of characters|`SMITH`|Last name|
396 |
397 |
398 | #### Austrian passport fields
399 |
400 | The following table lists the fields extracted by Smart IDReader from the document `aut.passport.type1`.
401 |
402 | | Field name | Document types | Format | Example | Description |
403 | |:-----------|:---------------|:-------|:--------|:------------|
404 | |`birth_date`|`aut.passport.type1`|`DD.MM.YYYY`|`01.01.1990`|Birth date (VIS)|
405 | |`birth_date_mrz`|`aut.passport.type1`|`YYMMDD`|`900101`|Date of birth as appears in MRZ|
406 | |`birth_date_mrz_formatted`|`aut.passport.type1`|`DD.MM.YYYY`|`01.01.1990`|Formatted date of birth from MRZ|
407 | |`birth_place`|`aut.passport.type1`|String of characters|`WIEN`|Birth place|
408 | |`dvr_number`|`aut.passport.type1`|`AAA DDDDDDD`|`DVR 0123456`|DVR number (vertical on the right)|
409 | |`expiry_date`|`aut.passport.type1`|`DD.MM.YYYY`|`01.01.2020`|Expiry date (VIS)|
410 | |`expiry_date_mrz`|`aut.passport.type1`|`YYMMDD`|`200101`|Expiry date as appears in MRZ|
411 | |`expiry_date_mrz_formatted`|`aut.passport.type1`|`DD.MM.YYYY`|`01.01.2020`|Formatted expiry date from MRZ|
412 | |`full_mrz`|`aut.passport.type1`|88 characters string|`P`|`01.01.1990 DEUTSCH`|Birth date and nationality as appears on the document|
448 | |`birth_date_mrz`|`deu.id.type2`|`YYMMDD`|`900101`|Date of birth as appears in MRZ|
449 | |`birth_date_mrz_formatted`|`deu.id.type2`|`DD.MM.YYYY`|`01.01.1990`|Formatted date of birth from MRZ|
450 | |`birth_place`|`deu.id.*`|String of characters|`BERLIN`|Birth place|
451 | |`birth_place_and_birth_date`|`deu.id.type2`|`DD.MM.YYYY `|`01.01.1990 BERLIN`|Birth date and birth place as appears on the document|
452 | |`expiry_date`|`deu.id.*`|`DD.MM.YYYY`|`01.01.2020`|Expiry date|
453 | |`expiry_date_mrz`|`deu.id.type2`|`YYMMDD`|`200101`|Expiry date as appears in MRZ|
454 | |`expiry_date_mrz_formatted`|`deu.id.type2`|`DD.MM.YYYY`|`01.01.2020`|Formatted expiry date from MRZ|
455 | |`full_mrz`|`deu.id.type2`|72 characters string|`IDD<<...<0`|Full MRZ zone in one line|
456 | |`name`|`deu.id.*`|String of characters|`JOHN`|First name|
457 | |`name_mrz`|`deu.id.type2`|String of characters|`JOHN`|First name from MRZ|
458 | |`nationality`|`deu.id.*`|String of characters|`DEUTSCH`|Nationality|
459 | |`number`|`deu.id.*`|`NNNNNNNNN` (alphanumeric) for `deu.id.type1`, 9 or 10 digits for `deu.id.type2`|`012345678`|Document number|
460 | |`number_mrz`|`deu.id.type2`|`DDDDDDDDD`|`012345678`|Document number from MRZ|
461 | |`rfid_number`|`deu.id.type1`|`DDDDDD`|`012345`|RFID access code|
462 | |`surname`|`deu.id.*`|String of characters|`SMITH`|Last name|
463 | |`surname_mrz`|`deu.id.type2`|String of characters|`SMITH`|Last name from MRZ|
464 | |`surname_second`|`deu.id.*`|String of characters|`SMITH`|Last name - second line|
465 |
466 |
467 | #### German passport fields
468 |
469 | The following table lists the fields extracted by Smart IDReader from document `deu.passport.type1`.
470 |
471 | | Field name | Document types | Format | Example | Description |
472 | |:-----------|:---------------|:-------|:--------|:------------|
473 | |`birth_date`|`deu.passport.type1`|`DD.MM.YYYY`|`01.01.1990`|Birth date (VIS)|
474 | |`birth_date_mrz`|`deu.passport.type1`|`YYMMDD`|`900101`|Date of birth as appears in MRZ|
475 | |`birth_date_mrz_formatted`|`deu.passport.type1`|`DD.MM.YYYY`|`01.01.1990`|Formatted date of birth from MRZ|
476 | |`birth_place`|`deu.passport.type1`|String of characters|`BERLIN`|Birth place|
477 | |`expiry_date`|`deu.passport.type1`|`DD.MM.YYYY`|`01.01.2020`|Expiry date (VIS)|
478 | |`expiry_date_mrz`|`deu.passport.type1`|`YYMMDD`|`200101`|Expiry date as appears in MRZ|
479 | |`expiry_date_mrz_formatted`|`deu.passport.type1`|`DD.MM.YYYY`|`01.01.2020`|Formatted expiry date from MRZ|
480 | |`full_mrz`|`deu.passport.type1`|88 characters string|`P from number)|
522 | |`birth_place`|`mys.id.type1`|String of characters|`REGION NAME`|Birth place (extracted
from number)|
523 | |`gender`|`mys.id.type1`|`PEREMPUAN` or `LELAKI`|`LELAKI`|Gender|
524 | |`name`|`mys.id.type1`|String of characters|`JOHN SMITH`|Full name|
525 | |`nationality`|`mys.id.type1`|String of characters|`WARGANEGARA`|Nationality|
526 | |`number`|`mys.id.type1`|`DDDDDD-DD-DDDD`|`900101-01-0123`|Document number|
527 |
528 |
529 | #### Japanese Driving licence fields
530 |
531 | The following table lists the fields extracted by Smart IDReader from document `jpn.drvlic.type1`.
532 |
533 | | Field name | Document types | Format | Example | Description |
534 | |:-----------|:---------------|:-------|:--------|:------------|
535 | |`number`|`jpn.drvlic.type1`|`DDDDDDDDDDDD`|`012345678901`|Document number|
536 |
537 |
538 | #### Japanese Health Insurance card fields
539 |
540 | The following table lists the fields extracted by Smart IDReader from document `jpn.insurance.type1`.
541 |
542 | | Field name | Document types | Format | Example | Description |
543 | |:-----------|:---------------|:-------|:--------|:------------|
544 | |`insurance_type`|`jpn.insurance.type1`|`DDDDD`|`01234`|Insurance type (top-right corner)|
545 | |`organization`|`jpn.insurance.type1`|`DDDDDDDD`|`01234567`|Organization code|
546 | |`serial_number`|`jpn.insurance.type1`|String of digits|`012`|Serial number|
547 | |`insurer_number`|`jpn.insurance.type1`|`DDDDDDDD`|`01234567`|Insurer number|
548 |
549 |
550 | #### Japanese passport fields
551 |
552 | The following table lists the fields extracted by Smart IDReader from document `jpn.passport.type1`.
553 |
554 | | Field name | Document types | Format | Example | Description |
555 | |:-----------|:---------------|:-------|:--------|:------------|
556 | |`birth_date`|`jpn.passport.type1`|`DD.MM.YYYY`|`01.01.1990`|Birth date (VIS)|
557 | |`birth_date_mrz`|`jpn.passport.type1`|`YYMMDD`|`900101`|Date of birth as appears in MRZ|
558 | |`birth_date_mrz_formatted`|`jpn.passport.type1`|`DD.MM.YYYY`|`01.01.1990`|Formatted date of birth from MRZ|
559 | |`birth_place`|`jpn.passport.type1`|String of characters|`TOKYO`|Birth place (Registered domicile)|
560 | |`expiry_date`|`jpn.passport.type1`|`DD.MM.YYYY`|`01.01.2020`|Expiry date (VIS)|
561 | |`expiry_date_mrz`|`jpn.passport.type1`|`YYMMDD`|`200101`|Expiry date as appears in MRZ|
562 | |`expiry_date_mrz_formatted`|`jpn.passport.type1`|`DD.MM.YYYY`|`01.01.2020`|Formatted expiry date from MRZ|
563 | |`full_mrz`|`jpn.passport.type1`|88 characters string|`P settings(engine.CreateSessionSettings());
54 | ```
55 |
56 | Note, that `RecognitionEngine::CreateSessionSettings()` is a factory method and returns an allocated pointer. You are responsible for deleting it.
57 |
58 | 3. Enable desired document types:
59 |
60 | ```cpp
61 | settings->AddEnabledDocumentTypes("mrz.*");
62 | ```
63 |
64 | See more about document types in [Specifying document types for Recognition Session](#specifying-document-types-for-recognition-session).
65 |
66 | 4. Specify session options (not required):
67 |
68 | ```cpp
69 | settings->SetOption("common.sessionTimeout", "5");
70 | ```
71 |
72 | See more about options in [Session Options](#session-options).
73 |
74 | 5. Subclass ResultReporter and implement callbacks (not required):
75 |
76 | ```cpp
77 | class ConcreteResultReporter : public se::smartid::ResultReporterInterface { /* callbacks */ }
78 |
79 | ConcreteResultReporter optional_reporter;
80 | ```
81 |
82 | See more about result reporter callbacks in [Result Reporter Callbacks](#result-reporter-callbacks).
83 |
84 | 6. Spawn Recognition Session:
85 |
86 | ```cpp
87 | unique_ptr session(engine.SpawnSession(*settings, &optional_reporter));
88 | ```
89 |
90 | 7. Call `ProcessSnapshot(...)`, `ProcessImageFile(...)` or similar methods:
91 |
92 | ```cpp
93 | se::smartid::RecognitionResult result = session->ProcessImageFile(image_path);
94 | ```
95 |
96 | When performing recognition in video stream you might want to process frames coming from the stream until `result.IsTerminal()` is `true`.
97 |
98 | 8. Use `RecognitionResult` fields to extract recognized information:
99 |
100 | ```cpp
101 | for (const std::string &field_name : result.GetStringFieldNames()) {
102 | const se::smartid::StringField &string_field = result.GetStringField(field_name);
103 |
104 | bool is_accepted = string_field.IsAccepted();
105 | std::string field_value = string_field.GetUtf8Value();
106 | }
107 | ```
108 |
109 | Apart from string fields there also are image fields:
110 |
111 | ```cpp
112 | for (const std::string &field_name : result.GetImageFieldNames()) {
113 | const se::smartid::ImageField &image_field = result.GetStringField(field_name);
114 |
115 | const se::smartid::Image &image = image_field.GetValue();
116 | }
117 | ```
118 |
119 |
120 | ## Smart IDReader C++ SDK Overview
121 |
122 | #### Header files and namespaces
123 |
124 | You can either include all-in-one header file or only required ones:
125 |
126 | ```cpp
127 | #include // all-in-one
128 | #include // common classes
129 | #include // result classes
130 | ```
131 |
132 | Smart IDReader C++ SDK code is located within `se::smartid` namespace.
133 |
134 | #### Code documentation
135 |
136 | All classes and functions have useful Doxygen comments.
137 | Other out-of-code documentation is available at `doc` folder of your delivery.
138 | For complete compilable and runnable sample usage code and build scripts please see `samples` folder.
139 |
140 | #### Exceptions
141 |
142 | Our C++ API may throw `std::exception` subclasses when user passes invalid input, makes bad state calls or if something else goes wrong. Most exceptions contain useful human-readable information. Please read `e.what()` message if exception is thrown.
143 |
144 | #### Factory methods and memory ownership
145 |
146 | Several Smart IDReader SDK classes have factory methods which return pointers to heap-allocated objects. **Caller is responsible for deleting** such objects _(a caller is probably the one who is reading this right now)_.
147 | We recommend using `std::unique_ptr` for simple memory management and avoiding memory leaks.
148 |
149 | #### Getters and setters
150 |
151 | We return const references in getters wherever possible, it's better to assign them to const references as well to avoid undesirable copying. If there is only getter without setter for some variable then most probably we did this by purpose because configuration is done somewhere else internally.
152 |
153 | ## Configuration bundles
154 |
155 | Every delivery contains one or several _configuration bundles_ – archives containing everything needed for Smart IDReader Recognition Engine to be created and configured.
156 | Usually they are named as `bundle_something.zip` and located inside `data-zip` folder.
157 |
158 | ## Specifying document types for Recognition Session
159 |
160 | Assuming you already created recognition engine and session settings like this:
161 |
162 | ```cpp
163 | // create recognition engine with configuration bundle path
164 | se::smartid::RecognitionEngine engine(configuration_bundle_path);
165 |
166 | // create session settings with se::smartid::RecognitionEngine factory method
167 | std::unique_ptr settings(engine.CreateSessionSettings());
168 | ```
169 |
170 | In order to call `engine.SpawnSession(settings, optional_reporter)` you need to specify enabled document types for session to be spawned using `se::smartid::SessionSettings` methods.
171 | **By default, all document types are disabled.**
172 |
173 | #### Supported document types
174 |
175 | A _document type_ is simply a string encoding real world document type you want to recognize, for example, `rus.passport.internal` or `mrz.mrp`. Document types that Smart IDReader SDK delivered to you can potentially recognize can be obtaining using `GetSupportedDocumentTypes()` method:
176 |
177 | ```cpp
178 | const vector > &supported_document_types = settings->GetSupportedDocumentTypes();
179 | ```
180 |
181 | This vector is two-dimensional because of the restrictions of current Smart IDReader SDK version: **you can only enable document types that belong to the same `vector`** of supported document types for a single session.
182 |
183 | You can find full list of supported document types of Smart IDReader with their fields in [Documents Reference](./DOCUMENTS_REFERENCE.html).
184 |
185 | #### Enabling document types using wildcard expressions
186 |
187 | Since all documents in settings are disabled by default you need to enable some of them.
188 | In order to do so you may use `AddEnabledDocumentTypes(string)` and `SetEnabledDocumentTypes(vector)` (removes all and adds each string of the vector) methods of `SessionSettings`:
189 |
190 | ```cpp
191 | // settings->AddEnabledDocumentTypes("*");
192 | settings->AddEnabledDocumentTypes("mrz.*");
193 | // settings->AddEnabledDocumentTypes("card.*");
194 | // settings->AddEnabledDocumentTypes("idmrz.*");
195 | // settings->AddEnabledDocumentTypes("rus.passport.*");
196 | // settings->AddEnabledDocumentTypes("rus.snils.*");
197 | // settings->AddEnabledDocumentTypes("rus.sts.*");
198 | // settings->AddEnabledDocumentTypes("rus.drvlic.*");
199 | ```
200 |
201 | You may also use `RemoveEnabledDocumentTypes(string)` method to remove already enabled document types.
202 |
203 | For convenience it's possible to use **wildcards** (using asterisk symbol) while enabling or disabling document types. When using document types related methods, each passed document type is matched against all supported document types. All matches in supported document types are added to the enabled document types list. For example, document type `rus.passport.internal` can be matched with `rus.*`, `*passport*` and of course a single asterisk `*`.
204 |
205 | In order to get actual enabled document types list after wildcard expression matching you can use:
206 |
207 | ```cpp
208 | const std::vector &enabled_doctypes = settings->GetEnabledDocumentTypes();
209 | ```
210 |
211 | As it was mentioned earlier, you can only enable document types that belong to the same `vector` of supported document types for a single session.
212 | If you do otherwise then informative exception will be thrown on `engine.SpawnSession(...)` call.
213 |
214 | It's always better to enable the minimum number of document types as possible if you know exactly what are you going to recognize because the system will spend less time deciding which document type out of all enabled ones has been presented to it.
215 |
216 | ## Session options
217 |
218 | Some configuration bundle options can be overriden in runtime using `se::smartid::SessionSettings` methods. You can obtain all option names and their values using:
219 |
220 | ```cpp
221 | const std::map &options = settings->GetOptions();
222 | ```
223 |
224 | You can change option values using `settings->SetOption(...)` method:
225 |
226 | ```cpp
227 | settings->SetOption("passport.extractTemplateImages", "true");
228 | settings->SetOption("passport.extractFieldImages", "true");
229 | ```
230 |
231 | Option values are always `std::string` so if you want to pass an integer or boolean it should be converted to string first. It's done like that to avoid unwanted complexity with 'variant' classes and so on.
232 |
233 | #### Common options
234 |
235 | There is a special subset of options which have names in form `common.` which are general session options, for example:
236 |
237 | ```cpp
238 | settings->SetOption("common.sessionTimeout", "5.0"); // timeout in seconds
239 | ```
240 |
241 | ## Result Reporter Callbacks
242 |
243 | Smart IDReader SDK supports optional callbacks during document analysis and recognition process before the `ProcessSnapshot(...)` or similar functions are finished.
244 | It allows the user to be more informed about the underlying recognition process and also helps creating more interactive GUI.
245 |
246 | To support callbacks you need to subclass `ResultReporterInterface` class and implement desirable callback methods:
247 |
248 | ```cpp
249 | class ConcreteResultReporter : public se::smartid::ResultReporterInterface {
250 | public:
251 | virtual void SnapshotRejected() override { }
252 | virtual void DocumentMatched(vector &match_results) override { }
253 | virtual void DocumentSegmented(const vector &segmentation_results) override { }
254 | virtual void SnapshotProcessed(const RecognitionResult &recog_result) override { }
255 | virtual void SessionEnded() override { }
256 | };
257 | ```
258 |
259 | Methods `DocumentMatched(...)` and `DocumentSegmented(...)` are especially useful for displaying document zones and fields bounds in GUI during live video stream recognition.
260 |
261 | We recommend using `override` keyword for C++11 because it greatly helps to avoid typos in function signatures.
262 |
263 | You also need to create an instance of `ConcreteResultReporter` somewhere in the code and pass it when you spawn the session:
264 |
265 | ```cpp
266 | ConcreteResultReporter reporter;
267 |
268 | unique_ptr session(engine.SpawnSession(*settings, &reporter));
269 | ```
270 | **Important!** Your `ResultReporterInterface` subclass instance must not be deleted while `RecognitionSession` is alive. We recommend to place them in the same scope.
271 |
272 | ## Java API
273 |
274 | Smart IDReader SDK has Java API which is automatically generated from C++ interface by SWIG tool.
275 |
276 | Java interface is the same as C++ except minor differences (e.g. STL containers wrappers), please see Java sample.
277 |
278 | There are several drawbacks related to Java memory management that you need to consider.
279 |
280 | #### Object deallocation
281 |
282 | Even though garbage collection is present and works, it's strongly advised to manually call `obj.delete()` functions for our API objects because they are wrappers to the heap-allocated memory and their heap size is unknown to the garbage collector.
283 |
284 | ```java
285 | RecognitionEngine engine = new RecognitionEngine(config_path); // or any other object
286 |
287 | // ...
288 |
289 | engine.delete(); // forces and immediately guarantees wrapped C++ object deallocation
290 | ```
291 |
292 | This is important because from garbage collector's point of view these objects occupy several bytes of Java memory while their actual heap-allocated size may be up to several dozens of megabytes. GC doesn't know that and decides to keep them in memory – several bytes won't hurt, right?
293 |
294 | You don't want such objects to remain in your memory when they are no longer needed so call `obj.delete()` manually.
295 |
296 | #### Result Reporter Interface scope
297 |
298 | When using optional callbacks by subclassing `ResultReportingInterface` please make sure that its instance have the same scope as `RecognitionSession`. The reason for this is that our API does not own the pointer to the reporter instance which cause premature garbage collection resulting in crash:
299 |
300 | ```java
301 | // BAD: may cause premature garbage collection of reporter instance
302 | class MyDocumentRecognizer {
303 | private RecognitionEngine engine;
304 | private RecognitionSession session;
305 |
306 | private void InitializeSmartIdReader() {
307 | // ...
308 | session = engine.SpawnSession(settings, new MyResultReporter());
309 |
310 | // reporter might be garbage collected there because session doesn't own it
311 | }
312 | }
313 | ```
314 |
315 | ```java
316 | // GOOD: reporter have at least the scope of recognition session
317 | class MyDocumentRecognizer {
318 | private RecognitionEngine engine;
319 | private RecognitionSession session;
320 | private MyResultReporter reporter; // reporter has session's scope
321 |
322 | private void InitializeSmartIdReader() {
323 | // ...
324 | reporter = new MyResultReporter();
325 | session = engine.SpawnSession(settings, reporter);
326 | }
327 | }
328 | ```
329 |
330 | ## C API
331 |
332 | Smart IDReader SDK has C API for use cases where C++ is not possible. Please see C sample for details.
333 |
334 | #### C memory management
335 |
336 | Since there are no constructors and destructors in C we use Create/Destroy pairs of functions which are used to allocate and deallocate objects.
337 |
338 | #### Return codes and exception messages
339 |
340 | C does not have exceptions so every function returns `int` which must be `0` when function call was successful. Also, there is `CSmartIdErrorMessage *` passed to every function with error buffer containing propagated exception message if any exception has been thrown.
341 |
--------------------------------------------------------------------------------
/doc/smartIdEngine.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartEngines/SmartIDReader-Android-SDK/f423e039a9b0358721fea1b8a36961bee7a6a727/doc/smartIdEngine.pdf
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------