├── 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 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 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 |